JVM之壓縮指標(CompressedOops)

二進位制之路發表於2019-01-26

對於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之壓縮指標(CompressedOops)

在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<Integer> 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檢視物件分配情況。

JVM之壓縮指標(CompressedOops)

JVM之壓縮指標(CompressedOops)

JVM之壓縮指標(CompressedOops)

開啟壓縮指標

壓縮指標預設開啟(-XX:+UseCompressedOops)。

$ java IntegerApplication
application is running...
複製程式碼

JVM之壓縮指標(CompressedOops)

每個Integer大小為:

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

所有Integer總大小為:

2000256*16=32004096bytes

關閉壓縮指標

設定引數-XX:-UseCompressedOops,關閉壓縮指標。

$ java -XX:-UseCompressedOops IntegerApplication
application is running...
複製程式碼

JVM之壓縮指標(CompressedOops)

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

JVM之壓縮指標(CompressedOops)

Object Header on a 64bit VM without compressed oops

JVM之壓縮指標(CompressedOops)

Object Header on a 32bit VM

JVM之壓縮指標(CompressedOops)


參考

《Java效能權威指南》Scott Oaks

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

rednaxelafx.iteye.com/blog/101007…

gist.github.com/arturmkrtch…

個人公眾號

更多文章,請關注公眾號:二進位制之路

二進位制之路

相關文章