Minor GC

 

- Eden 영역이 가득 찼을 때 발생하는 GC

- Minor GC가 발생했을 때 Old Gen(tenured)로 이동된 객체는 관계가 없다.

- Minor GC가 발생해도 stop-the-world가 발생할 수 있다.

 

 

Major GC

- Old Gen(tenured)에서 발생한 GC

 

 

Full GC

- Young, Old 전체 영역에 대한 GC

'JVM Optimization &Tuning' 카테고리의 다른 글

GC 튜닝이 불필요한 상황  (0) 2019.12.26
JVM Heap 영역  (0) 2019.02.27
JVM(Java Virtual Machine)에 대한 이해  (2) 2018.08.12

 

GC 모니터링 결과 분석 후, 일반적으로 다음과 같은 상황에서는 GC 튜닝이 크게 필요하지 않다.

 

1) Minor GC의 처리 시간이 50ms 내외로 빠른 경우

2) Minor GC 주기가 10초 내외로 빈번하지 않은 경우

3) Full GC의 처리 시간이 보통 1초 이내로 빠른 경우

4) Full GC 주기가 10분에 1회 정도로 빈번하지 않은 경우

 

 

일반적인 상황이라고 가정하며, 서비스마다 다르다.

 

예를 들어 Full GC 처리 시간이 1초가 소요 되더라도 어느 서비스에서는 만족하지 못할 수도 있다.

 

중요한 부분은 Full GC와 Minor GC의 처리 시간만으로 튜닝 여부를 결정짓지 않아야 한다는 점. 만약 Young Area 크기가 너무 작다면 Minor GC가 발생하는 빈도는 높아질 뿐만 아니라, Full GC의 횟수도 증가하기 마련이다.

'JVM Optimization &Tuning' 카테고리의 다른 글

Minor GC , Major GC, Full GC  (0) 2019.12.26
JVM Heap 영역  (0) 2019.02.27
JVM(Java Virtual Machine)에 대한 이해  (2) 2018.08.12


Java Heap


- Java Heap의 메모리 해제는 오로지 Garbage Collection에 의해 수행됨을 JVM 스펙에 제시되어 있다.

- 제시된 스펙을 각 Vendor(Oracle-Hotspot, IBM ..) 들은 최대한 따르고 있다.

- 하지만 이에 대한 Gabage Collection, Heap 영역의 구현은 각 Vendor 별로 다르다.



Oracle Hotspot JVM의 Heap


- 그 중 가장 대중적으로 알려진 Oracle HopSpot JVM의 구조는 다음과 같다.





1) Young Generation : Eden 영역과 Survivor영역으로 구성

 

- Eden 영역은 Object(객체)가 최초로 Heap에 할당되는 장소이다. 만일 Eden 영역이 가득 찼다면, Object의 참조 여부를 파악하고 Live Object는 Suvrvior 영역으로 넘긴다. 그리고 참조가 사라진 Garbage Object이면 남겨 놓는다. 그리고 모든 Live Object가 Survivor 영역으로 넘어간다면 Eden 영역을 모두 청소한다. 


- Survivor 영역은 Survivor0과 Survivor1로 구성되며 Eden 영역에 살아 남은 Object들이 잠시 머무르는 곳이며 Live Object들은 하나의 Survivor 영역만 사용하게 되며 이러한 전반적인 과정을 Minor GC라고 한다.




2) Old Generation


- Young Generation은 새로 Heap에 할당된 Object가 들어오는 것이 아닌, Survivor 영역에서 살아남아 오랫동안 참조 되었고 앞으로도 사용될 확률이 높은 Object들을 저장하는 영역이다. 이러한 Promotion 과정 중 Old Generation의 메모리가 충분하지 않으면 해당 영역에서 GC가 발생하는데 이를 Major GC라고 한다.(Tenured 영역에서 발행한 GC)



3) Perm 


- Perm 영역은 보통 Class Meta 정보나 Method의 메타 정보, static 변수와 상수 정보들이 저장되는 공간으로 흔히 메타데이터 저장 영역이라고 한다. 이 영역은 JAVA8 부터 Native Memory 영역으로 이동하였다.( 기존의 Perm영역에 존재하는 static object는 Heap 영역으로 옮겨졌다.)






JAVA7 까지의 Heap 영역과 Java8의 Heap 영역

이미지 :  http://equj65.net/tech/java8hotspot/






- 가장 큰 차이점은 Perm 영역이 Heap 영역에서 사라졌다는 점이다. Native Memory 영역은 일반적으로 OS Level에서 관리되며 Permanent 영역에 저장 되었던 Class나 Method의 메타 정보들이 Metaspace 영역으로 변경됨에 따라 Heap 영역의 확보의 Max 값을 크게 의식하지 않아도 된다. 


- Perm 영역 메모리 크기 옵션  -XX:PermSize / -XX:PermMaxSize, Metaspace 영역 메모리 크기 옵션 -XX:Metaspace / -XX:MaxMetaspaceSize


- Perm영역과 Metaspace 영역의 기본 값은 시스템 별로 크게 다를 수 있으므로 튜닝 시 초기치와 최대 치를 확인해야 하며, Mac OS 기준 확인 방법은 다음과 같다.



Java7

$ j./java -XX:+PrintFlagsFinal -version -Server | grep "PermSize"






Java8

$ java -XX:+PrintFlagsFinal -version -Server | grep "MetaspaceSize"





MaxMetaspaceSize는 18446.....byte (약 16Exabyte)의 큰 값을 가지고 프로세서가 취급할 수 있는 메모리의 상한치이다. Metaspace는 Native 메모리로 다루기 때문에 프로세스가 이용할 수 있는 메모리 자원을 최대한 활용 할 수 있다. 그러나 이 영역 또한 별도의 옵션을 통해 제한이 가능한데 독자적인 ClassLoader를 구현한 채 메모리 누수를 의심할 경우 

" -XX:MaxMetaspaceSize" 옵션을 활용하여 제한이 가능하다.


하지만 Metaspace는 필요에 따라 자동 증가하며 일반적으로 크게 주의를 갖고 설정할 필요는 없어 보인다.


'JVM Optimization &Tuning' 카테고리의 다른 글

Minor GC , Major GC, Full GC  (0) 2019.12.26
GC 튜닝이 불필요한 상황  (0) 2019.12.26
JVM(Java Virtual Machine)에 대한 이해  (2) 2018.08.12



JDK, JRE, JVM 그리고 메모리 구조






JDK

JDK는 자바 개발도구(Java Development Kit)의 약자이다.

JDK는 JRE 에서 개발을 위해 필요한 도구(javac, java, visualVM 등)들을 포함한다.


JRE

JRE는 자바 실행환경(Java Runtime Environment)의 약자이다.

JRE는 JVM 이 자바 프로그램을 동작시킬 때 필요한 라이브러리 파일들과 기타 파일들을 가지고 있다. JRE는 JVM의 실행환경을 구현했다고 할 수 있다.



JVM


위키에서는 JVM을 Java Byte Code를  실행하는 주체라고 정의하고 있다.

바이트 코드를 실행하는 주체의 의미는, 자바 소스코드로부터 만들어지는 자바 바이너리 파일(.class)을 실행할 수 있다는 뜻이다.

컴파일된 바이너리 코드(.class)는 어떤 JVM에서도 동작시킬 수 있게 된다.






출처: https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%EA%B0%80%EC%83%81_%EB%A8%B8%EC%8B%A0





JVM의 역할


  • 바이너리 코드를 읽는다.
  • 바이너리 코드를 검증한다.
  • 바이너리 코드를 실행한다.
  • 실행환경(Runtime Environment)의 규격을 제공한다. (필요한 라이브러리 및 기타파일)





JVM의 특징

1) 스택 기반의 가상 머신 

- 대표적인 컴퓨터 아키텍처인 인텔 x86 아키텍처나 ARM 아키텍처와 같은 하드웨어가 레지스터 기반으로 동작하는 비해 JVM 스택 기반으로 동작한다.


2) 심볼릭 레퍼런스

기본 자료형(primitive data type) 제외한 모든 타입(클래스와 인터페이스) 명시적인 메모리 주소 기반의 레퍼런스가 아니라 심볼릭 레퍼런스를 통해 참조한다.


3) 가비지 컬렉션(garbage collection)

 - 클래스 인스턴스는 사용자 코드에 의해 명시적으로 생성되고 가비지 컬렉션에 의해 자동으로 파괴된다.

4) 기본 자료형을 명확하게 정의하여 플랫폼 독립성 보장

- C/C++ 등의 전통적인 언어는 플랫폼에 따라 int 형의 크기가 변한다. JVM 기본 자료형을 명확하게 정의하여 호환성을 유지하고 플랫폼 독립성을 보장한다


5) 네트워크 바이트 오더(network byte order)

자바 클래스 파일은 네트워크 바이트 오더를 사용한다. 인텔 x86 아키텍처가 사용하는 리틀 엔디안이나, RISC 계열 아키텍처가 주로 사용하는 엔디안 사이에서 플랫폼 독립성을 유지하려면 고정된 바이트 오더를 유지해야 하므로 네트워크 전송 시에 사용하는 바이트 오더인 네트워크 바이트 오더를 사용한다. 네트워크 바이트 오더는 엔디안이다.



=> 많은 자바 개발자들이 알고 있는 메모리 할당과 해제를 알아서 수행해주는 가비지 컬렉터와 플램폼에 의존성 즉, JVM을 구성할 수 어느 플램폼에서든지 이식할 수 있다는 특징은 JVM의 가장 큰 장점일 것이다. 개인적으로 네트워크 프로그래밍을 하는 개발자라면, 자바언어는 빅 엔디안을 따른다는 것 쯤은 알고 있어야한다.






JVM
 수행과정



자바 컴파일러에 의해 컴파일된 클래스 파일을 JVM의 클래스로더에 의해 Runtime Data Areas(런타임 데이터 영역)에 적재하고, 실행엔진이 자바 바이트 코드를 실행시킨다.


여기서 흔히 알고 있는 특징인 자바 바이트 코드는 컴파일 시점이 아닌, Excution Engine에 의해 런타임(실행)시점에 실행된다는 점이 바로 위의 과정을 통해 알 수 있다.






Runtime Data Areas


런타임 데이터 영역은 JVM이 운영체제 위에서 실행되면서 할당받는 메모리 영역, 우리가 알고 있는 자바의 메모리 영역이다.




런타임 데이터 영역은 6개의 영역으로 나누어 지는데 , PC Register와 Stack, Native Method stack 은 쓰레드마다 차지 하는 영역이며 Heap, Method Area, Runtime Constant Pool 영역은 모든 쓰레드가 공유하는 영역이다.



1) PC 레지스터


- PC(Program Counter) 레지스터는 스레드마다 하나씩 존재하며 스레드가 시작될 생성된다. PC 레지스터는 현재 수행 중인 JVM 명령의 주소를 갖는다.


2) JVM Stack


- JVM Stack은  스레드마다 하나씩 존재하며 스레드가 시작될 생성된다. 스택 프레임(Stack Frame)이라는 구조체를 저장하는 스택으로, JVM 오직 JVM 스택에 스택 프레임을 추가하고(push) 제거하는(pop) 동작만 수행한다. 예외 발생 printStackTrace() 등의 메서드로 보여주는 Stack Trace 라인은 하나의 스택 프레임을 표현한다.


3) Native Method Stack


자바 외의 언어로 작성된 네이티브 코드를 위한 스택이다. , JNI(Java Native Interface) 통해 호출하는 C/C++ 등의 코드를 수행하기 위한 스택으로, 언어에 맞게 C 스택이나 C++ 스택이 생성된다.


4) Method Area (= Class Area, Static Area)


메서드 영역은 모든 스레드가 공유하는 영역으로 JVM 시작될 생성된다. JVM 읽어 들인 각각의 클래스와 인터페이스에 대한 런타임 상수 , 필드와 메서드 정보, Static 변수, 메서드의 바이트코드 등을 보관한다. 메서드 영역은 JVM 벤더마다 다양한 형태로 구현할 있으며, 오라클 핫스팟 JVM(HotSpot JVM)에서는 흔히 Permanent Area, 혹은 Permanent Generation(PermGen)이라고 불린다. 메서드 영역에 대한 가비지 컬렉션은 JVM 벤더의 선택 사항이다.


- 위의 말이 어려운데 한 마디로 정의하면 컴파일된 클래스파일의 자바 바이트 코드의 모든 데이터가 올라가는 영역이다. 포함되는 범위는 전역 변수, 메서드 정보, static 자료형이 붙은 필드와 메소드 등등 이며 모든 쓰레드가 공유하는 Method area에 올라가기 때문에 어느 쓰레드에서든지 클래스 내의 메소드 혹은 전역 변수 값에 접근할 수 있는 것이다.



5) Runtime Constant Pool


- 클래스 파일 포맷에서 constant_pool 테이블에 해당하는 영역이다. 메서드 영역에 포함되는 영역이긴 하지만, JVM 동작에서 가장 핵심적인 역할을 수행하는 곳이기 때문에 JVM 명세에서도 따로 중요하게 기술한다. 클래스와 인터페이스의 상수뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블이다. , 어떤 메서드나 필드를 참조할 JVM 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조한다.



6) Heap

인스턴스 또는 객체를 저장하는 공간으로 가비지 컬렉션 대상이다. JVM 성능 등의 이슈에서 가장 많이 언급되는 공간이다. 구성 방식이나 가비지 컬렉션 방법 등은 JVM 벤더의 재량이다.






Class Loader


- 위에서도 언급했지만, 자바는 컴파일 시점이 아닌 런타임 시점에 클래스를 처음으로 참조하며 해당 클래스를 로드하고 링크하는 동적 로드의 특성을 갖고 있다. 이 동적로드를 담당하는 부분이 JVM의 클래스 로더이다.


- 클래스로더의 특징은 다음과 같다.



1) 계층 구조: 클래스 로더끼리 부모-자식 관계를 이루어 계층 구조로 생성된다. 최상위 클래스 로더는 부트스트랩 클래스 로더(Bootstrap Class Loader)이다.

2) 위임 모델: 계층 구조를 바탕으로 클래스 로더끼리 로드를 위임하는 구조로 동작한다. 클래스를 로드할 때 먼저 상위 클래스 로더를 확인하여 상위 클래스 로더에 있다면 해당 클래스를 사용하고, 없다면 로드를 요청받은 클래스 로더가 클래스를 로드한다.

3) 가시성(visibility) 제한: 하위 클래스 로더는 상위 클래스 로더의 클래스를 찾을 수 있지만, 상위 클래스 로더는 하위 클래스 로더의 클래스를 찾을 수 없다.

4) 언로드 불가: 클래스 로더는 클래스를 로드할 수는 있지만 언로드할 수는 없다. 언로드 대신, 현재 클래스 로더를 삭제하고 아예 새로운 클래스 로더를 생성하는 방법을 사용할 수 있다.





Class Loader 우선순위


Bootstrap > Extention > System







클래스로더가 로드를 요청받으면, 클래스 로더 캐시 상위 클래스 로더 자기 자신 순서로 클래스가 있는지 확인한다

부트 스트랩 클래스로더까지 확인해도 없으면 요청받은 클래스 로더가 파일 시스템에서 해당 클래스를 찾는다.


1) BootStrap ClassLoader

- JVM이 실행될때 가장 먼저 실행되는 클래스 로더로, 자바 실행에 필요한 기본적은 클래스를 로딩한다.

- 다른 클래스로더와 달리 네이티브 코드로 구현되어 있다.


2) Extention ClassLoader

- 추가로 로딩되는 클래스로더로 별도로 클래스패스에 설정되어 있지 않아도 로딩된다.

- 다양한 보안 확장 기능을 여기서 로드한다.


3) System ClassLoader

- ClassPath에 정의 되어 있거나 JVM옵션에서 -cp, -classpath에 지정된 클래스들이 로딩된다.

- 시스템 클래스로더는 애플리케이션의 클래스들을 로드한다고 할 수 있다.

- 즉 사용자가 지정한 $CLASSPATH내의 클래스가 로드된다.



4) User-Defined Class Loader

- 애플리케이션 사용자가 직접 코드 상에서 사용하는 클래스 로더이다.

- 일반적으로 Jar 혹은 WebApp의 경우 War로 압축된 ClassPath의 Binary Code를 사용자 정의 클래스 로더가 로드한다.





클래스 로더가 로드하지 않은 클래스를 찾으면 다음과 같은 과정을 통해 로드하고 링크하며 초기화한다.






1) 로드: 찾은 클래스 파일을 Runtime Data Area에 로드한다.

2) 검증: 읽어 들인 클래스가 제대로 구성되어 있는지 검사한다. 클래스 로드 전 과정 중 가장 시간이 오래 걸리며 까다로운 검증을 거친다.

3) 준비: 클래스가 필요로 하는 메모리를 할당하고, 클래스에 정의된 필드, 메소드, 인터페이스들을 나타내는 데이터 구조를 준비한다.

4) 분석: 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다. ( 심볼릭 레퍼런스는 메모리 번지의 참조를 의미하는 것이 아니라 이름에 의한 참조를 의미)

5) 초기화: 클래스 변수들을 적절한 값으로 초기화한다.













참고 : 

- https://d2.naver.com/helloworld/1230

- https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94_%EA%B0%80%EC%83%81_%EB%A8%B8%EC%8B%A0

- https://docs.oracle.com/javase/8/docs/technotes/guides/vm/index.html

http://yoyojyv.tistory.com/48










'JVM Optimization &Tuning' 카테고리의 다른 글

Minor GC , Major GC, Full GC  (0) 2019.12.26
GC 튜닝이 불필요한 상황  (0) 2019.12.26
JVM Heap 영역  (0) 2019.02.27

+ Recent posts