一、概述

研究java对象头的目的是详细分析Java的synchronized锁的升级过程,因为synchronized在锁升级的时候,就是依赖对象头的信息来决定的。本博文针对64位的操作系统来对Java对象头进行详解。

二、详细分析

1. 用户态与内核态

内核态与用户态是操作系统的两种运行级别,当程序运行在3级特权级上时, 就可以称之为运行在用户态,因为这是最低特权级,是普通的用户进程运 行的特权级,大部分用户直接面对的程序都是运行在用户态;当程序运行在0级特权级上时,就可以称之为运行在内核态。运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态。

操作系统对于用户态线程(也叫纤程)的管理是由系统内核kernel来管理的。JVM中的线程与操作系统的原生线程对应关系一般是1:1的关系。对应关系除了1:1,也有m:1,也就是用户态的m个线程只对应内核态中的1个线程;m:n的关系,出现在go语言的协程中。计算机上的程序包括JVM是运行在计算机的用户空间上,运行在用户空间上的程序的特点就是,在进行一些敏感操作(比如网络读写、读写硬盘、内存映射等)的时候,需要通过工作在内核空间的系统内核kernel进行系统调用,这样的目的是使得操作系统更加的健壮。

synchronized什么是重量级琐?申请锁资源的时候需要通过操作系统内核kernel进行系统调用,这个调用过程将会由用户空间切换到内核空间,就是重量级琐;轻量级锁是只在用户空间就完成对用户空间锁的调度管理,直接生成汇编指令,不需要经过系统内核。jdk1.5推出JUC包,利用CAS采用的轻量级锁自旋锁来替代一些需要锁的地方,极大的提高效率。

2. CAS操作原理

CAS全拼又叫做compareAndSwap,从名字上的意思就知道是比较交换的意思。它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。隐藏的问题以及解决办法参考前文《我的jdk源码(二十四):AtomicInteger类和CAS机制》。

在linux_x86这个具体系统平台的代码中源码如下:

由两个红框的内容可以看到,底层的实现是CPU指令原语lock和cmpxchg组合执行的,值得一提的是cmpxchg指令虽然支持CAS,却并不是原子性的操作,所以我们看下LOCK_IF_MP是怎么操作的,实现的源码如下:

MP是multi processor的缩写,意为多处理器。所以在多核处理时,lock指令会锁住系统总线,进而锁定一个北桥信号。java中锁底层基本都是利用lock汇编指令完成。

3. JOL工具

(1) Java Object Layout,引入jar包,就可以在代码中观察跟踪synchronized的琐升级过程。

(2) 代码实现如下:

(3) 打印结果如下:

通过上面可以清晰的看到一个对象在内存之中,有哪些值,由哪些东西构成。

(4) 对象在内存中的存储布局:

mackword:存放琐标志位、偏向锁位、线程ID、分代年龄等内容,下面详细解读。

Klassword:也就是calss pointer,存储对象所属类的地址,就是为了标记到底是什么类的实例。

instance data:保存成员变量实例对象。

padding:是为了保证整个内容加起来能被8个字节(Byte)整除而填充的空间,JVM读数据是一块一块读的,这样做效率是最高的。

length:如果对象是数组,需要存储数组长度。

(5) 根据观察到的对象头结构如下:

详细解释:

(1) 当我们创建一个无锁态对象的时候:25位没有用;31位装的identity Hashcode,但是只有在被调用的时候,才填充,没有调用的时候是空的;1位没有使用的;4位分代年龄(解释在下面);1位偏向锁位;2位锁标志位。

(2) 偏向锁的时候:54位存下当前线程的ID;2位存批量撤销Epoch;1位没有使用;4位分代年龄;1位偏向锁位;2位锁标志位。

(3) 自旋锁:62位指向线程中的Lock Record的指针。Lock Record与锁重入有关,synchronize默认是可重入的。自旋锁在竞争锁的时候,会在自己的内存中创建一个Lock Record对象,抢到锁对象的资源时,锁对象头存的就是这个线程的Lock Record对象的指针,所以在重入的时候,会再创建一个Lock Record对象,利用Lock Record来记录到底琐了多少次。解锁的时候,就将一个Lock Record移除。

(4) 重量级琐:重量级琐是在C++代码层面进行的,会生成一个ObjectMonitor对象,这个对象中记录了一系列的队列,如下图:

(5) 分代年龄:JVM有10种垃圾回收器,前面7种都涉及分代年龄,采用分代算法。当我们创建一个对象的时候,把它放在年轻代中,每经过一次垃圾回收后年龄就+1,也就是垃圾回收无法回收掉这个对象,它的年龄就会不断的增长,到达15,因为4个字节最大为15,就转到老龄代,年轻代的回收就不再对它进行回收。

4. synchronized琐升级过程

注意:

(1) 琐升级路线:new -> 偏向锁 -> 轻量级琐(自旋锁、自适应自旋锁) -> 重量级琐

(2) 如果偏向锁没有启动,那么new出来的对象是普通对象;如果偏向锁已经启动,那么new出来的对象就是匿名偏向对象。

(3) 偏向锁什么情况下转为轻量级琐呢?只要有2个线程竞争同一个琐资源,所有线程都升级为轻量级琐,也就是都会自旋抢占琐资源。

(4) 自旋锁在竞争资源的时候,也是CAS操作,用CAS的方式修改琐对象的mackword。

(5) 自旋锁升级为重量级琐需要符合什么条件呢?jdk1.6以前,某个线程自旋次数上限达到默认的10次,会升级为重量级锁,因为一直自旋消耗CPU资源;自旋线程数量达到了默认是系统的CPU核数的1/2的时候,全部升级为重量级琐进入等待队列。jdk1.6之后,jdk提供了自适应自旋,jdk根据每个线程的运行情况来判断是否需要升级。

5. 查看设置的偏向锁参数

JVM参数分为3类:

第一类: 以-开头的,叫做标准参数,所有的JVM版本都支持,如下图:

第二类:以-X开头的,叫做非标准参数,是有版本差异的,有的版本支持,有的版本不支持。

第三类:以-XX开头的,是可以配置的参数。

利用以下命令,打印所有可以设置参数的项和值打印出来:

利用以下命令,可以查看一共有多少行:

利用以下命令,可以查看所有和偏向锁有关系的参数:

上图解释我们在创建一个对象的时候,即使默认启用了偏向锁,创建出来的对象也是无琐的,但是如果我们在创建对象前先设置5秒的延迟,再创建出来的对象就是偏向锁状态的,也就是匿名偏向锁,也就是上面琐升级过程图中的另一种情况。具体操作如下:

代码如下:

运行结果如下:

三、总结

本文主要介绍了java对象头的组成与synchronized升级过程,深入底层了解原理,很多细节在文中,需要各位仔细研读!

更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!

http://weixin.qq.com/r/xx3v9_7EY7McraqU90jV (二维码自动识别)

java对象头_我的并发编程(二):java对象头以及synchronized升级过程相关推荐

  1. java内存屏障_内存屏障 | 并发编程网 – ifeve.com

    本文我将和大家讨论并发编程中最基础的一项技术:内存屏障或内存栅栏,也就是让一个CPU处理单元中的内存状态对其它处理单元可见的一项技术. CPU使用了很多优化技术来实现一个目标:CPU执行单元的速度要远 ...

  2. java雪崩_【并发编程】java 如何解决redis缓存穿透、缓存雪崩(高性能示例代码)...

    [并发编程]java 如何解决redis缓存穿透.缓存雪崩(高性能示例代码) 发布时间:2018-11-22 16:48, 浏览次数:872 , 标签: java redis <>缓存穿透 ...

  3. java 线程 并发_多线程高并发编程总结

    多线程 第一章 一.终止线程的三种方法: 1.使用退出标志,是县城正常退出,也就是当run方法完成后线程终止. 2.stop不推荐 3.使用interrupt(打了一个停止标记,并不是真的停止线程). ...

  4. 【Java并发编程】java并发框架Executor学习笔记

    Java SE5的java.util.concurrent包中的执行器(Executor)将为你管理Thread对象,从而简化了并发编程.Executor在客户端和执行任务之间提供了一个间接层,Exe ...

  5. 【Java并发编程】Java多线程(四):FutureTask 源码分析

    前言:[Java并发编程]Java多线程(三):Runnable.Callable --创建任务的方式 在上一篇文章的末尾我们通过两个问题,引出了 FutureTask 及其设计思路,先来回顾一下: ...

  6. 视频教程-Java并发编程实战-Java

    Java并发编程实战 2018年以超过十倍的年业绩增长速度,从中高端IT技术在线教育行业中脱颖而出,成为在线教育领域一匹令人瞩目的黑马.咕泡学院以教学培养.职业规划为核心,旨在帮助学员提升技术技能,加 ...

  7. JUC并发编程(java util concurrent)(哔站 狂神说java juc并发编程 摘录笔记)

    JUC并发编程(java util concurrent) 1.什么是JUC JUC并不是一个很神秘的东西(就是 java.util 工具包.包.分类) 业务:普通的线程代码 Thread Runna ...

  8. java 线程工厂_Java并发编程:Java的四种线程池的使用,以及自定义线程工厂

    引言 通过前面的文章,我们学习了Executor框架中的核心类ThreadPoolExecutor ,对于线程池的核心调度机制有了一定的了解,并且成功使用ThreadPoolExecutor 创建了线 ...

  9. 【并发编程二十】协程(coroutine)_协程库

    [并发编程二十]协程(coroutine) 一.线程的缺点 二.协程 三.优点 四.个人理解 五.协程库 1.window系统 2.unix系统(包括linux的各个版本) 2.1.makeconte ...

最新文章

  1. 【转修正】sql server行版本控制的隔离级别
  2. Android TV 悬浮球模拟物理按键
  3. OpenCV中findContours函数的使用
  4. 02-24 面向对象--方法重载、隐藏
  5. ccxprocess可以禁用么_提效 | 5G时代网站还需要加速么?
  6. 【ArcGIS风暴】ArcGIS 10.2字段计算器(Field Calculator)批量条件赋值用法总结
  7. python视频处理代码_python如何实现视频转代码视频
  8. css-适配布局类型-流式布局-响应式布局
  9. bzoj 2623 所罗门的咒语
  10. python中给文件改名
  11. docker 查看镜像版本_Docker 安装及入门介绍 - 荏苒经十载
  12. Unity Shader 详细自学(一)
  13. 用 Telegram bot 搭个消息提醒服务
  14. 【笔试题】求最小公倍数 C++(两种方法)
  15. 弘辽科技:淘宝新商家怎么做起来?如何经营一个新店?
  16. 不习惯的 Vue3 起步五 のapiHooks 封装
  17. 深度强化学习:从像素玩Pong!
  18. 编写函数求两个数的最大公约数,采用递归法计算两数的最大公约数。
  19. margin-right右边距失效:CSS
  20. vue单页面程序对谷歌GA事件的应用

热门文章

  1. .NET中生成动态验证码
  2. 用 docker-compose 启动 WebApi 和 SQL Server
  3. Visual Studio 2019 16.1 使用 .NET Core 3.0
  4. .NET Core实战项目之CMS 第四章 入门篇-Git的快速入门及实战演练
  5. 走进 Cake for .NET
  6. .net core连接MongoDB
  7. EditPlust 批量添加自定义工具
  8. ArcGIS实验教程——实验三十八:基于ArcGIS的等高线、山体阴影、山顶点提取案例教程
  9. ArcGIS 10.6字段计算器(Field Calculator)字段任意填充编码序列(奇数、偶数序列、自定义间隔)
  10. C语言试题三十五之找出一维整型数组元素中最大的值和它所在的下标,最大的值和它所在的下标通过形参传回。主函数中x是数组名,n 是x中的数据个数,max存放最大值,index存放最大值所在元素的下标。