上一篇:JVM笔记(4)—— 运行时数据区——堆空间
一、栈、堆、方法区的交互关系
进程运行过程中,在方法中创建对象时通过方法区中的类型信息在堆中创建对应的对象,对象中又存有指向方法区中对应类型信息的指针,并将对象的地址引用存入虚拟机栈对应方法栈帧的局部变量表中。
二、方法区的理解
方法区是所有线程共享的区域,主要用于存储类加载系统加载进来的每个类的类信息,JIT即时编译器编译后的代码缓存等
jdk7及以前,方法区的实现称为永久代(PermGen space),8及以后则称为元空间(Metaspace)。
元空间与永久代的最大区别在于:
元空间不在虚拟机设置的内存中,而是直接使用本地内存
\color{red}{元空间不在虚拟机设置的内存中,而是直接使用本地内存}
元空间不在虚拟机设置的内存中,而是直接使用本地内存。因此更不容易出现OOM
三、设置方法区大小与OOM
四、方法区的内部结构
方法区主要存储类加载系统加载进来的每个类的类信息等,而方法区中一个类的类信息又包括类型信息、域信息、方法信息、运行时常量池四个部分。
1. 类信息
类型信息
域信息
也称为类的属性信息,类的域包括实例变量、类变量、类常量,域信息中要存储每个域的域名称、域类型、域修饰符,并且要存储这些域定义的顺序。
注意:jdk1.7及以后字符串常量池和类的静态变量被放到了堆中
方法信息
运行时常量池
在一个类的代码中有各种字面量(如字符串、数值等)或者其他类和方法,这些很长的字面量或者符号不可能直接写在字节码指令中,因此编译后在字节码文件中是用一个常量池来存储的,这样可以对符号重复利用,在字节码指令中直接用常量池索引来表示即可(例如字节码指令invokevirtual #3,表示执行索引#3存储的符号引用指向的方法)。常量池中包括各种字面量和对类型、域和方法的符号引用。
类的字节码文件加载到方法区后常量池即被转化为运行时常量池,其中对类型、域和方法的符号引用会转换为进程中其真实的地址。
五、方法区的演进细节
版本 | 演进 |
---|---|
jdk6 | 方法区实现为永久代,静态变量和字符串常量池StringTable存储在方法区中 |
jdk7 | 方法区实现为永久代,静态变量和字符串常量池转移到了堆中 |
jdk8 | 方法区实现为元空间,并且方法区从虚拟机内存转移到了本地内存 |
注意这里指的是静态变量的引用,其所指向的对象始终都是在堆中
StringTable为什么要调整位置?
前置知识:
- 什么是字符串常量池?
- 深入浅出java常量池和基本数据类型对象池
六、方法区的垃圾收集
方法区垃圾收集主要回收两部分内容:常量池中不被引用的常量,不再被使用的类