
java对象在内存中的布局:

对象头:
对象头中包含了MarkWord和类型指针,如果是数组对象,还会存在数组长度。

mark word是8字节,klassword压缩前8字节,压缩后4字节,一般都是取压缩后,实例数据如果是对象,或是引用,就是8个字节,最后还要加上对齐,必须是8的倍数。

Markword:
主要就是三个信息:锁信息(markword的最后两位决定上的是什么锁),hashcode,GC信息。
32位的对象头内MarkWord在默认情况下存储着对象的HashCode、分代年龄、是否偏向锁、锁标记位等信息
64位JVM中对象头内MarkWord的默认信息存储着HashCode、分代年龄、是否偏向锁、锁标记位、unused
MarkWord被设计成为一个非固定的数据结构,以便可以复用方便存储更多有效的数据,它会根据对象本身的状态复用自己的存储空间,除了上述列出的MarkWord默认存储结构外,还有如下可能变化的结构:


实例数据:
实例数据是指一个聚合量所有标量的总和,也就是是指当前对象属性成员数据以及父类属性成员数据。


对齐填充:

指针压缩:
压缩的是引用类型,比如object类型,原来是8字节,压缩后变成4字节,然后数组长度也变成4字节了。··

为什么32bit的指针支持寻址32GB?


JOL对象大小计算:

面试题:在java中创建一个Object对象会占用多少内存?
对象头大小markword+klassword(8)=16bytes,Object类中没有定义任何属性,不存在实例数据。
如果开启指针压缩,只会有markword+klassword/2(4)=12bytes,类元指针被压缩一半.
会出现4bytes的对齐填充,最终不管是否开启了指针压缩,大小应该为16字节。
数组对象大小计算:


实例对象大小计算:

Java对象分配过程详解:

类加载检测:

全限定名为key,查找.class文件。确保当前要分配的对象所在的类已经加载完成了。
内存分配:

栈上分配:
只有不会逃逸的对象才能在栈上分配,栈上分配的对象可以随着栈帧的销毁而自动回收,不需要GC回收。
如果一个对象在方法内部创建并且没有被外部引用,JVM 可能会选择在栈上分配该对象,以减少堆的压力。

基于逃逸分析的优化:




标量替换:


TLAB分配:

如果TLAB分配成功,初始化操作就会提前到内存分配这一步。


分配过程:



年老代分配:

新生代分配:

小结:

初始化内存:

设置对象头:

执行<init>函数:

一个对象从生到死的历程:
对象最终会出现在Eden区(TLAB分配也是在Eden区,栈上分配不算),而线程栈中会出现一个指向对象的引用,之后需要使用该对象时,直接通过引用中的直接地址或句柄访问该块内存区域中的对象数据。
对象的访问方式:
在Java中对象都是通过reference访问的,主要分为两种访问方式,一种为句柄访问,另一种则为直接指针访问。
句柄访问:

直接指针访问:

小结:

GC时的对象移动与对象晋升:

动态对象年龄判定:

空间分配担保机制:

小结:

对象引用类型:

强引用类型:

GC是不会回收强引用对象的,要想回收只能显式的将对象引用清空,obj=null。
软引用类型:

弱引用类型:


student是个强引用,而id=1(student)这个对象与zhuZiWR弱引用对象存在联系,所以在GC时,是可以通过student指针,判定出zhuZiWR弱对象属于存活对象!因此,GC时不会回收它!
虚引用类型:

