[Java]Java JVM 동작 과정



JVM 의 동작 과정에 대해서 알아보자

JVM 동작 과정

Java 라는 언어를 배우면서 Java 가 어떤 방식으로 실행되는지 알아봐야 겠다고 생각했다.

JVM 의 탄생 배경

Java 의 아버지라고 불리우는 제임스 고슬링 (소녀시대팬) 은 가전제품 내에 탑재하는 프로그램을 C/C++로 개발하던 사람이었다. 가전제품 특성상 매우 다양한 하드웨어를 가지고 있고 각각의 하드웨어에 맞는 프로그램을 개발하는 것이 번거로워서 Java를 개발하게 되었다고 한다.

이러한 배경 덕분에 Java의 큰 특징 중 하나가 플랫폼에 독립적이다는 점이다. 플랫폼에 독립적이다는 특징 덕분에 많은 사람들의 인기를 끌게 되었고 요즘도 가장 많이 사용하는 언어 중 하나이다.

플랫폼에 독립적이기 위해서 JVM 을 이용한다. 자바 가상 머신이라고도 하는데 자바 바이트코드를 실행할 수 있는 주체이다.

image

Java 프로그램 동작 과정

  1. 프로그램이 실행되면 JVM은 OS로 부터 이 프로그램이 필요로 하는 메모리를 할당 받는다.
  2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 읽어들여 자바 바이트코드(.class)로 컴파일 한다.
  3. Class Loader가 동적 로딩(Dynamic Loading)을 통해 class 파일들을 로딩 및 링크 하여 런타임 데이터 영역(Runtime Data Area), JVM의 메모리에 올린다.
  4. Execution Engine이 메모리에 올라간 class 들을 기계어로 변경해 명령어 단위로 실행한다.
  5. 실행과정 속에서 JVM은 상황에 따라 Thread Synchronization과 GC같은 관리작업을 수행한다.

image

JVM 구성

Class Loader (클래스 로터)

클래스 로더는 실행을 위해 자바 바이트 코드를 로드하고 검증한다. 검증 한 다음 자바 바이트 코드를 위해 메모리를 할당한다. 즉 RunTime 시점에 자바 바이트 코드들을 엮어서 RunTime Data Area로 적재하는 역활을 한다.

RunTime Data Areas

RunTime data areas 영역은 JVM 메모리를 나타낸다. 각각의 영역은 실행중에 다른 용도로 사용된다.

  • PC register : 현재 실행중인 명령어의 주소를 가지고 있는 곳
  • Stack area : 메소드 호출과 로컬 변수가 저장되는 메모리
  • Native method stack : Java 외의 언어로 작성된 네이티브 코드들을위한 Stack
  • Heap : 프로그램 상에서 런타임시 동적으로 할당하여 사용하는 영역
  • Method area : 클래스 이름, 부모클래스 이름, 메소드 정보 및 모든 정적 변수 등의 바이트 코드를 저장하는 곳

모든 스레드는 고유한 PC register, Stack , Native method stack 를 가지고 있지만 모든 스레드는 Heap, Method area 는 공유한다.

Execution Engine

Class Loader에 의해 메모리에 로드된 자바 바이트 코드들을 기계어로 변경해 명령어 단위로 실행하는 역할을 한다. 엔진 내부에는 Interpreter, JIT compiler, Garbage collector 가 있다. 이 중에서 기계어로 변경해주는 엔진은 Interpreter, JIT compiler 2 가지가 존재한다.

  1. Interpreter

    인터프리터 실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행한다. 다른 인터프리터 언어와 동일하게 작동한다. 그래서 이 방식은 한 줄 씩 수행하기 떄문에 느리다.

  2. JIT compiler (Just In Time compiler)

    적절한 바이트 코드를(자주 사용되는, 혹은 한번 사용 이후) 기계어로 변경해서 엔진이 기계어로 컴파일된 코드를 실행하도록 도와준다. 기계어로 다시 컴파일할 필요가 없음으로 속도가 매우 빠르다고 할 수 있다.

최초 JVM이 나왔을 당시 Interpreter방식이였기 때문에 속도가 느리다는 단점이 두각되었다. 하지만 JIT compiler 를 통해 속도에 대한 단점을 보완했다.. 그렇다고 해서 모든 코드를 JIT 컴파일러가 실행하는 것은 오버헤드를 야기 할 수 있다. JIT가 자바 바이트 코드를 기계어로 변환하는데 있어서 드는 비용이 매우 크기 때문이다. 그래서 처음엔 Interpreter 방식을 사용하다 일정 기준이 넘어가면 JIT compiler 로 미리 컴파일을 한다고 한다. 이러한 특징 때문에 가끔 자바 프로그램을 개발하다보면 같은 로직인데 최초 실행시에 느리게 작동하는 것을 느낄 수 있다.

  • Garbage collector

    GC는 Heap 메모리 영역에 생성된 객체들 중 참조되지 않는 객체들을 탐색 후 제거하는 역할을 한다. 매우 편리한 기능이지만 GC 만 믿고 작업한다면 성능저하가 올 수 있다. GC에 대해 기술하려면 양이 매우 많기 때문에 나중에 따로 Heap 영역과 함께 정리하도록 하려 한다.






© 2019. by ryulth

Powered by ryulth