자바(JVM) 메모리 모델 – 자바 메모리 관리

JVM 메모리 모델, 자바 메모리 관리를 이해하려면 자바 가비지 컬렉션의 작동을 이해하는 것이 매우 중요합니다. 오늘은 자바에서 메모리 관리, JVM 메모리의 다른 부분 및 가비지 컬렉션 튜닝을 모니터링하고 수행하는 방법을 살펴보겠습니다.

자바(JVM) 메모리 모델

위 이미지에서 볼 수 있듯이 JVM 메모리는 별도의 부분으로 나뉩니다. 넓은 의미에서 JVM 힙 메모리는 물리적으로 Young GenerationOld Generation으로 나뉩니다.

자바의 메모리 관리 – Young Generation

Young Generation은 모든 새로운 객체가 생성되는 곳입니다. Young Generation이 채워지면 가비지 수집이 수행됩니다. 이 가비지 수집을 Minor GC라고 합니다. Young Generation은 세 부분으로 나뉩니다 – Eden Memory와 두 개의 Survivor Memory 영역입니다. Young Generation Spaces에 대한 중요한 포인트:

  • 대부분의 새로 생성된 객체는 에덴 메모리 공간에 위치합니다.
  • 에덴 공간이 객체로 채워지면, 마이너 GC가 수행되고 모든 생존 객체가 생존자 공간 중 하나로 이동됩니다.
  • 마이너 GC는 또한 생존 객체를 확인하고 다른 생존자 공간으로 이동합니다. 따라서 한 번에 하나의 생존자 공간은 항상 비어 있습니다.
  • GC의 여러 주기 후에 살아남은 객체는 오래 지속되는 Old 세대 메모리 공간으로 이동됩니다. 일반적으로, 젊은 세대 객체가 오래 세대로 승격되기 전에 나이에 대한 임계값을 설정하여 수행됩니다.

자바에서의 메모리 관리 – 오래된 세대

오래된 세대 메모리에는 오랜 시간 동안 살아남은 객체들이 포함되어 있습니다. 일반적으로, 오래된 세대 메모리가 가득 찼을 때 가비지 수집이 수행됩니다. 오래된 세대 가비지 수집은 메이저 GC라고하며 일반적으로 더 오래 걸립니다.

전체 정지 이벤트

모든 Garbage Collections은 “Stop the World” 이벤트입니다. 왜냐하면 모든 응용 프로그램 스레드가 작업이 완료될 때까지 중지되기 때문입니다. 어린 세대는 수명이 짧은 객체를 유지하므로 Minor GC는 매우 빠르며 응용 프로그램에 영향을주지 않습니다. 그러나 Major GC는 모든 살아있는 객체를 확인하기 때문에 시간이 오래 걸립니다. Major GC는 응용 프로그램이 쓰레기 수집 기간 동안 응답하지 않게 만들기 때문에 최소화해야합니다. 따라서 응답성이 뛰어난 응용 프로그램이 있고 Major Garbage Collection이 많이 발생하는 경우 시간 초과 오류가 발생할 수 있습니다. 쓰레기 수집기에 걸리는 시간은 쓰레기 수집에 사용되는 전략에 따라 다릅니다. 그래서 고도로 응답성이 뛰어난 응용 프로그램에서 시간 초과를 피하기 위해 쓰레기 수집기를 모니터링하고 조정하는 것이 필요합니다.

Java 메모리 모델 – Permanent Generation

Permanent Generation 또는 “Perm Gen”에는 응용 프로그램에서 사용되는 클래스와 메서드를 설명하는 데 JVM이 필요로하는 응용 프로그램 메타데이터가 포함되어 있습니다. Perm Gen은 Java Heap 메모리의 일부가 아닙니다. Perm Gen은 응용 프로그램에서 사용되는 클래스를 기반으로 실행 시 JVM에 의해 채워집니다. Perm Gen에는 또한 Java SE 라이브러리 클래스와 메서드가 포함되어 있습니다. Perm Gen 객체는 완전한 쓰레기 수집에서 가비지 수집됩니다.

자바 메모리 모델 – 메소드 영역

메소드 영역은 Perm Gen의 공간 중 일부이며 클래스 구조(런타임 상수 및 정적 변수) 및 메소드와 생성자의 코드를 저장하는 데 사용됩니다.

자바 메모리 모델 – 메모리 풀

JVM 메모리 관리자에 의해 메모리 풀이 생성되어 구현이 지원되는 경우 불변 객체 풀을 만듭니다. 문자열 풀은 이러한 종류의 메모리 풀의 좋은 예입니다. 메모리 풀은 JVM 메모리 관리자 구현에 따라 힙 또는 Perm Gen에 속할 수 있습니다.

자바 메모리 모델 – 런타임 상수 풀

런타임 상수 풀은 클래스의 런타임 상수 풀의 클래스당 런타임 표현입니다. 클래스 런타임 상수와 정적 메소드를 포함합니다. 런타임 상수 풀은 메소드 영역의 일부입니다.

자바 메모리 모델 – 자바 스택 메모리

자바 스택 메모리는 스레드의 실행에 사용됩니다. 이들은 단명하고 메소드별로 특정된 값을 포함하며, 메소드에서 참조되는 힙(heap)에 있는 다른 객체에 대한 참조를 포함합니다. 스택 메모리와 힙 메모리의 차이를 읽어보는 것이 좋습니다.

자바의 메모리 관리 – 자바 힙 메모리 스위치

자바는 메모리 크기와 비율을 설정하는 데 사용할 수 있는 많은 메모리 스위치를 제공합니다. 일반적으로 사용되는 메모리 스위치 몇 가지는 다음과 같습니다:

VM Switch VM Switch Description
-Xms For setting the initial heap size when JVM starts
-Xmx For setting the maximum heap size.
-Xmn For setting the size of the Young Generation, rest of the space goes for Old Generation.
-XX:PermGen For setting the initial size of the Permanent Generation memory
-XX:MaxPermGen For setting the maximum size of Perm Gen
-XX:SurvivorRatio For providing ratio of Eden space and Survivor Space, for example if Young Generation size is 10m and VM switch is -XX:SurvivorRatio=2 then 5m will be reserved for Eden Space and 2.5m each for both the Survivor spaces. The default value is 8.
-XX:NewRatio For providing ratio of old/new generation sizes. The default value is 2.

대부분의 경우 위의 옵션이 충분하지만, 다른 옵션도 확인하려면 JVM 옵션 공식 페이지를 확인하십시오.

자바의 메모리 관리 – 자바 가비지 컬렉션

자바 가비지 컬렉션은 메모리에서 사용되지 않는 객체를 식별하고 제거하여 미래 처리를 위해 생성된 객체에 할당될 공간을 해제하는 프로세스입니다. 자바 프로그래밍 언어의 가장 좋은 기능 중 하나는 다른 프로그래밍 언어인 C와는 달리 메모리 할당 및 해제가 수동 프로세스가 아니라 자동 가비지 컬렉션이라는 것입니다. 가비지 컬렉터는 백그라운드에서 실행되는 프로그램으로 메모리의 모든 객체를 살펴보고 프로그램의 어느 부분에서도 참조되지 않는 객체를 찾습니다. 이러한 참조되지 않는 객체는 모두 삭제되고 공간은 다른 객체에 할당될 수 있도록 회수됩니다. 가비지 수집의 기본 방법 중 하나는 세 가지 단계로 구성됩니다:

  1. 표시: 이것은 가비지 컬렉터가 사용 중인 객체와 사용되지 않는 객체를 식별하는 첫 번째 단계입니다.
  2. 일반 삭제: 가비지 컬렉터는 사용되지 않는 객체를 제거하고 다른 객체에 할당할 수 있도록 빈 공간을 회수합니다.
  3. 압축을 사용한 삭제: 더 나은 성능을 위해 사용되지 않는 객체를 삭제한 후 모든 남은 객체를 함께 이동할 수 있습니다. 이렇게 하면 새로운 객체에 메모리를 할당하는 성능이 향상됩니다.

단순한 표시 및 삭제 접근 방식에는 두 가지 문제가 있습니다.

  1. 첫 번째는 대부분의 새로 생성된 객체가 사용되지 않으므로 효율적이지 않습니다.
  2. 둘째로, 여러 가비지 컬렉션 주기에 걸쳐 사용 중인 객체들은 앞으로도 사용 중일 가능성이 가장 높습니다.

위의 단순한 접근 방식의 위 단점들은 자바 가비지 컬렉션을 세대별로 만든 이유이며, 힙 메모리에는 젊은 세대와 오래된 세대 공간이 있습니다. 젊은 세대와 오래된 세대 사이에서 객체가 어떻게 스캔되고 이동하는지 이미 설명했습니다. 마이너 GC와 메이저 GC를 기반으로 다음 세대로 이동합니다.

자바 메모리 관리 – 자바 가비지 컬렉션 유형

우리 애플리케이션에서 사용할 수 있는 다섯 가지 유형의 가비지 컬렉션 방식이 있습니다. 애플리케이션에 가비지 컬렉션 전략을 활성화하기 위해 JVM 스위치를 사용하면 됩니다. 각각을 하나씩 살펴보겠습니다.

  1. Serial GC (-XX:+UseSerialGC): Serial GC는 젊은 세대와 오래된 세대 가비지 컬렉션, 즉 마이너 GC와 메이저 GC에 대해 간단한 마크-스위프-컴팩트 접근 방식을 사용합니다. Serial GC는 단순한 독립형 응용 프로그램과 CPU가 작은 기기와 같은 클라이언트 기기에서 유용합니다. 메모리 풋프린트가 낮은 작은 응용 프로그램에 적합합니다.
  2. 병렬 GC (-XX:+UseParallelGC): 병렬 GC는 시스템의 CPU 코어 수인 N개의 스레드를 생성하여 Young Generation 쓰레기 수집을 수행하는 직렬 GC와 동일합니다. JVM 옵션 -XX:ParallelGCThreads=n을 사용하여 스레드 수를 제어할 수 있습니다. 병렬 가비지 컬렉터는 여러 CPU를 사용하여 GC 성능을 향상시키므로 처리량 수집기로도 불립니다. 병렬 GC는 Old Generation 쓰레기 수집에 단일 스레드를 사용합니다.
  3. 병렬 Old GC (-XX:+UseParallelOldGC): 이는 병렬 GC와 동일하지만 Young Generation과 Old Generation 쓰레기 수집 모두에 여러 스레드를 사용합니다.
  4. 동시 표시 및 청소 (CMS) 컬렉터 (-XX:+UseConcMarkSweepGC): CMS 컬렉터는 동시 저 지연 콜렉터로도 불립니다. 이는 Old Generation의 쓰레기 수집을 담당합니다. CMS 컬렉터는 대부분의 쓰레기 수집 작업을 응용 프로그램 스레드와 동시에 수행하여 쓰레기 수집으로 인한 일시적인 멈춤을 최소화하려고 합니다. Young Generation에서의 CMS 컬렉터는 병렬 컬렉터와 동일한 알고리즘을 사용합니다. 이 가비지 컬렉터는 더 긴 일시적인 일시 중지를 감당할 수 없는 반응형 애플리케이션에 적합합니다. JVM 옵션 -XX:ParallelCMSThreads=n을 사용하여 CMS 컬렉터의 스레드 수를 제한할 수 있습니다.
  5. G1 Garbage Collector (-XX:+UseG1GC): Java 7부터 사용 가능한 Garbage First 또는 G1 가비지 컬렉터의 장기 목표는 CMS 컬렉터를 대체하는 것입니다. G1 컬렉터는 병렬, 동시 및 점진적으로 압축되는 저 지연 가비지 컬렉터입니다. Garbage First 컬렉터는 다른 컬렉터와 같이 작동하지 않으며 Young 및 Old 세대 공간 개념이 없습니다. 힙 공간을 여러 개의 동일한 크기의 힙 영역으로 나눕니다. 가비지 컬렉션이 호출되면 먼저 더 적은 라이브 데이터를 가진 영역을 수집하므로 “Garbage First”입니다. 이에 대한 자세한 내용은 Garbage-First Collector Oracle Documentation에서 확인할 수 있습니다.

Java에서 메모리 관리 – Java 가비지 컬렉션 모니터링

우리는 응용 프로그램의 가비지 컬렉션 활동을 모니터링하기 위해 Java 명령 줄과 UI 도구를 모두 사용할 수 있습니다. 예를 들어, 저는 Java SE 다운로드에서 제공하는 데모 응용 프로그램 중 하나를 사용하고 있습니다. 동일한 응용 프로그램을 사용하려면 Java SE Downloads 페이지로 이동하고 JDK 7 및 JavaFX 데모 및 샘플을 다운로드하십시오. 저는 사용하는 샘플 응용 프로그램은 Java2Demo.jar이며 jdk1.7.0_55/demo/jfc/Java2D 디렉터리에 있습니다. 그러나 이 단계는 선택 사항이며 가비지 컬렉션 모니터링 명령을 모든 Java 응용 프로그램에 대해 실행할 수 있습니다. 저의 데모 응용 프로그램을 시작하는 데 사용된 명령은 다음과 같습니다:

pankaj@Pankaj:~/Downloads/jdk1.7.0_55/demo/jfc/Java2D$ java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar Java2Demo.jar

우리는 JVM 메모리 및 가비지 컬렉션 활동을 모니터링하기 위해 jstat 명령 줄 도구를 사용할 수 있습니다. 이것은 표준 JDK와 함께 제공되므로 가져올 필요가 없습니다. jstat을 실행하려면 응용 프로그램의 프로세스 ID를 알아야 합니다. 이것은 ps -eaf | grep java 명령을 사용하여 쉽게 얻을 수 있습니다.

pankaj@Pankaj:~$ ps -eaf | grep Java2Demo.jar
  501 9582  11579   0  9:48PM ttys000    0:21.66 /usr/bin/java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseG1GC -jar Java2Demo.jar
  501 14073 14045   0  9:48PM ttys002    0:00.00 grep Java2Demo.jar

따라서 내 Java 응용 프로그램의 프로세스 ID는 9582입니다. 이제 아래에 표시된대로 jstat 명령을 실행할 수 있습니다.

pankaj@Pankaj:~$ jstat -gc 9582 1000
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT
1024.0 1024.0  0.0    0.0    8192.0   7933.3   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8026.5   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8030.0   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8122.2   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  0.0    0.0    8192.0   8171.2   42108.0    23401.3   20480.0 19990.9    157    0.274  40      1.381    1.654
1024.0 1024.0  48.7   0.0    8192.0   106.7    42108.0    23401.3   20480.0 19990.9    158    0.275  40      1.381    1.656
1024.0 1024.0  48.7   0.0    8192.0   145.8    42108.0    23401.3   20480.0 19990.9    158    0.275  40      1.381    1.656

jstat의 마지막 인수는 각 출력 간의 시간 간격입니다. 따라서 1초마다 메모리 및 가비지 컬렉션 데이터를 인쇄합니다. 각 열을 하나씩 살펴 보겠습니다.

  • S0C 및 S1C: 이 열은 Survivor0 및 Survivor1 영역의 현재 크기를 KB로 보여줍니다.
  • S0U 및 S1U: 이 열은 Survivor0 및 Survivor1 영역의 현재 사용량을 KB로 보여줍니다. 한 Survivor 영역은 항상 비어 있음을 유의하십시오.
  • EC와 EU: 이 열은 Eden 공간의 현재 크기와 사용량을 KB 단위로 보여줍니다. EU 크기가 증가하고 EC를 넘어서면 Minor GC가 호출되고 EU 크기가 감소합니다.
  • OC와 OU: 이 열은 Old 세대의 현재 크기와 현재 사용량을 KB 단위로 보여줍니다.
  • PC와 PU: 이 열은 Perm 세대의 현재 크기와 현재 사용량을 KB 단위로 보여줍니다.
  • YGC와 YGCT: YGC 열은 young 세대에서 발생한 GC 이벤트의 수를 표시합니다. YGCT 열은 Young 세대에 대한 GC 작업의 누적 시간을 표시합니다. Minor GC로 인해 EU 값이 감소하는 동일한 행에서 두 값이 증가하는 것에 유의하십시오.
  • FGC와 FGCT: FGC 열은 발생한 Full GC 이벤트의 수를 표시합니다. FGCT 열은 Full GC 작업에 대한 누적 시간을 표시합니다. Young 세대 GC 시간과 비교했을 때 Full GC 시간이 너무 높은 것에 유의하십시오.
  • GCT: 이 열은 GC 작업에 대한 총 누적 시간을 표시합니다. YGCT 및 FGCT 열 값의 합임을 유의하십시오.

jstat의 장점은 GUI가 없는 원격 서버에서도 실행될 수 있다는 것입니다. S0C, S1C 및 EC의 합계가 -Xmn10m JVM 옵션을 통해 지정된대로 10m임을 유의하십시오.

시각적 GC를 사용한 Java VisualVM

GUI에서 메모리 및 GC 작업을 보려면 jvisualvm 도구를 사용할 수 있습니다. Java VisualVM은 JDK의 일부이므로 별도로 다운로드할 필요가 없습니다. 단순히 터미널에서 jvisualvm 명령을 실행하여 Java VisualVM 애플리케이션을 시작하면 됩니다. 시작한 후에는 아래 이미지에 표시된 대로 도구 -< 플러그인 옵션에서 Visual GC 플러그인을 설치해야 합니다. Visual GC를 설치한 후에는 왼쪽 열에서 응용 프로그램을 열고 Visual GC 섹션으로 이동하면 됩니다. 아래 이미지에 표시된대로 JVM 메모리 및 가비지 컬렉션 세부 정보를 확인할 수 있습니다.

Java Garbage Collection Tuning

자바 가비지 컬렉션 튜닝은 애플리케이션의 처리량을 높이는 데 사용하는 마지막 옵션이어야 하며, 긴 GC 시간으로 인해 애플리케이션 타임아웃이 발생할 때만 사용해야 합니다. 로그에서 java.lang.OutOfMemoryError: PermGen space 오류를 확인하면 -XX:PermGen 및 -XX:MaxPermGen JVM 옵션을 사용하여 Perm Gen 메모리 공간을 모니터링하고 증가시키려고 시도해야 합니다. 또한 -XX:+CMSClassUnloadingEnabled을 사용하여 CMS 가비지 컬렉터로 성능을 확인할 수도 있습니다. 만약 많은 Full GC 작업을 확인한다면 Old generation 메모리 공간을 증가시켜 보는 것이 좋습니다. 전반적으로 가비지 컬렉션 튜닝에는 많은 노력과 시간이 필요하며 이에 대한 강제적인 규칙은 없습니다. 다양한 옵션을 시도하고 비교하여 애플리케이션에 가장 적합한 최상의 옵션을 찾아야 합니다. 이것이 자바 메모리 모델, 자바 메모리 관리 및 가비지 컬렉션에 관한 모든 내용입니다. JVM 메모리와 가비지 컬렉션 프로세스를 이해하는 데 도움이 되기를 바랍니다.

Source:
https://www.digitalocean.com/community/tutorials/java-jvm-memory-model-memory-management-in-java