对象由多部分构成的,对象头,属性字段、补齐区域等。所谓补齐区域是指如果对象总大小不是4字节的整数倍,会填充上一段内存地址使之成为整数倍。

后面两个很好理解,今天我主要想总结一下对象头:

对象头这部分在对象的最前端,包含两部分或者三部分:Mark Words、Klass Words,如果对象是一个数组,那么还可能包含第三部分:数组的长度。


Klass Word里面存的是一个地址,占32位或64位,是一个指向当前对象所属于的类的地址,可以通过这个地址获取到它的元数据信息。


Mark Word需要重点说一下,这里面主要包含对象的哈希值、年龄分代、锁标志位等,大小为32位或64位

当对象处于不同的锁状态时,它的Mark Word里的东西会被替换成不同东西,如下表所示:

1、对象未加锁的时候,lock标志位为01,包含哈希值、年龄分代和偏向锁标志位等,此时偏向锁标志位为0;

2、当对象被施加偏向锁时,哈希值和一部分无用内存会转化为锁主人的线程信息,以及加锁的时间戳epoch,此时lock标志位没变,偏向锁为1,也就是说,偏向锁和lock标志位共同决定是否偏向锁状态。

偏向锁的加锁步骤:

  • Load-and-test,也就是简单判断一下当前线程id是否与Markword当中的线程id是否一致.
  • 如果一致,则说明此线程已经成功获得了锁,继续执行下面的代码.
  • 如果不一致,则要检查一下对象是否还是可偏向,即“是否偏向锁”标志位的值。
  • 如果还未偏向,则利用CAS操作来竞争锁,也即是第一次获取锁时的操作。

3、当发生锁竞争时,偏向锁会变为轻量级锁,这时需要先将偏向锁进行锁撤销,这一步骤也会消耗不少的性能,轻量级锁的Mark Word中,lock标志位为00,其余内容被替换为一个指针,指向了栈里面的锁记录。

锁撤销的过程如下:

  • 在一个安全点停止拥有锁的线程。
  • 遍历线程栈,如果存在锁记录的话,需要修复锁记录和Markword,使其变成无锁状态。
  • 唤醒当前线程,将当前锁升级成轻量级锁。

所以,如果某些同步代码块大多数情况下都是有两个及以上的线程竞争的话,那么偏向锁就会是一种累赘,对于这种情况,我们可以一开始就把偏向锁这个默认功能给关闭

轻量级锁的加锁步骤:

  • 线程在自己的栈桢中创建锁记录LockRecord。
  • 将锁对象的对象头中的MarkWord复制到线程的刚刚创建的锁记录中。
  • 将锁记录中的Owner指针指向锁对象。
  • 将锁对象的对象头的MarkWord替换为指向锁记录的指针。

轻量级锁主要有两种:自旋锁和自适应自旋锁。自旋锁会导致空耗CPU且很可能锁不公平;自适应是指根据上一次该线程是否成功或者多久获取过该锁设置旋转次数,若上次失败很可能直接进入重量级锁

4、如果竞争线程增多,锁继续膨胀,变为重量级锁,也是互斥锁,即synchronized,其lock标志位为10,Mark Word其余内容被替换为一个指向对象监视器Monitor的指针。

特殊的是,如果此对象已经被GC标记过,lock会变为11,不含其余内容。


Monitor对象

每个对象都有一个Monitor对象相关联,Monitor对象中记录了持有锁的线程信息、等待队列等。Monitor对象包含以下三个字段:

  • _owner 记录当前持有锁的线程
  • _EntryList 是一个队列,记录所有阻塞等待锁的线程
  • _WaitSet 也是一个队列,记录调用 wait() 方法并还未被通知的线程

当线程持有锁的时候,线程id等信息会拷贝进owner字段,其余线程会进入阻塞队列entrylist,当持有锁的线程执行wait方法,会立即释放锁进入waitset,当线程释放锁的时候,owner会被置空,公平锁条件下,entrylist中的线程会竞争锁,竞争成功的线程id会写入owner,其余线程继续在entrylist中等待。


Monitor与Synchronized

对于Synchronized的同步代码块,JVM会在进入代码块之前加上monitorenter ,如果进入monitor成功,线程便获取了锁,一个对象的monitor同一时刻只能被一个线程锁占有;

对于同步方法,JVM会讲方法设置 ACC_SYNCHRONIZED 标志,调用的时候 JVM 根据这个标志判断是否是同步方法。

采用Synchronized给对象加锁会使线程阻塞,因而会造成线程状态的切换,而线程状态的切换必须要操作系统来执行,因此需要将用户态切换为内核态,这个切换的过程是十分耗时的都需要操作系统来帮忙,有可能比用户执行代码的时间还要长。


Synchronized是JVM级别的锁,它在不断被优化着,从目前来看Synchronized已经远没有以前那么“重”了,也大概就是JUC包源码(如ConcurrentHashMap)中大量使用Synchronized的原因把

Java对象头与锁的更多相关文章

  1. 并发王者课-青铜5:一探究竟-如何从synchronized理解Java对象头中的锁

    在前面的文章<青铜4:synchronized用法初体验>中,我们已经提到锁的概念,并指出synchronized是锁机制的一种实现.可是,这么说未免太过抽象,你可能无法直观地理解锁究竟是 ...

  2. 015-线程同步-synchronized几种加锁方式、Java对象头和Monitor、Mutex Lock、JDK1.6对synchronized锁的优化实现

    一.synchronized概述基本使用 为确保共享变量不会出现并发问题,通常会对修改共享变量的代码块用synchronized加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题. syn ...

  3. java对象头信息和三种锁的性能对比

    java头的信息分析 首先为什么我要去研究java的对象头呢? 这里截取一张hotspot的源码当中的注释 这张图换成可读的表格如下 |-------------------------------- ...

  4. 盘一盘 synchronized (一)—— 从打印Java对象头说起

    Java对象头的组成 Java对象的对象头由 mark word 和  klass pointer 两部分组成, mark word存储了同步状态.标识.hashcode.GC状态等等. klass  ...

  5. 探究java对象头

    探究java对象头 研究java对象头,我这里先截取Hotspot中关于对象头的描述,本文研究基于64-bit HotSpot VM 文件路径 openjdk-jdk8u-jdk8u\hotspot\ ...

  6. JVM源码分析之Java对象头实现

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Java对象头实现 HotSpot虚拟机中,对象在内存中的布局分为三 ...

  7. JAVA对象头详解(含32位虚拟机与64位虚拟机)

    为什么要学习Java对象头 学习Java对象头主要是为了解synchronized底层原理,synchronized锁升级过程,Java并发编程等. JAVA对象头 由于Java面向对象的思想,在JV ...

  8. jvm源码解析java对象头

    认真学习过java的同学应该都知道,java对象由三个部分组成:对象头,实例数据,对齐填充,这三大部分扛起了java的大旗对象,实例数据其实就是我们对象中的数据,对齐填充是由于为了规则分配内存空间,j ...

  9. Java 对象头那点事

    概览 对象头 存放:关于堆对象的布局.类型.GC状态.同步状态和标识哈希码的基本信息.Java对象和vm内部对象都有一个共同的对象头格式. (后面做详细介绍) 实例数据 存放:类的数据信息,父类的信息 ...

随机推荐

  1. poj 2689 Prime Distance (素数二次筛法)

    2689 -- Prime Distance 没怎么研究过数论,还是今天才知道有素数二次筛法这样的东西. 题意是,要求求出给定区间内相邻两个素数的最大和最小差. 二次筛法的意思其实就是先将1~sqrt ...

  2. poj 3675 Telescope (圆与多边形面积交)

    3675 -- Telescope 再来一题.这题的代码还是继续完全不看模板重写的. 题意不解释了,反正就是一个单纯的圆与多边形的交面积. 这题的精度有点搞笑.我用比较高的精度来统计面积,居然wa了. ...

  3. 用CSS画平行四边形

    <div class="jx">我是一个平行四边形</div> <style> .jx{ //定义div的高度宽度 transform:skew ...

  4. 自动为DEV GridView控件添加SizeChanged事件

    实现gdv设置的抽象对象,不用每个gdv控件都添加sizechanged事件,只需执行gdc绑定sql函数,在其中会自动添加SizeChanged事件. //2016.5.13 by sngk //根 ...

  5. [转]JavaWeb工程 目录结构

    今天为此事纠结了一段时间. 以下是mavaen推荐的项目目录. ├── pom.xml └── src     ├── main     │   ├── java     │   │   └── my ...

  6. TSLint-Angular 配置

    代码风格和语义的检查工具,帮助规范 TS 和 Angular 代码书写: 安装: => cnpm install // 安装相关依赖 全局安装 cnpm install -g tslint ty ...

  7. pip安装python包时报字符编码错

    比如安装scikit-learn时报错: django ascii’ codec can’t encode character 原因是用户目录或用户名存在中文,ascii不能解码,解决办法是在Pyth ...

  8. git 安装及基本配置

    git 基本上来说是开发者必备工具了,在服务器里没有 git 实在不太能说得过去.何况,没有 git 的话,面向github编程 从何说起,如同一个程序员断了左膀右臂. 你对流程熟悉后,只需要一分钟便 ...

  9. Koa搭建简单服务器

    1. dependencies "co-mysql": "^1.0.0", "koa": "^2.7.0", " ...

  10. js的cookie操作及知识点详解

    <html> <head> <script type="text/javascript"> function getCookie(c_name) ...