精华内容
下载资源
问答
  • java对象头MarkWord探索

    2021-03-10 04:28:37
    关于java对象头markword的文章有很多,基本都是说markword用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。具体是怎么存,怎么切换这些说...

    关于java对象头markword的文章有很多,基本都是说markword用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。具体是怎么存,怎么切换这些说的很少,这里使用hsdb深入跟踪了对象头markword,在这里记录下。

    实验环境:

    mac ox 10.12.6

    jdk8 64-Bit

    涉及工具:

    jdb,hsdb

    一. 局部变量生命周期 和 synchronized匿名锁执行过程

    d48c9b0fc1b4

    图1

    d48c9b0fc1b4

    图2

    breakM()方法栈帧局部变量表元素:

    1.lock对象

    2.a变量

    3.synchronized匿名monitor对象

    4.b变量

    5.synchronized匿名throwable对象

    =》 第3个和第4个变量生命周期只在synchronized块中,所以出了synchronized块后,变量c放入局部变量表第3个位置,变量d放入局部变量表第4个位置。(只根据定义确认变量生命周期,不管在定义域内后面是否会使用)

    说下这个主要是为了后面理解分析栈帧时,能清楚的知道栈帧局部变量表在每一行处时存储的值的意义。

    二.  先找一段openjdk关于对象头markword描述的源码说明

    d48c9b0fc1b4

    图3

    index[0,1]:锁标识/模式标识,一方面确定是否有锁,一方面确认字段解析方式

    1.index[0,1] = 01 无锁

    [header      | 0 | 01]  unlocked           regular object header

    此时需要判断 index[2],是否有偏向标识

    1.1 index[2] = 0  无偏向,此时字段结构为:

    unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)

    1.2 index[2] = 1 偏向,此时字段结构为:

    JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)

    ==》

    //    [JavaThread* | epoch | age | 1 | 01]       lock is biased toward given thread

    //    [0           | epoch | age | 1 | 01]       lock is anonymously biased

    2.index[0,1] = 00  轻量级锁

    [ptr             | 00]  locked             ptr points to real header on stack

    3.index[0,1] = 10  重量级锁

    [ptr             | 10]  monitor            inflated lock (header is wapped out)

    4.index[0,1] = 11  GC markSweep标志,标记对象不可用

    [ptr             | 11]  marked             used by markSweep to mark an object not valid at any other time

    三. 先看个简单的demo

    public class TestHashCode{

    public static void main(String[] args){

    breakM();

    }

    public static void breakM(){

    LockBean lock = new LockBean();    //_mark = 0x0000000000000005    0101    无锁,任意偏向

    int a = lock.hashCode();                    //_mark = 0x0000007fbe847c01      0001    无锁,无偏向,hashcode

    synchronized(lock){                           //_mark = 0x0000700004b13870     0000    轻量级锁,栈顶指针

    int b = lock.hashCode();                //_mark = 0x0000700004b13870     0000

    }

    int c = lock.hashCode();                    //_mark = 0x0000007fbe847c01

    int d = 32;

    }

    }

    breakM方法里先定义了一个对象,再取该对象hashcode值,然后对该对象加锁。后面_mark表示执行完这一行后该对象的markword值,是很清晰的一段markword变更流程,我们下面做具体分析。

    四. 轻量级锁执行过程分析

    代码进入同步块时,如果此同步对象没有被锁定(markword锁标志为01),jvm首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的MarkWord拷贝(8byte存当前markword值拷贝,8byte存当前对象地址)。

    =》如果此同步对象已被锁定(markword锁标志为00/10),则进入锁等待

    然后尝试使用CAS将当前锁对象的MarkWord更新为指向Lock Record的指针。如果更新成功,则这个线程就获取了该对象的锁,并且该对象MarkWord的锁标识位(最后2bit)将转变为00,即表示该对象当前处于轻量级锁状态。

    若更新操作失败,jvm会先检查该对象MarkWord是否已经是指向当前线程的栈帧,若是则说明已经获取过锁了,就直接进入同步块执行。否则说明当前锁对象已经被其他线程抢占了。

    如果有多个线程同时争用同一个锁,此时轻量级锁要膨胀为重量级锁,锁标志位状态值变为10,Mark Word中存储的是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。(膨胀为重量级锁后,会生成一个新的锁Lock Record记录。锁对象markword指向新生成的重量级锁Lock Record记录,并把锁标志位置为10。)

    轻量级锁的解锁也是通过CAS来做的,将Lock Record存储的之前markword值,CAS更新回锁对象的MarkWord中,更新成功则整个同步块完成;更新失败,则说明有其他线程尝试过获取该锁,那就要在释放锁的同时唤醒被挂起的线程。

    =》多线程争用锁时,锁对象markword会变更为指向 新的重量级锁Lock Record地址;=》锁对象markword指向的重量级锁Lock Record地址 和 当前线程栈顶Lock Record地址不一致,说明锁膨胀为重量级锁了,存在多线程竞争。则当前线程释放锁,同时唤醒被挂起的线程。

    五. 单线程下加锁前后锁对象markword分析

    d48c9b0fc1b4

    图4

    break1堆栈:

    d48c9b0fc1b4

    图5

    当前markword = 0x0000007fbe847c01,无锁/无偏向/hashcode,栈帧顶部无 Lock Record 记录;

    break2堆栈:

    d48c9b0fc1b4

    图6

    当前markword = 0x000070000cd93868,指向栈顶新加的 Lock Record 记录;

    Lock Record记录:8byte 存锁对象之前的markword值,8byte 存指向锁对象的指针;

    =》单线程场景下,不存在任何锁竞争/CAS失败,一切复合预期;

    六. 多线程下加锁前后锁对象markword分析

    d48c9b0fc1b4

    图7

    6.1 main线程到break2,new线程到break1 堆栈:

    d48c9b0fc1b4

    图8

    main线程获取锁,main线程当前栈帧为获取到锁记录的状态;new线程还未进入同步块;

    当前锁对象的markword指向main线程栈顶Lock Record记录;

    6.2 main线程先到break2(已获取锁,在同步块中未退出),new线程后到break2(尝试获取锁):

    d48c9b0fc1b4

    图9

    可以看到,new线程进入同步块代码后,new线程的栈帧顶部也添加了 Lock Record 记录。

    Q:=》new线程的 Lock Record前8byte存的值是 0x00..03,这个值是怎么来的?

    A:=》最后2bit 11 在markword定义里表示对象处于GC mark不可用状态。在发生锁争用时锁膨胀为重量级锁,线程进入锁等待状态后,线程栈顶的Lock Record其实存在已经没有意义了。

    main线程栈帧保持不变;

    锁对象的markword值更新为 0x00007fcb19846dfa,既不指向main线程Lock Record,也不指向new线程Lock Record。

    此时markword锁标志位为 10,意味着由于多线程获取锁,锁升级为 重量级锁 了,其他位的值,是指向重量级锁的指针值。

    取其他位的值,去掉锁标志位的值并用00填充,得到重量级锁指针 0x00007fcb19846df8,查看该重量级锁记录,可以看到,也是存的一个Lock Record记录,前8byte存的锁对象之前无锁时的markword值,后8byte存的指向锁对象的指针;

    Q:=》重量级锁Lock Record中前8byte存放的锁对象markword值,这个值是怎么取到或计算的?

    A: 取的当前锁对象所指向的Lock Record记录中存放的markword值。

    轻量级锁时,在同步块中运行对象hashcode方法后,由于要改变锁对象markword值(存储hashcode值),jvm的做法是:生成一个新的Lock Record记录(栈帧外),该新的Lock Record记录中存放锁对象含hashcode的markword值,然后将锁对象markwrd指向新的Lock Record记录;退出同步块时,也是从这里恢复锁对象markword值;

    若已经是重量级锁,则运行锁对象hashcode方法后,将更新重量级锁Lock Record记录中的markword值为含hashcode的markword值。

    6.3 main线程出了同步块,new线程在break2(已经获取到锁):

    d48c9b0fc1b4

    图10

    main线程释放锁之后,new线程获取到锁,此时只有new线程占用锁,但是由于已经膨胀为重量级锁了,此时new线程获取的锁记录依然是这个重量级锁。

    main线程释放锁后,main线程的栈顶Lock Record发生了一点变化,Lock Record中原本指向锁对象地址的指针,现在变为指向空地址的指针了。

    6.4 main线程出了同步块,new线程出了同步块:

    锁对象markword恢复成最后指向的Lock Record记录中前8byte中存储的锁对象进入同步块之前的markword值,由此,锁对象恢复到进入同步块之前的状态。

    至此,对markword的含义有了更清晰的理解。

    展开全文
  • 详解Mark Word

    2021-08-11 22:51:52
    对于一个Java对象来说,对象头是极其重要的,对象头主要有对象标记和类型指针两部分组成,接下来需要重点看下对象标记,也就是Mark Word,下面是针对64位JVM(那也就是说还有32位的,考虑到现在基本都是64的了,32的...

    对于一个Java对象来说,对象头是极其重要的,对象头主要有对象标记和类型指针两部分组成,接下来需要重点看下对象标记,也就是Mark Word,下面是针对64位JVM(那也就是说还有32位的,考虑到现在基本都是64的了,32的直接pass掉)的Mark Word来说的,它的组成是这样的:

    在这里插入图片描述

    一个Java对象由对象头,实例数据和对齐填充组成,其中对象头是极其重要的,对象头是由对象标记Mark Word和类型指针组成,其中又以Mark Word最重要,对于Mark Word它占8个字节也就是64位!

    ok,以上内容都清楚吧!那咱就继续!

    接下来咱就以锁这个切入点去详细的看看这个Mark Word!

    我们都知道在并发情况下也就是要解决线程安全的话要加锁,其实这个加锁就是对对象加锁,那如何去判断或者说知道这个对象加锁了没有或者加的什么锁,这些个信息其实就保存在对象头中的Mark Word中,上面这张图就是关于一个Mark Word的具体结构,可以看出,一个Java对象其实在不同的状态下,它是不一样的,主要是有不同的锁,比如没有锁,轻量级锁,还有什么偏向锁,重量级锁等等,不同的锁,在Mark Word中就会有不同的状态标记。


    JOL的引入

    这就是一个空类,啥也没有,要怎么看这个对象标记Mark Word呢?这里我们需要加入一个依赖,也就是你的Java项目是个maven项目,可以引入pom依赖,然后添加以下依赖:

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

    这个依赖是干嘛的呢?这是一个代码工具叫做JOL,也就是Java Object Layout,主要就是用来分析java虚拟机中的对象布局的,也就是在java对象在虚拟机中的大小和分布,ok,接下来加入上述这个依赖之后我们就可以这样操作

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

    这样我们就可以看出使用虚拟机的一些情况,看打印输出:
    在这里插入图片描述
    接着我们就来看下我们创建的那个空类是怎样的一个情况,可以这样操作:

    public static void main(String[] args) {
            MyClass myClass = new MyClass();
            //打印出相关的对象头信息
            System.out.println(ClassLayout.parseInstance(myClass).toPrintable());
        }
    

    得出以下内容:
    在这里插入图片描述
    OK了,相关信息打印出来了,记住这是myclass的相关内部信息,那这些都是啥呢?看上面的图,是不是有个“Object header” 就是对象头

    在这里插入图片描述
    这是啥?主要就是来看我们的一个空对象占多大空间,之前也说了,对象头包括对象标记8字节和类型指针8字节,我们先来看对象标记,是不是这个:

    在这里插入图片描述
    也就是起始位置是0,然后前进4个字节,此时的起始位置就变成了4,接着再前进4个字节,那此时起始位置就成了8,此时对象标记就占8个字节,这没啥问题,接着我们看,起始位置是8,然后继续前进4字节,到达12的时候,其实此时类型指针就结束了,也就是8到12,类型指针就占了4字节

    也就是此时这个对象就占据12个字节,可是不是说占据16个字节才对吗?我们知道java对象所占大小需要是8字节的整数倍,那为了满足这块,就又对齐填充了4字节,最终达到16字节 。

    为什么会这样?其实是因为这里发生了指针压缩,在说这个指针压缩之前,需要明白这样的一东西:

    在这里插入图片描述

    public class MyClass {
        char c='a';
        public static void main(String[] args) {
            MyClass myClass = new MyClass();
            //打印出相关的对象头信息
            System.out.println(ClassLayout.parseInstance(myClass).toPrintable());
        }
    }
    

    在空类中加入一个char类型,再看下结果:
    在这里插入图片描述
    接着我们再看一个:

    public class MyClass {
        char c='a';
        int s=100;
    }
    

    在这里插入图片描述

    为什么是6,一定要想明白了,记住8的倍数哦!对了,Instance size指的是一个对象有多大

    指针压缩

    接着我们再说之前指针压缩的问题,也就是类型指针是占8个字节的,可是实际上只占了4个字节,这就是指针压缩的问题,这里我们需要这么一个jvm参数,就是它:

    java -XX:+PrintCommandLineFlags -version
    

    也就是默认开启了指针压缩,所以我们的类型指针才变成了4字节。

    value值分析

    在这里插入图片描述

    展开全文
  • 概述对象实例由对象头、实例数据组成,其中对象头包括markword和类型指针,如果是数组,还包括数组长度;| 类型 | 32位JVM | 64位JVM|| ------ ---- | ------------| --------- || markword | 32bit | 64bit || 类型...

    概述

    对象实例由对象头、实例数据组成,其中对象头包括markword和类型指针,如果是数组,还包括数组长度;

    | 类型 | 32位JVM | 64位JVM|

    | ------ ---- | ------------| --------- |

    | markword | 32bit | 64bit |

    | 类型指针 | 32bit |64bit ,开启指针压缩时为32bit |

    | 数组长度 | 32bit |32bit |

    1554529210ec94b284ff376e9d1712be.png

    header.png

    36c26a1d8a2e9ea584bd8686bbc85b68.png

    compressed_header.png

    可以看到

    开启指针压缩时,markword占用8bytes,类型指针占用8bytes,共占用16bytes;

    未开启指针压缩时,markword占用8bytes,类型指针占用4bytes,但由于java内存地址按照8bytes对齐,长度必须是8的倍数,因此会从12bytes补全到16bytes;

    数组长度为4bytes,同样会进行对齐,补足到8bytes;

    另外从上面的截图可以看到,开启指针压缩之后,对象类型指针为0xf800c005,但实际的类型指针为0x7c0060028;那么指针是如何压缩的呢?

    实际上由于java地址一定是8的倍数,因此将0xf800c005*8即可得到实际的指针0x7c0060028,关于指针压缩的更多知识可参考官方文档。

    markword结构

    markword的结构,定义在markOop.hpp文件:

    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)

    [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 by markSweep to mark an object

    由于目前基本都在使用64位JVM,此处不再对32位的结构进行详细说明:

    偏向锁标识位

    锁标识位

    锁状态

    存储内容

    0

    01

    未锁定

    hash code(31),年龄(4)

    1

    01

    偏向锁

    线程ID(54),时间戳(2),年龄(4)

    00

    轻量级锁

    栈中锁记录的指针(64)

    10

    重量级锁

    monitor的指针(64)

    11

    GC标记

    空,不需要记录信息

    此处,有几点要注意:

    如果对象没有重写hashcode方法,那么默认是调用os::random产生hashcode,可以通过System.identityHashCode获取;os::random产生hashcode的规则为:next_rand = (16807seed) mod (2*31-1),因此可以使用31位存储;另外一旦生成了hashcode,JVM会将其记录在markword中;

    GC年龄采用4位bit存储,最大为15,例如MaxTenuringThreshold参数默认值就是15;

    当处于轻量级锁、重量级锁时,记录的对象指针,根据JVM的说明,此时认为指针仍然是64位,最低两位假定为0;当处于偏向锁时,记录的为获得偏向锁的线程指针,该指针也是64位;

    We assume that stack/thread pointers have the lowest two bits cleared.

    ObjectMonitor* monitor() const {

    assert(has_monitor(), "check");

    // Use xor instead of &~ to provide one extra tag-bit check.

    return (ObjectMonitor*) (value() ^ monitor_value);//monitor_value=2,value最右两位为10,因此异或之后最右两位为0

    }

    JavaThread* biased_locker() const {

    assert(has_bias_pattern(), "should not call this otherwise");

    return (JavaThread*) ((intptr_t) (mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place))));

    //~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place)为11111111111111111111110010000000,计算后的结果中,低10位全部为0;

    }

    由于java中内存地址都是8的倍数,因此可以理解为最低3bit为0,因此假设轻量级和重量级锁的最低2位为0是成立的;但为什么偏向锁的最低10位都是0?查看markOop.hpp文件,发现有这么一句话:

    // Alignment of JavaThread pointers encoded in object header required by biased locking

    enum { biased_lock_alignment = 2 << (epoch_shift + epoch_bits)

    //epoch_shift+epoch_bits=10

    };

    thread.hpp中重载了operator new:

    void* operator new(size_t size) { return allocate(size, true); }

    // ======= Thread ========

    // Support for forcing alignment of thread objects for biased locking

    void* Thread::allocate(size_t size, bool throw_excpt, MEMFLAGS flags) {

    if (UseBiasedLocking) {

    const int alignment = markOopDesc::biased_lock_alignment;//10

    size_t aligned_size = size + (alignment - sizeof(intptr_t));

    void* real_malloc_addr = throw_excpt? AllocateHeap(aligned_size, flags, CURRENT_PC)

    : os::malloc(aligned_size, flags, CURRENT_PC);

    void* aligned_addr = (void*) align_size_up((intptr_t) real_malloc_addr, alignment);

    assert(((uintptr_t) aligned_addr + (uintptr_t) size) <=

    ((uintptr_t) real_malloc_addr + (uintptr_t) aligned_size),

    "JavaThread alignment code overflowed allocated storage");

    if (TraceBiasedLocking) {

    if (aligned_addr != real_malloc_addr)

    tty->print_cr("Aligned thread " INTPTR_FORMAT " to " INTPTR_FORMAT,

    real_malloc_addr, aligned_addr);

    }

    ((Thread*) aligned_addr)->_real_malloc_address = real_malloc_addr;

    return aligned_addr;

    } else {

    return throw_excpt? AllocateHeap(size, flags, CURRENT_PC)

    : os::malloc(size, flags, CURRENT_PC);

    }

    }

    如果开启了偏移锁,在创建线程时,线程地址会进行对齐处理,保证低10位为0

    实例数据

    实例数据中主要包括对象的各种成员变量,包括基本类型和引用类型;static类型的变量会放到java/lang/Class中,而不会放到实例数据中;

    对于引用类型的成员(包括string),存储的指针;对于基本类型,直接存储内容;通常会将基本类型存储在一起,引用类型存储在一起;

    例如类Test的成员定义如下:

    private static Test t1=new Test();

    private Test t2;

    private int a=5;

    private Integer b=7;

    private String c="112";

    private BigDecimal d=new BigDecimal("5");

    private long e=9l;

    9a8845ac51bbb70e9713a97d403d0a82.png

    body.png

    可以看到long e、int a为基本类型,存储在一起;其它的引用类型存储在一起;int占用4bytes,不足8bytes,自动补足到8bytes;

    补充知识:java的对象物理结构,以及对象头中MarkWord与锁的关系

    java 对象头

    我们都知道,Java对象存储在堆(Heap)内存。那么一个Java对象到底包含什么呢?概括起来分为对象头、对象体和对齐字节。

    如下图所示:

    a4d4f8ed3c4c31c8a8c4265f36734d1f.png

    对象的几个部分的作用:

    1.对象头中的Mark Word(标记字)主要用来表示对象的线程锁状态,另外还可以用来配合GC、存放该对象的hashCode;

    2.Klass Word是一个指向方法区中Class信息的指针,意味着该对象可随时知道自己是哪个Class的实例;

    3.数组长度也是占用64位(8字节)的空间,这是可选的,只有当本对象是一个数组对象时才会有这个部分;

    4.对象体是用于保存对象属性和值的主体部分,占用内存空间取决于对象的属性数量和类型;

    5.对齐字是为了减少堆内存的碎片空间(不一定准确)。

    了解了对象的总体结构,接下来深入地了解对象头的三个部分。

    一、Mark Word(标记字)

    4340dc5af05fc987f6dd421aae25eab1.png

    以上是Java对象处于5种不同状态时,Mark Word中64个位的表现形式,上面每一行代表对象处于某种状态时的样子。其中各部分的含义如下:

    lock:2位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了lock标记。该标记的值不同,整个Mark Word表示的含义不同。biased_lock和lock一起,表达的锁状态含义如下:

    4f06ede1a392e08905dd2d76573607d4.png

    biased_lock:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。lock和biased_lock共同表示对象处于什么锁状态。

    age:4位的Java对象年龄。在GC中,如果对象在Survivor区复制一次,年龄增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。

    identity_hashcode:31位的对象标识hashCode,采用延迟加载技术。调用方法System.identityHashCode()计算,并会将结果写到该对象头中。当对象加锁后(偏向、轻量级、重量级),MarkWord的字节没有足够的空间保存hashCode,因此该值会移动到管程Monitor中。

    thread:持有偏向锁的线程ID。

    epoch:偏向锁的时间戳。

    ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针。

    ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针。

    c0af692dd85922098670bfe8d93c6a02.png

    二、Klass Word(类指针)

    这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM通过这个指针确定对象是哪个类的实例。该指针的位长度为JVM的一个字大小,即32位的JVM为32位,64位的JVM为64位。

    如果应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的JVM将会比32位的JVM多耗费50%的内存。为了节约内存可以使用选项+UseCompressedOops开启指针压缩,其中,oop即ordinary object pointer普通对象指针。

    开启该选项后,下列指针将压缩至32位:

    每个Class的属性指针(即静态变量)

    每个对象的属性指针(即对象变量)

    普通对象数组的每个元素指针

    当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。

    三、数组长度

    如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着JVM架构的不同而不同:32位的JVM上,长度为32位;64位JVM则为64位。

    64位JVM如果开启+UseCompressedOops选项,该区域长度也将由64位压缩至32位。

    以上这篇浅谈java对象结构 对象头 Markword就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持云海天教程。

    原文链接:https://www.jianshu.com/p/ec28e3a59e80

    展开全文
  • Mark Word 详解

    2021-10-17 15:07:57
    Mark Word 在32位 JVM 中: Mark Word 在64位 JVM 中: 锁标志位(lock) 区分锁状态,11时表示对象待GC回收状态, 只有最后2位锁标识(11)有效。 biased_lock 是否偏向锁,由于无锁和偏向锁的锁标识都是 01,没...

    Mark Word 在32位 JVM 中:
    在这里插入图片描述
    Mark Word 在64位 JVM 中:
    在这里插入图片描述

    • 锁标志位(lock)
      区分锁状态,11时表示对象待GC回收状态, 只有最后2位锁标识(11)有效。

    • biased_lock
      是否偏向锁,由于无锁和偏向锁的锁标识都是 01,没办法区分,这里引入一位的偏向锁标识位。

    • 分代年龄(age)
      表示对象被GC的次数,当该次数到达阈值的时候,对象就会转移到老年代。

    • 对象的hashcode(hash)
      运行期间调用System.identityHashCode()来计算,延迟计算,并把结果赋值到这里。当对象加锁后,计算的结果31位不够表示,在偏向锁,轻量锁,重量锁,hashcode会被转移到Monitor中。

    • 偏向锁的线程ID(JavaThread):
      偏向模式的时候,当某个线程持有对象的时候,对象这里就会被置为该线程的ID。 在后面的操作中,就无需再进行尝试获取锁的动作。

    • epoch
      偏向锁在CAS锁操作过程中,偏向性标识,表示对象更偏向哪个锁。

    • ptr_to_lock_record
      轻量级锁状态下,指向栈中锁记录的指针。当锁获取是无竞争的时,JVM使用原子操作而不是OS互斥。这种技术称为轻量级锁定。在轻量级锁定的情况下,JVM通过CAS操作在对象的标题字中设置指向锁记录的指针。

    • ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针。如果两个不同的线程同时在同一个对象上竞争,则必须将轻量级锁定升级到Monitor以管理等待的线程。在重量级锁定的情况下,JVM在对象的ptr_to_heavyweight_monitor设置指向Monitor的指针。

    展开全文
  • 概述对象实例由对象头、实例数据组成,其中对象头包括markword和类型指针,如果是数组,还包括数组长度;| 类型 | 32位JVM | 64位JVM|| ------ ---- | ------------| --------- || markword | 32bit | 64bit || 类型...
  • 深入理解Java的对象头mark word

    千次阅读 2021-02-07 22:22:44
    上一篇博客我们编译了Linux源码来证明了Java中有偏向锁,但是我们从周志明大佬的《深入理解java虚拟机》的书中知道,我们可以通过分析Java对象头中MarkWord来查看是那种锁,下面是32位JVM的对象中的Mark Word图,...
  • 其中普通对象布局:markword8Bytes用于标记锁信息、GC信息、IdentityHashCode等Class Pointer 类指针4Bytes用于标记该对象是哪个Class的实例开启内存压缩(-XX:+UseCompressedClassPointer)后为4字节,不开启内存压缩...
  • 因为前一段时间看到 NetAnalyzer 在Windows10系统下UI表现惨不忍睹,所以利用一段时间为了学习一下WPF相关的内容,于是停停...代码下载地址展示与说明代码同步编辑博客发布代码说明博客发布MarkWord支持博客园和CSD...
  • Java工具结构与锁实现原理及MarkWord详解https://www.pianshen.com/article/2382167638/我们都知道,Java工具存储在堆(Heap)内存。那么一个Java工具到底包罗什么呢?归纳综合起来分为工具头、工具体和对齐字节。如下...
  • java对象头的Mark Word

    2021-07-30 23:15:51
    //markword 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) //markword 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) //klass pointer 类元数据 43 c1...
  • JVM的markWord内容

    2021-07-07 20:27:58
    三、synchronized的锁升级过程中,markWord内容 锁状态:  当一个对象刚开始new出来时,该对象是无锁状态。此时偏向锁位为0,锁标志位01  如果有线程上锁:  指的就是把markword的线程ID改为自己线程ID的过程  ...
  • 以上是 Java对象处于5种不同状态时,Mark Word中 64位的表现形式,上面每一行代表对象处于某种状态时的样子。其中各部分的含义如下:【1】lock:2位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息...
  • 文章目录一、as if serial二、对象的创建过程三、对象占用多少字节MarkWord调用hasCode导致锁升级四、GC何为垃圾怎么发现垃圾引用计数器GC Root(可达性分析算法)GC算法标记-清除算法复制算法标记-压缩分代算法 ...
  • 目前的synchronized包含重量级锁,轻量级锁和偏向锁 synchronized可以保证可见性和原子性但是不能保证有序性 synchronized是可重入锁,重入次数必须得记录,解锁时要对应,记录的位置就在MarkWord里面 synchronized...
  • 再说对象头的MarkWord⑦. 聊聊Object obj = new Object() ①. 对象在堆内存中的存储布局 ①. 对象内部结构分为:对象头、实例数据、对齐填充(保证8个字节的倍数)。 ②. 对象头分为对象标记(markOop)和类元信息...
  • Java openjdk 提供jol 工具,可以查看class的头信息。 下载 jol 工具包:下载地址 选择一个版本,进去后下载 jol-cli-.-full.jar 一定要下载full 的jar 导入包。 打印: System.out.println(ClassLayout....
  • 占据2bits一样就不能区分是否偏向了吗?当然不是,还有一个1bit的biased_lock_bits用于标记是否处于偏向状态:
  • Mark word记录了对象和锁有关的信息,当某个对象被synchronized关键字当成同步锁时,那么围绕这个锁的一系列操作都和Mark word有关系。 Mark Word在32位虚拟机的长度是32bit、在64位虚拟机的长度是64bit。 Mark ...
  • //markword 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) //markword 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) //klass pointer类元数据 fa bc ...
  • Java Mark Word

    2021-11-29 10:51:32
    ----------------------------------------------------------------------------------| | Mark Word (64 bits) | Klass Word (64 bits) |默认开启指针压缩 |----------------------------------------------------...
  • 如果你已经知道什么是Mark Word,那我也希望你都好好阅读下本篇文章,因为你有可能发现不一样的切入点来帮助你更加深入的了解Mark Word,这对你来说是个很好的巩固所学知识的机会,同时也是一场技术交流,一个有逼格...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,488
精华内容 19,795
关键字:

markword