精华内容
下载资源
问答
  • 对象头是什么东西

    2020-12-23 14:12:11
    我们的头有口鼻眼耳,对象头也有一些东西,主要包含两部分:Mark Word(标记字)和Class Pointer(类指针),如果数组对象还得再加一项Array Length(数组长度)。  对象头为啥要有这些东东?Mark Word用来标记...

    对象头,顾名思义,就是对象的头。对象是实例化出来的,实例化的前提是必须有类这个模板。举个不大恰当的例子,人类就是个类,你我他就是人类实例化出来的对象。我们的头,自然就是对象头。我们的头有口鼻眼耳,对象头也有一些东西,主要包含两部分:Mark Word(标记字)和Class Pointer(类指针),如果是数组对象还得再加一项Array Length(数组长度)。

      对象头为啥要有这些东东?Mark Word用来标记运行时信息(每个对象各不相同),Class Pointer用来指向生成该对象所在的类(认祖归宗),Array Length告诉我们数组的长度。后两项简单易懂,最复杂的是这个Mark Word了,看下JVM的markWord.hpp是怎么规定它的:

    // The markWord describes the header of an object. 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)
    //
    //  64 bits:
    //  --------
    //  unused:25 hash:31 -->| unused_gap:1   age:4    biased_lock:1 lock:2 (normal object)
    //  JavaThread*:54 epoch:2 unused_gap:1   age:4    biased_lock:1 lock:2 (biased object)

     它说markWord描述了对象头,采用大端点表示,markWord包含了哈希码(hash)、GC分代年龄(age)、偏向锁(biased_lock)、锁状态(lock)和用来滥竽充数的间隔位(64位里那些unused开头的字段)。32位和64位机器存储这些东东的位大小各不相同。如果对象不再是普通对象(normal object),变成了一个偏向锁(biased object),那么markWord所含内容的哈希码就变成了持有锁的线程指针(JavaThread*)、偏向时间戳(epoch),其他不变。

      最后再说这个锁状态:

    //  - the two lock bits are used to describe three states: locked/unlocked and monitor.
    //
    //    [ptr             | 00]  locked             ptr points to real header on stack
    //    [header      | 0 | 01]  unlocked           regular object header
    //    [ptr             | 10]  monitor            inflated lock (header is wapped out)
    //    [ptr             | 11]  marked             used to mark an object

         00——轻量锁,存储内容是栈帧中指向锁对象的指针(ptr)

      01——无锁,至于是否偏向锁,需要再往前一位看,0:无锁,1:偏向锁。至于所存储之物,上面已提到:如果是普通对象(0),存储内容就是上哈希码、GC分代年龄;若是偏向锁(1),则是拥有锁的线程指针、偏向时间戳、对象分代年龄

      10——监视器锁(moniter),也就是重量锁,存储内容为指向锁对象的指针(ptr)

      11——GC标记

      好了,理论说完,下面举例说明:

      1、pom.xml新增jar包:

           <dependency>
                <groupId>org.openjdk.jol</groupId>
                <artifactId>jol-core</artifactId>
                <version>0.14</version>
            </dependency>
    

    2、新增测试类:

    import com.wlf.springcloudgateway.javabean.HelloWorld;
    import org.openjdk.jol.info.ClassLayout;
    
    import java.util.concurrent.TimeUnit;
    
    public class ObjectMarkWord {
    
        public static void main(String[] args) throws InterruptedException {
    
            // 实例化对象
            HelloWorld helloWorld = HelloWorld.builder().name("wlf").build();
    
            // 1、普通对象,无锁
            System.out.println("普通对象:");
            printfObjMarkWord(helloWorld);
    
            // 2、实例化数组,打印数组对象信息,无锁
            System.out.println("数组对象:");
            String[] address = new String[]{"hangzhou", "nanjing", "shenzhen"};
            printfObjMarkWord(address);
    
    
            // 3、单线程首次加锁,轻量锁
            System.out.println("轻量锁:");
            new Thread(() -> {
                synchronized (helloWorld) {
                    printfObjMarkWord(helloWorld);
                }
            }).start();
    
            TimeUnit.SECONDS.sleep(2);
    
            // 4、多个线程加锁,升级重量锁
            System.out.println("重量锁:");
            for (int i = 0; i < 2; i++) {
                new Thread(() -> {
                    synchronized (helloWorld) {
                        printfObjMarkWord(helloWorld);
                    }
                }).start();
            }
        }
    
        private static void printfObjMarkWord(Object obj) {
            System.out.println("-------------------------");
            System.out.println(ClassLayout.parseInstance(obj).toPrintable());
            System.out.println("-------------------------");
    
        }
    
    }
    import lombok.Data;
    
    @Data
    @Builder
    public class HelloWorld {
        private String name;
    }

    输出结果:

    普通对象:
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld 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)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    数组对象:
    -------------------------
    [Ljava.lang.String; 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)                           20 e9 dd 16 (00100000 11101001 11011101 00010110) (383641888)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     4                    (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
         20     4                    (alignment/padding gap)                  
         24    24   java.lang.String String;.<elements>                        N/A
    Instance size: 48 bytes
    Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
    
    -------------------------
    轻量锁:
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld object internals:
     OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
          0     4                    (object header)                           60 f4 05 1a (01100000 11110100 00000101 00011010) (436597856)
          4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    重量锁:
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld object internals:
     OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
          0     4                    (object header)                           ca da 66 17 (11001010 11011010 01100110 00010111) (392616650)
          4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld object internals:
     OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
          0     4                    (object header)                           ca da 66 17 (11001010 11011010 01100110 00010111) (392616650)
          4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    
    Process finished with exit code 0

    我本机是64位的,所以markWord、class pointer、array length都是8字节:

     有锁、无锁,看标黄的00、01,是否偏向锁,看标红的那一位。同一个对象的class pointer是一致的,从标黄处也能看出来。普通对象是无锁(01)的,首次加锁后变成了轻量锁(00,为啥不是偏向锁?参见)。接着再另起两个线程竞争锁,原来的轻量锁膨胀为重量锁。这里有个问题:为啥偏向锁没有出来冒泡

    展开全文
  • 对象头,顾名思义,就是...我们的头有口鼻眼耳,对象头也有一些东西,主要包含两部分:Mark Word(标记字)和Class Pointer(类指针),如果数组对象还得再加一项Array Length(数组长度)。  对象头为啥要有这些东东?...

      对象头,顾名思义,就是对象的头。对象是实例化出来的,实例化的前提是必须有类这个模板。举个不大恰当的例子,人类就是个类,你我他就是人类实例化出来的对象。我们的头,自然就是对象头。我们的头有口鼻眼耳,对象头也有一些东西,主要包含两部分:Mark Word(标记字)和Class Pointer(类指针),如果是数组对象还得再加一项Array Length(数组长度)。

      对象头为啥要有这些东东?Mark Word用来标记运行时信息(每个对象各不相同),Class Pointer用来指向生成该对象所在的类(认祖归宗),Array Length告诉我们数组的长度。后两项简单易懂,最复杂的是这个Mark Word了,看下JVM的markWord.hpp是怎么规定它的:

    // The markWord describes the header of an object.
    //
    // 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) // // 64 bits: // -------- // unused:25 hash:31 -->| unused_gap:1 age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:54 epoch:2 unused_gap:1 age:4 biased_lock:1 lock:2 (biased object)

      它说markWord描述了对象头,采用大端点表示,markWord包含了哈希码(hash)、GC分代年龄(age)、偏向锁(biased_lock)、锁状态(lock)和用来滥竽充数的间隔位(64位里那些unused开头的字段)。32位和64位机器存储这些东东的位大小各不相同。如果对象不再是普通对象(normal object),变成了一个偏向锁(biased object),那么markWord所含内容的哈希码就变成了持有锁的线程指针(JavaThread*)、偏向时间戳(epoch),其他不变。

      最后再说这个锁状态:

    //  - the two lock bits are used to describe three states: locked/unlocked and monitor.
    //
    //    [ptr             | 00]  locked             ptr points to real header on stack
    //    [header      | 0 | 01]  unlocked           regular object header
    //    [ptr             | 10]  monitor            inflated lock (header is wapped out)
    //    [ptr             | 11]  marked             used to mark an object
    

      00——轻量锁,存储内容是栈帧中指向锁对象的指针(ptr)

      01——无锁,至于是否偏向锁,需要再往前一位看,0:无锁,1:偏向锁。至于所存储之物,上面已提到:如果是普通对象(0),存储内容就是上哈希码GC分代年龄;若是偏向锁(1),则是拥有锁的线程指针偏向时间戳对象分代年龄

      10——监视器锁(moniter),也就是重量锁,存储内容为指向锁对象的指针(ptr)

      11——GC标记

      好了,理论说完,下面举例说明:

      1、pom.xml新增jar包:

            <dependency>
                <groupId>org.openjdk.jol</groupId>
                <artifactId>jol-core</artifactId>
                <version>0.14</version>
            </dependency>
    

      2、新增测试类:

    import com.wlf.springcloudgateway.javabean.HelloWorld;
    import org.openjdk.jol.info.ClassLayout;
    
    import java.util.concurrent.TimeUnit;
    
    public class ObjectMarkWord {
    
        public static void main(String[] args) throws InterruptedException {
    
            // 实例化对象
            HelloWorld helloWorld = HelloWorld.builder().name("wlf").build();
    
            // 1、普通对象,无锁
            System.out.println("普通对象:");
            printfObjMarkWord(helloWorld);
    
            // 2、实例化数组,打印数组对象信息,无锁
            System.out.println("数组对象:");
            String[] address = new String[]{"hangzhou", "nanjing", "shenzhen"};
            printfObjMarkWord(address);
    
    
            // 3、单线程首次加锁,轻量锁
            System.out.println("轻量锁:");
            new Thread(() -> {
                synchronized (helloWorld) {
                    printfObjMarkWord(helloWorld);
                }
            }).start();
    
            TimeUnit.SECONDS.sleep(2);
    
            // 4、多个线程加锁,升级重量锁
            System.out.println("重量锁:");
            for (int i = 0; i < 2; i++) {
                new Thread(() -> {
                    synchronized (helloWorld) {
                        printfObjMarkWord(helloWorld);
                    }
                }).start();
            }
        }
    
        private static void printfObjMarkWord(Object obj) {
            System.out.println("-------------------------");
            System.out.println(ClassLayout.parseInstance(obj).toPrintable());
            System.out.println("-------------------------");
    
        }
    
    }
    
    import lombok.Data;
    
    @Data
    @Builder
    public class HelloWorld {
        private String name;
    }
    

      在跑之前给IDEA去掉指针压缩(jdk8默认开启):Run -> Edit Configurations -> VM:options那一栏加上-XX:-UseCompressedOops

      输出结果:

    普通对象:
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld 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)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    数组对象:
    -------------------------
    [Ljava.lang.String; 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)                           20 e9 dd 16 (00100000 11101001 11011101 00010110) (383641888)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     4                    (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
         20     4                    (alignment/padding gap)                  
         24    24   java.lang.String String;.<elements>                        N/A
    Instance size: 48 bytes
    Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
    
    -------------------------
    轻量锁:
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld object internals:
     OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
          0     4                    (object header)                           60 f4 05 1a (01100000 11110100 00000101 00011010) (436597856)
          4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    重量锁:
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld object internals:
     OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
          0     4                    (object header)                           ca da 66 17 (11001010 11011010 01100110 00010111) (392616650)
          4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    -------------------------
    com.wlf.springcloudgateway.javabean.HelloWorld object internals:
     OFFSET  SIZE               TYPE DESCRIPTION                               VALUE
          0     4                    (object header)                           ca da 66 17 (11001010 11011010 01100110 00010111) (392616650)
          4     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4                    (object header)                           88 49 0e 17 (10001000 01001001 00001110 00010111) (386812296)
         12     4                    (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
         16     8   java.lang.String HelloWorld.name                           (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    -------------------------
    
    Process finished with exit code 0
    

      我本机是64位的,所以markWord、class pointer、array length都是8字节:

      有锁、无锁,看标黄的00、01,是否偏向锁,看标红的那一位。同一个对象的class pointer是一致的,从标黄处也能看出来。普通对象是无锁(01)的,首次加锁后变成了轻量锁(00,为啥不是偏向锁?参见)。接着再另起两个线程竞争锁,原来的轻量锁膨胀为重量锁。这里有个问题:为啥偏向锁没有出来冒泡?答案参见偏向锁是个什么东东? 

    展开全文
  • 一:对象头 HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。 HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分...

    目录

    一,对象头

    1,Mark Word

    2,指向类的指针

    3,数组长度

    二,实例数据

    三,对齐填充字节


    Java对象保存在内存中时,由以下三部分组成:

    1,对象头

    2,实例数据

    3,对齐填充字节

    一,对象头

    java的对象头由以下三部分组成:

    1,Mark Word

    2,指向类的指针

    3,数组长度(只有数组对象才有)

     

    1,Mark Word

    Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。

    Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

    Mark Word在不同的锁状态下存储的内容不同,在32位JVM中是这么存的:

    锁状态

    25bit

    4bit

    1bit

    2bit

    23bit

    2bit

    是否偏向锁

    锁标志位

    无锁

    对象的HashCode

    分代年龄

    0

    01

    偏向锁

    线程ID

    Epoch

    分代年龄

    1

    01

    轻量级锁

    指向栈中锁记录的指针

    00

    重量级锁

    指向重量级锁的指针

    10

    GC标记

    11

    其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态。

    JDK1.6以后的版本在处理同步锁时存在锁升级的概念,JVM对于同步锁的处理是从偏向锁开始的,随着竞争越来越激烈,处理方式从偏向锁升级到轻量级锁,最终升级到重量级锁。

     

    JVM一般是这样使用锁和Mark Word的:

    1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。

    2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。

    3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。

    4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。

    5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。

    6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。

    7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

     

    2,指向类的指针

    该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

    Java对象的类数据保存在方法区。

     

    3,数组长度

    只有数组对象保存了这部分数据。

    该数据在32位和64位JVM中长度都是32bit。

     

    二,实例数据

    对象的实例数据就是在java代码中能看到的属性和他们的值。

     

    三,对齐填充字节

    因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能。

     

    一:对象头

    HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。  

    HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。

    对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的额 外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机 中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志 位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。  

    对象头的另外一部分是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不一定要经过对象本身。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。  

     

    这里要特别关注的是锁标志位,  锁标志位与是否偏向锁对应到唯一的锁状态。

    所以锁的状态保存在对象头中,所以再理解

     

    1. Synchronized锁的到底是什么, 锁住的是代码还是对象)(答案锁的是对象)?
    2.  java中锁,锁的是对象,它是怎么实现的?

     

    这两个问题,就好懂了!

     

    二:锁的状态

    锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)。JDK 1.6中默认是开启偏向锁和轻量级锁的,我们也可以通过-XX:-UseBiasedLocking来禁用偏向锁。

     

    1、轻量级锁的加锁过程

      (1)在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word。这时候线程堆栈与对象头的状态如图2.1所示。

      (2)拷贝对象头中的Mark Word复制到锁记录中。

      (3)拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock record里的owner指针指向object mark word。如果更新成功,则执行步骤(4),否则执行步骤(5)。

      (4)如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图2.2所示。

      (5)如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁,自旋就是为了不让线程阻塞,而采用循环去获取锁的过程。

     

      图2.1 轻量级锁CAS操作之前堆栈与对象的状态

     

    图2.2 轻量级锁CAS操作之后堆栈与对象的状态

     

     

    三、偏向锁

      引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)。上面说过,轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时进一步提高性能。

    1、偏向锁获取过程:

      (1)访问Mark Word中偏向锁的标识是否设置成1,锁标志位是否为01——确认为可偏向状态。

      (2)如果为可偏向状态,则测试线程ID是否指向当前线程,如果是,进入步骤(5),否则进入步骤(3)。

      (3)如果线程ID并未指向当前线程,则通过CAS操作竞争锁。如果竞争成功,则将Mark Word中线程ID设置为当前线程ID,然后执行(5);如果竞争失败,执行(4)。

      (4)如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(safepoint)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。

      (5)执行同步代码。

    2、偏向锁的释放:

      偏向锁的撤销在上述第四步骤中有提到。偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

    3、重量级锁、轻量级锁和偏向锁之间转换

    该图主要是对上述内容的总结,如果对上述内容有较好的了解的话,该图应该很容易看懂。

     

     

     

    参考资料:

    https://blog.csdn.net/lkforce/article/details/81128115?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-2-81128115.nonecase&utm_term=java%E5%AF%B9%E8%B1%A1%E5%A4%B4%E6%98%AF%E4%BB%80%E4%B9%88

          http://blog.csdn.NET/u010723709/article/details/50341631

          http://www.cnblogs.com/paddix/p/5405678.html

          http://www.cnblogs.com/lingepeiyong/archive/2012/10/30/2745973.html

    展开全文
  • 对象头Monitor

    2021-01-15 11:09:23
    二、为什么要使用对象头 三、对象头的使用具体说明 四、数据结构分析 五、代码演示 前言 每个对象都有对象头对象头就可以把它理解成锁。jvm采用两个字节来存储对象头,如果对象数组,则会分配3个字节,多出来...

    文章目录

    一、对象头的组成
    二、为什么要使用对象头
    三、对象头的使用具体说明
    四、数据结构分析
    五、代码演示


    前言

    每个对象都有对象头,对象头就可以把它理解成锁。jvm采用两个字节来存储对象头,如果对象是数组,则会分配3个字节,多出来的一个字记录的是数组长度。

    提示:以下是本篇文章正文内容,下面案例可供参考

    一、对象头的组成?

    1.mark word 存储对象的hashCode、锁等信息或分代年龄或GC标志等信息。 2.Class Metadata Address 类型指针指向对象的类元数据,JVM通过这个指针确定该对象是哪个类的实例。

    在这里插入图片描述
    在这里插入图片描述

    二、为什么要使用对象头

    由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。

    三、 对象头的使用具体说明

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210115095009896.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhZGF0aQ==,size_16,color_FFFFFF,t_70#pic_center) 进入waitSet队列的线程,必须要当前对象实例调用notify()唤醒,不然就一直处于阻塞状态。 # 四、数据结构分析 在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)
    ObjectMonitor() {
        _header       = NULL;
        _count        = 0; //记录个数
        _waiters      = 0,
        _recursions   = 0;
        _object       = NULL;
        _owner        = NULL;
        _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet
        _WaitSetLock  = 0 ;
        _Responsible  = NULL ;
        _succ         = NULL ;
        _cxq          = NULL ;
        FreeNext      = NULL ;
        _EntryList    = NULL ; //处于等待锁的线程,会被加入到该列表
        _SpinFreq     = 0 ;
        _SpinClock    = 0 ;
        OwnerIsThread = 0 ;
      }
    

    ObjectMonitor中有两个队列,_WaitSet和_EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象),_owner 指向持有ObjectMonitor对象的线程地址。当多个线程同时访问一段同步代码时,首先会进入——EntryList集合,而当线程获取到锁的时候会进入到_Owner区域,并把monitor中的owner变量设置为当前线程同时monitor中的计数器count+1,而如果monitor的owner已经是当前线程,则再次尝试获取monitor,count+1(锁重入)。如果线程调用wait方法,将释放当前持有的monitor,owner变量恢复为null,count减1,同时该线程进入waitSet等待被notify唤醒。如果当前线程执行完毕也将释放monitor,以便其他线程获取。

    五、代码演示

    三个线程交替顺序打印ABC 题目描述:建立三个线程A、B、C,A线程打印10次字母A,B线程打印10次字母B,C线程打印10次字母C,但是要求三个线程同时运行,并且实现交替打印,即按照ABCABCABC的顺序打印。 方法: 使用synchronized, wait和notifyAll。 思路分析: 保证ThreadA->ThreadB->ThreadC按序执行,A想要执行,C必须得释放锁,B想要执行A就得释放锁,C想要执行B就得释放锁,三个线程成环。所以每个线程必须同时持有两个对象锁,才能进行打印。一个是prev,一个是自身对象锁。当前持有两个锁的线程调用notify唤醒下一个等待线程,只有等到同步代码块执行完毕后才会释放对象锁。再调用prev.wait立即释放prev对象锁。当前线程进入休眠,等待其他线程的notify操作再次唤醒。
    public class Synchronized_ABC {
        public static void main(String[] args) throws Exception {
    
            Object a=new Object();
            Object b=new Object();
            Object c=new Object();
            ThreadPrinter pa=new ThreadPrinter("A",c,a);
            ThreadPrinter pb=new ThreadPrinter("B",a,b);
            ThreadPrinter pc=new ThreadPrinter("C",b,c);
    
           new Thread(pa).start();
           Thread.sleep(10);
            new Thread(pb).start();
            Thread.sleep(10);
            new Thread(pc).start();
            Thread.sleep(10);
        }
        public static class ThreadPrinter implements Runnable{
            private String name;//线程名字
            private Object prev;//前一个线程对应的对象锁
            private Object self;//当前线程持有的对象锁
    
            private ThreadPrinter(String name, Object prev, Object self) {
                this.name = name;
                this.prev = prev;
                this.self = self;
            }
            @Override
            public void run() {
                int count = 3;
            while (count > 0) {// 多线程并发,不能用if,必须使用while循环
                    synchronized (prev) { // 先获取 prev 锁
                        synchronized (self) {// 再获取 self 锁
                            System.out.print(name);// 打印
                            count--;
    
                            self.notifyAll();// 唤醒其他线程竞争self锁,注意此时self锁并未立即释放。
                        }
                       //执行完self的同步块,进行锁释放
                        try {
                            if(count==0) {
                                prev.notify();//释放对象锁
                            }else {
                                prev.wait();//保证ABC顺序打印
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
    
    }
    
    
    展开全文
  • java对象内存布局与对象头 Object object = new Object()谈谈你对这句话的理解?...对象头里面保存着什么: 在64位系统中,MarkWord占8个字节,类型指针占了8个字节,一共16个字节 默认存储对象的HashCode,
  • 你好,我 yes。面向对象编程想必大家都耳熟能详,但是写了这么多代码你对面向对象有清晰的认识吗?来看看这几个问题:到底什么是面向对象编程?和面向过程编程有什么区别?什么又称为面向对象语...
  • 线程安全并发编程的重要关注点,造成线程安全问题的原因,有共享数据,多个线程操作共享数据。 解决方式,保证同一时刻只有一个线程操作数据,其他线程必须在线程处理完成之后在进行,访问互斥 synchronized保证...
  • Java-对象头详解

    2021-01-11 14:48:47
    这里只介绍64位虚拟机的对象头的结构和内容,主要分为以下四种状态介绍,无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。 对象头的基本结构 如下图所示 对象的结构和内容我这里就暂且不提了,本章主要讲述的...
  • java 对象头信息 (一)

    2019-07-18 23:04:18
    文章目录java头的信息分析首先为什么我要去研究java的对象头呢?1.对象头形式1.1.普通对象1.2.数组对象2.对象头的组成2.1.Mark Word2.2.class ...意思java的对象头在对象的不同状态下会有不同的表现形式,主要有...
  • java对象头信息

    千次阅读 多人点赞 2019-09-02 14:27:14
    做java开发几年了,但一直不知道如下问题: 1. 一个java对象到底占用了多少内存空间,应该如何...3. java对象是在那里设置了指针指向对应的方法区中的元数据的? 4. 在jvm垃圾回收时被标记为可回收但还未执行回...
  •   其中对象头又包含两部分信息:第一部分存储对象自身运行时的数据,如HashCode、Gc年龄、锁状态等等,另一部分存储了类型指针,虚拟机可以通过这一指针来确定这个对象哪个类的实例。   实例数据就是对象...
  • 题: Java对象创建的过程是什么? 答:创建过程有5步。 1.类加载检查。 2.分配内存空间。 3.初始化零值。 4.设置对象头。 5.执行init方法。
  • 什么是面向对象

    2019-11-15 15:42:15
    今天我们就来说说什么是面向对象和面向过程?其实刚工作一年基本上都做面向过程的,那时候也啥都不懂,只会做,不会说。好了废话不多说,先上问题: 农场有头大母牛,每年生小母牛,小母牛五年后生小母牛,...
  • 哈希值:它一个地址,用于栈对堆空间中对象的引用指向,不然栈无法找到堆中对象的 GC分代年龄:记录幸存者区对象被GC之后的年龄age,,一般age为15之后下一次GC就会直接进入老年代 锁状态标志:记录一些加锁的...
  • synchronized在锁什么 上锁就是改变对象的对象头 对象头所有对象开头的公共部分。 对象头由两个词组成。 第一个词MarkWord 第二个词klass pointer,类的原数据的地址(Class Metadata Address),以此可以...
  • 对象头 引用百度图片 简单分析 4bit 的二进制代表了分代年龄 那么就知道了jvm 的gc为什么是标记16次后进行元空间
  • 使用JOL查看对象头

    2021-03-22 11:13:14
    我们都知道synchronized的关键字,锁住的对象头,可是却无法直接的看到到底改变了什么,那么openjdk提供了jol可以直接查看到对象头的改变。 1.引入依赖 <dependency> <groupId>org.openjdk....
  • 作用分别是什么? 序号 内置对象 对应 Servlet 类 作用 1 request javax.servlet.http.HttpServletRequest 客户端的请求信息:Http协议信息、Cookie、请求参数等 2 response javax....
  • 一、什么是面向对象程序设计? 1、所有的东西都是对象对象保存着数据,且可对它自身进行操作; 2、可从要解决的问题上提出概念性的组件,然后再程序中将其表达为一个对象,(、身体、手、腿组成一个人); 3...
  • java 对象头 我们都知道,Java对象存储在堆(Heap)内存。那么一个Java对象到底包含什么呢?概括起来分为对象头、对象体和对齐字节。如下图所示: 对象的几个部分的作用: 1.对象头中的Mark Word(标记字)主要...
  • 对象头信息里面有哪些东西? 蚂蚁金服: 二面:java对象头里有什么 对象实例化 1.1、对象创建的方式 对象创建的方式 new:最常见的方式、单例类中调用getInstance的静态类方法,XXXFactory的静态方法 ...
  • 高效并发从JDK 5升级到JDK 6后一项重要的改进项,HotSpot虚拟机开发团队在这个版本上花费了大量的资源去实现各种锁优化技术,如适应性自旋、锁消除、锁膨胀、轻量级锁 、偏向锁等,这些技术都为了在线程之间更...
  • 但我连什么是对象头都不知道。 synchronized一种原子性内置锁,就是用C++写的,我们看不到其里面的源码,他就是一组指令 首先我们要知道java有哪些锁? 偏向锁,自旋锁,独占锁,共享锁,轻量级锁
  • request 对象是 javax.servlet.httpServletRequest类型的对象。 该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括信息、系统信息、请求方式以及请求参数等)。request对象的...
  • 编程中的对象一块内存区域,举例Person对象来说,区域4个字节记录的他的身高,接下来的4个字节记录的他的跳跃功能——通常个函数指针,指向另一块存储函数代码的内存区域——,再下来2个字节的年龄,...
  • 面向对象编程?你说的看着女朋友进行编程吗? 注释:没有对象怎办? 前几天网上看到一个有趣的面试问题: 农场有头大母牛,每年生小母牛,小母牛五年后生小母牛,年龄大于15便死亡????,问20年后农场一共...
  • 这一节非常重要,是整个并发锁的关键,因为重视提到无所、偏向锁、轻...对象头到底是什么东西呢?我们从hotspot的源码中贴上一段描述。 // 32 bits: // -------- // hash:25 ------------>| age:4 biased_lock
  • JSP有哪些内置对象,功能是什么

    万次阅读 2016-12-13 10:27:03
     pageContext对象的作用取得任何范围的参数,通过pageContext对象可以获取JSP页面的out、request、response、session、application等对象,或者可以重新定向客户的请求等,较少使用 二、request服务器端取得...
  • 在python里所有变量都是对象 import requests url = 'http://www.baidu.com/' respone = requests.get(url) # 参数 # 响应的url (访问网站被重定向 这真地址) print(respone.url) # 状态码 print(respone....
  • 文章目录Synchronized锁升级一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 Synchronized锁升级 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,996
精华内容 798
关键字:

对象头是什么