Java对象头的组成

2024-06-18 22:44
文章标签 java 对象 组成

本文主要是介绍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(){@Overridepublic 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(){@Overridepublic 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信息为yyThread.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会默认当前类产生了严重的问题,剥夺了该类的新实例对象使用偏向锁的权利

这篇关于Java对象头的组成的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1073265

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

java8的新特性之一(Java Lambda表达式)

1:Java8的新特性 Lambda 表达式: 允许以更简洁的方式表示匿名函数(或称为闭包)。可以将Lambda表达式作为参数传递给方法或赋值给函数式接口类型的变量。 Stream API: 提供了一种处理集合数据的流式处理方式,支持函数式编程风格。 允许以声明性方式处理数据集合(如List、Set等)。提供了一系列操作,如map、filter、reduce等,以支持复杂的查询和转

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM

详细分析Springmvc中的@ModelAttribute基本知识(附Demo)

目录 前言1. 注解用法1.1 方法参数1.2 方法1.3 类 2. 注解场景2.1 表单参数2.2 AJAX请求2.3 文件上传 3. 实战4. 总结 前言 将请求参数绑定到模型对象上,或者在请求处理之前添加模型属性 可以在方法参数、方法或者类上使用 一般适用这几种场景: 表单处理:通过 @ModelAttribute 将表单数据绑定到模型对象上预处理逻辑:在请求处理之前

eclipse运行springboot项目,找不到主类

解决办法尝试了很多种,下载sts压缩包行不通。最后解决办法如图: help--->Eclipse Marketplace--->Popular--->找到Spring Tools 3---->Installed。

JAVA读取MongoDB中的二进制图片并显示在页面上

1:Jsp页面: <td><img src="${ctx}/mongoImg/show"></td> 2:xml配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001

Java面试题:通过实例说明内连接、左外连接和右外连接的区别

在 SQL 中,连接(JOIN)用于在多个表之间组合行。最常用的连接类型是内连接(INNER JOIN)、左外连接(LEFT OUTER JOIN)和右外连接(RIGHT OUTER JOIN)。它们的主要区别在于它们如何处理表之间的匹配和不匹配行。下面是每种连接的详细说明和示例。 表示例 假设有两个表:Customers 和 Orders。 Customers CustomerIDCus