이미지출처 : https://brucehenry.github.io/blog/public/2018/02/07/JVM-Memory-Structure/
우선 내부적으로 도식화 한 것이고.
실제로 프로그램이 실행된다면 Java는 하나의 프로세스니까 그 내부에서 실행 단위는 Thread다. Thread가 여러개 만들어져 있다고 해보자. 그리고 스레드가 가지고 있는 영역을 박스로 칠했다.
첫 번째, Thread를 생성하면 가장 먼서 생성되는 기본 영역이 Stack Area랑, PC Registers다 .
JVM에서 스레드를 생성하면 stack 영역이 기본적으로 생성된다. 해당 스레드 안에서만 사용할 수 있는 stack 자료구조이다.
현재 수행할 JVM machine instructuion을 올려놓는 PC register 공간이다.
실제 운영체제의 PC register 와 같은 역할을 한다고 보면 된다.
컴퓨터 구조에서 실제로 명령어를 수행한다는 것은, 프로그램 카운터(PC)가 가리키는 주소의 명령어를 CPU가 가져와(fetch), 해석(decode), 실행(execute)하는 과정을 통해 프로그램이 동작한다는 의미이다.
이 프로그램 카운터의 값을 저장하는 레지스터가 CPU 내에 존재한다.
현재 실행 중인 머신 인스트럭션은 예를 들어 JVM 환경이라면, Java 바이트코드에 해당하는 JVM Instruction이 된다. 이 인스트럭션을 해석하고 실행하기 위해서는 해당 명령어를 보관할 레지스터나 실행 컨텍스트(context)가 필요하다.
그리고 이러한 실행 정보(예: PC 값, JVM의 명령어 포인터 등)는 실제 실행 중인 스레드(Thread)가 소유하게 된다.
즉, 하나의 스레드가 실제로 CPU 코어에 할당되어 실행될 때, 해당 스레드는 자신의 컨텍스트 안에 프로그램 카운터와 같은 실행 정보를 가지고 있어야 하며, 이것을 바탕으로 명령어를 하나씩 수행해 나간다.
Java는 JNI(Java Native Interface)를 통해 C, C++ 등으로 작성된 Native 라이브러리 함수를 호출할 수 있다.
이때 JNI를 통해 호출되는 네이티브 메서드는 해당 호출을 수행하는 스레드의 호출 스택(Stack Frame)에 포함된다.
그래서 다른 Java로 선언된 Method, Shared MEmory는 method 영역에 남는데 JNI로 호출하는 Native Method 호출은 호출하는 Thread에 남는다. 이렇게 해서 각 THread 마다 이런 공간들을 가지고 있고 Thread끼린는 서로 공유되지 않는다.
모든 스레드에서 공유되는 자원이다. JVM Heap 이라고 한다.
메소드 영역
Heap 영역
여기서 메소드 영역과 heap 영역의 구분은 논리적인 개념의 구분이다. 2.2에서 볼 수 있듯이 metaspace 도 heap 영역에 포함되어 있다.
이미지 출처 : https://sematext.com/blog/java-garbage-collection-logs/
변하지 않는 정보를 저장하는 공간이다. GC의 대상이 아니다.
클래스 정보, 메소드 정보, static 변수들이 저장된다. (Java 7까지는 Perm 영역)
GC는 가비지컬랙션이라고 해서
불필요한 객체를 지우는 것인데, GC의 대상이 아니다 라는 것은 안 지워지는 데이터인 것!
객체가 생성되면 맨 처음 위치하는 공간이다. (무조건)
Young Generation 의 일부이다.
Minor GC, Full GC의 대상이 된다.
Eden에 생성된 객체가 GC 시점에 살아남으면 이동하게 되는 공간이 Survivor Space다.
Eden에 있던 객체가 더 이상 Eden에 머물 수 없을 때, 살아남은 객체들은 Survivor 영역으로 이동하게 된다. (이러한 특성 때문에 Survivor Space는 '살아남은 객체들이 위치하는 공간'으로 볼 수 있다.)
Survivor Space는 Young Generation의 일부다.
Survivor 영역은 Space 0과 Space 1로 나뉘며, 두 공간은 번갈아 가며 사용된다. 0이 가득 차면 1로, 1이 가득 차면 다시 0으로 교차되며 사용된다. 두 영역은 역할에 차이가 없고, 논리적으로만 분리되어 있다.
Young Generation에 포함된 Eden과 Survivor Space는 Minor GC 및 Full GC의 대상이 된다.
Yougn Gen에서 오래 살아남은 객체가 최종적으로 머무르는 공간이다.
Major GC, Full GC의 대상이 된다.
Old Gen의 사용량/점유율이 높을수록 GC에 의해 성능의 부하가 크다.
서바이버 영역에서 계속 살아남은 객체는 일정 기준 이상 오래 생존한 것으로 판단되어 Tenured Space, 또는 Old Generation Space로 이동하게 된다.
이 영역에서 수행되는 GC는 Major GC라고 부르며, 정리 작업의 규모가 크다.
특히 Old Generation은 메모리 사용량이 많아질수록 GC의 부하가 커질 수 있다.
Young Generation Space는 객체가 빠르게 생성되고 빠르게 제거되는 것이 기본 목적이다.
따라서 Young Gen에서 빠르게 생성되었다가 사라지는 것은 정상적인 동작이다.
반면 Old Gen에 객체가 계속 쌓이고, 프로그램이 시작된 이후로 수개월 이상 지속적으로 공간이 많이 차 있는 상태라면, 이는 Old Gen의 메모리 관리에 문제가 있다고 볼 수 있다.
-xms=128m
으로 설정
-xmx=128m
으로 설정
Xms와 Xmx를 다르게 설정할 경우, JVM이 실행 중 메모리 부족으로 인해 Xmx까지 메모리를 확장하게 된다.
예를 들어, 메모리 사용률이 80%일 때 알림이 발생하도록 설정해두면, 이후 사용률이 50%로 감소한 것처럼 보일 수 있다.
이는 JVM이 내부적으로 최대 메모리를 확장했기 때문에 실제 점유율이 낮아진 것으로 나타나는 것이다.
이런 상황에서는 모니터링 시 혼동이 발생할 수 있다.
따라서 Xms와 Xmx를 동일하게 설정하면, JVM이 시작할 때 바로 전체 메모리를 확보하게 되고, 메모리 사용률의 변동도 실제 상태와 일치하게 나타난다.
이로 인해 운영 및 장애 대응이 수월해질 수 있다.
NewRatio
: Young Gen의 메모리 대비 Old Generation 의 크기 배수
NewSize
: Young Generation의 영역의 초기 사이즈
1310M
MaxNewSize
: Young Generation의 최대 사이즈
SurvivorRatio
: Survivor space 대비 young gen의 크기.