본문으로 바로가기
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


JVM 메모리 구조 이해하기


JVM(Java Virtual Machine)

이미지 출처 : http://cafe.naver.com/jjdev/227


 JVM(Java virtual machine)은 자바를 실행하기 위한 가상 기계라고 할 수 있습니다. 여기서 가상 기계란 말이 어색하다면 프로그램을 실행하기 위해 물리적 머신과 유사한 머신을 소프트웨어로 구현한 것이라고 해석할 수 있습니다. 

 JVM은 Java Byte Code를 OS에 맞게 해석해주는 역활을 합니다. Java Compiler가 *.java 파일을 컴피알을 하면 .class라는 java byte code로 변환시켜 주며, Byte Code는 기계어가 아니기 때문에 OS에서 바로 실행이 되지 않습니다. 이 때 JVM이 OS가 이해할 수 있도록 해석해줍니다.

 자바로 작성된 어플리케이션은 모두 JVM을 통해서 실행되기 때문에 자바 어플리케이션이 실행되기 위해서는 반드시 JVM이 필요합니다. 일반 어플리케이션의 코드는 OS만 거치고 하드웨어로 전달되는데 Java 어플리케이션은 JVM을 한 번 더 거치기 때문에, 그리고 하드웨어에 맞게 완전히 컴파일된 상태가 아니고 실행 시에 해석(interpret)되기 때문에 속도가 느리다는 단점을 가지고 있습니다. 그러나 바이트코드(컴파일된 자바코드)를 하드웨어의 기계어로 바로 변환해주는 JIT컴파일러와 향상된 최적화 기술이 적용되어서 속도의 격차를 많이 줄였습니다.


* JVM이란?

- Java Virtual Machine
- JAVA와 OS 사이에서 중계자 역할
- JAVA가 OS에 구애받지 않고 재사용을 가능하게 해 줌
- 메모리 관리 기능(Garbage Collection)

※ JVM(java.exe)은 무엇을 하는가?

 메모리를 할당한다.

 bytecode를 interpreter 형태로 OS에 맞추어 번역, 실행한다.

 번역, 실행 시 최적화를 수행한다.



JVM을 알아야 하는 이유

기계에는 한정된 자원이 있고 최고의 성능을 내기 위해 메모리 효율성을 알고 메모리 구조를 알아야 합니다. 또한 똑같은 기능을 제공하는 프로그램이더라도 메모리 효율에 따라 성능이 좌우됩니다. 또한 메모리 관리가 되지 않을 경우 속도저하 현상이나 튕김 현상 등이 일어날 수 있어 모르는 것보다 아는 것이 중요하다고 합니다

(저는 초급개발자라 아직 와닿지 않았지만.. 알아두려고 합니다.)


JVM 실행 과정

1. 프로그램이 실행되면 JVM은 OS로부터 프로그램에 필요한 메모리를 할당받는다.

2. 자바컴파일러(JAVAC)가 자바 소스코드(*.java)파일을 컴파일 하여 자바 바이트코드(.class)로 변환시킨다.

3. Class Loader을 통해 class파일들을 JVM으로 로딩한다.

4. 로딩된 class파일들은 Execution engine을 통해 해석된다.

5. 해석된 바이트코드는 Runtime Data Area에 배치되어 실질적인 수행이 이루어지게 된다.

이러한 과정속에서 필요에 따라 JVM은 Thread Synchronization과 GC(Garbage Collection)같은 작업을 수행한다.


JVM구조

프로그램이 실행되면, JVM은 OS으로부터 이 프로그램이 필요로 하는 메모리를 할당받고, JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리합니다. JVM은 크게 3부분으로 나눌 수 있습니다. 클래스 파일을 로딩한 뒤 검증하고 초기화하는 Class loader subSystem, 클래스 파일을 저장하는 Runtime DataArea(이곳은 다시 method area, heap, java stacks, pc registers, native method stacks의 5가지 영역으로 나누어집니다), 클래스 파일(바이트코드)를 플랫폼에 맞는 기계어로 변환시켜 실행하는 Execution engine 입니다.


Class Loader(클래스 로더)

JVM내로 클래스(.class)를 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈입니다. Runtime 시에 동적으로 클래스를 로드합니다. jar파일 내 저장된 클래스들을 JVM위에 탑재하고 사용하지 않는 클래스들은 메모리에서 삭제합니다. 클래스의 인스턴스를 생성하면 클래스 로더를 통해서 메모리에 로드하게 됩니다.


Execution Engine

Load된 Class의 ByteCode를 실행하는 Runtime Module이 바로 Execution Engine입니다. Class Loader를 통해 JVM 내의 Runtime Data Areas 에 배치된 바이트 코드는 Executin Engine에 의해 실행, 실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행합니다. 최초 JVM 이 나왔을 당시에는 Interperter방식(한 줄씩 해석하고 실행)이였기 때문에 속도가 느리다는 단점이 있었지만 JIT complier 방식을 통해 이 점을 보완했습니다. JIT는 ByteCode를 어셈블러 같은 NativeCode로 바꿔서 실행이 빠르지만 역시 변환하는데 비용이 발생했고, 이 같은 이유 때문에 JVM은 모든 코드를 JIT Compiler 방식으로 실행하지 않고 Interpreter 방식을 사용하다 일정한 기준이 넘어가면 JIT Compiler 방식으로 실행합니다.


1) Interpreter(인터프리터)

자바 바이트 코드를 명령어 단위로 읽어서 실행합니다. 이방시은 한 줄 씩 수행하기 때문에 느리다는 단점이 있습니다.

2) JIT(Just In Time)

인터프리터의 단점을보안하기 위해 도입된 것으로, 인터프리터 방식으로 실행하다가 적절한 시점에 바이트코드 전체를 컴파일하여 네이티브 코드로 변경하여 저장하고 , 재사용 할 때 변환된 네이티브 코드를 재사용하는 방식입니다. 초반에 메모리를 잡아두거나 하는 작업들이 있어서 초기 실행속도는 느릴 수도 있지만 이후로는 ByteCode를 사용할 때마다 네이티브 코드로 변환하는 작업이 들어 실행속도가 많이 향상됩니다.



Runtime Data Areas

JVM이 프로그램을 수행하기 위해 os로붜 별도로 할당받는 메모리 공간입니다. Runtime Data Areas는 크게 5가지 영역으러 나누어집니다.


1) PC Register

 Thread가 시작될 때 생성되며 생성될 때마다 생성되는 공간으로 스레드마다 하나씩 존재한다. Thread가 어떤 부분을 어떤 명령으로 실행해야할 지에 대한 기록을 하는 부분으로 현재 수행중인 JVM명령의 주소를 갖는다. 만약 Native Method를 수행한다면 PC Register은 Undefined상태가 된다. PC Register에 저장되는 Instruction의 주소는 Native Pointer일 수도 있고 Method ByteCode일 수도 있다. Native Method를 수행한다면 JVM을 거치지 않고 API를 통해 바로 수행된다.


2) Java Virtual Machine Stack

프로그램 실행과정에서 임시로 할당되었다가 메소드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장하기 위한 영역이다. 각종 형태의 변수나 임시 데이터, 스레드나 메소드의 정보를 저장한다. 메소드 호출 시마다 각각의 스택프레임(메서드만을 위한 공간)이 생성된다. 메서드 수행이 끝나면 프레임 별로 삭제를 한다. 메소드 안에서 사용하는 값(local variable)을 저자안다. 또 호출된 메소드의 매개변수, 지역변수, 리턴 값 및 연산 시 일어나는 값들을 임시로 저장한다.


3) Native Method Stack 

 Java 외의 언어로 작성된 네이티브 코드들을 위한 Stack, 즉 JNI(Java Native Interface)를 통해 호출 되는 C/C++등의 코드를 수행하기 위한 Stack

자바 프로그램이 컴파일 되어 생성되는 바이트 코드가 아닌 실제 실행할 수 있는 기계오로 작성된 프로그램을 실행시키는 영역이다.


4)Method Area( = Class area = Static area)

모든 쓰레드가 공유하는 메모리 영역이다. Method Area는 클래스, 인터페이스, 메소드, 필드, Static 변수 등의 바이트 코드 등을 보관한다. 자바 프로그램은 main 메소드의 호출에서부터 계속된 메소드의 호출로 흐름을 이어가기 때문이다. 대부분 인스턴스의 생성도 메소드 내에서 명령하고 호출한다. 사실상 컴파일 된 바이트코드의 대부분이 메소드 바이트코드이기 때문에 거의 모든 바이트코드가 올라간다고 봐도 상관없다. 이 공간에는 Runtime Constant Pool 이라는 별도의 관리 영역도 함께 존재한다. 이는 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 수행한다.



올라가는 정보의 종류

i) Field Information : 멤버변수의 이름, 데이터 타입, 접근 제어자에 대한 정보

ii) Method Information : 메서드의 이름, 리턴타입, 매개변수, 접근제어자에 대한 정보

iii) Type Information : - Type의 속성이 Class인지 Interface인지의 여부 저장
- Type의 전체이름(패키지명+클래스명)
- Type의 Super Class의 전체이름
(단, Type이 Interface이거나 Object Class인 경우 제외)
- 접근 제어자 및 연관된 interface의 전체 리스트 저장

iv) 상수 풀(Constant Pool)
- Type에서 사용된 상수를 저장하는 곳(중복이 있을 시 기존의 상수 사용)
- 문자 상수, 타입, 필드, Method의 symbolic reference(객체 이름으로 참조하는 것)도 상수 풀에 저장

v) Class Variable
- Static 변수라고도 불림
- 모든 객체가 공유 할 수 있고, 객체 생성 없이 접근 가능

vi) Class 사용 이전에 메모리 할당
- final class 변수의 경우(상수로 치환되어) 상수 풀에 값 복사


Method Area는 클래스 데이터를 위한 공간이라면 Heap 영역이 객체를 위한 공간입니다. Heap과 마찬가지로 GC관리 대상에 포함됩니다.


5) Heap 

객체를 저장하는 가상 메모리 공간, 프로그램 상에서 런타임시 동적으로 할당하여 사용하는 영역입니다.  *.class를 이용해 instance를 생성하면 Heap에 저장됩니다. new 연산자로 생성된 객체와 배열을 저장합니다. 힙은 세 부분으로 나눌 수 있습니다.



i) Permanent Generation

- 생성된 객체들의 정보의 주소 값이 저장된 공간

ii) New Area

- Eden : 객체들이 최초로 생성되는 공간
- Survivor : Eden에서 참조되는 객체들이 저장되는 공간

iii) Old Area :

-New Area에서 일정시간이상 참조되고 있는 객체들이 저장되는 공간


REFERENCE

http://cafe.naver.com/jjdev/227

https://medium.com/@lazysoul/jvm-%EC%9D%B4%EB%9E%80-c142b01571f2

http://asfirstalways.tistory.com/158