背景:从oracle
jdk1.6起,hotspot提供了一个参数UseCompressedOops,中文名字叫做压缩普通对象指针
-
ye@ye-Latitude-E5420:/tmp$ java -version
-
java version "1.7.0_11"
-
Java(TM) SE Runtime Environment (build 1.7.0_11-b21)
-
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)
-
ye@ye-Latitude-E5420:/tmp$ java -XX:+PrintFlagsFinal|grep UseCompressedOops
-
bool UseCompressedOops := true {lp64_product}
java对象在Heap里面的结构是这样的:对象头跟对象体,对象体跟C里面的结构体是一样的,对象头由两个域组成:用于存放hashcode、同步、GC的_mask域,和指向方法区该对象Class对象的指针——_klass域,对于64位系统,头部长度理论上讲应该是8+8=16字节
那开了这个参数到底有什么用呢,让我们来看一个测试,需要预装groovy
-
public class Person{
-
char a;
-
int b;
-
char c;
-
long d;
-
int e;
-
}
编译好
-
ye@ye-Latitude-E5420:~$ javac Person.java
1.开启压缩指针模式
-
ye@ye-Latitude-E5420:~$ export JAVA_OPTS=-XX:+UseCompressedOops
-
ye@ye-Latitude-E5420:~$ groovysh
-
Groovy Shell (1.8.6, JVM: 1.7.0_11)
-
Type 'help' or 'h' for help.
-
--------------------------------------------------------------------
-
groovy:000> new Person()
-
===> Person@3a610754
-
groovy:000>
-
再开一个终端,切root
-
root@ye-Latitude-E5420:/home/ye# pgrep java //非linux用户使用jps
-
15494
-
root@ye-Latitude-E5420:/home/ye# /home/q/java/jdk1.7.0_11/bin/java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
-
hsdb> attach 15494
-
Attaching to process 15494, please wait...
-
hsdb> class Person
-
Person @0x00000007807d76d0
-
hsdb> print 0x00000007807d76d0
-
public class Person @0x00000007807d76d0
-
Super Class
-
public class java.lang.Object @0x000000077fa02db0
-
Fields
-
char a; (offset = 28)
-
int b; (offset = 12)
-
char c; (offset = 30)
-
long d; (offset = 16)
-
int e; (offset = 24)
-
Methods
-
public void () @0x00000007807d7648;
-
Constant Pool
-
Constant Pool of [public class Person @0x00000007807d76d0] @0x00000007807d7478
2.关闭压缩指针模式
-
ye@ye-Latitude-E5420:~$ export JAVA_OPTS=-XX:-UseCompressedOops
-
ye@ye-Latitude-E5420:~$ groovysh
-
Groovy Shell (1.8.6, JVM: 1.7.0_11)
-
Type 'help' or 'h' for help.
-
--------------------------------------------------------------------
-
groovy:000> new Person()
-
===> Person@382839bd
-
groovy:000>
-
再开一个终端,切root
-
root@ye-Latitude-E5420:/home/ye# pgrep java
-
15577
-
root@ye-Latitude-E5420:/home/ye# /home/q/java/jdk1.7.0_11/bin/java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB
-
hsdb> attach 15577
-
Attaching to process 15577, please wait...
-
hsdb> class Person
-
Person @0x00007fc1b57f5638
-
hsdb> print 0x00007fc1b57f5638
-
public class Person @0x00007fc1b57f5638
-
Super Class
-
public class java.lang.Object @0x00007fc1b4a02e38
-
Fields
-
char a; (offset = 32)
-
int b; (offset = 24)
-
char c; (offset = 34)
-
long d; (offset = 16)
-
int e; (offset = 28)
-
Methods
-
public void () @0x00007fc1b57f55b0;
-
Constant Pool
-
Constant Pool of [public class Person @0x00007fc1b57f5638] @0x00007fc1b57f53c8
对比中我们至少可以得出两点结论
-
jvm对于对象会启用对齐优化,我们定义类时field的顺序在运行期会被打乱
-
关闭了压缩指针模式后,Person对象体偏移由 offset = 16变成了 offset = 12
所以开启压缩指针模式后,对象头的_klass域得到了压缩,居然变成了32位系统时的长度4字节了,我们都知道32位的长度最多只能表示4G的内存,那么HostSpot 究竟是如何处理的呢
我们引用官方文档:
这就是面对对象的好处,我们面对的最小地址单元不是byte,而是object,也就是说在jvm的世界里32位地址表示的不是4GB,而是4G个对象的指针,大概是32GB,解码过程就是把对象指针乘以8加上GC堆的初始地址就能得到操作系统本地64位地址了,编码过程相反
其中启用压指得有操作系统底层的支持:GC堆从虚拟地址0开始分配
进而我们可以得到压指面对的所有场景:
-
如果GC堆大小在4G以下,直接砍掉高32位,避免了编码解码过程
-
如果GC堆大小在4G以上32G以下,则启用UseCompressedOop
-
如果GC堆大小大于32G,压指失效(所以说服务器内存太大不好......)
考虑到内存对齐,Person对象开压指长度为32字节,不开为40字节
阅读(4927) | 评论(1) | 转发(0) |