[Java] Compressed Class Space와 Compressed Object Pointer

Jaemun Jung
7 min readNov 6, 2021

--

이전 글 [Java] Metaspace에서 Java 메모리 영역 중 Metaspace에 대해 알아보았다.이번에는 그 Metaspace의 일부를 차지하는 Compressed Class Space에 대해서 알아보자.

64bit 플랫폼에서, 핫스팟은 Compressed Object Pointers (“CompressedOops”)와 Compressed Class Pointers를 사용한다.

Metaspace와 Compressed Class Space의 차이

이전에 알아본 Metaspace와 Compressed Class Space의 차이는?

  • Compressed Class Space는 오직 class metadata — Klass만 보관한다.
    - InstanceKlass, ArrayKlass
    - UseCompressedClassPointers가 true일때만 활용됨
  • Metaspace는 더 큰 사이즈의 모든 class metadata를 보관한다.
    - Methods, Bytecodes, ConstantPool …

Compressed Class Pointer와 Compressed Object Pointer에 대해서 조금 더 알아보자.

Compressed Class Pointer & Compressed Object Pointer

먼저 Compressed Class Pointer & Compressed Object Pointer 모두 Compressed Pointer로, CompressedOops는 Java Heap의 object를, Compressed Class Pointer는 Metaspace의 class metadata를 가리킨다.
(참고로 “OOps”는 “ordinary” object pointer의 의미다.)

Compressed Pointer는 64bit 플랫폼에서도 32bit 포인터로 주소를 참조하는 방법이다. 어떻게?

32bit는 2³²까지 표현할 수 있으므로 최대 4G까지 주소 공간을 가리킬 수 있고, 64bit는 2⁶⁴까지 표현할 수 있으므로 이론상 18EB까지 주소 공간을 가리킬 수 있다. 하지만 64bit의 주소공간 자체가 크기 때문에 성능이 떨어질 수 밖에 없다.
더 많은 연산과 더 많은 메모리 공간을 필요로 하기 때문이다. 반면에 32bit를 활용하면 memory 사용량이 작으므로 pointer size 자체도 작아지고, cache를 더 활용할 수 있고, register를 더 쓸 수 있다는 장점 등이 있다.

32bit 기반의 포인터로는 4GB 이상의 주소공간을 가리킬 수 없게 되고, 결국 이 때문에 32bit 포인터는 Object의 주소 자체를 찌르지 않는다(못한다). 대신 offset을 가리키게 된다. 이게 Compressed Pointer의 동작방식이다.(이와 달리 Native OOP는 OOP가 바로 Object를 주소를 가리키게 된다.)

Java object는 그 header에 Java Heap이 아닌 Metaspace에 살고 있는 native structure인 Klass structrue에 대한 reference를 갖고 있다. 이때 32bit 값을 가진 compressed pointer를 통해서 64bit 주소를 찾아야하므로, 찾고자 하는 주소에 offset을 앞뒤로 붙여서 찾는 것이다.

(from https://stuefe.de/posts/metaspace/what-is-compressed-class-space/)

offset은 8의 n배수로 계산되어, 값이 1이면 8번 주소, 2번이면 16번 주소를 가리키게 된다. n개의 OOP로 기존보다 8배 더 많은 주소 공간을 표시할 수 있게 된다. 그래서 Heap영역이 32GB를 넘어가게 되면 그때부터 JVM은 64bit 기반의 OOP를 사용하게 되며, 이러면 성능이 급격히 저하되게 된다.

이렇게 offset 구조로 Klass를 찾아야 하므로, Metaspace는 연속된 구조로 할당되는 것이다.

XX:CompressedClassSpaceSize

compressed class space의 크기는 -XX:CompressedClassSpaceSize에 의해 정의될 수 있다. 별도로 정의하지 않을 경우 default값은 1G이며, MAX로 설정할 수 있는 값은 3G이다.

평균적으로 Klass structure의 사이즈는 1K 내외이므로, 1G의 Compressed Class Space는 100만개의 Klass structure를 가질 수 있다. 이 제한이 바로 우리가 클래스를 로드할 때 클래스 개수가 제한되는 유일한 제한이다.

CompressedClassPointers는 CompressedOops이 없으면 자동으로 disable된다.

CompressedOops는 -XX:-CompressedOops 를 통해 명시적으로 끄거나, 자바 힙이 32GB 이상일 때 자동으로 off 된다. (위에서 말한 것처럼 Offset 구조로 활용할 때 32GB까지만 포인팅할 수 있으므로, 그 이상은 64bit Native pointer를 쓰게 되므로)

Switches: UseCompressedClassPointers, UseCompressedOops

  • -XX:+UseCompressedOops : enables compressed object pointers.
  • -XX:+UseCompressedClassPointers : enables compressed class pointers.

두 스위치 모두 default는 ON이다.

만약 UseCompressedClassPointers가 off되면, compressed class space가 존재하지 않게 되고, 따라서 XX:CompressedClassSpaceSize 스위치는 무시된다.

-XX:+UseCompressedClassPointers는 -XX:+UseCompressedOops가 ON 되어있는 경우만 사용할 수 있다.
UseCompressedClassPointers를 끄고 UseCompressedOops만 키는 방식을 통해 일부 엣지 케이스에서 Metaspace의 memory footprint를 줄일 수 있기는 한데, 일반적으로 둘다 그대로 두는 것이 권장된다.

위에서 언급한것처럼 32bit offset구조를 사용하는 Compressed object pointers는 32G 이하의 Java Heap까지 활용 가능하며, 32G 이상의 Java heap에서는 compressed oops는 off된다. 이에 따라서 저절로 compressed class pointer도 off 된다.

References

http://java-latte.blogspot.com/2014/03/metaspace-in-java-8.html
https://stuefe.de/posts/metaspace/what-is-compressed-class-space/
http://xmlandmore.blogspot.com/2014/08/jdk-8-usecompressedclasspointers-vs.html
https://brunch.co.kr/@alden/35

--

--