jvm-java内存区域
jvm内存模型
运行时数据区域
- 程序计数器
它是一块较小的内存空间,它可以看作是当前线程所执行的字节码行号指示器。(线程私有的)
问题:使用PC寄存器存储字节码指令地址有什么用?为什么使用PC寄存器记录当前线程的执行地址?
因为CPU需要不断切换各个线程,我们需要知道切换回来后从哪里开始
JVM的字节码解释器需要通过改变PC寄存器的值来明确吓一跳应该执行什么字节码指令。
-
java虚拟机栈
线程私有
虚拟机栈是描述java方法执行的线程内存模型,每一个方法被执行的时候,java虚拟机都会创建一个栈帧用于存放局部变量表 ,操作数栈,动态链接,方法出口。每一个方法被调用到执行完毕的过程,就对应着一个栈帧进栈到出栈的过程。 -
本地方法栈
作用和java虚拟机栈类似,只是服务于native方法 -
java堆
线程共享 存放对象实例的。jvm规范上对java堆的描述是:“所有的对象实例以及数组都应当在堆上分配”但是现在已经不是那么绝对了。 -
方法区
方法区与java堆一样,是线程共享它用于存储已被虚拟机加载的类型信息,常量,静态变量,也就是编译器编译后的代码缓存等数据。
jdk8 之前,方法区的实现是永久代。这种实现会使java应用更容易OOM,因为有-XX:MaxPermSize 上限。
jdk8之后,改为本地内存来实现,使用元空间。 -
运行时常量池
运行时常量池是方法区的一部分。Class文件中除了有类的版本,字段,方法,接口等描述外,还有常量池。用于存放编译器生成的各种字面量与符号引用(连接后 会转为直接引用),后面这部分加载后放到方法区的运行时常量池。
HotSpot虚拟机对象
- 对象的创建
- 遇到字节码new指令时,先去检查参数是否能在常量池中定位到一个类的符号引用,并且检查引用类是否被加载,解析,初始化。
- 在对象已经被初始化之后,取到对象长度,先询问TLAB能否分配,如果不可以就在Eden中分配,(使用CAS 分配空间)
- 虚拟机对对象进行必要的设置,类信息。对象哈希码,对象GC分代年龄等等。
- java编译器在new 的时候会接着执行()方法,进行初始化。
- 对象的内存布局
对象在堆内存中的布局可以分为三个部分:对象头,实例数据和对
齐填充。- 对象头
1. 用于储存对象自身运行时的诗句,例如哈希码,GC分代年龄,锁状态标志,线程持有锁,偏向线程id,偏向时间戳。
2. 类型指针,也就是指向它的类型元数据的指针。 - 实例数据
程序里定义的各种类型的字段内容。 - 对齐填充
占位符的作用
- 对象头
- 对象的访问定位
- 第一种:句柄访问
java堆中将会划分出一块内存作为句柄池,reference中存储的就是句柄地址,句柄中包含对象实例数据的指针和对象类型数据的指针
优点:对象被移动的时候只需要改变句柄实例数据指针,reference不需要修改。 - 第二种:直接指针访问
reference中储存的就是对象实例数据地址,对象实例中就需要指向对象类型数据的指针(HotSpot 默认这种)
优点:速度快。
- 第一种:句柄访问