今天在网上冲浪,马上被这个神秘的面试题所吸引,Object o = new Object()占多少内存?第一眼看到就觉得似曾相识,有种想哭的感觉。我勒个去,这不和以前常问的基本数据类型占几个字节差不多么,面试官居然推陈出新了。
废话少说,直奔主题,大纲列起来。
上图就是传说中的对象布局。我们可以看到整个对象主要包括Header,InstanceDate,Padding。
Header就是我们常说的对象头
对象头又分为三个部分MarkWord、Klass、ArrayLength
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字节)。
这一块比较简单,自己算就完了.
数据类型 | 长度 | 默认值 |
---|---|---|
byte | 1 | 0 |
short | 2 | 0 |
int | 4 | 0 |
long | 8 | 0L |
float | 8 | 0.0f |
double | 4 | 0.0d |
char | 1 | ‘u0000’ |
boolean | 1 | false |
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米的大刀削苹果的绝活吧。
我们来列一下这个对象内存布局,
我们先搞个测试类
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