Synchronized关键字原理分析一

本文深入探讨Java对象头的内部结构,包括markword和klassword的作用,以及它们如何影响对象的状态,如无锁、轻量锁和重量锁。通过JOL工具分析对象布局,揭示了对象头如何存储hashcode、GC信息和锁状态,为理解Java内存模型和锁升级机制提供了详实的资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在说synchronized内置锁之前,首先先来研究一下对象头,因为所谓锁便是给对象一个标识,而这个标识便是存在对象头当中。
对象:对象头 + 实例数据 + 填充数据
对象头:markword + classpoint

一、对象头

接下来看下面的一张图
在这里插入图片描述
意思是java的对象头在对象的不同状态下会有不同的表现形式,主要有三种状态,无锁状态、加锁状
态、gc标记状态。那么我可以理解java当中的取锁其实可以理解是给对象上锁,也就是改变对象头的状
态,如果上锁成功则进入同步代码块。但是java当中的锁有分为很多种,从上图可以看出大体分为偏向
锁、轻量锁、重量锁三种锁状态。这三种锁的效率完全不同,我们只有合理的设计代码,才能合理的利
用锁、那么这三种锁的原理是什么?所以我们需要先研究这个对象头。

二、java对象的布局以及对象头
1.JOL来分析java的对象布局

首先maven添加JOL的依赖

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

A.java 源码

package com.enjoy.entity; public class A { }

JOLExample1.java

package com.enjoy.test; 
import com.enjoy.entity.A; 
import org.openjdk.jol.info.ClassLayout; 
import org.openjdk.jol.vm.VM;

public class Test1 { static A a= new A(); 
	public static void main(String[] args) { 		
      	System.out.println(VM.current().details()); 
      	System.out.println(ClassLayout.parseClass(A.class).toPrintable());
      	System.out.println(ClassLayout.parseInstance(a).toPrintable()); 
	} 
}

运行结果:
在这里插入图片描述
分析结果1
整个对象一共16B,其中对象头(Object header)12B,还有4B是对齐的字节(因为在64位虚拟机上
对象的大小必须是8的倍数),由于这个对象里面没有任何字段,故而对象的实例数据为0B;两个问题
1、什么叫做对象的实例数据呢?
2、那么对象头里面的12B到底存的是什么呢?
首先要明白什么对象的实例数据很简单,我们可以在A当中添加一个boolean的字段,大家都知道
boolean字段占1B,然后再看结果
A.java

package com.enjoy.entity; public class A { boolean f; }

运行结果2
在这里插入图片描述
分析结果2
整个对象的大小还是没有改变一共16B,其中对象头(Object header)12B,boolean字段f(对象的
实例数据)占1B、剩下的3B就是对齐字节。由此我们可以认为一个对象的布局大体分为三个部分分别
是对象头(Object header)、对象的实例数据字节对齐;

对象布局:
在这里插入图片描述
接下来讨论第二个问题,对象头为什么是12B?这个12B当中分别存储的是什么呢?

2、大小端模式

再接着往下研究对象的时候先要高明白大小端存储,一般家用笔记本都是小端模式,那么什么是小端模式呢?
高字节存在高地址,低字节存在低地址
其实说白了它就是倒着存储的,比如int a = 1占4个字节,转化成二进制就是00000000 00000000 00000000 00000001,小端存储就是
00000001 00000000 00000000 00000000。
在这里插入图片描述

3、对象头的规范

link.
首先引用openjdk文档当中对对象头的解释
上述引用中提到一个java对象头包含了2个word,并且好包含了堆对象的布局、类型、GC状态、同步状
态和标识哈希码,具体怎么包含的呢?又是哪两个word呢?
对象头的结构大概如下图
在这里插入图片描述
mark word为第一个word根据文档可以知他里面包含了锁的信息,hashcode,gc信息等等,第二个word是什么呢?
klass word为对象头的第二个word主要指向元空间中对象的数据,他是一个指针;

假设我们理解一个对象头主要上图两部分组成(数组对象除外,数组对象的对象头还包含一个数组长度),那么一个java的对象头多大呢?我们从JVM的源码注释中得知到一个mark word一个是64bit,那么klass的长度是多少呢?(12-8=4byte)所以我们需要想办法来获得java对象头的详细信息,验证一下他的大小,验证一下里面包含的信息是否正确。
根据上述利用JOL打印的对象头信息可以知道一个对象头是12B,其中8B是mark word 那么剩下的4B就 是klass word了,和锁相关的就是mark word了,那么接下来重点分析mark word里面信息
在无锁的情况下markword当中的前56bit存的是对象的hashcode,那么来验证一下

java代码

public class Test1 { static A a= new A(); 
//-XX:BiasedLockingStartupDelay=0 public static void main(String[] args) throws InterruptedException { 
	log.debug("----hashcode before"); 
	log.debug(ClassLayout.parseInstance(a).toPrintable()); //转化成16进制,方便比较
	log.debug(Integer.toHexString(a.hashCode())); 
	log.debug("----hashcode after"); //计算完hashcode之后的a对象的布局
	log.debug(ClassLayout.parseInstance(a).toPrintable()); 
	} 
}

运行结果
在这里插入图片描述
分析结果3:
没有进行hashcode之前的对象头信息,可以看到2b-8b之前的的56bit是没有值,打印完hashcode之后行就有值了,为什么是2-8B,不应该是1-7B呢?因为是小端存储。那么第一个字节当中的八位分别存的就是分带年龄、偏向锁信息,和对象状态,这个8bit分别表示的信息如下图;这个图会随着对象状态改变,下图是无锁状态下
在这里插入图片描述
其中的是否可偏向标识在无锁情况下会根据是否计算hashcode而变化;因为如果计算了hashcode之后对象便变得不可偏向;为什么?
关于对象状态一共分为五种状态,分别是无锁不可偏向、无锁可向锁、轻量锁、重量锁、GC标记,那么2bit,如何能表示五种状态(2bit最多只能表示4中状态分别是:01,00,10,11),jvm做的比较好的是把是否可偏向表示为一个状态,然后根据图中偏向锁的标识再去标识是可偏向的还是不可偏向的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值