JVM之压缩指针(CompressedOops)

编程

对于32位机器,进程能使用的最大内存是4G。如果进程需要使用更多的内存,需要使用64位机器。

对于Java进程,在oop只有32位时,只能引用4G内存。因此,如果需要使用更大的堆内存,需要部署64位JVM。这样,oop为64位,可引用的堆内存就更大了。

注:oop(ordinary object pointer),即普通对象指针,是JVM中用于代表引用对象的句柄。

在堆中,32位的对象引用占4个字节,而64位的对象引用占8个字节。也就是说,64位的对象引用大小是32位的2倍。

64位JVM在支持更大堆的同时,由于对象引用变大却带来了性能问题:

  1. 增加了GC开销

64位对象引用需要占用更多的堆空间,留给其他数据的空间将会减少,从而加快了GC的发生,更频繁的进行GC。

  1. 降低CPU缓存命中率

64位对象引用增大了,CPU能缓存的oop将会更少,从而降低了CPU缓存的效率。

为了能够保持32位的性能,oop必须保留32位。那么,如何用32位oop来引用更大的堆内存呢?

答案是压缩指针(CompressedOops)。

JVM的实现方式是,不再保存所有引用,而是每隔8个字节保存一个引用。例如,原来保存每个引用0、1、2...,现在只保存0、8、16...。因此,指针压缩后,并不是所有引用都保存在堆中,而是以8个字节为间隔保存引用。

在实现上,堆中的引用其实还是按照0x0、0x1、0x2...进行存储。只不过当引用被存入64位的寄存器时,JVM将其左移3位(相当于末尾添加3个0),例如0x0、0x1、0x2...分别被转换为0x0、0x8、0x10。而当从寄存器读出时,JVM又可以右移3位,丢弃末尾的0。(oop在堆中是32位,在寄存器中是35位,2的35次方=32G。也就是说,使用32位,来达到35位oop所能引用的堆内存空间)

 

 

在JVM中(不管是32位还是64位),对象已经按8字节边界对齐了。对于大部分处理器,这种对齐方案都是最优的。所以,使用压缩的oop并不会带来什么损失,反而提升了性能。

Oracle JDK从6 update 23开始在64位系统上会默认开启压缩指针。

32位HotSpot VM是不支持UseCompressedOops参数的,只有64位HotSpot VM才支持。

对于大小在4G和32G之间的堆,应该使用压缩的oop。

查看压缩指针的工作模式

在VM启动的时候,可以设置 -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode 参数来确认压缩指针的工作模式。

JDK 7

压缩指针默认开启:

 

  1. $ java -server -Xms2G -Xmx2G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version

  2.  
  3. heap address: 0x000000077ae00000, size: 2130 MB, zero based Compressed Oops

  4.  
  5. java version "1.7.0_79"

  6. Java(TM) SE Runtime Environment (build 1.7.0_79-b15)

  7. Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

  8. 复制代码

JDK 8

压缩指针默认开启:

 

  1. $ java -server -Xms2G -Xmx2G -XX:+UseConcMarkSweepGC -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version

  2.  
  3. heap address: 0x0000000080000000, size: 2048 MB, Compressed Oops mode: 32-bit

  4.  
  5. Narrow klass base: 0x0000000000000000, Narrow klass shift: 3

  6. Compressed class space size: 1073741824 Address: 0x000000013fe20000 Req Addr: 0x0000000100000000

  7. java version "1.8.0_121"

  8. Java(TM) SE Runtime Environment (build 1.8.0_121-b13)

  9. Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

  10. 复制代码

关闭压缩指针:

 

  1. $ java -server -Xms2G -Xmx2G -XX:+UseConcMarkSweepGC -XX:-UseCompressedOops -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode -version

  2. java version "1.8.0_121"

  3. Java(TM) SE Runtime Environment (build 1.8.0_121-b13)

  4. Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

  5. 复制代码

实例比较

测试环境:JDK 1.8.0_121

测试代码

 

  1. import java.util.LinkedList;

  2. import java.util.List;

  3. import java.util.Scanner;

  4. public class IntegerApplication {

  5. public static void main(String[] args) {

  6. List<Integer> intList = new LinkedList<>();

  7. for (int i = 0; i < 2000000; i++) {

  8. Integer number = new Integer(1);

  9. intList.add(number);

  10. }

  11. Scanner scanner = new Scanner(System.in);

  12. System.out.println("application is running...");

  13. String tmp = scanner.nextLine();

  14. System.exit(0);

  15. }

  16. }

  17. 复制代码

使用Eclipse Memory Analyzer查看Integer对象数量与大小

先运行程序IntegerApplication,再通过mat查看对象分配情况。

 

 

 

 

 

 

开启压缩指针

压缩指针默认开启(-XX:+UseCompressedOops)。

 

  1. $ java IntegerApplication

  2. application is running...

  3. 复制代码

 

 

每个Integer大小为:

64(Mark Word)+32(Compressed oops)+32(int)=128bits=16bytes

所有Integer总大小为:

2000256*16=32004096bytes

关闭压缩指针

设置参数-XX:-UseCompressedOops,关闭压缩指针。

 

  1. $ java -XX:-UseCompressedOops IntegerApplication

  2. application is running...

  3. 复制代码

 

 

每个Integer大小为:

64(Mark Word)+64(Compressed oops)+32(int)=160bits=20bytes

由于JVM内存分配需要根据字宽进行对齐,对于64位JVM,字宽为8个字节。因此,一个Integer实际占用24bytes,即192bits。

所有Integer总大小为:

2000256*24=48006144bytes

通过上面的实例可以看到,在开启压缩指针之后,oop大小确实是变成了32位,并且实际测试结果与理论分析是一致的。

Object Header

Object Header on a 64bit VM with compressed oops

 

 

Object Header on a 64bit VM without compressed oops

 

 

Object Header on a 32bit VM

 

 

参考

《Java性能权威指南》Scott Oaks

www.javacodegeeks.com/2016/05/com…

rednaxelafx.iteye.com/blog/101007…

gist.github.com/arturmkrtch…

以上是 JVM之压缩指针(CompressedOops) 的全部内容, 来源链接: utcz.com/z/512792.html

回到顶部