Chinaunix首页 | 论坛 | 博客
  • 博客访问: 234919
  • 博文数量: 19
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1182
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-20 23:47
个人简介

如果人生是一条隐含马尔科夫模型,那维特比算法告诉我已经偏离最优路线了,但如果起点是现在呢?

文章分类

全部博文(19)

文章存档

2020年(2)

2014年(3)

2013年(14)

分类: Java

2013-06-11 19:11:09

背景:从oracle jdk1.6起,hotspot提供了一个参数UseCompressedOops,中文名字叫做压缩普通对象指针

点击(此处)折叠或打开

  1. ye@ye-Latitude-E5420:/tmp$ java -version
  2. java version "1.7.0_11"
  3. Java(TM) SE Runtime Environment (build 1.7.0_11-b21)
  4. Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)
  5. ye@ye-Latitude-E5420:/tmp$ java -XX:+PrintFlagsFinal|grep UseCompressedOops
  6. bool UseCompressedOops := true {lp64_product}


    java对象在Heap里面的结构是这样的:对象头跟对象体,对象体跟C里面的结构体是一样的,对象头由两个域组成:用于存放hashcode、同步、GC_mask域,和指向方法区该对象Class对象的指针——_klass域,对于64位系统,头部长度理论上讲应该是8+8=16字节

那开了这个参数到底有什么用呢,让我们来看一个测试,需要预装groovy

点击(此处)折叠或打开

  1. public class Person{
  2.         char a;
  3.         int b;
  4.         char c;
  5.         long d;
  6.         int e;
  7. }

编译好

点击(此处)折叠或打开

  1. ye@ye-Latitude-E5420:~$ javac Person.java

1.开启压缩指针模式

点击(此处)折叠或打开

  1. ye@ye-Latitude-E5420:~$ export JAVA_OPTS=-XX:+UseCompressedOops
  2. ye@ye-Latitude-E5420:~$ groovysh
  3. Groovy Shell (1.8.6, JVM: 1.7.0_11)
  4. Type 'help' or 'h' for help.
  5. --------------------------------------------------------------------
  6. groovy:000> new Person()
  7. ===> Person@3a610754
  8. groovy:000>
  9. 再开一个终端,切root
  10. root@ye-Latitude-E5420:/home/ye# pgrep java    //非linux用户使用jps
  11. 15494
  12. 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
  13. hsdb> attach 15494
  14. Attaching to process 15494, please wait...
  15. hsdb> class Person
  16. Person @0x00000007807d76d0
  17. hsdb> print 0x00000007807d76d0
  18. public class Person @0x00000007807d76d0
  19. Super Class
  20. public class java.lang.Object @0x000000077fa02db0
  21. Fields
  22. char a; (offset = 28)
  23. int b; (offset = 12)
  24. char c; (offset = 30)
  25. long d; (offset = 16)
  26. int e; (offset = 24)
  27. Methods
  28. public void () @0x00000007807d7648;
  29. Constant Pool
  30. Constant Pool of [public class Person @0x00000007807d76d0] @0x00000007807d7478

2.关闭压缩指针模式

点击(此处)折叠或打开

  1. ye@ye-Latitude-E5420:~$ export JAVA_OPTS=-XX:-UseCompressedOops
  2. ye@ye-Latitude-E5420:~$ groovysh
  3. Groovy Shell (1.8.6, JVM: 1.7.0_11)
  4. Type 'help' or 'h' for help.
  5. --------------------------------------------------------------------
  6. groovy:000> new Person()
  7. ===> Person@382839bd
  8. groovy:000>
  9. 再开一个终端,切root
  10. root@ye-Latitude-E5420:/home/ye# pgrep java
  11. 15577
  12. 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
  13. hsdb> attach 15577
  14. Attaching to process 15577, please wait...
  15. hsdb> class Person
  16. Person @0x00007fc1b57f5638
  17. hsdb> print 0x00007fc1b57f5638
  18. public class Person @0x00007fc1b57f5638
  19. Super Class
  20. public class java.lang.Object @0x00007fc1b4a02e38
  21. Fields
  22. char a; (offset = 32)
  23. int b; (offset = 24)
  24. char c; (offset = 34)
  25. long d; (offset = 16)
  26. int e; (offset = 28)
  27. Methods
  28. public void () @0x00007fc1b57f55b0;
  29. Constant Pool
  30. Constant Pool of [public class Person @0x00007fc1b57f5638] @0x00007fc1b57f53c8



对比中我们至少可以得出两点结论

  1. jvm对于对象会启用对齐优化,我们定义类时field的顺序在运行期会被打乱

  2. 关闭了压缩指针模式后,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字节




阅读(4921) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~