on
JVM(Java Virtual Machine)에 대하여
JVM(Java Virtual Machine)에 대하여
1. JVM(Java Virtual Machine)이란?
자바 바이트 코드를 실행할 수 있는 주체
자바와 OS 사이에서 중개자 역할을 수행하며 자바 프로그램을 운영체제에 구애받지 않고 실행 할 수 있게 도와준다.
할 수 있게 도와준다. GC(Garbage Collector)를 통해 메모리 관리 를 자동으로 수행한다.
를 자동으로 수행한다. 스택 기반의 가상머신
자바 프로그램 실행 단계
개발자가 작성한 자바 소스 코드를 자바 컴파일러(javac)가 읽어들여 바이트 코드로(.class) 변환한다.
JVM은 이러한 바이트 코드를 읽어서 일련의 과정을 거쳐 해당 운영체제가 이해할 수 있는 기계어로 바꿔주는 역할을 한다.
2. JVM 구조
JVM 내부는 크게 4가지로 나뉜다.
1) Class Loader
JVM 내로 클래스 파일을 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다.
쉽게 풀어서 설명하면 Person.class와 같은 클래스 파일들을 엮어서 JVM이 운영체제로부터 할당 받은 메모리 영역(Runtime Data Area)에 적재하는 역할을 수행한다. 이때 런타임에 동적으로 클래스를 로드하게 된다.
2) Execution Engine
앞선 Class Loader에 의해 메모리에 적재된 클래스(바이트 코드)들을 기계어로 변경해 명렁어 단위로 읽어서 실행한다.
이때 명령어를 실행하는 방식은 2가지이다.
Interpreter 방식 바이트 코드를 명령어 단위로 읽어서 한 줄씩 실행한다. 인터프리터 언어의 단점을 그대로 가지고 있다. (속도가 느리다)
JIT(Just-In-Time) 방식 Interpreter 방식의 단점을 보완하기 위해서 도입되었다. 인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일해서 네이티브 코드로 변경하여 실행한다. 이때 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 빠르게 실행된다. JVM은 내부적으로 해당 메서드가 얼마나 실행되는지 체크하고 일정 수준을 넘어설 때 JIT 컴파일을 수행한다.
3) Garbage Collector
Heap 메모리 영역에 생성된 객체 중 참조되지 않는 객체들을 탐색 후 제거하는 역할을 한다.
이때 GC가 언제 동작하는지는 정확히 알 수 없다.
참고) GC 동작 방식
https://d2.naver.com/helloworld/1329
https://javabom.tistory.com/7?category=835783
4) Runtime Data Area
프로그램을 수행하기 위해 OS에서 할당 받은 JVM의 메모리 영역이다.
Runtime Data Area는 크게 5가지로 나뉜다.
Method Area 모든 쓰레드가 공유 하는 메모리 영역이다. 클래스, 인터페이스, 메서드, 필드, static 변수와 같은 바이트 코드를 보관한다.
Heap 모든 쓰레드가 공유 하는 메모리 영역이다. new 키워드를 통해 생성된 객체와 배열을 보관한다. 이때 생성되는 객체는 Method Area에 로드된 클래스여야 한다.
PC register 쓰레드가 시작될 때 생성되며, 쓰레드마다 하나씩 존재 한다. 쓰레드가 어디 부분을 어떤 명령으로 실행해야할지에 대한 기록을 하며, 현재 수행 중인 JVM 명령의 주소를 가진다.
Stack Area 메서드 호출 시마다 각각의 스택 프레임 이 생성된다. (해당 메서드만을 위한 공간) 지역 변수, 파라미터, 리턴 값, 연산에 사용되는 임시 값 등을 보관하는 영역이다. 해당 메서드 수행이 끝나게 되면 스택 프레임 단위로 삭제한다.
Native method stack 자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역이다.
3. 자바가 컴파일 되는 과정
1) 자바 소스파일(.java)을 작성한다.
2) 자바 컴파일러(javac)를 통해 해당 소스를 컴파일한다.
이때 .class 파일이 생성되며, 바이트코드의 각 명령어는 Opcode와 추가 피연산자로 이루어진다.
3) 컴파일된 바이트코드를 JVM의 클래스 로더에게 전달한다.
자바는 컴파일타임이 아니라 런타임에 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 특징이 있다. 이러한 동적 로드를 담당하는 부분이 JVM의 클래스 로더이다. 클래스 로더는 아래 그림과 같은 과정을 거쳐 클래스를 로드하고 링크하고 초기화한다.
클래스 로드 단계
Loading : 클래스 파일을 읽어 JVM 메모리에 로드한다. (힙영역, Metaspace) 이때 로드된 클래스와 부모 클래스의 FQN(Fully Qualified Name), 클래스/인터페이스/Enum 관련 여부, 변수/메서드 정보를 올리게 된다. 클래스 파일을 로드한 후 JVM은 힙 메모리에 해당 클래스 객체를 생성한다. Java.lang.Object.getClass() 메서드를 통해 해당 객체 참조를 얻을 수 있다.
: 클래스 파일을 읽어 JVM 메모리에 로드한다. 이때 로드된 클래스와 부모 클래스의 FQN(Fully Qualified Name), 클래스/인터페이스/Enum 관련 여부, 변수/메서드 정보를 올리게 된다. 클래스 파일을 로드한 후 JVM은 힙 메모리에 해당 클래스 객체를 생성한다. 메서드를 통해 해당 객체 참조를 얻을 수 있다. Verifying : 읽어 들인 클래스가 자바 언어 명세/JVM 명세에 부합하는지 검사한다. 만약 검증이 실패하면 런타임 에러가 발생한다. (java.lang.VerifyError) 클래스 로드의 전 과정 중에서 가장 많은 시간이 소요된다.
: 읽어 들인 클래스가 자바 언어 명세/JVM 명세에 부합하는지 검사한다. 만약 검증이 실패하면 런타임 에러가 발생한다. 클래스 로드의 전 과정 중에서 가장 많은 시간이 소요된다. Preparing : 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드/메서드/인터페이스를 나타내는 메모리 구조를 준비한다. (해당 메모리를 기본값으로 초기화한다는 의미로 받아들였다)
: 클래스가 필요로 하는 메모리를 할당하고, 클래스에서 정의된 필드/메서드/인터페이스를 나타내는 메모리 구조를 준비한다. (해당 메모리를 기본값으로 초기화한다는 의미로 받아들였다) Resolving : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스 로 변경한다. 이때 메서드 영역을 탐색한다.
: Initializing: 클래스 변수들을 적절한 값으로 초기화한다. (static 필드들을 설정한 값으로 초기화)
자바 1.8 이후 JVM 내의 PermGen 메모리 영역이 사라지고, Metaspace 메모리 영역이 생겼다.
4) 3번 과정을 통해 JVM 내 런타임 데이터 영역에 배치된 바이트코드가 Execution Engine에 의해 실행된다.
실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 기계어로 변환하여 실행한다. 앞서 설명한 것처럼 바이트코드는 Opcode(1바이트)와 추가 피연산자로 이루어져 있다. 따라서 실행 엔진은 하나의 Opcode를 가져와서 피연산자와 함께 작업을 수행한 다음, 다음 OpCode를 수행하는 식으로 동작한다.
실행 엔진이 동작하는 방식에 대한 자세한 설명은 위에 기술해놨으니 생략하도록 하겠다. (Interpreter, JIT 컴파일러)
from http://zundi.tistory.com/33 by ccl(A) rewrite - 2021-11-03 19:27:34