원문 캡처 · openjdk.org
원문 캡처 · openjdk.org

요약

Z 가비지 컬렉터(ZGC)가 애플리케이션의 요구에 따라 물리 메모리를 더 빠르고 효율적으로 확보하고 준비하도록 개선해 애플리케이션의 시작과 준비 시간을 줄인다.

목표와 제외 사항

목표는 애플리케이션이 첫 응답을 내놓기까지 걸리는 시작 시간과 최고 성능에 도달하기까지 필요한 준비 시간을 줄이는 것이다.

모든 운영체제에서 같은 메모리 관리 기법을 사용하는 것은 목표가 아니다. 예를 들어 리눅스에서는 대형 페이지를 기본으로 사용할 수 있지만 윈도우에서는 사용하지 않을 수 있다.

배경

JDK 15에 도입된 ZGC는 낮은 지연 시간과 높은 확장성을 목표로 설계됐다. 대부분의 작업을 애플리케이션 스레드가 실행되는 동안 처리하고, 해당 스레드는 짧게만 멈춘다. ZGC의 일시 정지 시간은 힙 크기와 무관하다. 애플리케이션은 수백 메가바이트에서 수 테라바이트까지 힙을 사용하면서도 짧은 정지 시간을 유지할 수 있다.

-Xms 옵션으로 설정하는 초기 힙 크기는 시작과 준비 시간에 큰 영향을 준다. ZGC에서는 운영체제에서 원시 메모리를 확보해 힙 메모리로 준비하는 작업에 비용이 많이 들기 때문이다. 초기 힙이 작으면 시작은 빠르지만, 애플리케이션이 더 많은 객체를 만들 때마다 ZGC가 필요에 따라 메모리를 조금씩 확보하고 준비해야 하므로 준비가 느리다. 반대로 초기 힙이 크면 애플리케이션 시작 전에 더 많은 메모리를 미리 확보하고 준비하므로 시작은 느리지만 준비는 빠르다.

일반적인 최신 하드웨어에서도 차이는 클 수 있다. ZGC를 사용할 때 HotSpot JVM은 초기 힙이 2MB면 약 0.1초 만에 시작하지만, 32GB면 10초 넘게 걸린다. -XX:+AlwaysPreTouch 옵션을 지정하면 준비 시간을 더 줄일 수 있지만 시작 시간은 더 길어진다.

이상적으로는 빠른 시작과 빠른 준비 중 하나를 선택할 필요가 없어야 한다. ZGC는 시작과 준비 과정, 그 이후에도 낮은 지연 시간을 유지하면서 힙을 메가바이트 단위에서 테라바이트 단위로 빠르게 늘릴 수 있어야 한다.

제안 내용

제안은 HotSpot JVM의 ZGC를 개선해 다음과 같이 동작하게 하는 것이다.

  • 기본 초기 힙을 2MB로 설정해 더 빠르게 시작한다.
  • 힙을 적극적으로 확장해 준비 시간을 줄인다.
  • 4KB 대신 2MB 단위로 메모리를 관리해 효율성을 높인다.

적응형 힙 크기 조정과 함께 적용하면 초기 힙 크기(-Xms)나 최대 힙 크기(-Xmx)를 설정하거나 -XX:+AlwaysPreTouch 옵션을 지정할 필요가 없고, 그렇게 할 이유도 없어진다. 애플리케이션을 실행할 때 ZGC를 선택하고 적응형 힙 크기 조정을 켜면 된다.

$ java -XX:+UseZGC -XX:+ZAdaptiveHeapSizing ...

아래 서버 애플리케이션 사례에서는 이 변경으로 시작 시간이 거의 40%, 준비 시간이 거의 90% 줄었다.

최신 메모리 관리의 기본 원리

자바 가상 머신(JVM)은 무한한 힙 메모리가 있는 것처럼 보이게 한다. ZGC 같은 가비지 컬렉터는 하드웨어와 운영체제의 기능을 이용해, JVM이 실행되는 컴퓨터의 한정된 물리 메모리로 그 환상을 구현한다.

가상 메모리와 물리 메모리

HotSpot JVM은 64비트 가상 주소 공간을 가진 운영체제 프로세스로 실행된다. JVM은 물리 메모리에 직접 접근하지 않는다. CPU가 메모리에 접근할 때마다 가상 주소를 물리 주소로 변환한다.

CPU의 메모리 관리 장치는 가상 주소 공간과 물리 주소 공간을 페이지로 나눈다. 페이지 크기는 보통 4KB다. 운영체제는 가상 페이지와 물리 페이지의 대응 관계를 페이지 테이블에 유지해 CPU의 주소 변환을 돕는다. 이 대응은 임의적일 수 있으므로, 연속된 가상 페이지가 연속된 물리 페이지에 대응한다고 보장할 수 없다. ZGC는 운영체제 API를 이용해 페이지 테이블을 조작하고, JVM 프로세스의 가상 페이지로 구성된 힙이 물리 페이지에 어떻게 대응하는지 관리한다.

가상 메모리 예약

시작할 때 ZGC는 가상 주소 공간에서 최대 힙 크기(-Xmx)의 16배에 해당하는 연속된 범위를 예약한다. 예를 들어 최대 힙이 1GB라면 ZGC는 가상 메모리 16GB, 즉 가상 페이지 400만 개를 예약한다. 가상 페이지 예약은 운영체제가 기록만 남기는 작업이므로 빠르다. 이 단계에서는 페이지 테이블에 가상 페이지와 물리 페이지의 대응 관계를 기록하지 않는다. 넓은 가상 주소 공간을 확보하면 대형 객체로 인해 생기는 단편화를 더 잘 관리할 수 있다.

물리 메모리 확정

시작할 때는 예약된 수많은 가상 페이지에 물리 메모리가 할당되어 있지 않다. 힙에 필요한 초기 물리 메모리를 확보하기 위해 ZGC는 운영체제에 JVM 프로세스가 쓸 물리 페이지를 확정(commit)해 달라고 요청한다. 페이지 수는 초기 힙 크기(-Xms)에 따라 정해진다. 초기 힙이 2MB라면 ZGC는 운영체제에 물리 페이지 512개를 확정해 달라고 요청해, 가상 페이지 512개로 된 힙을 만든다.

가상 페이지 예약과 달리 물리 페이지 확정은 단순한 기록 작업이 아니다. 운영체제는 먼저 사용하지 않는 물리 페이지를 찾아야 한다. 이 과정에서 다른 프로세스의 가상 페이지를 디스크로 내보내야 할 수 있고, 최악의 경우 다른 프로세스를 종료할 수도 있다. 물리 페이지를 찾은 뒤에는 정보 유출을 막기 위해 페이지를 0으로 채운다. 그 다음에야 해당 페이지를 힙의 가상 페이지에 대응시킬 수 있다.

힙 확장

자바 애플리케이션은 실행 중 힙에 객체를 만든다. 힙에서 객체를 넣을 공간을 찾지 못하면 ZGC는 OutOfMemoryError가 발생하지 않도록 운영체제에 JVM 프로세스용 물리 페이지를 더 확정해 달라고 요청해 힙을 확장한다. 예를 들어 힙을 2MB에서 16MB로 늘리려면, 앞서 예약한 400만 개의 가상 페이지 가운데 새 가상 페이지 3,584개를 새 물리 페이지 3,584개에 대응시켜야 한다.

초기 힙 생성과 마찬가지로 물리 페이지를 확정하는 작업은 단순한 기록이 아니므로 힙 확장도 느릴 수 있다.

힙 축소

ZGC는 힙의 가상 페이지를 2MB에서 32MB 사이 크기의 영역(region)으로 묶는다. ZGC는 실행 중 영역에 살아 있는 객체가 더는 없을 때 이를 감지한다. 쓰레기만 남은 영역은 비워진(evacuated) 상태라고 한다. 비워진 영역의 가상 페이지는 더는 필요하지 않으며, 32MB 영역이라면 최대 8,192개의 가상 페이지가 이에 해당한다.

비워진 영역의 가상 페이지는 이후 객체 할당에 쓸 수도 있고, 운영체제에 반환해 힙을 사실상 축소할 수도 있다. 힙을 줄이면 가비지 컬렉션 빈도가 증가하므로 ZGC는 힙 사용량과 가비지 컬렉션 빈도가 모두 낮을 때 힙을 축소하도록 휴리스틱을 사용한다. 힙을 축소할 때 ZGC는 운영체제에 비워진 영역의 가상 페이지에 대응된 물리 페이지의 매핑을 해제하고, 해당 물리 페이지를 JVM 프로세스에서 확정 해제하도록 요청한다.

메모리 재활용

ZGC는 비워진 영역에 확정된 물리 페이지를 다른 용도로 재활용해 객체 생성을 빠르게 할 수 있다. 예를 들어 new int[1024*1024] 표현식은 4MB 메모리가 필요한 배열을 만든다. ZGC는 힙에서 연속된 공간을 찾아야 한다. 힙 전체에 여유 공간이 4MB 이상 있어도, 그 공간이 서로 떨어진 가상 페이지에 흩어져 있을 수 있다.

이 경우 대부분의 가비지 컬렉터는 객체를 한 영역에서 다른 영역으로 옮겨 힙을 압축한다. 4KB 크기의 빈 가상 페이지 1,024개가 연속된 영역을 만들기 위해 객체를 힘들게 옮기는 방식이다. 하지만 일반적으로 이 작업에는 힙을 순회하고 살아 있는 객체를 표시한 뒤 옮기는 비용이 큰 가비지 컬렉션이 필요하다.

ZGC는 대신 운영체제의 재매핑(remapping) 기능을 사용해 충분한 연속 공간을 만든다. 재매핑은 페이지 테이블을 바꿔 일부 가상 페이지를 이전에 다른 가상 페이지를 뒷받침하던 물리 페이지에 대응시킨다. 비워진 힙 영역에는 쓰레기만 남아 있으므로 그 영역의 가상 페이지는 더는 필요하지 않다. 따라서 해당 가상 페이지에 대응된 물리 페이지를 다른 가상 페이지에 안전하게 사용할 수 있다. ZGC는 시작할 때 예약한 넓은 가상 주소 공간에서 연속된 가상 페이지 범위를 골라 이 물리 페이지에 대응시켜 재활용한다.

이 방식은 대형 객체를 위한 공간을 찾을 때 힙을 압축하는 가비지 컬렉션보다 몇 자릿수 빠르다. 새 물리 페이지를 확정하는 것이 아니라 이미 확정된 물리 페이지를 다른 용도로 쓰는 것이므로 JVM의 메모리 사용량은 바뀌지 않는다.

작은 초기 힙으로 더 빠르게 시작하기

기존 ZGC는 시작할 때 물리 메모리의 1/64을 초기 힙으로 확정했다. 이 정책에 따르면 물리 메모리가 1GB인 컴퓨터에서는 초기 힙이 16MB, 128GB인 컴퓨터에서는 2GB가 된다.

대체로 애플리케이션은 시작할 때 사용 가능한 메모리 양을 감지해 할당량을 조정하지 않는다. 배포 담당자는 초기 힙 크기를 달리해 성능을 경험적으로 측정한 다음 애플리케이션 시작 스크립트의 -Xms 값을 정한다. 이때 앞서 설명한 것처럼 빠른 시작과 느린 준비, 또는 느린 시작과 빠른 준비 사이에서 절충할 수 있다.

초기 힙 크기가 시작 시간에 미치는 영향은 JVM 시작부터 애플리케이션의 첫 응답까지 시간을 측정하면 쉽게 알 수 있다. 하지만 준비 시간에 미치는 영향은 측정하기 어렵다. 첫 응답뿐 아니라 응답 시간이 안정될 때까지 여러 응답을 측정해야 하기 때문이다. 실제로 이런 측정은 드물게 이뤄지므로, 애초에 평가할 필요를 없애는 편이 낫다.

제안은 ZGC가 기본적으로 초기 힙에 2MB만 확정하는 것이다. 운영체제의 메모리 확정은 단순한 기록이 아니므로 느릴 수 있다. 한 개의 2MB 영역만 들어갈 만큼만 확정하면 시작 시간을 줄일 수 있다.

적극적인 힙 확장으로 준비 시간 단축

작은 초기 힙으로 애플리케이션을 빠르게 시작할 수 있지만, 보통 그 크기만으로는 충분하지 않다. 애플리케이션이 객체를 계속 만들면 JVM은 가비지 컬렉션을 점점 더 자주 실행한다. 작은 힙만 수집하면 각 작업은 짧고, ZGC는 전용 작업자 스레드로 애플리케이션 스레드를 막지 않은 채 쓰레기를 수집한다. 그러나 결국 ZGC가 가비지를 수집하는 속도가 애플리케이션의 객체 생성 속도를 따라가지 못할 수 있다. 이때 ZGC는 물리 메모리를 더 확정해야 한다. 그렇지 않으면 다음 수집이 끝날 때까지 JVM이 객체 생성을 멈춰야 한다. 어느 쪽이든 애플리케이션이 멈출 수 있다.

제안은 ZGC가 현재보다 적극적으로 힙을 확장하는 것이다. 가비지 컬렉션 빈도가 높아지는 것을 처음 감지하면 ZGC는 선제적으로 힙을 확장한다. 시간이 지나면 애플리케이션의 일반적인 할당 속도를 추정해, 힙을 언제 선제적으로 확장할지 계산하는 데 사용한다. ZGC는 점점 더 큰 크기로 힙을 빠르게 확장한다. 따라서 안정적인 힙 크기에 도달하는 데 필요한 확장 횟수와 가비지 컬렉션 횟수는 로그 규모로 제한된다. 마지막으로 ZGC는 애플리케이션 스레드가 아닌 자체 작업자 스레드를 사용해 힙을 동시에 확장한다.

적극적인 힙 확장은 가비지 컬렉션 빈도를 낮춰 컬렉션의 CPU 오버헤드를 줄이고 애플리케이션 처리량을 높인다. 운영체제가 물리 페이지를 확정하는 동안 기다리는 일은 애플리케이션 스레드가 아니라 ZGC 작업자 스레드가 맡으므로 실행이 더 일관되고 일시 정지가 줄어든다. 애플리케이션 스레드가 CPU를 충분히 쓰지 못하는 상황을 막기 위해 ZGC는 사용 가능한 CPU 스레드의 25%를 넘겨 작업자 스레드에 사용하지 않는다.

동시 사전 터치로 준비 시간 단축

프로세스가 운영체제에 가상 페이지 범위를 물리 페이지 범위에 대응시켜 달라고 요청해도 운영체제는 페이지 테이블을 즉시 갱신하지 않는다. 어떤 프로세스는 가상 페이지를 전부 사용하지 않을 수 있으므로, 미리 갱신하는 일이 낭비가 될 수 있기 때문이다. 이에 따라 운영체제는 필요할 때 페이지 테이블을 점진적으로 갱신한다. 새로 매핑된 가상 페이지를 처음 읽거나 쓸 때 CPU가 페이지 폴트(page fault)를 일으키고, 운영체제는 해당 페이지를 대응하는 물리 페이지에 매핑하도록 페이지 테이블을 갱신한다.

애플리케이션이 객체를 많이 만들고 JVM이 많은 가상 페이지에 처음으로 쓰는 준비 과정에는 JVM 프로세스에서 페이지 폴트가 자주 발생한다. JDK 25의 준비 시간을 개선하기 위해 -XX:+AlwaysPreTouch 옵션을 사용할 것을 제안했다. 이 옵션을 사용하면 ZGC는 초기 힙의 각 가상 페이지를 새로 확정된 물리 페이지에 매핑한 직후 기록해 사전 터치한다. 이에 따라 운영체제는 페이지가 처음 사용될 때까지 기다리지 않고 페이지 테이블을 곧바로 갱신한다.

이 방식은 초기 힙에서 발생할 페이지 폴트를 모두 시작 과정으로 옮기므로 JVM 시작 시간이 길어진다. 초기 힙(-Xms)을 작게 설정하면 영향을 알아차리지 못할 수도 있지만, 초기 힙을 최대 힙(-Xmx)과 같은 크기로 설정하면 시작이 훨씬 느려질 수 있다. 다음은 -XX:+AlwaysPreTouch를 켜고 초기 힙과 최대 힙 크기를 달리했을 때 JVM 시작에 걸린 실제 시간이다.

-Xms2M -Xmx2M 0.034s
-Xms20M -Xmx20M 0.041s
-Xms200M -Xmx200M 0.110s
-Xms2G -Xmx2G 0.634s
-Xms20G -Xmx20G 5.672s

제안은 시작할 때가 아니라 ZGC가 힙을 확장할 때 사전 터치하는 것이다. ZGC 작업자 스레드가 물리 페이지를 더 확정해 힙을 확장할 때 새로 매핑된 가상 페이지에 기록한다. 그러면 운영체제가 해당 페이지가 처음 쓰일 때까지 기다려 점진적으로 갱신하는 대신 페이지 테이블을 즉시 갱신한다.

대형 페이지로 메모리 접근 속도 향상

최신 애플리케이션은 기가바이트 단위의 데이터를 일상적으로 처리하므로 가상 메모리를 4KB 페이지 단위로 구성하는 것은 효율적이지 않다. 리눅스와 윈도우는 대형 페이지(huge page)를 지원하며, 크기는 보통 2MB다. 예를 들어 8MB 배열을 작은 페이지 2,048개 대신 대형 페이지 4개에 저장할 수 있다.

페이지 수가 줄고 크기가 커지면 접근 속도가 빨라진다. 가상 주소 공간이 매우 크므로 페이지 테이블은 단순한 검색표가 아니라 여러 단계로 된 계층 구조다. JVM이 가상 주소를 역참조할 때 CPU는 계층을 차례로 탐색해 물리 주소를 찾는다. 대형 페이지를 쓰면 CPU가 한 단계의 검색을 건너뛸 수 있다. 또한 CPU는 최근 가상 주소와 물리 주소의 대응 관계를 캐시에 보관한다. 대형 페이지 하나는 캐시 항목 하나만 차지하지만, 이에 대응하는 작은 페이지 512개는 캐시 항목 512개를 차지한다. 따라서 대형 페이지에서는 더 넓은 주소 범위에 대해 캐시 적중 가능성이 높아진다.

과거에는 리눅스 커널에서 대형 페이지가 기본으로 비활성화돼 있었다. 대형 페이지를 켜려면 최고 사용자 권한으로 커널을 설정해야 했지만, 많은 배포 환경에서는 현실적으로 어렵다.

2022년에 출시된 리눅스 6.1부터는 커널을 설정하지 않아도 모든 프로세스가 지정한 범위의 작은 가상 페이지를 대형 페이지로 바꿔 달라고 운영체제에 요청할 수 있다. ZGC는 이 API를 사용해 힙 확장 중 작은 페이지를 대형 페이지로 바꾼다. 그 결과 시간이 지나면 힙 전체가 대형 페이지로 뒷받침된다. 이 변환은 물리 메모리 조각 모음이 필요할 수도 있어 비용이 클 수 있으므로, 애플리케이션에 미치는 영향을 줄이기 위해 ZGC 작업자 스레드가 수행한다.

시작 및 준비 시간 사례 연구

물리 메모리 32GB인 컴퓨터에서 Spring PetClinic 애플리케이션의 시작과 준비 과정을 살펴봤다.

준비 시간을 정량화하기 위해 JVM 시작 직후 애플리케이션의 응답 시간을 측정했다. 구체적으로 꼬리 지연 시간을 나타내는 99번째 백분위 응답 시간(P99)을 측정했다. 이 애플리케이션의 허용 가능한 응답 시간은 20ms라고 가정했다.

맞춤형 부하 생성 도구로 애플리케이션에 부하를 줬다. 도구는 먼저 로드 밸런서의 상태 확인 요청을 흉내 내 단일 요청을 보낸다. 그 뒤 초당 1,000건에서 시작해 10초 동안 선형으로 늘려 초당 30,000건까지 요청한다. 이 증가 구간은 JVM이 자주 실행되는 경로를 기계어로 JIT 컴파일해야 최고 성능에 도달한다는 가정 아래 설정된 로드 밸런서를 모방한다. 애플리케이션이 처음부터 초당 30,000건의 요청을 받으면 P99 응답 시간은 밀리초가 아니라 초 단위가 된다. 요청률을 점진적으로 높이면 전체 과정에서 허용 가능한 지연 시간을 유지할 수 있다. 증가 구간이 끝난 뒤에도 부하 생성 도구는 2분 동안 초당 30,000건의 요청을 계속 보낸다.

증가 구간이 끝난 뒤 P99 응답 시간이 계속 20ms 아래에 머물면 애플리케이션이 준비된 것으로 간주한다.

사례 1: 초기 힙이 작고 사전 터치를 하지 않은 JDK 25

시작 시간을 줄이기 위해 JDK 25 JVM의 초기 힙을 작게(2MB), 최대 힙을 크게(20GB) 설정했다.

$ java -XX:+UseZGC -Xms2M -Xmx20G -jar petclinic.jar

JVM 시작부터 첫 응답까지 5.86초가 걸렸다. 그러나 애플리케이션이 더 많은 메모리를 요구하면서 ZGC가 힙을 2MB에서 20GB로 확장해야 했기 때문에 P99 응답 시간은 수십 초 동안 반복해서 20ms를 넘었다. 이 과정에서는 물리 페이지를 조금씩 확정하고, 해당 가상 페이지를 처음 쓸 때 페이지 폴트가 발생해 지연 시간이 나빠졌다. 시간이 지나면서 할당은 새로 확정된 메모리보다 ZGC가 재활용한 기존 메모리에서 더 많이 처리됐다. 이에 따라 P99가 20ms를 넘는 일이 줄어들었다. 실행 40초 무렵에는 P99 급증이 드물었고, 약 80초가 지나 애플리케이션이 준비됐다.

사례 2: 초기 힙이 크고 사전 터치한 JDK 25

준비 시간을 줄이기 위해 JDK 25 JVM의 초기 힙을 크게(20GB) 설정하고 사전 터치를 켰다.

$ java -XX:+UseZGC -Xms20G -Xmx20G -XX:+AlwaysPreTouch -jar petclinic.jar

큰 초기 힙에 필요한 물리 페이지를 확정하고 해당 가상 페이지를 사전 터치해야 하므로 시작 시간은 5.86초에서 9.45초로 늘었다. 반면 확정 및 사전 터치된 물리 페이지가 충분해 준비 과정은 뚜렷하게 개선됐다. P99 응답 시간은 한 번도 20ms를 넘지 않았으므로, 처음 10초 동안 요청률을 높이는 구간이 끝나면 애플리케이션은 준비된 상태였다.

사례 3: JDK NN

앞서 설명한 ZGC 개선 사항은 작은 초기 힙에 따른 빠른 시작, 적극적인 힙 확장과 동시 사전 터치에 따른 빠른 준비, 대형 페이지의 일반적인 이점을 결합한다.

$ java -XX:+UseZGC -XX:+ZAdaptiveHeapSizing -jar petclinic.jar

시작에는 5.84초가 걸렸다. 사례 1과 거의 같고 사례 2보다 38% 짧다. 사례 2처럼 P99 응답 시간은 한 번도 20ms를 넘지 않았으므로, 처음 10초 동안 요청률을 높이는 구간이 끝나면 애플리케이션이 준비됐다.

낮은 시작 시간을 유지하면서 준비 시간이 사례 1의 80초에서 사례 3의 10초로 줄어든 것은 적극적인 힙 확장, 동시 사전 터치, 대형 페이지를 함께 적용한 결과로 볼 수 있다. 각 기능을 따로 켜고 끄면 그 영향을 확인할 수 있다.

  • 적극적인 확장과 대형 페이지는 사용하되 동시 사전 터치를 끄고 사례 3을 다시 실행하면 준비 시간은 34초다.
  • 적극적인 확장과 동시 사전 터치는 사용하되 대형 페이지를 끄고 다시 실행하면 준비 시간은 18초다.

세 기능을 함께 사용하면 준비 과정이 10초의 요청률 증가 구간 안에 모두 끝난다. 99번째 백분위 응답 시간이 낮게 유지되면 JVM이 애플리케이션의 자주 실행되는 경로를 기계어로 JIT 컴파일하는 데 더 많은 CPU 자원을 쓸 수 있다는 점에서 의미가 있다.

향후 작업

빠른 준비의 핵심은 동시 가비지 컬렉션에 CPU 자원을 써 힙 사용량을 줄이는 동시에, 메모리를 동시 확정해 힙 용량을 늘리는 것이다. 앞으로 이 두 작업 사이에서 CPU 자원을 더 명시적으로 균형 있게 배분해 준비 시간을 더 줄이는 방법을 살펴볼 예정이다.

Project Leyden의 사전 컴파일(Ahead-of-time, AOT) 코드 컴파일(JEP 544)은 준비 중 애플리케이션을 기계어로 JIT 컴파일하는 오버헤드를 없앤다. 그러면 ZGC가 동시 가비지 컬렉션이나 메모리 동시 확정, 또는 두 작업 모두에 더 많은 CPU 자원을 쓸 수 있다.

대안

초기 힙에 2MB를 확정하는 대신 더 많은 양을 확정하면 적극적인 확장의 필요를 줄일 수 있다. 그러나 사용자가 초기 힙 크기를 지정하지 않았다면 더 큰 힙을 원한다고 가정하지 않는 편이 타당하다. 객체를 거의 만들지 않는 애플리케이션도 있어 초기 힙을 키워도 도움이 되지 않을 수 있다. 애플리케이션에 비해 초기 힙이 너무 작을 때 생기는 단점은 애플리케이션 실행 중 힙 메모리를 확정하고 사전 터치하며, 힙을 지수적으로 확장해 완화한다.

위험과 가정

  • 기본 초기 힙이 물리 메모리 양에 따라 정해지지 않고 2MB가 되면, 배포 환경에서 더 큰 초기 힙을 기대하도록 튜닝된 애플리케이션에 문제가 생길 수 있다. 예를 들어 JDK 25에서 초기 힙 기본값이 2GB인 물리 메모리 128GB 컴퓨터에 맞춰 튜닝된 애플리케이션은 JDK NN에서 초기 힙이 2MB가 되면 준비가 느려질 수 있다. 시작 뒤 P99 응답 시간이 허용 수준을 자주 넘는 기간이 길어지는 형태로 나타날 수 있다. 배포 담당자가 JVM 기능의 큰 개선 이후 가비지 컬렉터 설정을 다시 조정해야 한다는 점을 알고 있다고 가정한다.
  • 많은 애플리케이션은 초기 힙을 2GB보다 훨씬 작게 설정하는 스크립트로 시작한다. 이런 애플리케이션은 JDK 25보다 JDK NN에서 더 빠르게 준비될 가능성이 높다. 자바 생태계 전반에서는 제안된 ZGC 개선이 시작 시간과 준비 시간 모두에 순이익을 가져올 것으로 가정한다.