• java 虚拟机对锁对优化


    Java对象头(不用记内容,只需要知道,对象头里面,会保存一些信息)

    锁存在Java对象头里。如果对象是数组类型,则虚拟机用3个Word(字宽)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,一字宽等于四字节,即32bit。(下面这个表格讲的很清楚)

    长度

    内容

    说明

    32/64bit

    Mark Word

    存储对象的hashCode或锁信息等。

    32/64bit

    Class Metadata Address

    存储到对象类型数据的指针

    32/64bit

    Array length

    数组的长度(如果当前对象是数组)

    Java对象头里的Mark Word里默认存储对象的HashCode,分代年龄和锁标记位。32位JVM的Mark Word的默认存储结构如下:

    25 bit

    4bit

    1bit

    是否是偏向锁

    2bit

    锁标志位

    无锁状态

    对象的hashCode

    对象分代年龄

    0

    01

    在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。Mark Word可能变化为存储以下4种数据: 

    锁状态

    25 bit

    4bit

    1bit

    2bit

    23bit

    2bit

    是否是偏向锁

    锁标志位

    轻量级锁

    指向栈中锁记录的指针

    00

    重量级锁

    指向互斥量(重量级锁)的指针

    10

    GC标记

    11

    偏向锁

    线程ID

    Epoch

    对象分代年龄

    1

    01

    在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下:  

    锁状态

    25bit

    31bit

    1bit

    4bit

    1bit

    2bit

    cms_free

    分代年龄

    偏向锁

    锁标志位

    无锁

    unused

    hashCode

    0

    01

    偏向锁

    ThreadID(54bit) Epoch(2bit)

    1

    01

    偏向锁,简单的讲,就是在锁对象的对象头中有个ThreaddId字段,这个字段如果是空的,
    第一次获取锁的时候,就将自身的ThreadId写入到锁的ThreadId字段内,将锁头内的是否偏向锁的状态位置1.
    这样下次获取锁的时候,直接检查ThreadId是否和自身线程Id一致,如果一致,则认为当前线程已经获取了锁,因此不需再次获取锁,
    略过了轻量级锁和重量级锁的加锁阶段。提高了效率

    流程是这样的 偏向锁->轻量级锁->重量级锁
    第一步,检查MarkWord里面是不是放的自己的ThreadId ,如果是,表示当前线程是处于 “偏向锁”
    第二步,如果MarkWord不是自己的ThreadId,锁升级,这时候,用CAS来执行切换,新的线程根据MarkWord里面现有的ThreadId,通知之前线程暂停,
    之前线程将Markword的内容置为空。
    第三步,两个线程都把对象的HashCode复制到自己新建的用于存储锁的记录空间,接着开始通过CAS操作,
    把共享对象的MarKword的内容修改为自己新建的记录空间的地址的方式竞争MarkWord,
    第四步,第三步中成功执行CAS的获得资源,失败的则进入自旋
    第五步,自旋的线程在自旋过程中,成功获得资源(即之前获的资源的线程执行完成并释放了共享资源),则整个状态依然处于 轻量级锁的状态,如果自旋失败
    第六步,进入重量级锁的状态,这个时候,自旋的线程进行阻塞,等待之前线程执行完成并唤醒自己

    • 锁消除
         锁消除是一种更彻底的锁优化,Java虚拟机在Jit编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁的时间,如果不可能存在竞争,为什么还要加锁呢?这是因为咋Java软件开发过程中,我们必然会使用一些JDK的内置API,比如StringBuffer,Vector等,你在使用的时候,也许根本不会考虑这些对象内部是如何实现的,但是真实环境中,缺不存在竞争,如果虚拟机检测到这种情况,就会将这些无用的锁操作去除,
    复制代码
    public String[] createStrings() {
        Vector<String> v = new Vector<>();
        for (int i = 0; i < 100; i++) {
            v.add(Integer.toString(i));
        }
        return v.toArray(new String[]{});
    }
    复制代码
         锁消除涉及一项关键技术为逃逸分析,所谓逃逸分析就是观察某一个变量是否会逃出某一个作用域,在本例子中,变量v显然没有逃出createStrings()方法之外,以此为基础,虚拟机才可以大胆地将v内部的加锁操作去除,
         逃逸分析必须在-server模式下进行,可以使用-XX:+DoEscapeAnalysis参数打开逃逸分析.使用-XX:+EliminateLocks参数可以打开锁消除.
  • 相关阅读:
    go 基础(二)
    go 基础(一)
    小程序生成带有多参数的太阳码
    php 3.2 生成压缩文件,并下载
    layer confirm 三种选择按钮
    无限级分类,抓取某元素的所有下级id
    bootstrap 自带字体颜色
    ajaxSubmit 页面生成的html 中含有表单提交表单方式
    php 保留2位小数
    日期范围
  • 原文地址:https://www.cnblogs.com/sg9527/p/8022341.html
Copyright © 2020-2023  润新知