JVM-JVM中对象的结构

对象内存布局

对象里的三个区:

  1. 对象头(Header):Java对象头占8byte。如果是数组则占12byte。因为JVM里数组size需要使用4byte存储。
    标记字段MarkWord:

    用于存储对象自身的运行时数据,它是synchronized实现轻量级锁和偏向锁的关键。

    默认存储:对象HashCode、GC分代年龄、锁状态等等信息。

    为了节省空间,也会随着锁标志位的变化,存储数据发生变化。
    标记字段的结构:

    类型指针KlassPoint:

    是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

    开启指针压缩存储空间4byte,不开启8byte。

    JDK1.6+默认开启
    数组长度 如果对象是数组,则记录数组长度,占4个byte,如果对象不是数组则不存在。
    对齐填 保证数组的大小永远是8byte的整数倍。

  2. 实例数据(Instance Data):生成对象的时候,对象的非静态成员变量也会存入堆空间

  3. 对齐填充(Padding):JVM内对象都采用8byte对齐,不够8byte的会自动补齐。

案例1:

xml 复制代码
<dependency>
	<groupId>org.openjdk.jol</groupId>
	<artifactId>jol-core</artifactId>
	<version>0.9</version>
</dependency>
java 复制代码
 Object o = new Object();
        System.out.println("new Object:" +
                ClassLayout.parseInstance(o).toPrintable());

注:首先对象头是包含MarkWord和类型指针这两部分信息的;

开启指针压缩的情况下,存放Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节;

新建Object对象,会在内存占用16个字节,其中Header占12个(MarkWord占8个+KlassPoint占4个),没有实例数据,补充对齐4个。

结论:对象大小 = 对象头12 + 实例数据0 + 对齐填充4 = 16 bytes

案例2:

java 复制代码
public class TT {
    public static void main(String[] args) {
        Hero a = new Hero();
        System.out.println("new A:" +
                ClassLayout.parseInstance(a).
                        toPrintable());
        Hero b = new Hero(1, true, "test");
        System.out.println("赋值后:" +
                ClassLayout.parseInstance(b).
                        toPrintable());
    }

    static class Hero {
        int i;
        boolean flag;
        String str;

        public Hero() {

        }

        public Hero(int i, boolean flag, String str) {
            this.i = i;
            this.flag = flag;
            this.str = str;
        }
    }
}

对象的大小 = 12对象头 + 4*3的实例数据 + 0的填充 = 24bytes

对象头存储信息分析

1 如果是空对象

java 复制代码
 Object obj = new Object();

2 带锁的对象
可以看一下这篇文章关于头的顺序

  • 偏向锁
java 复制代码
// java 默认5s以上才会开启偏向锁
Thread.sleep(5001);
Object lock = new Object();
printObj(lock, 1);
synchronized (lock) {
    printObj(lock, 2);
}
  • 轻量级锁
java 复制代码
Thread.sleep(5001);
   Object lock = new Object();
   printObj(lock, 1);
   synchronized (lock) {
       printObj(lock, 2);
   }
   Thread.sleep(1000);
   new Thread(() -> {
       synchronized (lock) {
           System.out.println("get lock one");
           printObj(lock, 3);
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
       }
   }).start();

这里我们有Main线程和新开的一个线程来竞争资源,所以就会升级为一个轻量级锁。

  • 重量级锁
java 复制代码
for (int i = 0; i < 10; i ++) {
 new Thread(() -> {
     synchronized (lock) {
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             throw new RuntimeException(e);
         }
         System.out.println("get lock three");
     }
 }).start();
  printObj(lock, 5);

对象的访问

有两种方式:

  1. 句柄:稳定,对象被移动只要修改句柄中的地址

  2. 直接指针:访问速度快,节省了一次指针定位的开销

    对象类型数据存储的是对象的元信息,比如有哪些字段,那些方法

相关推荐
ThisIsClark2 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
王佑辉2 小时前
【jvm】内存泄漏与内存溢出的区别
jvm
大G哥4 小时前
深入理解.NET内存回收机制
jvm·.net
泰勒今天不想展开4 小时前
jvm接入prometheus监控
jvm·windows·prometheus
东阳马生架构1 天前
JVM简介—3.JVM的执行子系统
jvm
程序员志哥1 天前
JVM系列(十三) -常用调优工具介绍
jvm
后台技术汇1 天前
JavaAgent技术应用和原理:JVM持久化监控
jvm
程序员志哥1 天前
JVM系列(十二) -常用调优命令汇总
jvm
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 天前
聊聊volatile的实现原理?
java·jvm·redis