Notice
Recent Posts
Recent Comments
Link
나의 GitHub Contribution 그래프
Loading data ...
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Code in Life

JVM의 구조와 Java의 실행과정, Garbage Collection 동작원리 본문

Java

JVM의 구조와 Java의 실행과정, Garbage Collection 동작원리

퓨끼 2020. 12. 23. 22:26

구조

  • 스택기반의 가상머신 (ARM 아키텍처같은 하드웨어는 레지스터 기반으로 동작)

역할

  • Java와 OS사이에서 중개자역할을 수행하며 Java가 OS에 구애받지 않고 재사용 가능하도록 해줌
  • 메모리관리, Garbage Collection을 수행한다.

알아야하는 이유

  • 동일한 프로그램이라도 메모리 관리에 따라 성능이 좌우된다. 메모리가 관리되지 않는다면 속도저하 현상이나 튕김 현상이 발생할 수 있다.

Java Application 실행과정

  1. 프로그램이 실행되면 JVM은 OS로부터 프로그램이 필요한 만큼 메모리를 할당받는다. 이때, 용도에 따라 여러 영역으로 나누어 관리한다.
  2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 읽어들여 자바 바이트코드(.class)로 변환시킨다.
  3. Class Loader를 통해 class파일들을 JVM으로 로딩한다.
  4. 로딩된 Class파일들은 Execution engine을 통해 해석된다.
  5. 해석된 바이트코드는 Runtime Data Areas에 배치되어 실질적인 수행을 한다. 이때 JVM은 필요에 따라 Thread Synchronization과 GC같은 관리작업을 수행한다.

 

JVM 구성

  • Class Loader : Java는 컴파일 타임이 아니라 런타임에 클래스를 처음 참조할 때 해당 클래스를 로드하고 링크하는데, 이를 수행한다.
  • Execution Engine : 클래스를 실행시키며, 2가지 방식이 존재한다.
    • Interpreter : 자바 바이트 코드를 명령어 단위로 읽어서 실행한다. 한 줄 씩 수행하기 때문에 느리다라는 단점을 가지고 있다.
    • JIT(Just - In - Time) : 인터프리터 방식의 단점을 보완하기 위해 도입된 JIT 컴파일러이다. 적절한 시점에 바이트코드 전체를 컴파일하여 네이티브 코드로 변경하고 캐시에 보관하여 필요할 때 꺼내서 사용할 수 있어 빠르게 수행을 실행하는 방식이다. 물론 한 번만 실행되는 코드라면 인터프리팅이 유리하기 때문에 내부적으로 해당 메서드가 얼마나 자주 수행되는지 정도를 체크하고 일정정도를 넘을 때에만 컴파일을 수행한다.
  • Garbage collector : GC를 수행
  • Runtime Data Area : 프로그램을 수행하기 위해서 OS로부터 할당받은 메모리 공간

1) PC Register : Thread가 시작될 때, 각각의 Thread 별로 생성되며 현재 수행중인 JVM 명령어 주소를 가리킨다.

2) JVM Stack : 매개변수, RET, 지역변수, 메서드등을 저장한다.

3) Native Method Stack : 바이트코드를 번역한 기계어들의 저장소. 이 부분을 통해 커널에 접근할 수 있다.

4) Method Area(=Class Area = Static Area) : 클래스 정보를 메모리 공간에 올릴 때 초기화 대상을 저장하기 위한 메모리 공간. 클래스, 인터페이스, 메소드, 필드, Static변수 등의 바이트 코드를 보관하며, 모든 쓰레드가 공유하는 메모리 영역이다. 이 곳에는 Runtime Constant Pool이라는 별도의 관리 영역이 존재하며, 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 수행한다. GC의 관리 대상이다.

5 ) Heap : 객체와 배열를 생성하여 저장하는 가상 메모리 공간이며, Java 7부터 String Contant Pool이 Method Area에서 Heap영역으로 변경되었다. 마찬가지로 GC의 관리 대상이다. Heap은 크게 세 가지 영역으로 나누어진다.

객체들이 최초로 생성되면 Eden영역에 위치하며, 이 영역에 인스턴스가 가득차게 되면 첫 번째 GC가 발생한다. (minor GC) 그러면 참조가 남아있는 객체를 mark하고 Survivor 영역으로 복사하고 Eden 영역을 비운다. Survivor 영역도 가득차면 같은 방식으로 다른 Survivor 영역에 복사하고 비운다. 이를 반복하다가 살아남으면 Old 영역으로 이동된다. Old 영역에서는 반대로 삭제되어야 하는 객체를 찾아 한꺼번에 삭제한다. (Major GC) 이때 GC를 실행하는 스레드를 제외한 나머지 스레드는 모두 작업을 중단하며 이를 'stop-the-world'라고 하는데, 이것이 의도치 않은 장애의 원인이 될 수 있다. 따라서 이를 위해 힙 영역을 조정하는 것을 GC 튜닝이라고 하며, 이때 JVM의 메모리는 마음대로 조정해선 안된다.

그럼 Garbage Collection은 어떤 원리로 소멸시킬 대상을 찾을까?

reachability라는 개념을 사용하는데, 어떤 객체에 유효한 참조가 있으면 reachable, 없으면 unreachable로 구별하며 unreachable 객체를 가비지로 간주해 GC를 수행한다.

힙 영역에 할당된 객체는 또 다른 객체를 참조할 수 있기 때문에 참조 사슬이 형성되는데, 공통적으로 최초의 참조인 root set을 찾아 unreachable객체와 weakly reachable객체(=참조 트리의 말단부분에 존재하는 객체)를 가비지 객체로 간주하여 메모리에서 회수한다.

 

출처 : asfirstalways.tistory.com/158

참고 

d2.naver.com/helloworld/329631

'Java' 카테고리의 다른 글

객체지향과 SOLID(5대 원칙)  (0) 2020.12.24
Comments