공부방
JVM(Java Virtual Machine) 본문
JVM이란?
- JVM은 Java 프로그램을 실행해주는 가상의 컴퓨터
- 컴퓨터처럼 생긴 소프트웨어라고 생각하면 된다.
- 실제 컴퓨터(CPU, OS) 위에 자바 전용 미니 컴퓨터가 하나 더 올라간다고 보면 된다.
JVM이 필요한 이유?
- 컴퓨터를 쓸 때 각각 다른 운영체제(Windows, Mac, Linux, 모바일 등)를 사용한다.
- 그런데 운영체제마다 작동 방식이 달라서, 원래는 그에 맞게 코드를 따로 만들어야 한다.
- Java는 한 번만 만들면, 어디서든 실행되는 게 목표이고, 이걸 가능하게 해주는 게 바로 JVM이다.
예시 및 요약 흐름도
.java (원시 자바 코드)
↓ 컴파일 (javac)
.class (Bytecode)
↓ 실행 (java 명령어)
JVM
↓
운영체제(OS)
↓
CPU 실행
- MyProgram.java 라는 자바 코드를 작성했다고 가정
- 이 코드는 그냥 텍스트이다. CPU나 OS는 이걸 이해하지 못한다.
- 그래서 javac 라는 명령어(Java Compiler)를 써서 .java 파일을 컴파일 해야 한다.
- 여기서 Java compiler는 JDK(Java Development Kit)를 설치하면 bin에 존재하는 javac.exe를 말한다. (즉, JDK에 Java compiler가 포함되어 있다는 소리)
- javac 명령어를 통해 .java를 .class로 컴파일 할 수 있다.
자바 컴파일 과정
단계 | 설명 | 결과 |
1단계 | MyProgram.java 파일 작성 | 텍스트 |
2단계 | javac Program.java 실행 | 컴파일됨 |
3단계 | Program.class 생성 | Bytecode가 담긴 파일 생성 |
Bytecode란?
- 가상 컴퓨터(VM)에서 돌아가는 실행 프로그램을 위한 이진 표현법
- 자바 바이트 코드(Java bytecode)는 JVM이 이해할 수 있는 언어로 변환된 자바 소스코드를 의미한다.
- 자바 컴파일러에 의해 변환된 코드의 명령어 크기가 1바이트라서 자바 바이트 코드라고 불리고 있다.
- 바이트 코드는 다시 실시간 변역기 또는 JIT 컴파일러(Just-in-time Compliation)에 의해 바이너리 코드로 변환된다.
- 바이너리 코드 : 바이너리 코드 또는 이진 코드라고 한다.
컴퓨터가 인식할 수 있는 0과 1로 구성된 이진 코드
- 바이너리 코드 : 바이너리 코드 또는 이진 코드라고 한다.
- 즉, CPU가 이해하는 언어는 바이너리 코드, 가상 머신이 이해하는 코드는 바이트 코드이다.
- 자바는 CPU가 바로 이해할 수 있는 기계어(0과 1)로 컴파일되는 게 아니다.
- 기계어 : 0과 1로 이루어진 바이너리 코드
- 기계어가 이진코드로 이루어졌을 뿐 모든 이진코드가 기계어인 것은 아니다.
- 기계어는 특정한 언어가 아니라 CPU가 이해하는 명령어 집합이며, CPU 제조사마다 기계어가 다를 수 있다.
- 기계어 : 0과 1로 이루어진 바이너리 코드
- JVM이 이해할 수 있는 중간단계 코드 = Bytecode로 컴파일된다.
- 이게 .class 파일이다.
JIT 컴파일러
- 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일러
- 인터프리터 방식의 단점을 보완하기 위해 도입되었다.
- 인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하여 기계어로 변경하고, 이후에는 해당 더 이상 인터프리팅 하지 않고 기계어로 직접 실행하는 방식
- 기계어(컴파일된 코드)는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 빠르게 수행하게 된다.
- 물론 JIT 컴파일러가 컴파일하는 과정은 바이트 코드를 인터프리팅하는 것보다 훨씬 오래 걸리므로 한 번만 실행되는 코드라면 컴파일 하지 않고 인터프리팅하는 것이 유리하다.
- 따라서 JIT 컴파일러를 사용하는 JVM들은 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고 일정 정도를 넣을 때에만 컴파일을 수행한다.
- 자바에선 자바 컴파일러가 자바 프로그램 코드를 바이트 코드로 변환한 다음, 실제 바이트 코드를 실행하는 시점에서 자바 가상 머신(JVM, 정확히는 JRE)이 바이트 코드를 JIT 컴파일을 통해 기계어로 변환한다.
JDK vs JRE?
- JDK
- Java를 사용하기 위해 필요한 모든 기능을 갖춘 Java용 SDK(Software Development Kit)
- JDK는 JRE를 포함하고 있다.
- JRE에 있는 모든 것 뿐만 아니라 컴파일러(javac)와 jdb, javadoc과 같은 도구도 있다.
- 즉, JDK는 프로그램을 생성, 실행, 컴파일할 수 있다.
- JRE
- Java Runtime Environment(자바 런타임 환경)
- JVM + 자바 클래스 라이브러리(Java Class Library)등으로 구성되어 있다.
- 컴파일된 Java 프로그램을 실행하는데 필요한 패키지이다.
JVM의 역할
이제 Program.class를 실행하려면?
java Program
- JDK 디렉토리의 /bin 폴더에 존재하는 java.exe는 JVM을 구동시키기 위한 명령 프로그램(JRE->Java Runtime Environment)
- java 명령어로 JVM 실행
- .class 파일(bytecode)를 읽는다.
- 운영체제(Windows, Mac 등)가 이해할 수 있게 번역해준다.
- 번역한 걸 CPU에게 전달해서 실행되게 해준다.
즉, JVM은 자바가 말한 걸 OS와 CPU가 이해할 수 있게 대신 말해주는 역할.
JVM 구성요소
클래스 로더
- JVM 내로 클래스 파일(*.class)를 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈
- 런 타임시 동적으로 클래스를 로드하고 jar 파일 내 저장된 클래스들을 JVM 위에 탑재
- 즉, 클래스를 처음으로 참조할 때, 해당 클래스를 로드하고 링크하는 역할
실행 엔진
- 클래스를 실행시키는 역할
- 클래스 로더가 JVM 내의 런타임 데이터 영역에 바이트 코드를 배치시키고, 이것은 실행 엔진에 의해 실행된다.
- 자바 바이트 코드(*.class)는 기계가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태로 기술된 것
- 그래서 실행 엔진은 이와 같은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경한다.
- 인터프리터 : 실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행하지만 한 줄씩 수행하기 때문에 느리다는 단점이 있다.
- JIT : 인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하여 기계어로 변경하고, 이후에는 해당 더 이상 인터프리팅 하지 않고 기계어로 직접 실행하는 방식
가비지 콜렉터
- 더 이상 사용되지 않는 인스턴스를 찾아 메모리에서 삭제
Runtime Data Area
- 프로그램을 수행하기 위해 OS에서 할당받은 메모리 공간
- PC Register
- Thread가 시작될 때 생성되며 생성될 때마다 생성되는 공간으로, 스레드마다 하나씩 존재
- Thread가 어떤 부분을 어떤 명령으로 실행해야할 지에 대한 기록을 하는 부분으로 현재 수행 중인 JVM 명령의 주소를 갖는다.
- JVM 스택 영역
- 프로그램 실행 과정에서 임시로 할당되었다가 메소드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장하기 위한 영역
- 각종 형태의 변수나 임시 데이터, 스레드나 메소드의 정보를 저장한다.
- 메소드 호출 시마다 각각의 스택 프레임(그 메서드만을 위한 공간)이 생성된다. 메서드 수행이 끝나면 프레임 별로 삭제를 한다.
- 메소드 안에서 사용되는 값들을 저장한다. 또 호출된 메소드의 매개변수, 지역변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다.
- Native method stack
- 자바 프로그램이 컴파일되어 생성되는 바이트 코드가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역
- Java가 아닌 다른 언어로 작성된 코드를 위한 공간
- Java Native Interface를 통해 바이트 코드로 전환하여 저장하게 된다.
- 일반 프로그램처럼 커널이 스택을 잡아 독자적으로 프로그램을 실행시키는 영역
- Method Area(= Class Area = Static area) : 클래스 정보를 처음 메모리 공간에 올릴 때 초기화되는 대상을 저장하기 위한 메모리 공간
- Runtime Constant Pool : 스태틱 영역에 존재하는 별도의 관리 영역
상수 자료형을 저장하여 참조하고 중복을 막는 역할을 수행
- Runtime Constant Pool : 스태틱 영역에 존재하는 별도의 관리 영역
- PC Register
Head 영역
- 객체를 저장하는 가상 메모리 공간
- new 연산자로 생성되는 객체와 배열을 저장
- Class Area(Static Area)에 올라온 클래스들만 객체로 생성할 수 있다.
- 힙은 세 부분으로 나뉘어진다.(Permanent Generation, New/Young Generation, Old Generation)
- Permanent Generation
- 직역하면 영구적인 세대
- 생성된 객체들의 정보의 주소값이 저장된 공간.
- 클래스 로더에 의해 load되는 Class, Method 등에 대한 Meta 정보가 저장되는 영역이고 JVM에 의해 사용된다.
- Reflection을 사용하여 동적으로 클래스가 로딩되는 경우에 사용.
- Reflection : 객체를 통해 클래스의 정보를 분석해내는 프로그래밍 기법
구체적인 클래스 타입을 알지 못해도, 컴파일된 바이트 코드를 통해 역으로 클래스의 정보를 알아내어 사용할 수 있다는 뜻
- Reflection : 객체를 통해 클래스의 정보를 분석해내는 프로그래밍 기법
- New/Young 영역
- 이 곳의 인스턴스들은 추후 가비지 콜렉터에 의해 사라진다.
- 생명 주기가 짧은 "젊은 객체"를 GC 대상으로 하는 영역
- 여기서 일어나는 가비지 콜렉트를 Minor GC 라고 한다.
- Eden : 객체들이 최초로 생성되는 공간
- Survivor 0, 1 : Eden에서 참조되는 객체들이 저장되는 공간
- Eden 영역에 객체가 가득차게 되면 첫번째 가비지 콜렉터가 발생한다.
- Eden 영역에 있는 값등을 Survivor 1 영역에 복사하고 이 영역을 제외한 나머지 객체를 삭제한다.
- Old 영역
- 이 곳의 인스턴스들은 추후 가비지 콜렉터에 의해 사라진다.
- 생명 주기가 긴 "오래된 객체"를 GC 대상으로 하는 영역
- 여기서 일어나는 가비지 콜렉트를 Major GC 라고 한다.
Minor GC에 비해 속도가 느리다. - New / Young Area에서 일정시간 참조되고 있는 살아남은 객체들이 저장되는 공간
- Permanent Generation
정리
- 자바 코드(.class)를 CPU가 이해할 수 있게 번역해서 실행시켜주는 역할
- Java는 운영체제에 상관없이 실행
- Windows에서 만든 .java 파일을 Mac이나 Linux에서 그대로 실행 가능
- 컴퓨터에 JVM만 있으면 어디서든 실행 가능
'문법' 카테고리의 다른 글
쿠키 vs 세션 vs 토큰 (0) | 2025.03.27 |
---|---|
Java의 예외 처리 (0) | 2025.03.24 |
자바 스레드(Thread)? (0) | 2025.03.24 |
프로세스(Process) vs 스레드(Thread)를 쉽게 알아보기 (0) | 2025.03.20 |