方法区(实现方式:永久代|元空间)
首先明确:只有Hotspot才有方法区的概念。其他虚拟机是不存在这一概念的。
方法区是规范,是逻辑概念,原则上如何实现方法区属于虚拟机实现细节,不受《Java虚拟机规范》管束,并不要求统一。
所以永久代和元空间都是HotSpot实现方法区这一概念的方式。
- Java8之前的版本,用
永久代
实现方法区; - Java8之后的版本,用
元空间
实现方法区;
元空间与永久代最大的区别在于:
永久代在堆内存中,元空间在堆外
JVM在1.8时用元空间代替永久代的原因
随着Web领域的发展,Java程序变得越来越大,需要加载的内容也越来越多,永久代是放在堆内存中的,占用的是堆内存空间,所以容易OOM
而元空间放在本地内存中,使用元空间之后,就可以将数据直接存储在本地内存当中,减少了OOM的可能性。
方法区内部结构
方法区主要是用来存放已被虚拟机加载的「类相关信息」:包括类信息、常量池
- 类信息又包括了类的版本、字段、方法、接口和父类等信息。
- 常量池又可以分「静态常量池」和「运行时常量池」
- 静态常量池主要存储的是「字面量」以及「符号引用」等信息,静态常量池也包括了我们说的「字符串常量池」。
- 「运行时常量池」存储的是「类加载」时生成的「直接引用」等信息。
从「逻辑分区」的角度而言「常量池」是属于「方法区」的
但从「物理分区」来说,是属于堆的。因为从「JDK7」以后,就已经把「运行时常量池」和「静态常量池」转移到了「堆」内存中进行存储
下面详解类信息:
类型信息
对每个加载的类型(类class、接口interface、枚举enum、注解annotation)
JVm必须在方法区
中存储以下类型信息
:
- 这个类型的完整有效名称(包名.类名)
- 这个类型直接父类的完整有效名
- 这个类型的修饰符(public,abstract,final的某个子集)
- 这个类型直接接口的一个有序列表
属性信息
包括:
属性名称
域类型(8种基本类型,引用类型等)
域修饰符(public,private,protected,static,final,volatile)
方法信息
JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序:
- 方法名称
- 方法的返回类型(或void)
- 方法参数的数量和类型(按顺序)
- 方法的修饰符(public,private,protected,static,final,synchronized,native,abstract的一个子集)
- 方法的字节码(bytecodes)、操作数栈、局部变量表及大小
- 异常表
包括:每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引
方法区的演进细节
Hotspot中方法区的变化:
JDK6的时候
JDK7的时候
JDK8的时候,元空间大小只受物理内存影响