【JVM】Java对象的内存布局

2 篇文章 0 订阅

前言

从一道【某Y】的面试题入手,今天分析一下Java对象的内存布局:
问下面这个Java对象在64位虚拟机中占多大的内存?

class Test{
	int a;
	Integer number;
}

【下文以jdk1.8的hotspot虚拟机为例】


一、介绍一下对象内存布局

一个对象由一下几部分构成

(1)对象头

  • 对象头中包含mark word,Klass pointer,如果是数组的话还会存下数组大小

(2)实例数据

  • 存储的是类中的成员变量(静态变量可不存这里哦

(3)填充数据

  • 为了将对象的大小凑整为8的倍数

二、对象内部各个区域的作用

1.mark word 标记词

mark word的作用主要是为了记录对象的锁信息的,像是Synchronized和一些锁操作(如下图)【等书到了,我会另外补全这里的知识】,在64位操作系统下,标记词占64bit;在32位系统下,占32bit;
在这里插入图片描述锁状态+锁操作+CMS过程中用到的标记信息
深入探讨mark word


2.Klass pointer 类型指针

我们知道类的信息和对象是分开存储的,对象存在堆中,而类信息存在方法区(jdk1.8的元空间)中。那么两者之间通过什么进行绑定呢?这个时候Klass Pointer类型指针就发挥了作用!
像是对象的一些信息包括(hash码,对象首地址,GC分代年龄)都存在方法区,JVM要获取一个对象的信息,就可以通过这个Klass pointer从方法区获取。

关于Klass pointer的大小,在32位虚拟机中占32bit。
在64位虚拟机中,可以是32bits也可以是64bits,取决条件是一个叫做指针压缩的优化是否开启

  • 未开启指针压缩时,类型指针占用8B (64bit)
  • 开启指针压缩情况下,类型指针占用4B (32bit)

具体看这篇文章:指针压缩是什么?

需要注意的是jdk6以后的指针压缩是默认开启的(当然也可以手动关掉,在“指针压缩是什么?”一文中有详细讲解),主要还是一种内存空间的优化策略


3.数组长度

这一部分只有对象是数组的时候,才会存在于对象头部,占用4字节(32bit)空间。


4.实例数据

  • 1.基本类型占用的字节为:
java基本类型占用字节
byte,boolean1字节
char,short2
int,float4
long,double8
  • 2.引用类型占用的字节为:

对于引用类型占用,如果开启了指针压缩,那么占4字节,如果关闭指针压缩,占8字节。(包括String,等基本类型的包装类型

  • 3.如果该类继承于其他父类:

那就是子类会继承父类的成员变量。父类的成员变量会加到子类中。
有个问题:【父类的private变量,子类能继承但是无权使用,会不会加到子类对象的实例数据中?】

  • 4.静态变量?

静态变量并不与堆存在一起,jdk1.7之前是存在方法区中的,自从jdk1.7及之后版本都是存在堆中,但不是存在对象的实例数据中,而是分开存储。【具体是怎么存的等书到了再补充…】

  • 5.另外

Jvm在对象的实例数据中并不是按照我们声明的先后,从前到后进行排序的,而是涉及了另一个优化方式,叫做【字段重排序】


5.填充数据

填充数据的作用仅仅是为了:如果上面4部分的数据加起来,如果所占用的字节数不是8的倍数,那么填充数据就进行填充,填充到8的倍数!
【原因】64位的操作系统一次IO也就是64位,也就是8个字节,将对象凑整成8的倍数,每次IO的李利用率就提升了


三、使用工具查看对象头大小

可以通过OpenJDK的 jol 工具将对象的内存布局进行打印
这个工具是可以在Oracle的JDK中打印信息的,放心使用
下面是pom的依赖部分

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

使用案例

public class Test {
    public static void main(String[] args) {
        System.out.println(VM.current().details());  打印虚拟机当前情况
        String wang = "a";
        ClassLayout layout = ClassLayout.parseInstance(wang);  
        System.out.println(layout.toPrintable());  打印对象内存布局占用详细情况
        System.out.println(layout.headerSize());   打印对象头大小
        System.out.println(layout.instanceSize()); 打印对象占用大小
    }
}

在这里插入图片描述


四、回收一下前言的题目答案

问下面这个Java对象在64位虚拟机中占多大的内存?

class Test{
	int a;
	Integer number;
}

默认情况下指针压缩开启
mark word 8字节+ klass pointer 4字节 + int 4字节 + Integer引用类型4字节 = 20字节
因为不是8的倍数,因此补充数据4字节凑整
最后Test对象24字节。

验证一下

1. 使用jol

public class Test {
    public static void main(String[] args) {
        System.out.println(VM.current().details());
        Wang wang = new Wang();
        ClassLayout layout = ClassLayout.parseInstance(wang);
        System.out.println(layout.toPrintable());
    }
}

2. 最后输出
在这里插入图片描述默认是开启指针压缩的内存优化,因此占用24个字节。


写在最后

后续会再写一篇文章,专门验证每个区域的内存大小,以及指针压缩的内存优化细节

使用jol查看对象内存占用



————— END —————




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值