Java内存学习-int类型
使用openjdk中jol-java object layout工具,可以输出对象在内存中分布情况,了解内存结构、消耗及访问机制
以下文章以Int类型为例
1.图例
64 bit jvm with pointer compression
64bit jvm
32bit jvm
2.不同参数对比
对比三个输出,
如果VM小于64G, object header大小 12 bytes= 8 (mark word) + 4 (class word)
如果VM大于64G, object header大小 16 bytes= 8 (mark word) + 8 (class word)
如果VM关闭指针压缩, object header大小 16 bytes= 8 (mark word) + 8 (class word)
2.1.开启指针压缩后JOL输出
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) # Mark word
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) # Mark word
8 4 (object header) be 22 00 20 (10111110 00100010 00000000 00100000) (536879806)# Class word
12 4 int Integer.value 1
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
java.lang.Integer@506c589ed object externals:
ADDRESS SIZE TYPE PATH VALUE
6b0fd3480 16 java.lang.Integer 1
size : 16
2.2.最大堆设置超过64g后的JOL输出
# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) # Mark word
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) # Mark word
8 4 (object header) c8 30 32 28 (11001000 00110000 00110010 00101000) (674377928)# Class word
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)# Class word
16 4 int Integer.value 1
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Integer@531d72cad object externals:
ADDRESS SIZE TYPE PATH VALUE
b2ab94b48 24 java.lang.Integer 1
size : 24
2.3.关闭指针压缩的JOL输出
# Running 64-bit HotSpot VM.
# Objects are 8 bytes aligned.
# Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) # Mark word
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) # Mark word
8 4 (object header) c8 30 cd 16 (11001000 00110000 11001101 00010110) (382546120) # Class word
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)# Class word
16 4 int Integer.value 1
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Integer@531d72cad object externals:
ADDRESS SIZE TYPE PATH VALUE
d3e14b48 24 java.lang.Integer 1
size : 24
2.4.关于对象指针压缩
OOP = “ordinary object pointer” 普通对象指针。
通常64位JVM消耗的内存会比32位的大1.5倍,这是因为对象指针在64位架构下,长度会翻倍(更宽的寻址)。对于那些将要从32位平台移植到64位的应用来说(假如编译和运行环境同为32或同为64不需要手动开启),内存占用会增多,不太合理。从JDK 1.6 update14开始,64 bit JVM正式支持了 -XX:+UseCompressedOops 这个可以压缩指针,起到节约内存占用的新参数。
启用CompressOops后,会压缩的对象:
• 每个Class的属性指针(静态成员变量)
• 每个对象的属性指针
• 普通对象数组的每个元素指针
压缩也有局限性,针对一些特殊类型的指针,JVM是不会优化的。比如指向PermGen的Class对象指针,本地变量,堆栈元素,入参,返回值,NULL指针不会被压缩。
机制是解释器在解释字节码时,植入压缩指令(不影响正常和JVM优化后的指令顺序)。当对象被读取时,解压,存入heap时,压缩。
关于大小:
32位系统,占用 8 字节。Mark Word占用4个字节,Klass Pointer占用4个字节,数组长度占用4个字节。实际数据:引用类型占用4个字节。
64位系统,开启UseCompressedOops压缩时,占用 12 字节。Mark Word占用8个字节,Klass Pointer占用4个字节,数组长度占用4个字节。实际数据:引用类型占用4个字节。
64位系统,关闭UseCompressedOops压缩或者最大堆64G时,占用16字节。 Mark Word占用8个字节,Klass Pointer占用8个字节,数组长度占用4个字节。实际数据:引用类型占用8个字节。
3.JOL使用方法
3.1.方法一:启动java应用时加载jol,及指定参数
指定classpath及jar参数,查看自己的类布局
java -jar jol-cli-0.3.2-full.jar internals -cp demoapp.jar com.zc.test.jol.TO1
指定classpath参数,查看自己的类布局
java -cp demoapp.jar;jol-cli-0.3.2-full.jar org.openjdk.jol.Main internals com.zc.test.jol.TO1
3.2.方法二:代码加载jol,查看自己的对象或类布局
如下代码样例
import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.info.GraphLayout;
import org.openjdk.jol.vm.VM;
//-XX:+UseCompressedOops 与 -XX:-UseCompressedOops
public class CompressOops1 {
public static void main(String[] args) {
int i = 1;
System.out.println(VM.current().details());
//查看对象内部信息
System.out.println(ClassLayout.parseInstance(i).toPrintable());
//查看对象外部信息
System.out.println(GraphLayout.parseInstance(i).toPrintable());
//获取对象总大小
System.out.println("size : " + GraphLayout.parseInstance(i).totalSize());
}
}
3.3.JOL常用的三个函数方法
-
ClassLayout.parseInstance(object).toPrintable():查看对象内部信息
-
GraphLayout.parseInstance(object).toPrintable():查看对象外部信息,包括引用的对象
-
GraphLayout.parseInstance(object).totalSize():查看对象总大小
3.4.JOL输出信息介绍
输出4, 1, 1, 2, 2, 4, 4, 8, 8 分别表示:引用句柄, byte, boolean, char, short, int, float, double, long长度
输出# Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift.表示启用指针压缩
3.4.输出信息中对象组成
对象头
普通对象:
Mark Word:包含HashCode、分代年龄、锁标志等。
Klass Pointer:指向当前对象的Class对象的内存地址。
数组对象:
Mark Word:包含HashCode、分代年龄、锁标志等。
Klass Pointer:指向当前对象的Class对象的内存地址。
Length:数组长度
4.参考
Java Objects Inside Out-https://shipilev.net/jvm/objects-inside-out/
Java Memory Management - https://training.xceptance.com/java/410-memory-management.html