static에 대한 이해가 부족했고 JVM 내부 동작에 대해 알게 되면 static 뿐만 아니라 메모리 최적화에도 도움을 얻을 수 있다는 조언을 얻어 공부하게 되었다.
JVM은 크게 세 가지 요소로 구성된다.
클래스 로더(Class Loader)
- 자바 컴파일러는 .java파일을 .class파일(바이트코드 형식)로 컴파일한다.
- 클래스 로더는 이 컴파일된 클래스 파일을 로드하여 클래스 파일 정보를 메모리 영역에 올리고 검증하고 static(정적, 컴파일 이후 변하지 않는) 변수들을 초기화 하는 등의 역할을 한다.
실행 엔진(Execution Engine)
- 실행 엔진은 JVM이 로드한 클래스 파일을 실행한다. 실행 엔진에는 Interpreter, JIT Compiler, Garbage Collector가 있다.
- 이 중 Interpreter와 JIT Compiler는 바이트 코드를 기계어로 변환하여 os에 전달한다.
- Interpreter 방식은 바이트 코드를 한 줄씩 해석하고 실행하고 JIT COmpiler 방식은 인터프리터가 반복해서 변환하는 코드를 동적으로 기계어로 컴파일하여 실행 속도를 향상 시킨다.
메모리 영역(Memory Areas)
- 자바 프로그램의 실행과 관련된 데이터와 자원을 저장한다. 주요한 메모리 영역은 다음과 같다.
- 메서드 영역(Method Area) : 클래스 정보, 상수 풀(Constant Pool), 메서드 코드 등의 정보를 저장한다.
- 힙(Heap) : 동적으로 생성된 객체와 배열을 저장한다. 가바지 컬렉터가 불 필요한 객체를 지우는 곳
- 스택(Stack) : 메서드 호출과 관련된 데이터를 저장하는 곳, 쓰레드마다 별도의 스택이 생성, 메서드 호출 시 프레임이라는 자료구조로 각 메서드마다 하나씩 쌓임
- PC Register : 현재 실행되고 있는 명령어의 주소를 저장하고 있는 곳, 멀티 쓰레드 환경에서 한 쓰레드가 작업을 하다가 멈추고 다른 쓰레드에게 cpu 점유를 넘겨주고 다시 돌아왔을 때 이전에 어떤 명령을 수행하고 있었는지 기억하고 있는 곳
- Native Method Stack) : Java 외부에서 사용되는 네이티브 코드(C/C++)로 작성된 메서드를 실행할 때 사용되는 스택
위 JVM의 메모리 영역이 Java Runtime Data Area이다. 이 영역은 크게 두가지로 나뉜다.
- 모든 쓰레드가 공유하는 영역
- Method Area
- Heap
- 쓰레드 종료 시 소멸되는 스택 영역
- Stack(JVM Language Stacks)
- PC Registers
- Native Method Stacks
동적으로 할당한 메모리 영역 중 사용하지 않는 영역을 탐지하여 해제하는 기능
- Stack
- 정적으로 할당한 메모리 영역
- 원시 타입의 데이터가 값과 함께 할당
- Heap영역에 생성된 Object 타입의 데이터의 참조 값 할당
- Heap
- 동적으로 할당한 메모리 영역
- 모든 Object 타입의 데이터가 할당
- Heap 영역의 Object를 가리키는 참조 변수가 Stack에 할당
- 장점
- 메모리 누수 낮출 수 있음
- 해제된 메모리에 재접근하는 낭비 줄임
- 해제한 메모리 다시 해제하는 낭비 줄임
- 단점
- GC 작업은 순수 오버헤드, 프로그램이 해야 하는일처럼 컴퓨터 리소스를 써서 GC를 하는 동안 프로그램이 멈춰야 하기 때문
- 개발자는 언제 GC가 메모리를 해제하는지 정확히 알기 어려움
JVM GC가 기본적으로 돌아가는 방식
Reference Counting의 순환 참조 문제를 해결 가능
루트에서부터 객체까지 접근 가능한지가 기준
Mark : 그래프 순회를 통해 연결된 객체를 찾아내는 것
Sweep : 연결이 끊어진 객체를 지우는 것
루트로부터 연결된 객체는 Reachable, 끊어진 객체는 Unreachalbe
이렇게 Sweep 이후에 분산된 메모리를 정리해서 메모리 파편화를 막는 것은 Compaction
Mark and Sweep에서 Compaction은 필수는 아님
Mark and Sweep의 단점
- JVM의 Heap 영역은 Young Generaion과 Old Generation으로 나뉜다.
- Stop The World
GC를 실행하기 위해 JVM이 어플리케이션 실행을 멈추는 것, 이 stop the world의 시간을 최적화 하는것이 GC의 최적화
*** GC 튜닝(개념만 알고 가고 나중에 해보자)
- 성능 개선 단계의 최종단계, 객체 생성자체를 줄이려는 코드 레벨에서의 개선이 선행된 후 진행하는 것
- Old Generation으로 넘어가는 객체 최소화하기
- Major GC 시간을 짧게 유지하기
참고한 사이트