JVM基础之JAVA对象

176 阅读5分钟

简介

     了解java对象是进行JVM调优的前提,才能在流量高峰前夕预判所需要的内存大小,,本篇文章分享的内容包括,java对象在内存的布局、如何计算java对象的大小、如何用程序输出java对象的大小及指针压缩的原理。

内存布局

对象的内存布局是什么

由于java中不需要程序员对内存进行操作,所以提到java对象的内存布局,容易让java程序员一头雾水,下来简单解释下。

java对象我们都好理解,就是java代码中一个对象

例: 下面代码中test就是一个java对象

Test test = new Test();

当java程序运行起来后,JVM就会将Test加载进内存,当JVM解析到new Test()这句代码时,就会对应生成一个c++对象(oop模型),且把该对象放到内存中(堆),我们要研究的对象布局就是,该c++对象在内存中的布局。

image.png

布局

image.png

(以下内容基于64位机器,因为32位机器很少了)

  • Mark Word

      作用是标记锁状态等信息,不详细说了

      大小: 8B

  • 类型指针

      作用是指向由哪个类型生成的。

      大小: 开启指针压缩:4B 关闭指针压缩:8B

      备注 : 针压缩后面会讲

  • 数组长度       作用是记录数组长度的,如果该兑现不是数组,那就没有这块区域

      大小: 4B

  • 实例数据       作用是记录真实的数据

      大小 bool(1B),byte(1B),char(2B),int(4B),floan(4B),long(8B),double(8B) ,引用类型(开启指针压缩4B,未开启8B)

  • 对象填充       作用是填充对象大小,使该对象大小能够被8字节整除

计算对象大小

前置工作

加入依赖包

org.openjdk.jol jol-core 0.16

案例1

public class Example_1 {

}

我们来分析一下Example_1的大小

(开启指针压缩)

Mark Word类型指针数组长度实例数据对象填充
84004

解释: Mark Word 加类型指针大小为12,不能被8整除,所以要补充4B填充到16

程序结果

image.png

案例2

public class Example_2 {
    int i = 2;
    boolean b;
}

(开启指针压缩)

Mark Word类型指针数组长度实例数据对象填充
84057

解释: 实例数据为一个int,一个boolean,各占4 B,1B所以为5B。

程序结果:

image.png

案例3

public class Example_3 {

    List<String> list = new ArrayList<>();

}

(开启指针压缩)

Mark Word类型指针数组长度实例数据对象填充
84400

解释: Mark Word+类型指针+数组长度为16,刚好能被8整除,所以不需要再进行对象填充

程序结果:

image.png

指针压缩

指针

我们先来简单解释下类型指针

指针: c,c++的概念,可以简单理解为存放的是内存地址

image.png

类型指针

前面文章讲过了,class文件被JVM加载后,会在元空间生成Klass对象,类型指针就是指向该Klass对象的内存地址

image.png

原理

内存机制

为了讲清指针压缩原理,我们要先铺垫一下计算机内存机制。

image.png

内存单元

一个完整的内存内部会划分为,若干个小格子称之为内存单元,每个内存单元的大小是一个字节。且每个内存单元都有唯一的一个内存地址

引脚

引脚分为数据信号引脚,和地址信号引脚,地址信号引脚接受电信号(如5v电压表示1,0v表示0),

读数据

  1. 地址引脚接受电信号00111101
  2. 读取地址为00111101内存单元例的数据
  3. 通过数据信号引脚将数据返回。

32位机器能够表示的内存有多大?

32位机器也就是,有32个地址引脚,能表示的内存单元的个数为 2^32 个,每个内存单元的大小为1byte所以,能表示的内存大小为 2^32byte,

    2^32byte = 2^22KB = 2^12MB=2^2GB = 4GB

    (如果不懂这个换算,先补一下二进制)

指针压缩原理

我们上文中提到过java对象有对象对齐填充的部分保证每个对象的大小都是8的倍数,这就说明任何一个对象的内存地址减第一个对象的内存地址一定能整除8,例如第一个对象的内存地址是0,后续对象的内存地址一定是8的倍数。这样就可以再次对内存进行分组,每组的大小为8byte

image.png

32G瓶颈

我们来算一下开启指针压缩后用4B可以表示大内存

4B = 2^32bit,可以表示 2^32个java中的分组,每个分组大小为8byte,也就是2^328 byte = 2^322^3 byte = 2^35 byte = 2 ^25 KB = 2^15MB = 2^5GB = 32GB

image.png