零、问题
Object object = new Object()谈谈你对这句话的理解? 一般而言JDK8按照默认情况下,new一个对象占多少内存空间? 位置所在:JVM里堆→新生区→伊甸园区
一、对象的内存布局
在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据、对齐填充(保证8个字节的倍数)
二、对象头
- 对象标记
- 类元信息(类型指针):指向该对象元数据的首地址
在64位系统中,Mark Word占了8个字节,类型指针占了8个字节,一共是16个字节
1、对象标记
默认存储对象的HashCode、分代年龄和锁标志位等信息。 这些信息都是与对象自身定义无关的数据,所以MarkWord被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。 它会根据对象的状态复用自己的存储空间,也就是说在运行期间MarkWord里存储的数据会随着锁标志位的变化而变化。
package com.lori.juc2023.juc9;
public class ThreadLocalDemo2 {
public static void main(String[] args) {
Object o = new Object();//new 一个对象,内存占多少
System.out.println(o.hashCode());//这个hashcode记录在什么地方?
synchronized (o){ //某个对象怎么知道自己被加锁了多少次
}
System.gc();//手动gc,超过15次可以从新生代到老年区,这个数据在哪里存储
}
}
2、类元信息(类型指针)
new对象出来的多个实例对象均来自一个模板,类元信息就是指向模板的指针
对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
三、实例数据
存放类的属性(Field)数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
四、对齐填充
虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。这部分内存按8字节补充对齐。
class ConSumer{ //只有一个对象头,有16个字节,其中对象标记占8字节,类元信息占8字节,
// 我们又有两个属性,id int类型 4字节,flag Boolean类型,1个字节,16+4+1=21字节,虚拟机要求结果是8的整数倍字节,此时,需要对其填充为24字节
boolean flag;
int id;
}
五、官网地址
http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html
底层源码理论证明:http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/89fb452b3688/src/share/vm/oops/oop.hpp
六、Object obj = new Object()
1、使用JOL
展示java虚拟机内部布局
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
package com.lori.juc2023.juc9;
import org.openjdk.jol.vm.VM;
public class ThreadLocalDemo2 {
public static void main(String[] args) {
//当前java虚拟机的详细情况
System.out.println(VM.current().details());
//所有对象分配的字节都是8的整数倍
System.out.println(VM.current().objectAlignment());
}
}
new 一个Object的信息:
public class ThreadLocalDemo2 {
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Process finished with exit code 0
- 模板中没有设置属性时:
package com.lori.juc2023.juc9;
import org.openjdk.jol.info.ClassLayout;
public class ThreadLocalDemo2 {
public static void main(String[] args) {
Object o = new Object();
// System.out.println(ClassLayout.parseInstance(o).toPrintable());
ConSumer conSumer = new ConSumer();
System.out.println(ClassLayout.parseInstance(conSumer).toPrintable());
}
}
class ConSumer{ //只有一个对象头,有16个字节,其中对象标记占8字节,类元信息占8字节,
// 我们又有两个属性,id int类型 4字节,flag Boolean类型,1个字节,16+4+1=21字节,虚拟机要求结果是8的整数倍字节,此时,需要对其填充为24字节
/* boolean flag;
int id;*/
}
com.lori.juc2023.juc9.ConSumer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Process finished with exit code 0
- 模板中设置属性
package com.lori.juc2023.juc9;
import org.openjdk.jol.info.ClassLayout;
public class ThreadLocalDemo2 {
public static void main(String[] args) {
Object o = new Object();
// System.out.println(ClassLayout.parseInstance(o).toPrintable());
ConSumer conSumer = new ConSumer();
System.out.println(ClassLayout.parseInstance(conSumer).toPrintable());
}
}
class ConSumer{ //只有一个对象头,有16个字节,其中对象标记占8字节,类元信息占8字节,
// 我们又有两个属性,id int类型 4字节,flag Boolean类型,1个字节,16+4+1=21字节,虚拟机要求结果是8的整数倍字节,此时,需要对其填充为24字节
boolean flag;
int id;
}
com.lori.juc2023.juc9.ConSumer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int ConSumer.id 0
16 1 boolean ConSumer.flag false
17 7 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 7 bytes external = 7 bytes total
Process finished with exit code 0
2、GC年龄
GC年龄采用4位bit存储,最大为15, 例如MaxTenuringThreshold参数默认值就是15
64位Java虚拟机中,对象分代年龄占4位,4位最大1111转换成十进制是15,所以最大是15
七、压缩指针
-XX:+PrintCommandLineFlags :java虚拟机启动时,打印出使用的参数
默认开启了压缩指针,所以我们看到的上面的类元信息占了4个字节。 默认12+4:4是对齐填充
手动关闭压缩;-XX:-UseCompressedClassPointers