JVM이란 JAVA Virtual Machine, 자바 가상 머신의 약자이다.
JVM 역할은 자바 애플리케이션을 클래스 로더로 읽어 자바 API와 함께 실행하는 것이다. JVM은 JAVA와 OS 사이에서 중개자 역할을 수행하여 JAVA가 OS에 구애받지 않고 재사용을 가능하게 해준다. 일반 애플리케이션은 OS와 바로 맞붙어 있어 OS 종속적이다. 그래서 다른 OS에서 실행시키려면 애플리케이션을 OS에 맞게 변경해야 한다. 반면에 Java 애플리케이션은 JVM하고만 상호작용 하기 때문에 OS와 하드웨어에 독립적이다. 그래서 다른 OS에서도 프로그램 변경없이 실행이 가능하다. 대신에, JVM은 OS에 종속적이기 때문에 해당 OS에서 실행가능한 JVM이 필요하다. 그리고 메모리 관리, Garbage collection을 수행하며 스택기반의 가상머신이다.
동일한 기능을 하는 프로그램이더라도 메모리 관리에 따라 성능이 좌우된다. 메모리 관리가 되지 않을 경우 속도 저하, 프로그램 중단과 시스템/서비스 장애 등이 발생할 가능성이 있다.
따라서 한정된 메모리를 효율적으로 사용해 최고의 성능을 끌어내야 한다.
프로그램이 실행되면, JVM은 OS로부터 프로그램이 요구하는 메모리를 할당받는다. JVM은 할당받은 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.
자바 컴파일러가 자바 소스코드를 읽고 자바 바이트코드(.class)로 변환시킨다.
변경된 class 파일은 클래스 로더를 통해 JVM 메모리 영역으로 로딩한다.
로딩된 class 파일은 Execution engine을 통해 해석된다.
해석된 바이트 코드는 메모리 영역(Runtime Data Area)에 배치되어 실행된다. 실행 과정 중에 JVM은 필요에 따라 스레드 동기화나 가비지 컬렉션 같은 메모리 관리 작업을 수행한다.
JVM 내로 클래스(.class파일)를 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다. Runtime 시에 동적으로 클래스를 로드한다. jar 파일 내 저장된 클래스들을 JVM 위에 탑재하고 사용하지 않는 클래스는 메모리에서 삭제한다. 자바는 클래스를 처음 참조할 때, 해당 클래스를 로드하고 링크하고 이 역할을 클래스 로더가 수행한다.
클래스를 실행시키는 역할이다. 클래스 로더가 JVM 내 런타임 데이터 영역에 바이트 코드를 배치시키면 실행 엔진은 바이트 코드를 실행한다. 실행 엔진은 바이트 코드를 JVM 내부에서 실행할 수 있는 형태로 변경한다.
실행 엔진은 Interpreter와 JIT(Just-In-Time Compile) 방식을 함께 사용한다.
바이트 코드 명령어를 하나씩 읽어서 해석하고 실행. 하나하나의 실행은 빠르나, 전체적인 실행속도가 느리다는 단점
인터프리터의 단점을 보완하기 위해 도입되었으며 바이트 코드 전체를 컴파일하여 바이너리 코드로 변경하고 바이너리 코드를 직접 실행하는 방식이다. 하나씩 인터프리팅하여 실행하는 것이 아니라 바이트 코드 전체가 컴파일된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠르다.
JVM이 프로그램을 수행하기 위해 OS에서 별도로 할당한 메모리 공간 WAS 성능에 문제가 발생했을 때, 대부분 이 영역들이 원인이 된다.
Thread가 시작될 때 생성되며 생성될 때마다 생성되는 공간으로 스레드마다 하나씩 존재한다. 스레드가 특정 부분을 어떤 명령으로 실행할 것인지 기록하는 영역으로 현재 수행 중인 JVM 명령의 주소를 갖는다.
각 스레드는 하나의 Stack 영역을 할당 받는다. 즉, 스레드는 각자의 메모리 공간을 가지고 메소드를 수행한다. 프로그램 실행과정에서 메소드 호출 시 할당되었다가 메소드 종료 시 소멸되는 특성의 데이터가 저장되는 영역이다. 임시 데이터, 스레드나 메소드 정보를 저장한다. 메소드가 호출될 시 스택 프레임(호출된 메소드 공간)이 할당된다. 메소드 수행이 끝나면 해당 프레임은 삭제된다. 호출된 메소드의 매개 변수, 지역 변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다. 메소드 내부에 객체를 참조하는 지역 변수가 Stack Area에 할당되는데 할당된다의 의미는 다음과 같다.
void Method()
{
Person A = new Person();
}
객체가 생성되어 Heap
공간에 객체 데이터가 올라가면 객체 데이터를 가리키는 참조값
이 할당된다.
A
지역변수는 객체 데이터가 아닌 참조값
이 할당된다.
자바 프로그램이 컴파일되어 생성되는 바이트 코드가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역이다. JAVA가 아닌 다른 언어로 작성된 코드를 위한 공간이다. JAVA Native Interface가 바이트 코드로 전환하여 저장한다. 일반 프로그램처럼 커널이 스택을 잡아 독자적으로 프로그램을 실행시키는 영역이다. 즉, C 언어로 커널에 접근이 가능하다.
Method 영역은 프로그램의 시작부터 종료까지 메모리에 남아 있는다. 프로그램 실행 중 클래스나 인터페이스를 사용하면 JVM은 Class Loader를 이용해 클래스와 인터페이스의 메타 데이터를 Method Area에 저장한다. 클래스가 로드되는 시점은 해당 클래스가 사용되기 위해 호출되는 시점이다. 이 영역은 JVM당 하나만 생성된다. 인스턴스 생성에 필요한 정보도 존재하기 때문에 JVM의 모든 스레드는 Method Area를 공유한다. JVM의 다른 메모리 영역에서 해당 정보에 대한 요청이 오면, 실제 물리 메모리 주소로 변환해서 전달해준다. 기초 역할을 하므로 JVM이 작동하면 생성되며 종료 시까지 유지되는 공통 영역이다. 메타 데이터는 Type Information, Runtime Constant Pool, Field Information, Method Information, Class Variable을 의미한다.
Type명
Package name + Class name
Type 종류
Type이 Class인지 Interface인지에 관한 정보
Type 제어자
접근 제어자(Public, Private, Default 등), 그 외 제어자(abstract, final 등)
연관된 Interface 정보
사용된 Interface 정보
Heap 영역은 코드 실행을 위해 Java로 구성된 객체
및 JRE 클래스들이 탑재된다. 이 곳에서는 문자열 정보를 가진 String Pool, 실제 데이터를 가진 인스턴스, 배열 등이 저장된다. JVM당 하나만 생성되고 해당 영역이 가진 데이터는 모든 Java Stack 영역에서 참조되어, 스레드 간 공유된다. Heap 영역이 가득 차면 OutOfMemoryError
를 발생시킨다. 다른 객체의 필드 또는 스택에 존재하는 다른 메소드가 Heap Area에 생성된 객체를 참조할 수 있다. 메소드가 호출되어 스택영역에서는 참조값을 저장하고 Hear Area에 객체의 데이터를 저장한다.