一篇关于java方法区详解

方法区 保存在着被加载过的每一个类的信息;这些信息由类加载器在加载类的时候,从类的源文件中抽取出来;static变量信息也保存在方法区中; 可以看做是将类(Cl

方法区

保存在着被加载过的每一个类的信息;这些信息由类加载器在加载类的时候,从类的源文件中抽取出来;static变量信息也保存在方法区中;

可以看做是将类(Class)的元数据,保存在方法区里;

方法区是线程共享的;当有多个线程都用到一个类的时候,而这个类还未被加载,则应该只有一个线程去加载类,让其他线程等待;

方法区的大小不必是固定的,jvm可以根据应用的需要动态调整。jvm也可以允许用户和程序指定方法区的初始大小,最小和最大限制;

方法区同样存在垃圾收集,因为通过用户定义的类加载器可以动态扩展Java程序,这样可能会导致一些类,不再被使用,变为垃圾。这时候需要进行垃圾清理。 

图例(方法区中都保存什么)

类型信息

包括以下几点:

类的完整名称(比如,java.long.String)类的直接父类的完整名称类的直接实现接口的有序列表(因为一个类直接实现的接口可能不止一个,因此放到一个有序表中)类的修饰符可以看做是,对一个类进行登记,这个类的名字叫啥,他粑粑是谁、有没有实现接口, 权限是啥;

类型的常量池 (即运行时常量池)

每一个Class文件中,都维护着一个常量池(这个保存在类文件里面,不要与方法区的运行时常量池搞混),里面存放着编译时期生成的各种字面值和符号引用;这个常量池的内容,在类加载的时候,被复制到方法区的运行时常量池 ;

字面值:就是像string, 基本数据类型,以及它们的包装类的值,以及final修饰的变量,简单说就是在编译期间,就可以确定下来的值;

符号引用:不同于我们常说的引用,它们是对类型,域和方法的引用,类似于面向过程语言使用的前期绑定,对方法调用产生的引用;

存在这里面的数据,类似于保存在数组中,外部根据索引来获得它们 ; 

字段信息

  • 声明的顺序
  • 修饰符
  • 类型
  • 名字

方法信息

  • 声明的顺序
  • 修饰符
  • 返回值类型
  • 名字
  • 参数列表(有序保存)
  • 异常表(方法抛出的异常)
  • 方法字节码(native、abstract方法除外,)
  • 操作数栈和局部变量表大小

类变量(即static变量)

非final类变量

  • 在java虚拟机使用一个类之前,它必须在方法区中为每个非final类变量分配空间。非final类变量存储在定义它的类中;

final类变量(不存储在这里)

  • 由于final的不可改变性,因此,final类变量的值在编译期间,就被确定了,因此被保存在类的常量池里面,然后在加载类的时候,复制进方法区的运行时常量池里面 ;final类变量存储在运行时常量池里面,每一个使用它的类保存着一个对其的引用; 

对类加载器的引用

jvm必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。

如果一个类型是由用户类加载器加载的,那么jvm会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。

对Class类的引用

jvm为每个加载的类都创建一个java.lang.Class的实例(存储在堆上)。

而jvm