学习——java对象解析和锁升级

申请对象

是使用new创建一个对象时,字节码指令如下

package jvm.study;
CODE:
public class Test {
	public static void main(String[] args) {
		Test t = new Test();
	}
}
Main 方法中BYTECODE
    0  new jvm.study.Test [1]
    3  dup
    4  invokespecial jvm.study.Test() [16]
    7  astore_1 [t]
    8  return

1,,当调用new指令后,jvm会在堆内存中申请一块内存(如果有属性,初始化为零值),同时在操作数栈中push一个引用指向新申请内存
2,执行dup指令,复制new指令时创建的引用,push到操作数栈,此时,栈中有两个指向新申请的内存
3,invokespecial指令,通过常量池找到需要jvm.study.Test()构造方法,同时弹出栈顶的元素找到当前构造方法所属的Class对象,执行对象的构造(如果有属性初始值的,在此更新零值为初始值)
4,astore_1指令,把引用栈顶元素赋值给本地变量t,同时弹出栈顶,一个java对象构造完成

:JVM优化可能会将指令3和指令4重排序,这里就会出现半初始化对象被其他线程使用,导致程序出问题!!!
某大厂的一道面试题DCL(double check lock)单例,需要使用volatile关键字标记吗?

对象头

java对象通常是有对象头,对象体,填充区三部分。对象长度必须是8BYTE的整数倍,所以长度不够时需要填充区去填充,这部分空间就被浪费了
对象头
对象头通常是不定长的,它通常有mark wordkclass point两部分组成,如果对象时数组时,它还需要额外的空间存储数组的长度(64位JVM下占64位,当开启压缩指针-XX:+UseCompressedOops时,32位)
对象头的布局

|--------------------------------------------------------------------------------------------------------------|
|                                              Object Header (128 bits)                                        |
|--------------------------------------------------------------------------------------------------------------|
|                        Mark Word (64 bits)                                    |      Klass Word (64 bits)    |       
|--------------------------------------------------------------------------------------------------------------|
|  unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |     OOP to metadata object   |  无锁
|----------------------------------------------------------------------|--------|------------------------------|
|  thread:54 |         epoch:2      | unused:1 | age:4 | biased_lock:1 | lock:2 |     OOP to metadata object   |  偏向锁
|----------------------------------------------------------------------|--------|------------------------------|
|                     ptr_to_lock_record:62                            | lock:2 |     OOP to metadata object   |  轻量锁
|----------------------------------------------------------------------|--------|------------------------------|
|                     ptr_to_heavyweight_monitor:62                    | lock:2 |     OOP to metadata object   |  重量锁
|----------------------------------------------------------------------|--------|------------------------------|
|                                                                      | lock:2 |     OOP to metadata object   |    GC
|--------------------------------------------------------------------------------------------------------------|

lock: 锁状态标记位,该标记的值不同,整个mark word表示的含义不同。
biased_lock:偏向锁标记,为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
在这里插入图片描述

age:Java GC标记位对象年龄(4bit,所以设置最大最大gc年龄时最大值不能超过15)。
identity_hashcode:对象标识Hash码,采用延迟加载技术。当对象使用HashCode()计算后,并会将结果写到该对象头中。当对象被锁定时,该值会移动到线程Monitor中。
thread:持有偏向锁的线程ID和其他信息。这个线程ID并不是JVM分配的线程ID号,和Java Thread中的ID是两个概念。
epoch:偏向时间戳。
ptr_to_lock_record:指向栈中锁记录的指针
Lock record:The lock record holds the original value of the object’s mark word and also contains metadata necessary to identify which object is locked
当字节码解释器执行monitorenter字节码轻度锁住一个对象时,就会在获取锁的线程的栈上显式或者隐式分配一个lock record。
lock record 包含两部分数据,分别是

  1. 对象被锁前的mark word
  2. 指向被锁队对象的引用
//Open jdk 实现
// A BasicObjectLock associates a specific Java object with a BasicLock.
// It is currently embedded in an interpreter frame.
class BasicObjectLock VALUE_OBJ_CLASS_SPEC {
 private:
  BasicLock _lock;                        // the lock, must be double word aligned
  oop       _obj;                         // object holds the lock;
};
class BasicLock VALUE_OBJ_CLASS_SPEC {
 private:
  volatile markOop _displaced_header;
};

lock record 作用:

  • 持有displaced word和锁住对象的元数据;
  • 解释器使用lock record来检测非法的锁状态;
  • 隐式地充当锁重入机制的计数器

ptr_to_heavyweight_monitor:指向线程Monitor的指针。

对象锁的升级

public static void main(String[] args) throws Exception {
        A o = new A();
        System.out.println(o.toString());
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }

对象未加锁时布局如下:
在这里插入图片描述

 public static void main(String[] args) throws Exception {

        A o = new A();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        synchronized (o) {
            System.out.println("App.main syn block");
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
        System.out.println("App.main. realease lock..");
        System.out.println(ClassLayout.parseInstance(o).toPrintable());

    }

在这里插入图片描述

public static void main(String[] args) throws Exception {

        A o = new A();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        new Thread(() -> {
            synchronized (o) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("App.main syn block");
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }

        }).start();
        new Thread(() -> {
            synchronized (o) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();

    }

在这里插入图片描述
测试偏向锁,可以使用下列代码

public static void main(String[] args) throws InterruptedException {
    Thread.sleep(5000);
    A a = new A();
    System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值