반응형

Java 24의 JEP 450: Compact Object Headers로 메모리 효율성 높이기

2025년 3월 릴리즈된 Java 24에서 도입된 가장 주목할 만한 실험적인 기능 중 하나는 JEP 450: Compact Object Headers입니다. 이 실험적 기능은 HotSpot JVM의 객체 헤더 크기를 64비트 아키텍처에서 96

128비트(12

16바이트)에서 64비트(8바이트)로 줄여 힙 크기를 감소시키고 데이터 지역성을 향상시키는 것을 목표로 합니다.

객체 헤더란 무엇인가?

Java에서 힙에 저장된 모든 객체는 메타데이터를 가지고 있으며, 이는 객체의 헤더에 저장됩니다. 이 헤더는 객체의 타입, 배열 형태, 내용과 관계없이 일정한 크기를 차지합니다. 현재 64비트 HotSpot JVM에서는 객체 헤더가 JVM 구성에 따라 96비트(12바이트)에서 128비트(16바이트)까지 차지합니다.

객체 헤더는 다음과 같은 중요한 정보를 포함합니다:

  • 가비지 컬렉션 관련 정보 (포워딩 포인터, 객체 나이 등)
  • 타입 시스템 (객체의 클래스 식별, 메서드 호출, 타입 체크에 사용)
  • 락킹 정보 (경량 및 중량 락에 대한 정보)
  • 해시 코드 (객체의 안정적인 식별 해시 코드)

컴팩트 객체 헤더의 메모리 효율성 측정 실험

100MB 크기의 힙에서 OutOfMemoryError를 인위적으로 발생시키는 실험을 통해 -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders 옵션의 효과를 측정해 보았습니다.

실험 설계

  1. 테스트 환경: Java 24 (amazon openjdk 24.0.0.36.2), Mac 시스템(sequoia), 최대 힙 크기 100MB
  2. 두 가지 시나리오 테스트:
    • 기본 설정 (컴팩트 객체 헤더 비활성화)
    • 컴팩트 객체 헤더 활성화 (-XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders)
  3. 많은 수의 작은 객체를 생성하여 OutOfMemoryError 유도
  4. 각 시나리오에서 생성된 객체 측정

실험 코드

import java.util.ArrayList;
import java.util.List;

public class MemoryTest {
    public static void main(String[] args) {
        long count = 0;
        try {
            List<Object> objects = new ArrayList<>();
            while (true) {
                objects.add(new Object());
                count++;
            }
        } catch (OutOfMemoryError e) {
            System.err.println("OutOfMemoryError occurred at count = " + count);
        }
    }
}

실험 결과 (M4-MAC)

기본 설정 (컴팩트 객체 헤더 비활성화)

$ java -Xmx100m MemoryTest
OutOfMemoryError occurred at count = 5132541

컴팩트 객체 헤더 활성화

$ java -Xmx100m -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders MemoryTest
OutOfMemoryError occurred at count = 6153400

실험 결과 (Rocky 8.10-x64)

기본 설정 (컴팩트 객체 헤더 비활성화)

$ java -Xmx100m MemoryTest
OutOfMemoryError occurred at count = 4102267

컴팩트 객체 헤더 활성화

$ java -Xmx100m -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders MemoryTest
OutOfMemoryError occurred at count = 6153400

실험 결과 (Win11-Arm64)

기본 설정 (컴팩트 객체 헤더 비활성화)

$ java -Xmx100m MemoryTest
OutOfMemoryError occurred at count = 4102267

컴팩트 객체 헤더 활성화

$ java -Xmx100m -XX:+UnlockExperimentalVMOptions -XX:+UseCompactObjectHeaders MemoryTest
OutOfMemoryError occurred at count = 6153400

OS 일반 UseCompact
MAC 5,132,541 6,153,400
Linux_x64 4,102,267 6,153,400
Win_arm64 4,102,267 6,153,400

분석

컴팩트 객체 헤더를 활성화했을 때 Mac기준 약 19.89%, Linux 및 Win11(Arm64)의 경우 50% 더 많은 객체를 생성할 수 있었습니다. 이는 JEP 450에서 예상한 대로 10~20% 정도의 메모리 절약 효과가 있음을 보여줍니다. ( Linux, Windows는 50% ?? )

특이한 점은 Mac과 Linux의 기본 설정 사용시 객체의 생성 가능한 개수가 다른 부분도 확인 할수 있었습니다. 또한 옵션을 사용시에는 동일한것으로 보아 옵션을 사용하면 JVM이 객체 헤더를 정확히 64비트(8바이트)로 표준화하기 때문에 플랫폼별 차이가 동일해지는 것을 확인할수 있습니다.

객체당 헤더 크기가 12~16바이트에서 8바이트로 줄어들면서, 같은 100MB 힙 공간에 더 많은 객체를 저장할 수 있게 되었습니다. 특히 작은 객체들이 많은 애플리케이션에서 이 효과는 더욱 두드러질것으로 보입니다.

실무적 의미

이 실험 결과는 다음과 같은 실무적인 의미를 갖습니다:

  1. 서버 비용 절감: 동일한 하드웨어에서 더 많은 객체를 처리할 수 있어 서버 비용을 절감할 수 있습니다.
  2. 성능 향상: 작은 객체들이 많은 애플리케이션에서 캐시 효율성이 향상되어 성능이 개선될 수 있습니다.
  3. GC 압력 감소: 전체적인 메모리 사용량이 감소하면서 가비지 컬렉션 발생 빈도가 줄어들고, 이는 특히 GC 일시 중지에 민감한 애플리케이션에서 중요합니다.
  4. 마이크로서비스 배포 밀도 증가: 각 마이크로서비스 인스턴스가 더 적은 메모리를 사용하므로, 동일한 인프라에서 더 많은 인스턴스를 실행할 수 있습니다.

고려사항 및 한계

  1. 실험적 기능: 현재 Java 24에서는 실험적 기능으로 제공되므로 -XX:+UnlockExperimentalVMOptions 플래그와 함께 사용해야 합니다.
  2. 압축된 클래스 포인터 필요: 이 기능은 압축된 클래스 포인터가 활성화된 경우에만 작동합니다. 압축된 클래스 포인터를 비활성화하면 컴팩트 객체 헤더도 비활성화됩니다.
  3. 큰 힙 크기 제한: ZGC 이외의 가비지 컬렉터를 사용할 경우 8TB 이상의 힙에서는 컴팩트 객체 헤더가 비활성화됩니다.
  4. JVMCI 호환성: 현재 x64에서 JVMCI와 함께 사용할 경우 지원되지 않습니다.

결론

JEP 450: Compact Object Headers는 Java 애플리케이션의 메모리 효율성을 크게 향상시킬 수 있는 중요한 기능입니다. 실험 결과에 따르면 동일한 힙 크기에서 약 20%(50%) 더 많은 객체를 생성할 수 있었으며, 이는 대규모 Java 애플리케이션에서 상당한 비용 절감과 성능 향상으로 이어질 수 있습니다.

향후 Java 릴리스에서는 이 기능이 실험적 상태를 넘어 기본으로 활성화될 가능성이 높으며, 마이크로서비스 아키텍처와 클라우드 환경에서의 Java 애플리케이션 배포 및 운영에 큰 이점을 제공할 것으로 기대됩니다.

Edited by claude.ai

반응형
반응형

Java 24 환경에서 guava / sun.misc.Unsafe 경고 메시지

Java 플랫폼은 지속적인 진화를 거치며 성능 및 보안성을 높이기 위해 내부 API를 지속적으로 수정하거나 폐기하고 있다.
Java 24에서 도입된 JEP 498 ("Warn upon Use of Memory-Access Methods in sun.misc.Unsafe") 은 이러한 변경 사항의 대표적인 예로, 내부적으로 널리 사용되었던 sun.misc.Unsafe 클래스의 메모리 접근 메서드 사용에 대한 경고를 제공한다.

경고 메시지의 기술적 배경

Java의 sun.misc.Unsafe 클래스는 JVM 메모리에 직접 접근하거나 저수준 동기화 및 원자적 연산을 수행하는 기능을 제공하여, 높은 성능을 요구하는 다양한 프레임워크 및 라이브러리에서 널리 사용되었다. 그러나 이러한 기능의 무분별한 사용은 보안 및 안정성 측면에서 잠재적 위협이 될 수 있으므로, Oracle은 JEP 260 및 최근의 JEP 498을 통해 해당 클래스의 사용을 점진적으로 폐기하고자 하고 있다. Java 24부터는 이 클래스의 특정 메서드 호출이 있을 때 아래와 같은 경고 메시지를 출력한다.

WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/deploy/WEB-INF/lib/guava-33.4.0-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release

이는 Guava 33.4.5 버전 이전 까지 여전히 Java 9 이후 환경에서도 Unsafe API를 사용하여 객체의 필드 접근 위치를 가져오는 방식을 채택했기 때문이다.

해결 방안 및 버전 업그레이드의 필요성

해당 문제는 근본적으로 Guava 라이브러리 내부의 메모리 접근 방식에 기인한다. 다행히 Google은 Java 9 이상의 환경을 고려하여 Guava 33.4.5 버전부터 이 문제를 해소하였다. 구체적으로 Guava 개발팀은 내부적으로 사용하던 Unsafe 기반 접근 방식을 제거하고, Java 표준 API를 활용하는 방향으로 리팩토링을 수행했다.

Guava의 의존성을 다음과 같이 최신 버전으로 업데이트하면, 해당 경고 메시지를 효과적으로 제거할 수 있다.
2025.03.20 일 기준 Android 부분은 미완료.

<!-- Maven 기반 프로젝트의 예시 -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.4.5-jre</version>
</dependency>

Guava 33.4.5에서의 주요 변경점

Guava 33.4.5 버전의 변경 내역 중 핵심적인 부분은 다음과 같다:

  • Java 9 이상의 환경에서 sun.misc.Unsafe 클래스 기반 메모리 접근 방식을 표준 Java API로 대체하였다.
  • JVM의 향후 버전 업그레이드에서 발생할 수 있는 호환성 문제를 선제적으로 방지하였다.

변경 내역 출처: Guava 33.4.5 공식 Release Note

실제 적용 예시

  • 적용전
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/deploy/webapps/ROOT/WEB-INF/lib/guava-33.4.0-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
20-Mar-2025 22:04:13.321 정보 [main] org.apache.catalina.startup.HostConfig.deployDescriptor 배치 descriptor [/tomcat9/conf/Catalina/localhost/ROOT.xml]의 배치가 [7,639] 밀리초 내에 완료되었습니다.
20-Mar-2025 22:04:13.324 정보 [main] org.apache.coyote.AbstractProtocol.start 프로토콜 핸들러 ["http-nio-127.0.0.1-8080"]을(를) 시작합니다.
20-Mar-2025 22:04:13.328 정보 [main] org.apache.catalina.startup.Catalina.start 서버가 [7682] 밀리초 내에 시작되었습니다.
  • 적용후
20-Mar-2025 22:34:27.156 정보 [main] org.apache.catalina.startup.HostConfig.deployDescriptor 배치 descriptor [/tomcat9/conf/Catalina/localhost/ROOT.xml]의 배치가 [5,634] 밀리초 내에 완료되었습니다.
20-Mar-2025 22:34:27.160 정보 [main] org.apache.coyote.AbstractProtocol.start 프로토콜 핸들러 ["http-nio-127.0.0.1-8080"]을(를) 시작합니다.
20-Mar-2025 22:34:27.164 정보 [main] org.apache.catalina.startup.Catalina.start 서버가 [5683] 밀리초 내에 시작되었습니다.

결론

Java 24 환경에서 Guava 라이브러리 사용 중 sun.misc.Unsafe 경고 메시지를 접할 경우, Guava를 33.4.5 이상의 버전으로 업그레이드하는 것이 권장됩니다.

Edited by ChatGPT

반응형

+ Recent posts