공부기록
JVM 본문
JVM?
Java Virtual Machine (이하 JVM)은 추상적인 컴퓨팅 머신이다. JVM은 Java 컴파일러에 의해 생성된 바이트코드를 기계어로 바꾸어주는 역할을 한다. JVM은 java로 작성된 프로그램에게 그들을 실행시키기 위한 프로그램으로 보여지게 된다. 때문에, java 프로그램들은 동일한 인터페이스와 라이브러리를 가지고 작성될 수 있게 된다. JVM은 특정한 OS에 호환되도록 만들어져서, Java 프로그램을 해당 OS에서 돌아가도록 만든다. 이러한 식으로 Java 프로그램은 플랫폼 독립성을 띄게 된다. 다만 JVM 자체는 플랫폼 종속적인 특성을 띈다.
자바 실행 과정
작성된 자바 코드는 Java Compiler 에 의해 바이트코드로 변환된다.
Class Loader는 이 변환된 바이트코드를 JVM 내부로 반입하여 class를 로드하고 link작업을 통해 배치하는 등 일련의 작업을 한다. 런타임시에는 class를 Runtime Data Area로 load한다.
Execution Engine은 Class Loader를 통해 JVM 내부로 들어와 Runtime Data Area (JVM 메모리) 에 배치된 바이트코드들을 명령어 단위로 실행시킨다.
JVM 메모리 구조 (Runtime Data Area)
Runtime Data Area는 JVM의 메모리로 Java 어플리케이션이 실행하면서 할당받은 메모리영역이다.
각 메모리 영역에 대한 세부 내용은 아래의 표와 같다.
영역 | 용도 | 사용기간 | 스레드공유 |
---|---|---|---|
Method (Static) | JVM에서 읽어들인 클래스와 인터페이스에 대한 런타임 상수 풀, 메서드와 필드, Static 변수, 메서드 바이트 코드 등을 보관 | JVM 시작시 생성, 프로그램 종료시까지 명시적으로 null 선언시 GC 대상 구성방식이나 GC 방법은 JVM 벤더마다 약간 다를 수 있다. |
모든 스레드에서 공유 |
Runtime Constant Pool | Method Area 영역에 포함되지만 독자적 중요성을 띈다. 클래스 파일 constant_pool 테이블에 해당하는 영역 클래스와 인터페이스 상수, 메서드와 필드에 대한 모든 레퍼런스 저장 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주조를 찾아 참조 |
||
Heap Area | 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 메모리 영역 New 연산자를 통해 생성한 객체, 또는 인스턴스와 배열을 저장 JVM이 관리 |
||
Stack Area | 선입후출(FILO) 구조 메서드 호출 시 생성되는 스레드 수행정보를 기록하는 Frame 저장 메서드 정보, 지역변수, 매개변수, 연산 중 발생하는 임시 데이터 저장 |
scope나 메서드가 끝날 때 | 각 스레드 |
PC 레지스터 | 현재 실행 중인 JVM 주소를 가지고 있다. CPU 명령어 즉 Instruction을 수행한다. CPU Instruction 수행하는 동안 필요한 정보를 CPU 내 기억장치인 레지스터에 저장 연산 및 결과값을 메모리에 전달하기 전 CPU내 기억장치임 |
||
Native Method Stack Area | 다른 언어로 작성된 네이티브 코드를 위한 메모리 C/C++ 등의 코드를 수행하기 위한 스택 Native 메서드의 매개변수, 지역변수 등을 바이트코드로 저장 |
native interface 호출 시 생성 native interface 종료 시 생성 |
Garbage Collection
JVM의 garbage collector는 gc를 수행하여 메모리를 관리한다. 아래는 메모리관리의 상세한 내용이다.
Automatic Garbage Collection
automatic garbage collection은 heap memory를 참조하여, 어떤 객체가 사용되는지 안되는지를 식별하고 사용이 되지 않는 객체를 삭제하는 것이다. 사용중인 객체, referenced object는 프로그램이 여전히 그 객체의 포인터를 유지하는 것을 말하낟. 사용중이지 않은 객체, unreferenced object는 당신의 프로그램의 어떤 부분에서도 참조당하지 않는 객체를 말한다. 그러므로 unreferenced object 때문에 할당된 메모리는 다시 수거된다.
자바에서, 메모리를 관리하는 것은 garbage collector에 의해 자동으로 수행된다. 기본적인 절차는 아래와 같다.
Step 1 : Marking
gc는 referenced object의 메모리 조각과 unreferenced object의 메모리 조각을 식별한다. 이것은 시스템의 모든 객체가 검사되어야 한다면 많은 시간비용이 드는 작업이 될 수 있다.
Step 2 : Normal Deletion
unreferenced object들을 제거하고 빈 공간을 포인터로 참조한다.
메모리 할당자는 새로운 객체가 할당될 때를 위해 이 빈 공간을 위한 참조를 가지고 있어야 한다.
Step 2A : Deletion with Compacting
Normal Deletion보다 나은 성능을 가지기 위해, 객체를 삭제한 뒤 살아남은 객체들을 메모리의 한 쪽 공간으로 몰 수 있다 (memory compaction). 이것은 새로운 객체의 할당을 더 빠르고 쉽게 만든다. 메모리 할당자는 빈 공간의 맨 앞의 참조에 대한 정보를 가지고, 순차적으로 메모리를 할당한다.
JVM Generations
모든 객체를 mark 하고 compact하는 것은 비효율이다. 객체가 많을 수록 더 많은 시간비용이 들기 때문이다.
분석에 따르면 대부분의 객체는 빨리 죽기 때문에 빨리 죽는 객체집합과 늦게 죽는 객체 집합을 나누어 관리할 수 있다. 즉 살아남은 시간에 따라 세대를 나누어 객체집합을 관리하면 JVM의 성능을 향상 시킬 수 있다.
이 때문에 JVM의 heap 은 Young Generation, Old Generation, Permanent Generation과 같은 각 세대의 영역들로 쪼개지게 된다.
Young Generation은 모든 새로운 객체가 항당되고 나이를 먹는 곳이다. young generation이 다 차면 minor garbage collection이 일어난다. minor collection은 높은 객체 사망률을 가정하여 최적화 될 수 있다. 죽은 객체들로 찬 young generation은 매우 빠르게 회수된다. 살아남은 몇 개의 객체들은 나이를 먹고 결국 old generation으로 이동하게 된다.
Stop the World Event - 모든 minor collection은 "Stop the World" 이벤트이다. 이것은 어플리케이션의 모든 스레드가 이 작업이 끝날 때 까지 중지되는 것을 말한다. minor garbage collection은 항상 stop the world 이벤트이다.
Old Generation은 오래 살아남은 객체를 저장하기 위해 사용된다. 보통, 어린 객체에게 임계값이 설정이 되고 먹은 나이가 이 임계값에 도달하면 그 객체는 old generation으로 이동한다. 결국 old generation도 메모리 회수를 하게 되는데, 이 이벤트를 major garbage collection 이라고 한다.
Major Garbage Collection도 Stop the World 이벤트이다. major collection은 보통 훨씬 더 느린데 이 이벤트는 모든 살아있는 객체에 대한 것이기 때문이다. 그래서 반응형 어플리케이션의 경우, 이 major collection은 최소화 되어야 한다. Stop The World 이벤트의 길이는 이 영역을 위해 사용되는 garbage collector에 의해 영향을 받는다.
Permanent Generation 은 어플리케이션의 메소드와 클래스를 표현하기 위해 JVM이 필요로 하는 메타데이터들을 포함하고 있다. permanent generation은 JVM이 어플리케이션에 의해 사용되는 클래스에 기반하여 생성한다. 추가적으로 Java SE library 클래스들과 메서드들은 여기에 저장될 수 있다.
만약 JVM이 어떤 클래스가 더 이상 필요가 없고, 다른 클래스를 위한 저장공간이 필요하다고 판단하면 클래스들은 회수될 수 있다 (unloaded). permanent generation은 full garbage collection에 포함되어 있다.
Java Garbage Collections
heap의 각 공간들이 어떻게 상호작용하는지 살펴보자. 이하는 JVM에서 객체들이 할당되고 나이먹는 과정을 설명한 것이다.
- 먼저, 모든 객체들은 eden space에 할당된다.
- eden space가 꽉 차면, minor gc가 발생한다.
- referenced object들은 first survivor space로 이동한다. unreferenced object들은 eden space가 비워지면서 제거된다.
- 다음 minor gc에서 똑같은 일이 eden space에서 벌어진다. 하지만 이 경우에서 referenced object들은 second survivor space(S1)로 이동한다. 그리고, 지난 minor gc에서 first survivor space (S0) 에 있던 객체들은 나이를 먹고 S1으로 이동한다. 모든 객체들이 S1으로 이동한 이후, S0와 eden은 비워진다. 이제 survivor space에는 서로 다른 나이를 가진 객체가 있게된다.
- 다음 minor gc에서, 똑같은 일이 벌어진다. 다만 survivor space가 바뀐다. S1에서 S0로 이동하게 되고 S1과 eden은 비워진다.
- minor gc가 끝나고, 객체의 나이가 임계값에 도달하면 (8) 그들은 young generation에서 old generation으로 이동한다.
- minor gc가 계속 되면서 old generation의 공간이 차오른다.
- old generation 영역이 차오르고, major gc가 실행된다.
출처
https://limkydev.tistory.com/51
https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
'Programming > JAVA' 카테고리의 다른 글
ITEM 2 : 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2021.12.03 |
---|---|
item1. 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2021.12.01 |
쓰레드(2) (0) | 2021.10.25 |
쓰레드(1) (0) | 2021.10.25 |