您的位置:首页 > 其它

方法区

2016-04-02 14:31 253 查看
       在JVM实例中,对装载的类型信息是存储在一个逻辑方法内存区中,当Java虚拟机加载了一个类的时候,它会跟着这个Class的类型去路径里面查找对应的class文件,类加载器读取这个class文件(线性二进制数据),然后将该文件传递给Java虚拟机,JVM从二进制数据中提取信息并且将这些信息存储在方法区,而类中声明(静态)变量就是来自于方法区中存储的信息。

       JVM虚拟机将搜索和使用类型的一些信息也存储在方法区中以方便应用程序加载读取该数据。设计者在设计过程也考虑到要方便JVM进行Java应用程序的快速执行,而这种取舍主要是为了程序在运行过程中内存不足的情况能够通过一定的取舍去弥补内存不足的情况。

       在JVM内部,所有的线程共享相同的方法区,因此,访问方法区的数据结构必须是线程安全的,如果两个线程都试图去调用去找一个名为Lava的类,比如Lava还没有被加载,只有一个线程可以加载该类而另外的线程只能够等待。

       方法区的大小在分配过程中是不固定的,随着Java应用程序的运行,JVM可以调整其大小,需要注意一点,方法区的内存不需要是连续的,因为方法区内存可以分配在内存堆中,即使是虚拟机JVM实例对象自己所在的内存堆也是可行的,而在实现过程是允许程序员自身来指定方法区的初始化大小的。

  同样的,因为Java本身的自动内存管理,方法区也会被垃圾回收的,Java程序可以通过类扩展动态加载器对象,类可以成为“未引用”向垃圾回收器进行申请,如果一个类是“未引用”的,则该类就可能被卸载。

方法区针对具体的语言特性有几种信息是存储在方法区内的:
1.类型信息

       • 类型的完全限定名(java.lang.String格式)

       • 类型的完全限定名的直接父类的完全限定名(除非这个父类的类型是一个接口或者java.lang.Object)

       • 不论类型是一个类或者接口

       • 类型的修饰符(例如public、abstract、final)

       • 任何一个直接超类接口的完全限定名的列表

       在JVM和类文件名的内部,类型名一般都是完全限定名(java.lang.String)格式,在Java源文件里面,完全限定名必须加入包前缀,而不是在开发过程写的简单类名,而在方法上,只要是符合Java语言规范的类的完全限定名都可以,而JVM可能直接进行解析,比如:(java.lang.String)在JVM内部名称为java/lang/String,这就是在异常捕捉的时候经常看到的ClassNotFoundException的异常里面类信息的名称格式。
2.常量池

      针对类型加载的类型信息,JVM将这些存储在常量池里,每个类型都对应一个常量池,常量池是一个根据类型定义的常量的有序常量集,包括字面常量值(String、Integer、 Float常量等)以及符号引用(类和接口的全限定名、字段名称和描述符、方法名称和描述符),类和接口的全限定名指的是当前类的全限定名。

      字段名称指的是类或接口的实例变量或类变量,字段的描述符是一个指示字段的类型的字符串。如在一个类中有以下形式的声明:A a = null, 则a为字段名,A为字段描述符。

      方法的描述符也是个字符串,该字符串指示了方法的返回值和参数的数量、顺序和类型。

      在运行时,JVM从常量池中获得符号引用,然后在运行时解析成引用项的实际地址,最后通过常量池中的全限定名、方法和字段描述符,把当前类或接口中的代码与其它类或接口中的代码联系起来。

       整个常量池会被JVM的一个索引引用,如同数组里面的元素集合按照索引访问一样,JVM针对这些常量池里面存储的信息也是按照索引方式进行。实际上常量池在Java程序的动态链接过程起到了一个至关重要的作用。
3.字段信息

针对字段的类型信息,下边的信息是存储在方法区里面的:

       • 字段名

       • 字段类型

       • 字段修饰符(public,private,protected,static,final,volatile,transient)
4.方法信息

针对方法信息,下边信息存储在方法区上:

       • 方法名

       • 方法的返回类型(包括void)

       • 方法参数的类型、数目以及顺序

       • 方法修饰符(public,private,protected,static,final,synchronized,native,abstract)

针对非本地方法,还有些附加方法信息需要存储在方法区内:

       • 方法字节码

       • 方法中局部变量区的大小、方法栈帧

       • 异常表
5.类变量信息(静态变量)

       类变量在一个类的多个实例之间共享,这些变量直接和类相关,而不是和类的实例相关,(定义过程简单理解为类里面定义的static类型的变量),针对类变量,其逻辑部分就是存储在方法区内的。在JVM使用这些类之前,JVM先要在方法区里面为定义的non-final变量分配内存空间;常量(定义为final)则在JVM内部则不是以同样的方式来进行存储的,尽管针对常量而言,一个final的类变量是拥有它自己的常量池,作为常量池里面的存储某部分,类常量是存储在方法区内的,而其逻辑部分则不是按照上边的类变量的方式来进行内存分配的。虽然non-final类变量是作为这些类型声明中存储数据的某一部分,final变量存储为任何使用它类型的一部分的数据格式进行简单存储。
6.ClassLoader引用

       对于每种类型的加载,JVM必须检测其类型是否符合了JVM的语言规范,对于通过类加载器加载的对象类型,JVM必须存储对类的引用,而这些针对类加载器的引用是作为了方法区里面的类型数据部分进行存储的。
7.类Class的引用

       JVM在加载了任何一个类型过后会创建一个java.lang.Class的实例,虚拟机必须通过一定的途径来引用该类型对应的一个Class的实例,并且将其存储在方法区内
8.方法表

       为了提高访问效率,必须仔细的设计存储在方法区中的数据信息结构。除了以上讨论的结构,jvm的实现者还添加一些其他的数据结构,如方法表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: