java 64 位的指针压缩_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在支持更大堆的同时,由于对象引用变大却带来了性能问题:

增加了GC开销

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

降低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所能引用的堆内存空间)

357b1a939eb718785da1d592df8fd11a.png

在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

压缩指针默认开启:

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

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

java version "1.7.0_79"

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

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

复制代码

JDK 8

压缩指针默认开启:

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

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

Narrow klass base: 0x0000000000000000, Narrow klass shift: 3

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

java version "1.8.0_121"

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

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

复制代码

关闭压缩指针:

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

java version "1.8.0_121"

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

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

复制代码

实例比较

测试环境:JDK 1.8.0_121

测试代码

import java.util.LinkedList;

import java.util.List;

import java.util.Scanner;

public class IntegerApplication {

public static void main(String[] args) {

List intList = new LinkedList<>();

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

Integer number = new Integer(1);

intList.add(number);

}

Scanner scanner = new Scanner(System.in);

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

String tmp = scanner.nextLine();

System.exit(0);

}

}

复制代码

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

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

09d7a21543fcb374de1651d2380f94b6.png

07cc5b95ababcd53bf38347957ae657a.png

425f4268a541b59505d03bfca92e4d15.png

开启压缩指针

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

$ java IntegerApplication

application is running...

复制代码

d84ff626e3dbb4b1f6c98a305a5440e4.png

每个Integer大小为:

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

所有Integer总大小为:

2000256*16=32004096bytes

关闭压缩指针

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

$ java -XX:-UseCompressedOops IntegerApplication

application is running...

复制代码

d1dbb715d436259ab7263cc59d40d3a1.png

每个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

936863cde5e07fc0d4a9a40b2014e666.png

Object Header on a 64bit VM without compressed oops

3b6b2cbd0e362eb2638e25100b677e01.png

Object Header on a 32bit VM

633762a28059025706129302b8c22d21.png

参考

《Java性能权威指南》Scott Oaks

个人公众号

更多文章,请关注公众号:二进制之路

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值