从Java对象布局markword看syncronized的本质

自作多情 提交于 2020-04-05 15:55:15

可以通过以下工具来查看Java对象的布局:JOL=Java Object Layout。 

<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.10</version>
</dependency>

先看下在JVM中,一个引用的长度占用多少字节:

import org.openjdk.jol.vm.VM;

public class JavaObjectLayout {
    public static void main(String[] args) {
        System.out.println("------------VM details---------------");
        System.out.println(VM.current().details());
    }
}

这个details()方法由JOL类库中的HotspotUnsafe类实现:

运行结果:

运行结果中可以看到:
1.JVM是64位的HotSpot
2.默认开启oop(Ordinary Object Pointer,普通对象指针)压缩,可设置JVM参数-XX:-UseCompressedOops关闭指针压缩
3.默认开启Class pointer(类对象指针)的压缩,可设置JVM参数-XX:-UseCompressedClassPointer关闭指针压缩
4.对象按照8字节来对齐
5.对象属性(即上方的Field sizes by type)的长度:4字节(oopSize, 普通对象引用),其余的基本类型的字节数和java中一致
6.数组类型(即上方的Array element sizes)的长度:4字节(引用的长度),其余的基本类型的字节数和java中一致
由此可以得出,当前JVM使用4字节保存对象的引用
(关于oop压缩和class pointer的压缩,可以参考这篇文章: https://blog.csdn.net/a469517790/article/details/104916916/ )

普通对象布局

mark word 8Bytes 用于标记锁信息、GC信息、IdentityHashCode等
Klass Pointer 类指针 4Bytes 用于标记该对象是哪个Class的实例
开启内存压缩(-XX:+UseCompressedClassPointer)后为4字节,默认是开启的,
不开启内存压缩为8个字节(下面有例子)
成员变量 视成员变量的类型和数量而定 如果没有成员变量,则这一块为空
Padding 对齐 视上述字节而定 一个对象占用的字节数必须是8的倍数,不足的用padding对齐

接下来看看普通对象的布局:编写代码:

import org.openjdk.jol.info.ClassLayout;

public class JavaObjectLayout {
    public static void main(String[] args) {
        System.out.println("------------Object---------------");
        Object o = new Object();
        String s = ClassLayout.parseInstance(o).toPrintable();
        System.out.println(s);
    }
}


可以看到,前12个字节都是object header,什么是object header?引用OpenJDK的专业术语的原文:

object header

Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object's layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.

对象头是由GC管理的,堆中的Java对象的开始部分,由两个word组成:mark word和klass pointer。

mark word

The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

klass pointer

The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the "klass" contains a C++ style "vtable".

所以,这12字节中前8个字节(前两行)是markword,后4个字节(第三行)是对象指针。由于Object类没有成员变量,这块为空,因此最后4个字节(第四行)是对齐。

如果执行的时候使用JVM参数-XX:-UseCompressedClassPointers关闭类指针压缩,则class pointer将占用8字节:

数组对象布局

mark word 8Bytes 用于标记锁信息、GC信息、IdentityHashCode等
Klass Pointer
类指针
4Bytes 用于标记该对象是哪个Class的实例
开启内存压缩后为4字节,默认是开启的,
可通过(-XX:-UseCompressedClassPointer)关闭压缩,,关闭后为8字节 
数组长度 4Bytes 标记数组有多少个元素
数组内容 根据数组类型m和长度n而定,长度为m*n 如果元素为基本类型,比如byte/boolean/short/char/int/long/double,则m为对应的基本类型长度;
如果元素为对象m是4字节的引用
(前文已经提到:
当前JVM使用4字节保存对象的引用)
如果数组长度为0,这一块为空
Padding 对齐 视上述字节而定 一个对象占用的字节数必须是8的倍数,不足的用padding对齐

同理可以查看数组对象的布局:

import org.openjdk.jol.info.ClassLayout;

public class JavaObjectLayout {
    public static void main(String[] args) {
        System.out.println("------------Object[2]---------------");
        Object[] os = new Object[2];
        System.out.println(ClassLayout.parseInstance(os).toPrintable());
    }
}


可以看到,从偏移量12开始,数组长度为4字节(上文object header的引用里提到,这两个words之后,紧接着就是数组长度),值为2,然后16字节开始,为2x4=8字节的数组内容(上文提到:每个对象的引用长度为4字节)。

syncronized关键字的本质

现在,我们去掉JVM参数,并用syncronized对该对象加锁,看看markword有什么变化:

import org.openjdk.jol.info.ClassLayout;

public class JavaObjectLayout {
    public static void main(String[] args) {
        System.out.println("------------Object---------------");
        Object o = new Object();
        synchronized (o) {
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }
}


可以看到,markword的前4个字节的内容发生了变化。因此,syncronized加锁的本质,是修改了该对象的markword

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!