티스토리 뷰
자바 프로그램를 이해하는 데 있어서 메모리 구조를 이해하는 것은 큰 도움이 된다. 우선 자바 프로그램의 실행 구조를 알아보자.
JAVA의 실행과정
컴파일러 : JAVA의 경우 소스코드를 실행하기 위해 Bytecode 형태로 변환하는데 이를 통해 변환된 코드를 target code라고 부른다. 이는 특정 기계에서 실행 가능하기 때문에 이 같은 이름이 붙었다.
인터프리터 : 이전의 JVM에서는 인터프리터만 사용할 수 있었습니다.(JDK 1.1 이전) 이전 포스팅에서 JIT 컴파일러에 대해 잠깐 이야기 나왔었는데 JAVA의 성능문제로 이 JIT 컴파일러가 추가되게 됩니다. (JDK 1.2 이후) 그러므로 현재의 JAVA를 인터프리터나 컴파일러로 정의내리긴 어렵습니다. 인터프리터로 시작하지만 그 과정에서 일부 컴파일이 되기도 하는... JAVA에서 인터프리터니 컴파일러니 하이브리드니 논쟁은 맞지 않는 것 같습니다.
JIT 컴파일러 : 초기 JVM은 인터프리터 방식(한줄씩 읽어 실행하는 방식)으로 실행했는데 때문에 속도가 매우 느렸다. 때문에 JIT 컴파일러라는 방식으로 이 점을 보완하였고 JIT는 바이트 코드를 어셈블러 같은 Native code로 바꾸어 실행하게 되었다. 하지만 Native code 변환 자체에도 자원을 소요해야 하므로 모든 코드를 컴파일 하지 않고 두가지를 방법을 같이 사용하게 된 것이다.
인스트럭션 : CPU를 동작시키는 명령어로 인스트럭션 집합을 인스트럭션 세트라 부르며 CPU의 전형적인 특성은 이 인스트럭션 세트에 의해 결정됩니다. 프로그램이 CPU가 해야할 일을 알려주는 작업지시서라면 인스트럭션은 구체적으로 실행해야할 행동을 지정해주는 값이라 할 수 있습니다.
1. 인스트럭션은 CPU가 행해야 할 작업을 지정하는 일종의 정형화된 언어이다.
2. 사용자는 원하는 동작에 대한 인스트럭션을 기술하여 메모리에 저장한다.
3. CPU는 인스트럭션을 순차적으로 읽어와 실행한다. 이 떄, 메모리에서 인스트럭션을 읽어오는 과정을 '패치'라고 부른다.
4. 인스트럭션은 연산의 종류를 나타내는 '동작입력 값'과 연산의 대상이 되는 값인 '오퍼랜드'를 지정해야 수행 가능한다. 이 때의 동작입력 부분을 '오퍼레이션 코드' 혹은 'OP코드'라고 한다.
5. 연산의 견과는 레지스터에 저장되고 연산의 입력 값인 각각의 오퍼랜드는 인스트럭션에 인스턴스 형태로 기록될 수 있으며 이를 I-타입 인스트럭션이라 부른다. 또, 오퍼랜드 값을 임의의 레지스터에 저장하고 인스트럭션에는 이 레지스트를 지정하는 값을 넣은 형태의 R-타입 인스트럭션도 있다.
인스트럭션의 경우 이해라기 어려운 부분이라 기계어 또는 '직접실행 코드' 정도로 이해하고 넘어가도록 하자.
JVM 메모리 구조
자바 가상머신(JVM)은 바이트 코드를 플랫폼 즉, OS에 맞게 해석해주는 역할을 한다. 자바의 플랫폼 독립적인 환경을 조성하는 특징적인 부분이다. 자바 컴파일러는 .class라는 바이트코드 파일로 변환시킵니다. 바이트코드 자체는 기계어가 아니기 때문에 OS에서 즉각 실행할 수 없습니다. 이때 JVM이 바이트코드를 해석해주며 JVM은 크게 Class Loader, Excution Engine, Runtime Data Area 세가지로 구성되어 있습니다.
Class Loader : Runtime 시점에 Class 파일을 메모리(Runtime Data Area)에 로드합니다. 클래스의 인스턴스를 생성하게 되면 이 클래스 로더를 통해 메모리로 로드됩니다.
Runtime Data Area : JVM이 프로그램을 수행하기 위해서 OS로부터 할당받은 메모리 공간입니다. 이전 포스팅에서 Java 프로그램은 플랫폼 독립적이지만 JVM은 플랫폼 종속적이다라고 한적이 있는데 위와 같은 이유로 JVM은 플랫폼 종속적인 성격을 가집니다. 각 영역의 특징을 살펴보면,
- Method area : Class Area라고도 부르며 클래스 파일의 바이트코드가 로드되는 영역입니다. 이 영역에서는 클래스, 인터페이스, 메소드, 필드 등을 보관하고 모든 쓰레드가 공유하는 메모리 영역입니다.
- Heap : 힙 영역에 보관되는 메모리는 런타임 시 동적으로 할당하여 사용합니다. 객체(인스턴스) 저장을 하는데 사용하며 Heap 영역이 부족해지면 가비지컬렉션이 실행되어 메모리를 정리합니다. 코드에서 사용하는 new 키워드를 통해 생성된 인스턴스(객체)가 저장됩니다.
- Java virtual machine stack : 일반적으로 스택영역이라 함은 이 영역을 지칭하고 지역변수, 매개변수, 임시변수, 주소 등을 저장합니다. 메소드가 종료되면서 메모리가 해제됩니다.
- PC register : Java의 PC 레지스터는 일반적인 CPU의 작동과는 조금 다른데 Register(CPU내의 기억장치)기반이 아닌 Stack을 기반으로 동작합니다. 이게 무슨 말이냐면 CPU가 바로 알아들을 수 있는 명령어(Native Method, OS 별로 다르다)가 아닌 JVM을 통해 번역이 번역이 필요한 명령어를 사용하는 것입니다. 따라서 Native Method를 수행할 때는 JVM을 거치지 않고 바로 수행합니다. 이를 통해 플랫폼 독립적인 특성을 유지할 수 있습니다.
- Native method : Java 이외의 언어로 작성된 네이티브 코드를 위한 영역입니다. C/C++ 등으로 작성된 코드는 JNI(Java Native Interface)로 호출 후에 수행합니다. 이를 위해 별도로 Native method stack이라는 공간을 사용하며 이 안에서는 Native code들을 새로운 프레임으로 관리하는데 이는 JNI 이용 시 JVM 내부에 영향을 주지 않기 위함입니다.
Execution Engine : 로드된 Class의 바이트코드를 실행하는 Runtime 모듈이 바로 Execution engine입니다. Execution 엔진은 실행코드를 인스트럭션으로 변환하여 실행하는데 이 과정에서 JIT가 사용됩니다. 반복되는 코드 등은 해석단계를 다시 거칠 필요없이 Native code로 사용하는 것이 효율이 좋으므로 이러한 코드들이 변환되게 됩니다. Class loader를 통해 JVM 내의 Runtime Data Area에 배치된 바이트 코드는 Execute 엔진에 의해 명령어 단위로 실행되게 됩니다.
'프로그래밍 > Java' 카테고리의 다른 글
Java 프로그래밍 -프로세스, 쓰레드- (1) | 2018.03.02 |
---|---|
Java 프로그래밍 -패키지- (1) | 2018.02.26 |
Java 프로그래밍 -연산- (0) | 2018.02.23 |
Java 프로그래밍 -이스케이프 문자- (1) | 2018.02.23 |
Java 프로그래밍 -입출력- (0) | 2018.02.23 |
- Total
- Today
- Yesterday
- oracle
- C#
- node.js
- REACT
- hoisting
- .NET
- vue
- nuxt.js
- RDBMS
- alpine.js
- delegate
- aws
- vue.js
- DevOps
- Azure
- Quasar
- 이진탐색 #중복
- JavaScript
- garbage collection
- svelte
- Gatsby.js
- LINQ
- Next.js
- SQLite
- Cloud
- MySQL
- gcp
- nosql
- PostgreSQL
- Angular
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |