目录
java对象内存布局 - JOL(Java Object Layout)
Java内存结构
堆内存
堆内存是线程共享的,存放对象信息,GC管理的内存区域;
堆是运行时数据区域,Java代码可触及的内存;
非堆就是JVM使用的内存;
虚拟机栈(栈内存)
线程栈是线程私有的,线程栈中,每一个方法调用都会生成一个栈帧,栈帧中包含操作数栈,
局部变量数组,类引用:
1,操作数栈中存放的是算术运算指令直接操作的数据;
2,局部变量数组中依次存放this指针(非静态方法),所传入的参数,字节码中的局部变
量。long类型以及double类型的值占两个单元,其余类型占一个单元。
3,类引用指向当前方法在运行时常量池中对应的class。
方法区
方法区是线程共享的,存放已被加载的类信息、常量、静态变量;
在JDK8之前的HotSpot虚拟机上,是用永久代来实现方法区的;永久代和堆使用的物理内存
是连续的,虽然逻辑上是隔离的,但是永久代和老年代是一起做垃圾回收的,只要有一边满了,就
会触发永久代和老年代的垃圾收集。
JDK8和之后的方法区被放到了非堆的元数据区,不会触发垃圾回收。
程序计数器
记录线程中指令执行的位置,方便线程切换时,能找到之前的执行位置。每个线程都有各自
独立的计数器。
本地方法栈
C语言实现的本地方法的方法栈。
Class指针压缩空间
堆内的对象都有一个对象头,对象头包含了一个指针,指针指向方法区中对象对应的类;在
64位的机器上,上述指针是8个字节(64位);为了节省空间,上述指针在开启压缩的情况下,会
被压缩成4字节(32位),压缩后的指针寻址空间是4G;被寻址的空间需要配合压缩之后的指针;
为了配合压缩后的指针,元数据区分为:
1,non-class part:包含其他的所有 metadata;
2,class part:存放class信息,需要一个连续的不超过 4G 的内存;class part就是压缩类空
间(Compressed Class Space)。
JIT热点代码缓存区
Java虚拟机需要将字节码翻译成机器码,然后运行。
翻译过程有两种形式:
第一种是解释执行,即逐条将字节码翻译成机器码并执行;
第二种是即时编译(JIT:Just-In-Time compilation),先将一个方法中包含的所有字节码编
译成机器码,然后再执行;
解释执行的优势在于无需等待编译,即时编译的优势在于实际运行速度更快。
JVM默认采用混合模式,综合了解释执行和即时编译两者的优点。
大部分的不常用的代码,无需耗费时间将其编译成机器码,采取解释执行的方式运行;
对于小部分的热点代码,将其编译成机器码,以达到理想的运行速度。
JIT热点代码缓存区,存放的就是编译后的热点代码;
Java对象内存布局 - JOL(Java Object Layout)
对象头
Java对象都有一个对象头,对象头包含由标记字段和类型指针。
标记字段:存储Java虚拟机有关该对象的运行数据,如哈希码、GC 信息以及锁信息;占64
位,8字节(64位系统);
类型指针:指向方法区中该对象的类;占64位,8字节(64位系统);
压缩指针
为了节省空间,对象头中的类型指针在开启压缩的情况下,会被压缩成4字节(32位);
压缩指针不仅可以作用于对象头的类型指针,还可以作用于引用类型的字段,以及引用类型
数组。
对象对齐
为了方便压缩后的指针寻址,JVM堆中对象的起始地址需要对齐至8的倍数。
压缩后的指针是4字节32位,寻址空间是2的32次方,即为4G;但是JVM堆中的对象寻址对齐
至8的倍数,这样就可以模拟出32G(4G*8)的地址空间,超过32GB则会关闭压缩指针;
即使关闭了压缩指针,Java虚拟机还是会进行内存对齐。
对象内字段对齐
对象中的字段也会进行内存对齐。
例子:Java 虚拟机要求long字段、double字段,以及非压缩指针状态下的引用字段地址为8的
倍数。
字段内存对齐的原因之一,是让字段只出现在同一 CPU 的缓存行中。
CPU缓存行是 CPU Cache 中的最小单位,一个缓存行的大小通常是 64 字节(取决CPU),
CPU Cache 由若干缓存行组成。