Java对象头的组成

介绍对象头之前先说一下Java对象内部的组成结构:

1,成员变量(Data1...DataN)

2, 对象头

Java对象头的组成(根据对象头分析对象状态借此优化代码)

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

Java对象的对象头由 mark word 和 meaData,数组长度 三部分组成,

markword: 记录着线程同步锁状态、线程id、标识、hashcode、GC状态等等。

meaData: 元数据指针,指向元空间

数组长度:(类包含了数据对象,才有数组长度)

注意:

1,对象是存在堆里面的

2,类的Class是加载到元空间的(类.Class)

3,指针指向的也是元空间

package com.oujiong.locklearning;

import org.openjdk.jol.info.ClassLayout;

/**
 * 打印对象头信息分析代码,进行优化
 * @USER: hx
 * @DATE: 2021/10/25
 **/
public class TypeLock {


    String filed = "输出的第一行内容和锁状态内容(thread id和epoch)对应" +
                     "\n第三行中表示的是被指针压缩为32位的klass pointer" +
                     "\n第四行则是我们创建的A对象属性信息 1字节的boolean值" +
                     "\n第五行则代表了对象的对齐字段 为了凑齐64位的对象,对齐字段占用了3个字节,24bit\n";


    //todo
    //    biased_lokc lock    状态
    //    0           01      无锁
    //    1           01      偏向锁
    //    0           00      轻量级锁
    //    0           10      重量级锁
    //    0           11      GC标记

    //todo lock:  锁状态标记位,该标记的值不同,整个mark word表示的含义不同。
    //todo biased_lock:偏向锁标记,为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
    //todo age:Java GC标记位对象年龄。
    //todo identity_hashcode:对象标识Hash码,采用延迟加载技术。当对象使用HashCode()计算后,并会将结果写到该对象头中。当对象被锁定时,该值会移动到线程Monitor中。
    //todo thread:持有偏向锁的线程ID和其他信息。这个线程ID并不是JVM分配的线程ID号,和Java Thread中的ID是两个概念。
    //todo epoch:偏向时间戳。
    //todo ptr_to_lock_record:指向栈中锁记录的指针。
    //todo ptr_to_heavyweight_monitor:指向线程Monitor的指针。
    public static void main(String[] args) throws InterruptedException {
        //todo 无锁状态
//        System.err.println("-------------------------无锁状态----------------------------");
//        A a = new A();
//        System.out.println(ClassLayout.parseInstance(a).toPrintable());

        //todo 偏向锁状态-可以理解成 “特殊状态的偏向锁”,偏向锁就绪状态
        //todo JVM启动时会进行一系列的复杂活动,比如装载配置,系统类初始化等等。
        //todo 在这个过程中会使用大量synchronized关键字对对象加锁,且这些锁大多数都不是偏向锁(但是可以理解成正处于偏向状态,准备好进行偏向了)
        //todo 为了减少初始化时间,JVM默认延时加载偏向锁,也可以设置JVM参数 -XX:BiasedLockingStartupDelay=0 来取消延时加载偏向锁。
//        System.err.println("-------------------------偏向锁就绪状态----------------------------");
//        Thread.sleep(5000);
//        A a = new A();
//        System.out.println(ClassLayout.parseInstance(a).toPrintable());

//        System.err.println("-------------------------偏向锁----------------------------");
        //todo 偏向锁
        //todo 此时对象a,对象头内容(输出的第一行内容)有了明显的变化,当前偏向锁偏向主线程
//        Thread.sleep(5000);
//        A a = new A();
//        synchronized (a){
//            System.out.println(ClassLayout.parseInstance(a).toPrintable());
//        }

        //todo 轻量级锁,一个线程放了,但主线程还会和放了的线程存在竞争关系
//        System.err.println("-------------------------轻量级锁----------------------------");
//        Thread.sleep(5000);
//        A a = new A();
//
//        Thread thread1= new Thread(){
//            @Override
//            public void run() {
//                synchronized (a){
//                    System.out.println("thread1 locking -- 偏向锁 thread1");
//                    System.out.println(ClassLayout.parseInstance(a).toPrintable()); //偏向锁
//                }
//            }
//        };
//        //todo 表示的线程转换为RUNNABLE状态
//        thread1.start();
//        //todo 等待thread1执行完,在执行往下运行
//        thread1.join();
//        //todo 使用场景:主要是为了暂停当前线程,把cpu片段让出给其他线程,减缓当前线程的执行。
//        //todo 1. sleep是帮助其他线程获得运行机会的最好方法,但是如果当前线程获取到的有锁,sleep不会让出锁。
//        //todo 2. 线程睡眠到期自动苏醒,并返回到可运行状态(就绪),不是运行状态。
//        //todo 3. sleep()是静态方法,只能控制当前正在运行的线程
//        Thread.sleep(10000);
//
//        synchronized (a){
//            System.out.println("main locking -- 轻量级锁");
//            //todo thread1虽然已经退出同步代码块,但主线程和thread1仍然为锁的交替竞争关系.故此时主线程输出结果为轻量级锁。
//            System.out.println(ClassLayout.parseInstance(a).toPrintable());//轻量锁
//        }


        //todo 重量级锁,让两个线程相互竞争a对象,不顺序释放,升级成重量级锁 -- 没有合理的利用锁
        System.err.println("-------------------------重量级锁----------------------------");
        Thread.sleep(5000);
        TypeLock a = new TypeLock();
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                synchronized (a){
                    System.out.println("thread1 locking");
                    System.out.println(ClassLayout.parseInstance(a).toPrintable());
                    try {
                        //让线程晚点儿死亡,造成锁的竞争
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread thread2 = new Thread(){
            @Override
            public void run() {
                synchronized (a){
                    System.out.println("thread2 locking");
                    System.out.println(ClassLayout.parseInstance(a).toPrintable());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread1.start();
        thread2.start();

    }

}

偏向锁批量重偏向与批量撤销

批量重偏向:当一个线程创建了大量对象并执行了初始的同步操作,后来另一个线程也来将这些对象作为锁对象进行操作,会导偏向锁重偏向的操作

批量撤销:在多线程竞争剧烈的情况下,使用偏向锁将会降低效率,于是乎产生了批量撤销机制

JVM的默认参数值#

通过JVM的默认参数值,找一找批量重偏向和批量撤销的阈值

设置JVM参数-XX:+PrintFlagsFinal,在项目启动时即可输出JVM的默认参数值

当然我们可以通过

-XX:BiasedLockingBulkRebiasThreshold 和 -XX:BiasedLockingBulkRevokeThreshold 来手动设置阈值

intx BiasedLockingBulkRebiasThreshold   = 20   默认偏向锁批量重偏向阈值

intx BiasedLockingBulkRevokeThreshold  = 40   默认偏向锁批量撤销阈值

批量偏向和批量撤销案例

package com.oujiong.locklearning;

import org.openjdk.jol.info.ClassLayout;

import java.util.ArrayList;
import java.util.List;

/**
 * 批量偏向锁
 * @USER: hx
 * @DATE: 2021/10/25
 **/
public class BatchBiasLock {


    String filed = "输出的第一行内容和锁状态内容(thread id和epoch)对应" +
            "\n第三行中表示的是被指针压缩为32位的klass pointer" +
            "\n第四行则是我们创建的A对象属性信息 1字节的boolean值" +
            "\n第五行则代表了对象的对齐字段 为了凑齐64位的对象,对齐字段占用了3个字节,24bit\n";



//    public static void main(String[] args) throws Exception {
//
//        System.err.println("---------------------批量重偏向--------------------------");
//        //todo 延时产生可偏向对象--偏向锁状态-可以理解成 “特殊状态的偏向锁”,偏向锁就绪状态
//        Thread.sleep(5000);
//
//        //todo 创造100个偏向线程t1的偏向锁
//        List<BatchBiasLock> listA = new ArrayList<>();
//        Thread t1 = new Thread(() -> {
//            for (int i = 0; i <100 ; i++) {
//                BatchBiasLock a = new BatchBiasLock();
//                synchronized (a){
//                    listA.add(a);
//                }
//            }
//            try {
//                //todo 为了防止JVM线程复用,在创建完对象后,保持线程t1状态为存活 sleep 100000000 不会释放该锁,一直持有者
//                Thread.sleep(100000000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        });
//        //todo 启动,但t1始终没有释放锁
//        t1.start();
//
//        //睡眠3s钟保证线程t1创建对象完成
//        Thread.sleep(3000);
//        System.out.println("打印t1线程,list中第20个对象的对象头:");
//        System.out.println((ClassLayout.parseInstance(listA.get(19)).toPrintable()));
//
//        //todo 创建线程t2竞争线程t1中已经退出同步块的锁
//        Thread t2 = new Thread(() -> {
//            //这里面只循环了30次!!!
//            for (int i = 0; i < 30; i++) {
//                BatchBiasLock a =listA.get(i);
//                synchronized (a){
//                    //分别打印第19次和第20次偏向锁重偏向结果
//                    if(i==18||i==19){
//                        System.out.println("第"+ ( i + 1) + "次偏向结果");
//                        System.out.println((ClassLayout.parseInstance(a).toPrintable()));
//                    }
//                }
//            }
//            try {
//                Thread.sleep(10000000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        });
//        //todo 启动,但t1始终没有释放锁
//        t2.start();
//
//        //todo 所以两个线程还是存在着竞争关系
//        Thread.sleep(3000);
//        System.out.println("打印list中第11个对象的对象头:");
//        System.out.println((ClassLayout.parseInstance(listA.get(10)).toPrintable()));
//        System.out.println("打印list中第26个对象的对象头:");
//        System.out.println((ClassLayout.parseInstance(listA.get(25)).toPrintable()));
//        System.out.println("打印list中第41个对象的对象头:");
//        System.out.println((ClassLayout.parseInstance(listA.get(40)).toPrintable()));
//
//        System.err.println("---------------------批量重偏向--------------------------");
        //todo 简单总结#
        // 概念: 根据设置BiasedLockingBulkRebiasThreshold(默认偏向锁批量重偏向阈值)达到重偏向阈值
        // 会自动升级成轻量级锁(如果是在其他线程竞争内)
//    }

    public static void main(String[] args) throws Exception {
        System.err.println("---------------------批量撤销#--------------------------");

        Thread.sleep(5000);
        List<BatchBiasLock> listA = new ArrayList<>();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i <100 ; i++) {
                BatchBiasLock a = new BatchBiasLock();
                synchronized (a){
                    listA.add(a);
                }
            }
            try {
                Thread.sleep(100000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        Thread.sleep(3000);

        Thread t2 = new Thread(() -> {
            //todo 这里循环了40次,达到了批量撤销的阈值,重新偏向的就是批量撤销
            for (int i = 0; i < 40; i++) {
                BatchBiasLock a =listA.get(i);
                //todo t2对listA 从索引下边0到39的偏向锁进行了重偏向
                synchronized (a){
                }
            }
            try {
                Thread.sleep(10000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t2.start();

        //———————————分割线,前面代码不再赘述——————————————————————————————————————————
        //todo
        // 前20个对象,并没有触发了批量重偏向机制,线程t2执行释放同步锁后,转变为无锁形态
        // 第20~40个对象,触发了批量重偏向机制,对象为偏向锁状态,偏向线程t2,线程t2的ID信息为xx
        // 而41个对象之后,也没有触发了批量重偏向机制,对象仍偏向线程t1,线程t1的ID信息为yy

        Thread.sleep(3000);
        System.out.println("打印list中第11个对象的对象头:");
        System.out.println((ClassLayout.parseInstance(listA.get(10)).toPrintable()));
        System.out.println("打印list中第26个对象的对象头:");
        System.out.println((ClassLayout.parseInstance(listA.get(25)).toPrintable()));
        System.out.println("打印list中第90个对象的对象头:");
        System.out.println((ClassLayout.parseInstance(listA.get(89)).toPrintable()));


        Thread t3 = new Thread(() -> {
            for (int i = 20; i < 40; i++) {
                BatchBiasLock a =listA.get(i);
                synchronized (a){
                    //todo  分别打印第21次和第23次偏向锁重偏向结果
                    //todo 达到设置批量撤销的阈值,进行批量撤销打印消息头
                    if(i==20||i==22){
                        System.out.println("thread3 第"+ i + "次");
                        System.out.println((ClassLayout.parseInstance(a).toPrintable()));
                    }
                }
            }
        });
        t3.start();

        //todo 简单总结#
        // 根据设置BiasedLockingBulkRevokeThreshold (偏向锁批量撤销阈值),超过了设置的阈值,自动
        // 经过锁膨胀,转换成轻量级锁,创建出的新对象剥夺了使用偏向锁的权利

        // 1、批量重偏向和批量撤销是针对类的优化,和对象无关(因此重新输出新实例A的对象状态是 无锁状态的)
        // 2、偏向锁重偏向一次之后不可再次重偏向 (所以t3的对象经过锁膨胀都从 "偏向锁" 升级成 "轻量级锁")
        // 3、当某个类已经触发批量撤销机制后,JVM会默认当前类产生了严重的问题,剥夺了该类的新实例对象使用偏向锁的权利
        Thread.sleep(10000);
        System.out.println("重新输出新实例A");
        System.out.println((ClassLayout.parseInstance(new BatchBiasLock()).toPrintable()));

        System.err.println("---------------------批量撤销#--------------------------");

    }

}

简单总结

1、批量重偏向和批量撤销是针对类的优化,和对象无关

2、偏向锁重偏向一次之后不可再次重偏向

3、当某个类已经触发批量撤销机制后,JVM会默认当前类产生了严重的问题,剥夺了该类的新实例对象使用偏向锁的权利

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/720888.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MB-iSTFT-VITS 模型论文思路与实验分享:基于VITS架构优化的轻量级文本转语音模型

参考文献&#xff1a; [1] Kawamura M, Shirahata Y, Yamamoto R, et al. Lightweight and high-fidelity end-to-end text-to-speech with multi-band generation and inverse short-time fourier transform[C]//ICASSP 2023-2023 IEEE International Conference on Acoustics…

【C#上位机应用开发实战】—机器视觉检测

#机器视觉 在现代工业生产中&#xff0c;机器视觉检测技术扮演着越来越重要的角色。它通过计算机视觉技术来实现对工件的自动化检测和判断&#xff0c;大大提高了生产效率和产品质量。而在机器视觉检测的应用中&#xff0c;C#作为一种简洁易用且功能强大的编程语言&#xff0c…

搭贝低代码开发平台:高效、灵活、经济的软件开发解决方案

在当今快速发展的数字化时代&#xff0c;企业对于快速、灵活且成本效益高的软件开发需求日益增长。搭贝低代码开发平台以其强大的功能和用户友好的体验&#xff0c;正在成为众多企业&#xff0c;特别是中小企业&#xff0c;软件开发的首选工具。 &#x1f4c8; 什么是低代码开发…

鸿蒙开发网络管理:【@ohos.net.socket (Socket连接)】

Socket连接 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import socket from ohos.net.socket;socket.constructUDPSocketInstance constructUDPSocketInstance(): UDPSocket 创建…

061、Python 包:模块管理

包&#xff08;Package&#xff09;是一种用于组织模块的层次结构。包实际上就是一个包含了__init__.py文件的目录&#xff0c;该文件可以为空或包含包的初始化代码。通过使用包&#xff0c;可以更好地组织和管理大型项目中的模块&#xff0c;避免命名冲突&#xff0c;并提高代…

利用C#和Snap7工具模拟S7通信(包含DB地址讲解)

之前写过一篇用KepServerEx做模拟S7的通信数据&#xff0c;参考链接&#xff1a; 通过C#和KepServer完成模拟S7协议通信_c# 与kepserver-CSDN博客 但KepServerEx是收费的&#xff0c;而且模拟的DB块超过64就不行了&#xff0c;当然Snap7在本文中也是只能模拟DB1、DB2和DB3的数…

HTML 全局属性介绍及示例

HTML 全局属性是一组可以在任何HTML元素中使用的属性。这些属性提供了一种方式来定义元素的通用行为或外观。以下是一些常见的HTML全局属性及其示例。 id id 属性为元素提供了一个唯一的标识符。它不能在 <head>, <html>, <meta>, <script>, <sty…

数据压缩还能这么玩,国产数据库有救了!

页级压缩 opengauss数据库是以数据页面&#xff08;Page&#xff09;为单位进行压缩解压&#xff0c;本特性自openGauss 3.0.0版本开始引入&#xff0c;通过对数据页的透明页压缩和维护页面存储位置的方式&#xff0c;做到高压缩、高性能。提高数据库对磁盘的利用率。 页级压缩…

FL Studio没有声音怎么办 FL Studio声音卡顿怎么办

FL Studio是一款综合创作歌曲的宿主软件&#xff0c;这款软件的里面内置了很多效果器和插件&#xff0c;非常适合创作电子音乐&#xff0c;很多创作电子音乐的小伙伴都喜欢使用此款软件。不过有些刚接触FL Studio的小伙伴&#xff0c;在使用此软件的时候&#xff0c;会出现一些…

openh264 帧间预测编码原理:WelsMdP16x16函数

openh264 帧间预测编码 帧间预测编码是视频压缩技术中的关键方法之一&#xff0c;它主要用于减少视频序列中时间维度上的冗余。这种编码方式依赖于视频帧之间的空间相关性&#xff0c;通过预测和补偿来减少数据量&#xff0c;从而实现高效的视频压缩。帧间预测编码广泛应用于各…

短路是怎么形成的

1. 短路分为电源短路和用电器短路。 电源短路&#xff1a;电流不经过任何用电器&#xff0c;直接由正极经过导线流向负极&#xff0c;由于电源内阻很小&#xff0c;导致短路电流很大&#xff0c;特别容易烧坏电源。 用电器短路&#xff1a;也叫部分电路短路&#xff0c;即一根…

全国产城市轨道交通运营公安AI高清视频监控系统

方案简介 城市轨道交通运营公安高清视频监控系统解决方案针对运营部门和公安部门的安保需求&#xff0c;选用华维视讯的各类前端和视频编解码、控制产品&#xff0c;通过统一平台提供视频监控服务和智能应用&#xff0c;满足轨道交通运营业主客运组织和抢险指挥的需求&#xff…

【idea】解决springboot项目中遇到的问题

一、Maven报错Could not find artifact com.mysql:mysql-connector-j:pom:unknown in aliyunmaven解决及分析 报错 创建springboot项目&#xff0c;勾选数据库驱动&#xff0c;springboot版本为3&#xff0c;现在改成了2.7.2&#xff0c;Maven就发生了报错Could not find art…

从兼职到大神:新手必看的UE材质原理讲解

对于刚接触UE的同学来说&#xff0c;材质篇章往往是令人望而生畏的一大板块。但材质的一些基本原理其实并不难&#xff0c;只要稍作理解便可以轻松入门。接下来我们便分为材质类型和节点类型两个知识板块来介绍材质的相关内容。 材质类型 材质分类 金属&#xff1a;金属材质一…

【C语言】数组参数和指针参数详解

在写代码的时候难免要把【数组】或者【指针】传给函数&#xff0c;那函数的参数该如何设计呢&#xff1f; 1 一维数组传参 #include <stdio.h> void test(int arr[])//ok? {} void test(int arr[10])//ok? {} void test(int* arr)//ok? {} void test2(int* arr[20])…

单载波水声通信技术研究【附MATLAB代码】

文章来源&#xff1a;​微信公众号&#xff1a;EW Frontier 摘要 水下无线通信因其在海洋科研、国防、救援及资源开发等方面的关键作用而备受关注。声波作为水中信息传输的有效载体&#xff0c;推动了水声通信技术的发展&#xff0c;其中单载波调制技术由于其高频谱利用率、结…

Vue60-TodoList案例-全局事件总线

一、全局事件总线的适用场景 虽然全局事件总线使用于任意组件之间的通信&#xff0c;但是没有必要处处用它。 数据在哪里&#xff0c;操作数据的方法就在哪里&#xff01; 二、TodoList案例-全局事件总线 适用于全局总线的场景&#xff1a;Item和App&#xff08;爷孙关系&…

RFID期末复习 | 防碰撞算法 | 信源编码 | 差错控制 | 系统调制 | S50卡 | 无源标签 | 工作频率 | 自动识别

防碰撞算法 ALOHA算法 ALOHA算法是一种随机接入方法&#xff0c;其基本思想是采取标签先发言的方式&#xff0c;当标签进入读写器的识别区域内时就自动向读写器发送其自身的ID号&#xff0c;在标签发送数据的过程中&#xff0c;若有其他标签也在发送数据&#xff0c;将会发生…

深度神经网络——什么是降维?

引言 什么是降维&#xff1f; 降维是用于降低数据集维度的过程&#xff0c;采用许多特征并将它们表示为更少的特征。 例如&#xff0c;降维可用于将二十个特征的数据集减少到仅有几个特征。 降维通常用于无监督学习任务 降维是一个用于降低数据集维度的过程&#xff0c;采用许…

从0到1,揭秘AI产品经理的高薪秘诀,转型之路与实战资源全解析

前言 随着算法模型的日益精进、计算能力的显著提升以及海量数据的积累&#xff0c;人工智能领域正以前所未有的速度蓬勃发展。 在国家政策的积极推动、社会资本的强劲注入下&#xff0c;人工智能产业正处于技术快速进步的黄金时期&#xff0c;其影响力广泛渗透至教育智能化、…