我们的对象到底长什么样子?快来使用工具来分析你的对象

本文通过一个面试问题探讨了Java对象在内存中的布局,包括Header、InstanceData和Padding三部分。介绍了对象头的MarkWord、Klass和ArrayLength,以及不同环境下它们的大小。同时,文章推荐使用JOL工具来查看Java对象的具体布局,展示了开启和关闭指针压缩对对象大小的影响,并给出了不同类型对象的内存占用情况。
摘要由CSDN通过智能技术生成

今天在网上冲浪,马上被这个神秘的面试题所吸引,Object o = new Object()占多少内存?第一眼看到就觉得似曾相识,有种想哭的感觉。我勒个去,这不和以前常问的基本数据类型占几个字节差不多么,面试官居然推陈出新了。

废话少说,直奔主题,大纲列起来。

image.png

上图就是传说中的对象布局。我们可以看到整个对象主要包括Header,InstanceDate,Padding。

Header就是我们常说的对象头

对象头又分为三个部分MarkWordKlassArrayLength

MarkWord 用于存放的对象运行时的数据。

这里存放我们面试经常问的Synchronized中需要用的锁息息相关的锁状态标志,还有我们经常使用的hashCode,在对象调用hashCode方法之后也会把值存在这里,GC对象的分代年龄,偏向锁指向的线程ID等等等,一大堆面试题都在这里,你说重不重要(具体细节下篇文章再说)。

这一块的大小和运行环境有关,32位操作系统中它占32bit,64位操作系统中占64bit。

Klass:类型指针

存放指向该对象类元数据(方法区)的指针,虚拟机通过这一个指针来判断它属于哪一个类。这一块的大小也和运行环境有关,32位操作系统中它占32bit,64位操作系统中占64bit但是如果开启了指针压缩( -XX:+UseCompressedClassPointers 运行参数,这参数应用的前提是开启对象压缩 -XX:+UseCompressedOops )的话它就是32bit 也就是4字节,正常情况下是默认开启指针压缩的

Array length:数组长度 这个比较特殊只有数组对象才有,int类型 占用4字节,用于表示数组的长度

InstanceData: 实例数据

这一块就是存放我们类的成员变量,基本数据类型占用空间如下图,如果属性是对象的话,那么和kclass一样也和操作系统,指针压缩有关,32位操作系统中它占32bit(4字节),64位操作系统中占64bit(8字节)但是如果开启了指针压缩那么他就是32bit(4字节)。

这一块比较简单,自己算就完了.

数据类型长度默认值
byte10
short20
int40
long80L
float80.0f
double40.0d
char1‘u0000’
boolean1false

Padding :对象补齐

这一块是应虚拟机的要求,如果以上数据不能被8的倍数则存在,用于补齐符合 HotSpot 虚拟机的要求一个对象的起始地址必须是8字节的整数倍。

例如Object o = new Object() 这里我们先说一下环境,64位操作系统+开启指针压缩

Header: MarkWord(8字节)+Klass(4字节) 因为不是数组所以没有ArrayLength

InstanceData: 没有成员变量,所以这一块是0字节

Padding: 上面是8+4+0=12字节,明显不是8的倍数,所以这一块补充了4字节

所以 new Object() 现在是12+4 = 16字节。

我们再看下题目,哈哈 是不是还忘了一个Object o 这个指针也占了4字节。

所以答案就出来了Object o = new Object() 占用了20个字节。

哈哈哈 今天就到这里了,走过路过不要错过,有钱的捧个钱场没钱的捧个人场。

龙套A:说得还挺像真的,我们怎么知道你是不是在忽悠我们。

哎呦,我这暴脾气。我40米长的大刀呢?

注: 下面场面比较血腥了,捧人场的可以撤了,捧钱场的可以留下来看更精彩的表演。

这时候就要让你这个杠精来见识见识我40米长的大刀了。

说到这不得不提一下 JOL,JOL全称为Java Object Layout,是分析JVM中对象布局的工具。现在我们用另外一件武器Maven把我们的40米长的大刀运过来。

1.引入JOL (JAVA Object Layout)jar包

<!-- JOL java内存布局 -->
<dependency>
      <groupId>org.openjdk.jol</groupId>
      <artifactId>jol-core</artifactId>
      <version>0.10</version>
</dependency>

2.使用JOL打印对象布局

public class JOLTest {
    public static void main(String[] args) {
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

3.看到JOL的打印结果我们得到new Object() 有16个字节,其中对象头占12个字节,丢失了4个字节。

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

看看,40米的大刀效果就是不俗。来都来了,就给你们表演下40米的大刀削苹果的绝活吧。

我们来列一下这个对象内存布局,

image.png

我们先搞个测试类

import org.openjdk.jol.info.ClassLayout;

/***
 * JVM的对象布局
 */
public class JOLTest {
    public static void main(String[] args) {
        //空对象
        EmptyObject o = new EmptyObject();
        //数组对象
        EmptyObject[] array = new EmptyObject[1024];
        //不用对齐的对象
        IntObject intObject = new IntObject();
        //引用对象的对象
        ReferenceObject referenceObject = new ReferenceObject();
        
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println(ClassLayout.parseInstance(array).toPrintable());
        System.out.println(ClassLayout.parseInstance(intObject).toPrintable());
        System.out.println(ClassLayout.parseInstance(referenceObject).toPrintable());
    }

}
class EmptyObject{

}

class IntObject{
    private int i;
}

class ReferenceObject{
    private EmptyObject emptyObject = new EmptyObject();
}

哦了,我们把开了指针压缩和没开的分开对比一下,上面是开了指针压缩的结果。

首先我们先看下空对象:我们发现Header多了4个字节,8-16是klass占用的位置8字节,并且没有指针压缩时,对象大小是8的16字节,是8的倍数,所以没有了padding

开启指针压缩:MarkWord(8字节)+Klass(4字节)+Arraylength(4字节)+Padding(4字节)=16字节

未开启指针压缩:MarkWord(8字节)+Klass(8字节)=16字节

#开启指针压缩
juc.EmptyObject object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
#未开启指针压缩
juc.EmptyObject object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           00 35 27 1c (00000000 00110101 00100111 00011100) (472331520)
     12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

我们在看看数组对象:

我们可以看到在header中最后一行开始出现arrayLength 表示数组长度 值是1024

开启指针压缩:MarkWord(8字节)+Klass(4字节)+Arraylength(4字节)+InstanceData(4096)=4112字节

未开启指针压缩:MarkWord(8字节)+Klass(8字节)+Arraylength(4字节)+Padding(4字节)+InstanceData(4096)=4120字节

#开启指针压缩
[Ljuc.EmptyObject; object internals:
 OFFSET  SIZE              TYPE DESCRIPTION                               VALUE
      0     4                   (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                   (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                   (object header)                           81 c1 00 f8 (10000001 11000001 00000000 11111000) (-134168191)
     12     4                   (object header)                           00 04 00 00 (00000000 00000100 00000000 00000000) (1024)
     16  4096   juc.EmptyObject EmptyObject;.<elements>                   N/A
Instance size: 4112 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

#未开启指针压缩
[Ljuc.EmptyObject; object internals:
 OFFSET  SIZE              TYPE DESCRIPTION                               VALUE
      0     4                   (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                   (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                   (object header)                           40 37 27 1c (01000000 00110111 00100111 00011100) (472332096)
     12     4                   (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     4                   (object header)                           00 04 00 00 (00000000 00000100 00000000 00000000) (1024)
     20     4                   (alignment/padding gap)                  
     24  4096   juc.EmptyObject EmptyObject;.<elements>                   N/A
Instance size: 4120 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

有int属性的对象:

开启指针压缩:MarkWord(8字节)+Klass(4字节)+InstanceData(4字节)=16字节

未开启指针压缩:MarkWord(8字节)+Klass(8字节)+InstanceData(4字节)+Padding(4字节)=24字节

#开启指针压缩
juc.IntObject object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           bd c1 00 f8 (10111101 11000001 00000000 11111000) (-134168131)
     12     4    int IntObject.i                               0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
#未开启指针压缩
juc.IntObject object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           f0 3a 27 1c (11110000 00111010 00100111 00011100) (472333040)
     12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     4    int IntObject.i                               0
     20     4        (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

有Object引用的对象:

开启指针压缩:MarkWord(8字节)+Klass(4字节)+InstanceData(4字节)=16字节

未开启指针压缩:MarkWord(8字节)+Klass(8字节)+InstanceData(4字节)+Padding(4字节)=24字节

#未开启指针压缩
juc.ReferenceObject object internals:
 OFFSET  SIZE              TYPE DESCRIPTION                               VALUE
      0     4                   (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                   (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                   (object header)                           05 c2 00 f8 (00000101 11000010 00000000 11111000) (-134168059)
     12     4   juc.EmptyObject ReferenceObject.emptyObject               (object)
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
#未开启指针压缩
juc.ReferenceObject object internals:
 OFFSET  SIZE              TYPE DESCRIPTION                               VALUE
      0     4                   (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4                   (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4                   (object header)                           28 40 27 1c (00101000 01000000 00100111 00011100) (472334376)
     12     4                   (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     4   juc.EmptyObject ReferenceObject.emptyObject               (object)
     20     4                   (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

唉呀妈呀,太累了,溜了溜了,今天就到这里了,明天再详细讲讲MarkWord。各位看官再见,再见。

注:

查看java运行环境和参数 java -XX:+PrintCommandLineFlags -version

可以看出jdk1.8 64位系统 运行参数默认带了指针压缩。

-XX:InitialHeapSize=267362880 -XX:MaxHeapSize=4277806080 -XX:+PrintCommandLineFlags 
-XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

关闭指针压缩需要在JVM运行参数上加-XX:-UseCompressedClassPointers

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值