참고: 

- http://aljjabaegi.tistory.com/387

- https://jeong-pro.tistory.com/148

- https://huelet.tistory.com/entry/JVM-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B5%AC%EC%A1%B0

- https://hoonmaro.tistory.com/19



1. JAVA의 컴파일 과정

(1) Java Compiler(javac 명령어 실행)에 의해 Java Source(.java 확장자)로부터 Byte Code(.class 확장자)가 생성된다.


(2) JVM에 있는 Class Loader에 의해 Byte Code는 JVM내로 로드되고 실행엔진에 의해 기계어로 해석되어 메모리 상(Runtime Data Area)에 배치된다.


(3) 실행엔진에는 Interpreter와 JIT(Just-In-Time) Compiler가 있는데, Interpreter에 의해 Byte Code를 한 줄씩 읽어 실행하다가 적절한 시점에 Byte Code 전체를 컴파일하고 더이상 인터프리팅하지 않고 해당 코드를 직접 실행한다. 


JIT Compiler에 의해 해석된 코드는 캐시에 보관하기 때문에 한 번 컴파일된 후에는 빠르게 수행할 수 있다는 장점이 있습니다. 하지만 코드 전체를 컴파일하기 때문에 인터프리팅하는 것보다 시간이 오래 걸리므로 한 번만 실행해도 되는 코드에 대해서는 인터프리팅하는 것이 유리합니다.

- Interpreter : 자바 Byte Code를 한 줄씩 실행. 전체 성능면에서 불리.

- JIT Compiler : 전체 Byte Code를 컴파일하고 캐시에 보관해놓고 직접 실행. 한 번만 실행해도 되는 코드에 대해서는 Interpreter가 유리.




2. JVM 메모리 구조

(1) Heap Area: new 명령어를 통해 생성된 인스턴스와 배열 등의 참조형 변수 정보가 저장되는 공간입니다. 물론, Method Area에 올라온 클래스들과 관련된 것만 저장됩니다. GC(Garbage Collector)의 대상이 됩니다.


(2) Stack Area: 클래스 내의 메소드에서 사용되는 매개변수, 지역변수, 리턴값 등의 정보들이 저장되는 공간입니다. LIFO(Last In First Out) 방식으로 메소드 실행 시 저장되었다가 실행이 완료되면 제거됩니다. 임시 저장공간으로 생각하시면 됩니다.


(3) Method Area(Class Area 또는 Static Area): 클래스 관련 필드 정보, 메소드 정보, Type 정보(interface인지, class인지에 대한 구분), 상수(constant) pool, static 변수, final class 변수 등의 정보들이 저장되는 공간입니다.


(4) PC Register Area: 쓰레드마다 하나씩 생성되고 JVM 명령의 주소값이 저장되는 공간입니다.


(5) Native Method Stack Area: 자바 외 다른 언어의 호출을 위해 할당되는 영역입니다. 자바에서 C/C++의 메소드를 호출할 때 사용하는 Stack 영역이라고 생각하시면 됩니다.




예를 들어, getList(), insertList(), updateList(), deleteList() 등의 CRUD 메소드가 있는 ListController class가 있을 때 이 클래스와 메소드의 정보는 실행엔진에 의해 Method 영역에 올라가며, 클래스의 메소드 호출이 발생하면 Method 영역의 정보를 읽어 해당 메소드의 매개변수, 지역변수 리턴값 등이 Stack 영역에 올려져 처리됩니다. 그리고 메소드의 실행이 끝나면 Stack 영역으로부터 자동으로 제거됩니다.

만약, 메소드 내에 New 명령어로 생성한 인스턴스나 배열이 있을 경우, 해당 값은 Heap 영역에 저장되고, Stack 영역에는 이 Heap 영역의 값을 참조할 수 있는 메모리 주소값만 저장됩니다. 그래서 배열을 System.out.println(); 하게 되면 메모리 주소값이 출력됩니다.




3. JVM GC

자바에서는 메모리를 명시적으로 지정하여 해제하지 않기 때문에 Heap 영역의 메모리를 관리하는 Garbage Collector의 역할이 중요합니다.


- 자바8 이전의 Heap 영역은 아래의 그림과 같이 크게 세 영역으로 나뉘게 됩니다.

자바8부터는 Permanent 영역(Java Heap 영역 중 하나) 대신에 Metaspace라는 Native 메모리 영역에 저장됩니다.


● New(Young) Generation: 이 영역은 자바 객체가 생성되자마자 저장되고, 생긴지 얼마 안되는 객체가 저장되는 영역입니다. 시간이 지나 우선순위가 낮아지면 Old 영역으로 옮겨집니다. Young 영역으로부터 garbage를 수집하는 것을 Minor GC라고 합니다.

● Old(Tenured) Generation: Young Generation 영역에서 저장되었던 객체 중에 오래된 객체가 이동되어 저장되는 영역입니다. 이 영역으로부터  garbage를 수집하는 것을 Major GC라고 합니다.
● Permanent Generation(자바8부터는 Metaspace): 클래스 로더에 의해 로드되는 클래스, 메소드 등에 대한 meta 정보가 저장되는 영역이며 JVM에 의해 사용됩니다. 리플렉션을 사용하여 동적으로 클래스가 로딩되는 경우에 사용됩니다. 내부적으로 리플렉션 기능을 자주 사용하는 Spring Framework를 이용할 경우 이 영역에 대한 고려가 필요합니다.


<Minor, Major GC>

Heap 영역에 객체가 생성되면 최초로 Eden 영역에 할당됩니다. 그리고 이 영역에 데이터가 어느정도 쌓이게 되면 참조 정도에 따라 Servivor1, Servivor2 중 빈 공간으로 이동되거나 회수됩니다. New Generation(Eden 영역 + Servivor1,2 영역) 영역이 차게 되면 또 참조 정도에 따라 Old영역으로 이동되거나 회수됩니다. 이렇게 New Generation과 Tenured Generation(Old 영역)에서의 GC를 Minor GC 라고 합니다.

 

Old 영역에 할당된 메모리가 허용치를 넘게 되면, Old 영역에 있는 모든 객체들을 검사하여 더이상 참조되지 않는 객체들을 전부 삭제하는 GC가 실행됩니다. 시간이 오래 걸리는 작업이고 이 때 GC를 실행하는 쓰레드를 제외한 모든 쓰레드는 작업을 멈추게 됩니다. 이를 'Stop-the-World' 라 합니다. 그리고 이렇게 'Stop-the-World'가 발생하고 Old영역의 메모리를 회수하는 GC를 Major GC라고 합니다. 앞에서 말한 것과 같이 Major GC가 실행되면 이것이 종료될 때까지 다른 모든 쓰레드가 멈추기 때문에 성능에 영향을 끼칠 수 밖에 없습니다.


<Minor GC vs Major GC vs Full GC>

ㄴ참고: https://plumbr.io/blog/garbage-collection/minor-gc-vs-major-gc-vs-full-gc

1. Collecting garbage from Young space (consisting of Eden and Survivor spaces) is called a Minor GC.

2. Major GC is cleaning the Tenured space.

3. Full GC is cleaning the entire Heap – both Young and Tenured spaces.

  Unfortunately it is a bit more complex and confusing. To start with – many Major GCs are triggered by Minor GCs, so separating the two is impossible in many cases. On the other hand – many modern garbage collections perform cleaning the Tenured space partially, so again, using the term “cleaning” is only partially correct.

This leads us to the point where instead of worrying whether the GC is called Major or Full GC, you should focus to finding out whether the GC at hand stopped all the application threads or was it able to progress concurrently with the application threads.

'IT > Java' 카테고리의 다른 글

상속(Inheritance) 개념  (0) 2018.11.11
Wrapper class 개념  (0) 2018.11.10
== vs equals() 개념  (0) 2018.11.04
try-catch-finally 실행 순서  (0) 2018.10.25
e.printStackTrace()  (0) 2018.07.18

+ Recent posts