码农的世界sparrow 框架程序员

JVM对象到底长啥样?synchronize长啥样?

2018-11-09  本文已影响134人  zh_harry

解释

在计算机世界里,所有的数据(data)都是在二进制形式存储的,这些二进制数可以表示我们人类社会中的各种信息,比如文本,十进制数据,浮点数,等等。
那这里边有一个问题,计算机自己是如何识别这些信息的呢?


explain.jpg

简单回答是计算机本身不能识别,这需要程序设计人员的解释
如上图所示,当我们写入一个二进制时,我们问计算机这里的二进制数据是什么?它是无法回答的,如何想得到正确的答案,还需要另一部分数据,就是元数据(数据的描述信息),一般我们的计算机操作提令里会隐含着包含这些信息,比如iadd(int 类型相加)。
类似的元数据在计算机底层有很多。举个例子:
一个二进制位只有两个模式,这两个模式只能是0或是1,而这两个模式都有可能是数据本身,比如一个文件只有一位,文件内容是0或是1都是正常的,那么这个位有没有被占用,用当前位就无法表示了,需要另外一部分数据来描述这个位,这也可以理解为元数据。
这也是磁盘区块管理及内存页框的基本原理。
当然磁盘和内存不会一次只操作一位,磁盘的基本存储单位是扇区。
所有盘面上的同一磁道构成一个圆柱,通常称做柱面(Cylinder),每个圆柱上的磁头由上而下从“0”开始编号。数据的读/写按柱面进行,即磁 头读/写数据时首先在同一柱面内从“0”磁头开始进行操作,依次向下在同一柱面的不同盘面即磁头上进行操作,只在同一柱面所有的磁头全部读/写完毕后磁头才转移到下一柱面,因为选取磁头只需通过电子切换即可,而选取柱面则必须通过机械切换。

disk.jpeg

关于磁盘和文件系统管理详见
https://www.jianshu.com/p/5f77b221165e
推荐冬瓜哥《大话存储》

Java Object

JAVA 对象一般都是复杂对象,那么和上述基本原理一致,数据本身会存在一个地方(堆),那么对于复杂对象的访问,要知道对象的某个field是在什么位置,具体数据是什么?这些元数据也存在一个地方(堆 方法区),对对象的引用也应该保存起来。因为JVM GC的时侯需要遍历所有引用以便回收,这个对象用OOPMAP(详见周志明《深入理解JAVA虚拟机第二版》根枚举遍历)结构存储。

句柄访问 指针访问

mark word

JAVA对象除以上内容之外还有一个非常重要的部分就是mark word
The object header consists of a mark word and a klass pointer.
对象头包括markworkklass pointer

The mark word has word size (4 byte on 32 bit architectures, 8 byte on 64 bit architectures) and
mark work 与机器的字长一致,32位是4个字节,64位是8个字节

the klass pointer has word size on 32 bit architectures. On 64 bit architectures the klass pointer either has word size, but can also have 4 byte if the heap addresses can be encoded in these 4 bytes.
64位下klass也可以通过压缩指针的方式变成4个字节,JVM参数为UseCompressedOops
This optimization is called "compressed oops" and you can also control it with the option UseCompressedOops.

原文参考
https://stackoverflow.com/questions/26357186/what-is-in-java-object-header
以下截至open jdk markOop.hpp

/ Bit-format of an object header (most significant first, big endian layout below):
//
//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
//  64 bits:
//  --------
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
//  size:64 ----------------------------------------------------->| (CMS free block)
//
//  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
//  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
//  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
//  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)

实际占位情况

通过JOL 可以查看对象的实际占用情况
源码如下

 public static class A {
        private boolean a;
    }
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

**** Fresh object
org.openjdk.jol.samples.JOLSample_14_FatLocking$A 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)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
     12     1   boolean A.a                                       false
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

Using compressed oop with 3-bit shift?这句话是什么意思?
为什么压缩指针要移3位 先补补脑
https://wiki.openjdk.java.net/display/HotSpot/CompressedOops
https://stackoverflow.com/questions/25120546/trick-behind-jvms-compressed-oops

Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

 out.printf("# %-19s: %d, %d, %d, %d, %d, %d, %d, %d, %d [bytes]%n",
                "Field sizes by type",
                oopSize,
                sizes.booleanSize,
                sizes.byteSize,
                sizes.charSize,
                sizes.shortSize,
                sizes.intSize,
                sizes.floatSize,
                sizes.longSize,
                sizes.doubleSize
        );

Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

        out.printf("# %-19s: %d, %d, %d, %d, %d, %d, %d, %d, %d [bytes]%n",
                "Array element sizes",
                U.arrayIndexScale(Object[].class),
                U.arrayIndexScale(boolean[].class),
                U.arrayIndexScale(byte[].class),
                U.arrayIndexScale(char[].class),
                U.arrayIndexScale(short[].class),
                U.arrayIndexScale(int[].class),
                U.arrayIndexScale(float[].class),
                U.arrayIndexScale(long[].class),
                U.arrayIndexScale(double[].class)
        );

对象头分析

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)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)

当前环境为64位并开启压缩指针markwork占8byte即

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)

第三行为压缩指针4byte

8     4           (object header)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)

JDK synchronize

JDK 1.6之后对synchronize进行过优化,会涉及锁升级,理论上通过jol和open-jdk markOop 对markwork的定义,是可以查询锁的变化的,但由于当前笔者能力有限,参考JAVA并发编译艺术 方腾飞 的这部分内容与markOop 结构定义不完全一致。
实际锁的变化如下

joi 源码
https://github.com/sparrowzoo/jol
测试源码
JOLSample_14_FatLocking

**** Fresh object
org.openjdk.jol.samples.JOLSample_14_FatLocking$A 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)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
     12     1   boolean A.a                                       false
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

**** Before the lock
org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           98 68 0b 11 (10011000 01101000 00001011 00010001) (285960344)
      4     4           (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
      8     4           (object header)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
     12     1   boolean A.a                                       false
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

**** With the lock
org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           9a 76 01 9d (10011010 01110110 00000001 10011101) (-1660848486)
      4     4           (object header)                           b5 7f 00 00 (10110101 01111111 00000000 00000000) (32693)
      8     4           (object header)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
     12     1   boolean A.a                                       false
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

**** After the lock
org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           9a 76 01 9d (10011010 01110110 00000001 10011101) (-1660848486)
      4     4           (object header)                           b5 7f 00 00 (10110101 01111111 00000000 00000000) (32693)
      8     4           (object header)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
     12     1   boolean A.a                                       false
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

**** After System.gc()
org.openjdk.jol.samples.JOLSample_14_FatLocking$A object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           09 00 00 00 (00001001 00000000 00000000 00000000) (9)
      4     4           (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4           (object header)                           16 f0 00 f8 (00010110 11110000 00000000 11111000) (-134156266)
     12     1   boolean A.a                                       false
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

关于锁升级的理论参考

http://ifeve.com/java-synchronized/
https://blog.csdn.net/qq838642798/article/details/64439761

总结

综上所述

上一篇下一篇

猜你喜欢

热点阅读