Synchronized原理


1、认识JAVA对象的组成结构

对象的内存结构:在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。在64位系统中,对象在JVM中分配的大小为8的整数位。下图为对象内存结构:

image.png

如何证明上述内容?我们定义一个类,然后通过jol-core将对象信息打印出来看看。类代码如下:

public class Car {

}

在POM文件中引入如下包:

        
            org.openjdk.jol
            jol-core
            0.9
        

打印对象信息代码如下:

public class ObjectStruct {
    private static Car car = new Car ();
    public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(car ).toPrintable());
    }
}

输出结果如下:


image.png

给类增加字段,如下

public class Car {
    private String str;
}

image.png

纠错说明:上面截图中红色字体描述位应改为字节。

2 java对象头
2.1 java对象头组成

Hotspot中对java对象头的说明:

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.

从Hotspot JVM规范可以看出,在对象头中包含了GC状态,数据同步状态以及hashcode识别码。头部由两个词组成的(就类似于java中的类属性),按官方文档中描述,是由mark wordklass 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".
2.2 java对象头makeword部分分析

1、下载openjdk源码
下载地址:http://hg.openjdk.java.net
2、打开markwordoops.hpp文件,路径src/share/vm/oops/markOop.hpp,注释里有如下描述:

//  32 bits:[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l37)
//  --------[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l38)
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l39)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l40)
//             size:32 ------------------------------------------>| (CMS free block)[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l41)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l42)
//[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l43)
//  64 bits:[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l44)
//  --------[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l45)
//  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l46)
//  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l47)
//  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)[](http://hg.openjdk.java.net/jdk8u/jdk8u60/hotspot/file/37240c1019fd/src/share/vm/oops/markOop.hpp#l48)
//  size:64 ----------------------------------------------------->| (CMS free block)

由于现在大部分都是64位系统,故只分析64位系统的对象头信息。从注释中可以看到64系统markword总共包含64位,以及对象不同状态位的值情况,对象未被使用时,位的分布情况如下图:

微信截图_20191120112625.png

结合这张图,我们对比上面部分输出的头部信息。也就是下图中的前8字节,

image.png

抽出来值如下:
00000001 00000000 00000000 00000000
00000000 00000000 00000000 00000000
好像与文档中的描述不符?不是前25位未使用,31位为hashcode,为何前8位有一个1,而后面31全为0,也就是说没有存hashcode的值。JDK采用延迟加载的方式生成。只有调用hashcode()时,才会写入对象头。若一个类的hashCode()方法被重写,对象头中将不存储hashcode信息,因为一般我们自己实现的hashcode()并未将生成的值写入对象。
将上面的测试代码稍做修改,调用对象的hashcode()方法,再看看打印出来的对象头信息。

public class ObjectStruct {
    private static L l = new L();
    public static void main(String[] args) {
        l.hashCode();
        System.out.println(ClassLayout.parseInstance(l).toPrintable());
    }
}

返回的对象头信息(make word部分)

01 a5 e5 4a (00000001 10100101 11100101 01001010)
01 00 00 00 (00000001 00000000 00000000 00000000)

从第9位开始,值发生了变化,但是我们发现,跟JVM规范里还是不一样,不是前25位不使用,中间31位为hash identity,这就涉及到了大小端模式,所谓的大小端模式是指:
大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
(未完待续)

你可能感兴趣的:(Synchronized原理)