Java 메모리 관리

참고 항목

Azure Spring Apps는 Azure Spring Cloud 서비스의 새 이름입니다. 서비스에 새 이름이 지정되었지만, 자산을 업데이트하는 동안 스크린샷, 비디오, 다이어그램과 같은 일부 위치에서는 당분간 이전 이름이 표시됩니다.

이 문서의 적용 대상: ✔️ 기본/표준 ✔️ 엔터프라이즈

이 문서에서는 Azure Spring Apps에서 호스트되는 Java 애플리케이션의 동작을 이해하는 데 도움이 되는 Java 메모리 관리와 관련된 다양한 개념을 설명합니다.

Java 메모리 모델

Java 애플리케이션의 메모리에는 여러 부분이 있으며 부분을 나누는 방법에는 여러 가지가 있습니다. 이 문서에서는 Java 메모리를 힙 메모리, 비힙 메모리 및 직접 메모리로 구분하여 설명합니다.

힙 메모리

힙 메모리는 모든 클래스 인스턴스와 배열을 저장합니다. 각 JVM(Java 가상 머신)에는 스레드 간에 공유되는 하나의 힙 영역만 있습니다.

Spring Boot Actuator는 힙 메모리 값을 관찰할 수 있습니다. Spring Boot Actuator는 힙 값을 jvm.memory.used/committed/max의 일부로 사용합니다. 자세한 내용은 메모리 문제를 해결하기 위한 도구jvm.memory.used/committed/max 섹션을 참조하세요.

힙 메모리는 신규 세대이전 세대로 나뉩니다. 이러한 용어는 관련 용어와 함께 다음 목록에 설명되어 있습니다.

  • 신규 세대: 모든 새로운 개체는 신규 세대에 할당되고 숙성됩니다.

    • Eden 공간: 새 개체가 Eden 공간에 할당됩니다.
    • Survivor 공간: 개체는 하나의 가비지 수집 주기에서 살아남은 후 Eden에서 생존자 공간으로 이동됩니다. Survivor 공간은 s1과 s2의 두 부분으로 나눌 수 있습니다.
  • 이전 세대 : 임기 공간이라고도 합니다. 오랫동안 생존자 공간에 남아 있는 개체는 이전 세대로 이동됩니다.

Java 8 이전에는 영구 생성이라는 또 다른 섹션도 힙의 일부였습니다. Java 8부터 영구 생성이 힙이 아닌 메모리의 메타스페이스로 대체되었습니다.

힙이 아닌 메모리

힙이 아닌 메모리는 다음 부분으로 나뉩니다.

  • Java 8부터 영구 생성(또는 permGen)을 대체한 비힙 메모리의 일부입니다. Spring Boot Actuator는 이 섹션을 관찰하고 jvm.memory.used/committed/max의 일부로 사용합니다. 즉, jvm.memory.used/committed/max는 힙 메모리와 비힙 메모리의 이전 permGen 부분의 합계입니다. 이전 영구 세대는 다음 부분으로 구성됩니다.

    • 메타스페이스 - 클래스 로더가 로드한 클래스 정의를 저장합니다.
    • 압축된 클래스 포인터를 위한 압축 클래스 공간입니다.
    • 코드 캐시 - JIT에서 컴파일한 네이티브 코드를 저장합니다.
  • 스레드 스택과 같은 다른 메모리는 Spring Boot Actuator에서 관찰되지 않습니다.

직접 메모리

직접 메모리는 nio 및 gzip과 같은 타사 라이브러리에서 사용되는 java.nio.DirectByteBuffer에 의해 할당된 네이티브 메모리입니다.

Spring Boot Actuator는 직접 메모리 값을 관찰하지 않습니다.

다음 다이어그램에는 이전 섹션에서 설명한 Java 메모리 모델이 요약되어 있습니다.

Java 메모리 모델을 보여 주는 다이어그램

Java 가비지 수집

Java GC(가비지 수집)에는 "Minor GC", "Major GC" 및 "Full GC"의 세 가지 용어가 있습니다. 이러한 용어는 JVM 사양에 명확하게 정의되어 있지 않습니다. 여기서는 "Major GC" 및 "Full GC"를 동등한 것으로 간주합니다.

Minor GC는 Eden 공간이 가득 차면 수행됩니다. 신규 세대의 죽은 개체를 모두 제거하고 살아있는 개체를 Eden 공간에서 생존자 공간의 s1로 이동하거나 s1에서 s2로 이동합니다.

전체 GC 또는 주요 GC는 전체 힙에서 가비지 수집을 수행합니다. 전체 GC는 전체 GC로만 정리할 수 있는 메타스페이스 및 직접 메모리와 같은 부분도 수집할 수 있습니다.

최대 힙 크기는 부 GC 및 전체 GC의 빈도에 영향을 줍니다. 최대 메타스페이스 및 최대 직접 메모리 크기는 전체 GC에 영향을 미칩니다.

최대 힙 크기를 더 낮은 값으로 설정하면 가비지 수집이 더 자주 발생하여 앱 속도가 약간 느려지지만 메모리 사용량이 더 잘 제한됩니다. 최대 힙 크기를 더 높은 값으로 설정하면 가비지 수집 빈도가 낮아져 OOM(메모리 부족) 위험이 더 많이 발생할 수 있습니다. 자세한 내용은 메모리 부족 문제로 인한 앱 다시 시작 문제메모리 부족 문제 유형 섹션을 참조하세요.

메타스페이스 및 직접 메모리는 전체 GC에서만 수집할 수 있습니다. 메타스페이스 또는 직접 메모리가 가득 차면 전체 GC가 발생합니다.

Java 메모리 구성

다음 섹션에서는 Java 메모리 구성의 중요한 측면에 대해 설명합니다.

Java 컨테이너화

Azure Spring Apps의 애플리케이션은 컨테이너 환경에서 실행됩니다. 자세한 내용은 Java 애플리케이션 컨테이너화를 참조하세요.

중요한 JVM 옵션

JVM 옵션을 사용하여 메모리의 각 부분의 최대 크기를 구성할 수 있습니다. Azure CLI 명령을 사용하거나 Azure Portal을 통해 JVM 옵션을 설정할 수 있습니다. 자세한 내용은 메모리 문제를 해결하기 위한 도구문제 해결을 위한 구성 수정 섹션을 참조하세요.

다음 목록에서는 JVM 옵션에 대해 설명합니다.

  • 힙 크기 구성

    • -Xms는 절대값으로 초기 힙 크기를 설정합니다.
    • -Xmx는 절대값으로 최대 힙 크기를 설정합니다.
    • -XX:InitialRAMPercentage는 힙 크기/앱 메모리 크기의 백분율로 초기 힙 크기를 설정합니다.
    • -XX:MaxRAMPercentage는 힙 크기/앱 메모리 크기의 백분율로 최대 힙 크기를 설정합니다.
  • 직접 메모리 크기 구성

    • -XX:MaxDirectMemorySize는 절대값으로 최대 직접 메모리 크기를 설정합니다. 자세한 내용은 Oracle 설명서의 MaxDirectMemorySize를 참조하세요.
  • 메타스페이스 크기 구성

    • -XX:MaxMetaspaceSize는 절대값으로 최대 메타스페이스 크기를 설정합니다.

기본 최대 메모리 크기

다음 섹션에서는 기본 최대 메모리 크기를 설정하는 방법을 설명합니다.

기본 최대 힙 크기

Azure Spring Apps는 기본 최대 힙 메모리 크기를 Java 앱용 앱 메모리의 약 50%-80%로 설정합니다. 특히 Azure Spring Apps는 다음 설정을 사용합니다.

  • 앱 메모리 <가 1GB인 경우 기본 최대 힙 크기는 앱 메모리의 50%입니다.
  • 1GB <= 앱 메모리 < 2GB인 경우 기본 최대 힙 크기는 앱 메모리의 60%입니다.
  • 2GB <= 앱 메모리 < 3GB인 경우 기본 최대 힙 크기는 앱 메모리의 70%입니다.
  • 3GB <= 앱 메모리인 경우 기본 최대 힙 크기는 앱 메모리의 80%입니다.

기본 최대 직접 메모리 크기

JVM 옵션을 사용하여 최대 직접 메모리 크기를 설정하지 않으면 JVM은 자동으로 최대 직접 메모리 크기를 Runtime.getRuntime.maxMemory()에서 반환된 값으로 설정합니다. 이 값은 최대 힙 메모리 크기와 거의 같습니다. 자세한 내용은 JDK 8 VM.java 파일을 참조하세요.

메모리 사용량 레이아웃

힙 크기는 처리량의 영향을 받습니다. 기본적으로 구성할 때 기본 최대 힙 크기를 유지할 수 있습니다. 이 크기는 다른 부분에 적합한 메모리를 남깁니다.

메타스페이스 크기는 클래스 수와 같은 코드의 복잡성에 따라 달라집니다.

직접 메모리 크기는 처리량과 nio 및 gzip과 같은 타사 라이브러리 사용에 따라 달라집니다.

다음 목록에서는 2GB 앱에 대한 일반적인 메모리 레이아웃 샘플을 설명합니다. 이 목록을 참조하여 메모리 크기 설정을 구성할 수 있습니다.

  • 총 메모리(2048M)
  • 힙 메모리: Xmx는 1433.6M(총 메모리의 70%)입니다. 일별 메모리 사용량의 참조 값은 1200M입니다.
    • 신규 세대
      • 생존자 공간(S0, S1)
      • Eden 공간
    • 이전 세대
  • 힙이 아닌 메모리
    • 관찰된 부분(Spring Boot Actuator에서 관찰됨)
      • 메타스페이스: 일일 사용량 참조 값은 50M-256M입니다.
      • 코드 캐시
      • 압축 클래스 공간
    • 관찰되지 않은 부분(Spring Boot Actuator에서 관찰되지 않음): 일일 사용량 참조 값은 150M-250M입니다.
      • 스레드 스택
      • GC, 내부 기호 및 기타
  • 직접 메모리: 일일 사용량 참조 값은 10M-200M입니다.

다음 다이어그램은 동일한 정보를 보여줍니다. 회색의 숫자는 일일 메모리 사용량의 참조 값입니다.

2GB 앱의 일반적인 메모리 레이아웃 다이어그램.

전반적으로 최대 메모리 크기를 구성할 때는 메모리에 있는 각 부분의 사용량을 고려해야 하며, 모든 최대 크기의 합계가 사용 가능한 총 메모리를 초과해서는 안 됩니다.

Java OOM

OOM은 애플리케이션이 메모리가 부족하다는 것을 의미합니다. 컨테이너 OOM 및 JVM OOM이라는 두 가지 개념이 있습니다. 자세한 내용은 메모리 부족 문제로 인한 앱 다시 시작 문제를 참조하세요.

참고 항목