java 对象方法 内存 共享_JVM-Java对象的内存布局

新建Java对象

新建对象方式new语句

反射机制

Object.clone方法

反序列化

Unsafe.allocateInstance方法

其中,Object.clone方法和反序列化通过直接复制已有的数据,来初始化新建对象的实例字段。 Unsafe.allocateInstance方法则没有初始化实例字段,而new语句和反射机制,则是通过调用构造器来初始化实例字段。

new语句

以new语句为例,它编译而成的字节码将包含用来请求内存的new指令,以及用来调用构造器的invokespecial指令。

当我们调用一个构造器时,它将优先调用父类的构造器,直至Object类。 这些构造器的调用者皆为同一对象,也就是通过new指令新建而来的对象。

通过new指令新建出来的对象,它的内存其实涵盖了所有父类中的实例字段。 也就是说,虽然子类无法访问父类的私有实例字段,或者子类的实例字段隐藏了父类的同名实例字段,但是子类的实例还是会为这些父类实例字段分配内存的。

构造器

子类的构造器需要调用父类的构造器。

如果父类存在无参数构造器的话,该调用可以是隐式的,也就是说Java编译器会自动添加对父类构造器的调用。 但是,如果父类没有无参数构造器,那么子类的构造器则需要显式地调用父类带参数的构造器。

显式调用构造器

显式调用分为两种, 一是直接使用“super”关键字调用父类构造器, 二是使用“this”关键字调用同一个类中的其他构造器。

无论是直接的显式调用,还是间接的显式调用,都需要作为构造器的第一条语句,以便优先初始化继承而来的父类字段。

压缩指针

为了尽量减少对象的内存使用量,64位Java虚拟机引入了压缩指针的概念对应虚拟机选项 -XX:+UseCompressedOops,默认开启),将堆中原本64位的Java对象指针压缩成32位的。

对象头(object header)

在Java虚拟机中,每个Java对象都有一个对象头(object header),这个由标记字段和类型指针所构成。

其中,标记字段用以存储Java虚拟机有关该对象的运行数据,如哈希码、GC信息以及锁信息,而类型指针则指向该对象的类。

引入基本类型

在64位的Java虚拟机中,对象头的标记字段占64位,类型指针的标记字段占64位。 也就是说,每一个Java对象在内存中的额外开销就是16个字节。

以Integer类为例,它仅有一个int类型的私有字段,占4个字节。 因此,每一个Integer对象的额外内存开销至少是400%。 这就是为什么Java要引入基本类型的原因之一。

内存对齐

内存对齐(对应虚拟机选项 -XX:ObjectAlignmentInBytes,默认值为 8)。

默认情况下,JVM堆中对象的起始地址需要对齐至8的倍数。 如果一个对象用不到8N个字节,那么空白的部分空间就浪费掉了。这些浪费掉的空间就称之为对象间的填充(padding)。

内存对齐选项(-XX:ObjectAlignmentInBytes),通过配置内存对齐选项来进一步提升寻址范围。 但是,这同时也可能增加对象间填充,导致压缩指针没有达到原本节省空间的效果。

当然,就算是关闭了压缩指针,JVM还是会进行内存对齐。 此外,内存对齐不仅存在于对象与对象之间,也存在于对象中的字段之间。 比如说,JVM要求long字段、double字段,以及非压缩指针状态下的引用字段地址为8的倍数。

为什么要字段内存对齐

字段内存对齐是让字段只出现在同一CPU的缓存行中。 如果字段不是对齐的,那么就有可能出现跨缓存行的字段。 也就是说,该字段的读取可能需要替换两个缓存行,而该字段的存储也会同时污染两个缓存行。 这两种情况对程序的执行效率而言都是不利的。

字段重排列

字段重排列,就是JVM重新分配字段的先后顺序,以达到内存对齐的目的。

排列方法和规则

JVM中有三种排列方法(对应 Java 虚拟机选项 -XX:FieldsAllocationStyle,默认值为 1)。

会遵循两个规则。如果一个字段占据C个字节,那么该字段的偏移量需要对齐至NC。 这里的偏移量指的是字段地址与对象的起始地址差值。

子类所继承字段的偏移量,需要与父类对应字段的偏移量保持一致。

其实就是更好的执行内存对齐标准,会调整字段在内存中的分布,达到方便寻址和节省空间的目的。

@Contended

Java 8 还引入了一个新的注释 @Contended,该注解就是用来解决虚共享问题的,被该注解标识的变量,会独占一个CPU缓存行。但也因此浪费了大量的内存空间。

虚共享

当两个线程分别访问一个对象中的不同volatile字段,理论上是不涉及变量共享和同步要求的。 但是如果两个volatile字段处于同一个CPU缓存行中,对其中一个volatile字段的写操作,会导致整个缓存行的写回和读取操作,进而影响到了另一个volatile变量,也就是实际上的共享问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值