허원철의 개발 블로그

Java - JVM Architecture 본문

java

Java - JVM Architecture

허원철 2017. 3. 8. 17:18
이번 글은 Java Virtual Mechine (이하 JVM)에 대해 다뤄보고자 합니다.
 
 
Java는 운영체제에 종속적이다, 독립적이다 라는 말을 합니다. 어떻게 독립적으로 돌아가는 것 일까?
 
 
운영체제에 어떻게 독립적일까?
 

 
- 단순하게 그림만 보고 설명을 보충하자면, Java라는 언어는 JVM이라는 하나의 소프트웨어 위에 돌아갑니다. 
(C계열같은 언어는 OS에서 실행이 되기 때문에 Java보다 빠릅니다.)
 
 
Java 동작과정 
 

 

1) IDE에서 Java Code 작성(.java)
2) Java Compiler가 Java Code 컴파일
3) Java Byte Code로 변환(.class)
4) 실행
 
 
그렇다면 JVM은 무엇인가?
 
- 바이트 코드를 실행할 수 있는 주체입니다.
 
 
JVM이 왜 중요한가?
 
- JVM은 메모리 관리 기법 중 하나인 Garbage Collection(이하 GC)이 일어납니다.
 
※ GC알고리즘은 다양합니다. 각각의 특성과 동작원리를 제대로 알지 못하고 사용하게 되면 서비스 중에 큰 위험이 닥칠 수 도 있습니다. 또한 그 영역 어떤 원리로 GC가 일어나는지 또한 중요합니다.
 
 
그렇다면 JVM 구조를 알아보자
 

 

1) Class Loader : 런타임시점에서 클래스를 로딩하게 해줍니다. 인스턴스가 생성이 되면 Class Loader를 통해 메모리에 적재됩니다.
 
2) JVM Memory( = Runtime Data Areas) : OS로 부터 별도로 할당 받은 Memory 영역입니다.
 
 ① Method Area
 - 모든 Thread가 공유하는 Memory 영역입니다. ( Class, Interface, Method, Field, Static Field 등이 저장됩니다. )
 
 ② Heap 
 - 런타임시, 동적으로 할당하여 사용하는 영역입니다. ( 인스턴스가 생성되면 Heap에 저장됩니다. )
 
 ③ JVM Stack
  ⑴ Thread
  - Thread의 수행 정보를 Frame을 통해 저장됩니다. 
  - Thread가 생성될 때 생성되므로, Thread별로 각각 생성됩니다.
  - 각 Thread 별로 서로 접근은 불가능 합니다.
  ⑵ Method
  - Method가 호출되며, Method와 Method 정보가 쌓입니다.
  - Method 정보로는 매개변수, 지역변수, 임시변수, Method Address가 있습니다.
  - Method 호출이 종료되면 메모리 공간에서 사라집니다.
 
 ④ PC Register
 - CPU 내의 기억장치인 레지스터와 다르게 작동합니다. (Stack - Base)
 - Native Stack를 수행 할 때, JVM을 거치지 않고 API를 통해 바로 수행합니다. (이때, PC Register는 Undefined 됩니다.)
 - 각 Thread 별로 하나씩 존재하고, JVM Instruction의 주소를 가지게 됩니다.
  
 ※ OS나 CPU입장에서보면 JVM은 하나의 프로세스이기 때문에, 버퍼공간으로 사용하기 위해 PC Register라는 메모리 영역이 존재하는 것 입니다.
 
 ⑤ Native Stack
 - Java 외의 언어로 작성된 네이티브 코드들을 위한 Stack입니다.
 - JNI(Java Native Interface)를 통해 호출되는 C/C++ 등의 코드를 수행하기 위한 Stack입니다.
 - JVM 내부에 영향을 주지 않기 위함입니다.
 
 ※ JNI란?
 - JVM에 실행되고 있는 자바코드를 네이티브 응용 프로그램(특히 하드웨어와 운영 체제 플랫폼)들과 C, C++ 그리고 어샘블리 같은 다른 언어들로 구현된 라이브러리에 의해 호출되거나 호출할 수 있는 프로그래밍 프레임워크이다. (참고 : 자바_네이티브_인터페이스)
 
3) Execution Engine : Byte Code를 실행하는 Runtime Module입니다.
- Class Loader를 통해 JVM 내의 Runtime Data Areas에 배치된 Byte Code를 명령어 단위로 읽어서 실행합니다.
- 최초에는 Interperter 방식(한 줄씩 해석하고 실행)이여서 속도가 느린 단점이 있었습니다.
- JIT Compiler 방식으로 느린 것은 보완 했습니다.
 
※ JIT Compiler 이란?
- Byte Code를 어셈블러 같은 Native Code로 바꿔 실행합니다. 물론 무조건 바뀌는 것이 아니라 JIT 역시 변환하는 과정에 비용이 발생하기 때문에, Interpreter 방식을 사용하다가 일정 기준이 넘어가면 바뀐다고 합니다.
 
4) Garbage Collector

 
Heap Area 파헤쳐 보기
 
 
1) Eden
- 객체들이 최초로 생성되는 공간입니다.
 
2) Survivor
- Eden에서 참조되는 객체들이 저장되는 공간입니다.
 
3) Old
- Eden 과 Survivor에사 마저 지워지지 않고 살아남은 객체들이 저장되는 공간입니다.
 
4) Permanent
- 생성된 객체들의 정보를 가리키는 주소 값이 저장된 공간입니다.
① 클래스의 메소드(바이트 코드 포함)
② 클래스의 이름
③ 상수 풀 정보
④ 객체 배열 및 클래스와 연결된 유형 배열
⑤ JVM에 의해 생성된 내부 객체
⑥ 컴파일러의 최적화에 사용되는 정보
 
- Permanent 의 문제점
① Static Object의 남용
② Class와 Method의 증가
 
※ Permanent Area가 가득차서 OOM(Out Of Memory)가 발생할 수 있습니다. 
 
- 기존 대응방안
① Permanent을 적절히 늘립니다.
② 정기적인 재구동
 
 
변화된 Heap Area From Java 8...
 
- Java 8 이전까지는 위와 같은 Heap 영역의 형태를 가지고 있었습니다. 하지만 Java 8 이후로는 Permanent 영역이 사라지면서 Heap 영역이 아닌 Native Stack 영역 안에 Metaspace 영역으로 바뀌었습니다. (Oracle의 JRockit 및 IBM의 JVM과 유사합니다.)
 
 
Permanent Area vs Metaspace Area
 


'java' 카테고리의 다른 글

Thread Pool 이해하기  (401) 2017.04.01
Java - Garbage First(G1) Garbage Collection  (417) 2017.03.12
Java - Compare GC  (406) 2017.03.07
Java - GC (Garbage Collection)  (420) 2017.03.05
Java - Optional API  (415) 2017.02.19
Comments