美文网首页
JVM 系列 - 内存区域 - 方法区(六)

JVM 系列 - 内存区域 - 方法区(六)

作者: _晓__ | 来源:发表于2018-12-19 16:46 被阅读0次

    特点

    • 方法区(Method Area)与 Java 堆一样,是所有线程共享的内存区域。
    • JDK7 之前(永久代)用于存储已被虚拟机加载的类信息、常量、字符串常量、类静态变量、即时编译器编译后的代码等数据。
    • Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
    • 运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本/字段/方法/接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将类在加载后进入方法区的运行时常量池中存放。运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的是 String.intern() 方法。受方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

    Java 中基本类型的包装类的大部分都实现了常量池技术,这些类是 Byte、Short、Integer、Long、Character、Boolean,另外 Float 和 Double 类型的包装类则没有实现。另外 Byte、Short、Integer、Long、Character 这5种整型的包装类也只是在对应值在-128到127之间时才可使用对象池。

    运行时常量池
    • 实现区域
      永久代:存储包括类信息、常量、字符串常量、类静态变量、即时编译器编译后的代码等数据。可以通过 -XX:PermSize-XX:MaxPermSize 来进行调节。当内存不足时,会导致 OutOfMemoryError 异常。JDK8 彻底将永久代移除出 HotSpot JVM,将其原有的数据迁移至 Java Heap 或 Native Heap(Metaspace),取代它的是另一个内存区域被称为元空间(Metaspace)。
      元空间(Metaspace):元空间是方法区的在 HotSpot JVM 中的实现,方法区主要用于存储类信息、常量池、方法数据、方法代码、符号引用等。元空间的本质和永久代类似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。,理论上取决于32位/64位系统内存大小,可以通过 -XX:MetaspaceSize-XX:MaxMetaspaceSize 配置内存大小。

    随JDK版本变迁的方法区

    JDK6

    • Klass 元数据信息
    • 每个类的运行时常量池(字段、方法、类、接口等符号引用)、编译后的代码
    • 静态字段(无论是否有final)在 instanceKlass 末尾(位于 PermGen 内)
    • oop(Ordinary Object Pointer(普通对象指针)) 其实就是 Class 对象实例
    • 全局字符串常量池 StringTable,本质上就是个 Hashtable
    • 符号引用(类型指针是 SymbolKlass)

    JDK7

    • Klass 元数据信息
    • 每个类的运行时常量池(字段、方法、类、接口等符号引用)、编译后的代码
    • 静态字段从 instanceKlass 末尾移动到了 java.lang.Class 对象(oop)的末尾(位于 Java Heap 内)
    • oop 与全局字符串常量池移到 Java Heap 上
    • 符号引用被移动到 Native Heap 中

    JDK8

    • 移除永久代
    • Klass 元数据信息
    • 每个类的运行时常量池、编译后的代码移到了另一块与堆不相连的本地内存 -- 元空间(Metaspace)

    关于 Open JDK 移除永久代的相关信息:http://openjdk.java.net/jeps/122

    类信息

    方法区 详细信息
    类型信息 1. 类型的全限定名 2. 超类的全限定名 3. 直接超接口的全限定名 4. 类型标志(该类是类类型还是接口类型) 5. 类的访问描述符(public、private、default、abstract、final、static)
    类型的常量池 存放该类型所用到的常量的有序集合,包括直接常量(如字符串、整数、浮点数的常量)和对其他类型、字段、方法的符号引用。常量池中每一个保存的常量都有一个索引,就像数组中的字段一样。因为常量池中保存中所有类型使用到的类型、字段、方法的符号引用,所以它也是动态连接(栈中对应的方法指向这个引用)的主要对象(在动态链接中起到核心作用)。
    字段信息 1. 字段修饰符(public、protect、private、default) 2. 字段的类型 3. 字段名称
    方法信息 1. 方法名 2.方法的返回类型(包括void)3. 方法参数的类型、数目以及顺序 4. 方法修饰符(public、private、protected、static、final、synchronized、native、abstract) 5. 针对非本地方法,还有些附加方法信息需要存储在方法区中(局部变量表大小和操作数栈大小、方法体字节码、异常表)
    类变量(静态变量) 指该类所有对象共享的变量,即使没有创建该对象实例,也可以访问的类变量。它们与类进行绑定
    指向类加载器的引用 JVM 必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么 JVM 会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。JVM 在动态链接的时候需要这个信息。当解析一个类型到另一个类型的引用的时候,JVM 需要保证这两个类型的类加载器是相同的。这对 JVM 区分名字空间的方式是至关重要的。
    指向 Class 实例的引用 JVM 为每个加载的类和接口都创建一个 java.lang.Class 实例(JDK6 存储在方法区,JDK6 之后存储在 Java 堆),这个对象存储了所有这个字节码内存块的相关信息,如平时使用的 this.getClass().getName() this.getClass().getDeclaredMethods() this.getClass().getDeclaredFields(),可以获取类的各种信息,都是通过这个 Class 引用获取。
    方法表 为了提高访问效率,必须仔细的设计存储在方法区中的数据信息结构。除了以上讨论的结构,JVM 的实现者还可以添加一些其他的数据结构,如方法表。JVM 对每个加载的非虚拟类的类型信息中都添加了一个方法表,方法表是一组对类实例方法的直接引用(包括从父类继承的方法)。JVM 可以通过方法表快速激活实例方法。(这里的方法表与 C++ 中的虚拟函数表一样。正像 Java 宣称没有指针了,其实 Java 里全是指针。更安全只是加了更完备的检查机制,但这都是以牺牲效率为代价的,Java 的设计者始终是把安全放在效率之上的,所有 Java 才更适合于网络开发)

    疑问?

    Class 对象存储在哪个区域?

    相关文章

      网友评论

          本文标题:JVM 系列 - 内存区域 - 方法区(六)

          本文链接:https://www.haomeiwen.com/subject/ijfthqtx.html