JVM

2022. 7. 18. 01:41Java

0. 들어가기 앞서

같은 로봇이지만 왼쪽에서는 서로 언어가 달라 알아 듣지 못하고 있다.

기계와 사람의 언어는 다릅니다. 프로그래머가 프로그램 코드를 작성 한다고 할 때, 

0과 1만 해석 가능한 컴퓨터가 사람의 언어로 작성된 코드 자체를 이해하기란 불가능합니다. 따라서 프로그래머의 코드를 컴퓨터가 이해 가능한 기계어로 컴파일 해주어야 합니다.

 

 

1. JVM?

Linux로 컴파일한 코드는 리눅스 환경에서만 작동한다

 

> Java 이전의 언어, 즉, C / C++에서는 컴파일 플랫폼과 타겟 플랫폼이 다를 경우, 프로그램이 동작하지 않았습니다. 

> 즉, 윈도우에서 컴파일한 실행코드가 리눅스 환경에서는 동작하지 않았습니다. 이는 코드가 운영체제에 종속적라는 뜻입니다.

> 동일한 플랫폼에서 컴파일과 실행을 같이 한다면, 프로그램은 아무 문제 없이 동작합니다. 

> 문제는 배포를 할 때 발생합니다. 플랫폼이 달라질 경우 프로그램은 동작하지 않습니다.

> C/C++에서는 크로스 컴파일을 통해 다른 플랫폼의 실행 파일을 만들어 문제를 해결했지만 Java에서는 JVM을 사용함으로써 조금 다르게 접근하였습니다.

 

 

JVM으로 문제 해결 

> Java를 만든 썬마이크로 시스템즈에서는 다음과 같은 슬로건을 내세웠습니다.

 

WORA -> Write Once, Run Anywhere

 

> 즉, JVM(Java Virtual Machine)을 통해 코드를 한 번만 작성하여도 다른 운영체제에서 실행 가능하도록 만든 것이죠.

> 각 운영체제에는 운영체제에 맞는 JVM이 설치 되어 있고, 그 JVM을 통해 각 운영체제 별로 별다른 컴파일 없이 코드를 하나만 작성할 수 있게 되는 것입니다.

다양한 OS용 JVM

> 왜 JVM인가?

자바는 네트워크에 연결된 모든 디바이스에서 작동하는 것이 목적이었습니다. 따라서 디바이스마다 운영체제와 하드웨어가 다르기 때문에 플랫폼에 의존하지 않도록 운영체제를 설계해야했던 것입니다.

 

2. Java의 실행 과정

프로그램 작성/ 실행과정

 

> 앞서 말했듯이 자바소스코드 그 자체로는 CPU가 인식하지 못합니다. 따라서 기계어로 컴파일하는 것이 필요합니다. 

자바 코드를 저장하면 *.java 파일 생성되고 이를 컴파일(javac) 하면 JVM이 인식 가능한 *.class 바이트코드(byte code)로 변환되고 이를 각 운영체제에 맞는 JVM이 기계어(binary code)로 변경해주어 이 기계어를 읽은 컴퓨터가 프로그램을 실행하게 됩니다.

 

+ JVM이 바이트 코드를 읽는 방식

> JVM은 바이트코드를 명령어 단위로 읽어서 해석합니다. 

이 때, Interpreter 방식과 JIT 컴파일 방식 두 가지를 혼합합니다. 

Interpreter : 바이트 코드를 한 줄씩 해석, 실행하는 방식입니다. 느리다는 단점이 있습니다.

JIT (Just In Time) : JIT 컴파일러는 같은 코드를 매번 해석하지 않고, 실행할 때, 컴파일을 하면서 해당 코드를 캐싱합니다. 이후에 변경된 부분만 컴파일하고, 나머지는 캐싱된 코드를 이용하여 실행합니다. 

바이트 코드를 JIT 컴파일러를 이용해 바이트코드를 실행하는 시점에 각 OS에 맞는 Native Code로 변환하여 실행속도를 개선하였습니다만, bytecode에서 nativecode로 변환하는데에 비용이 소요 되어 interpreter방식과 혼합하여 사용하는 추세입니다.  

 

 

3. JVM의 내부 구조

> JVM은 class Loader, Execution Engine Garbage Collector, Runtime Data Area로 크게 4가지 영역으로 나뉩니다. 

 

+  클래스 로더(Class Loader)

> 동적으로 클래스를 로딩해주는 역할을 합니다. 

> 자바에서 소스를 작성하여 컴파일 하면 .class파일이 생성되는데 이때, 클래스로더는 .class 파일를 묶어서 JVM이 운영체제로부터 할당 받은 메모리 영역인 Runtime Data Area에 적재합니다. 

자바에서는 동적으로 클래스를 읽어오기때문에 프로그램이 실행 중인 런타임에서 모든 코드가 JVM과 연결됩니다. 

 

+  실행 엔진 (Excecution Engine)

> JVM으로 로드된 .class 파일(바이트코드)들은 Runtime Data Area의 Method Area에 적재되는데, 적재된 이후에 JVM은 Method Area의 바이트 코드를 실행 엔진(Execution Engine)에 제공하여, 바이트 코드를 실행시킵니다

실행 엔진은 바이트코드를 명령어 단위로 읽어서 실행합니다. 이때, 로드된 바이트코드를 실행하는 런타임 모듈이 실행 엔진(Execution Engine)입니다. 

 

+ GC 

> JVM의 힙 영역에서 동적으로 할당했던 메모리 영역 중 더 이상 사용되지 않는 메모리 영역을 주기적으로 삭제하는 프로세스를 말합니다. 

> 힙 영역에서 더는 사용하지 않는 메모리를 자동으로 회수해 주기 때문에  프로그래밍을 할 때, 따로 메모리 관리를 하지 않아도 되고, 혹시 모를 메모리 누수(Memory Leak)를 방지할 수 있습니다. 

> 다만, 메모리가 언제 해제되는지 정확하게 알 수 없고, GC가 동작하는 동안 JVM의 또 다른 동작이 멈춰 오버헤드가 발생합니다. 이로 인해 GC가 너무 자주 실행되면 소프트웨어 성능 하락의 문제가 되기도 합니다.  

 

+ 런타임 데이터 영역(Run time data Area)

> JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역입니다. 

> Run time data Area의 종류에는  method(static) 영역, Heap 영역, Stack 영역, PC register, Native method stack이 있습니다. 

 

1. method(static) 영역

> JVM이 시작될 때 생성되는 공간입니다. byte코드, 클래스 정보, 변수 정보, static으로 선언한 변수를 저장하고 모든 thread에서 공유합니다. 

> 클래스 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보와 같은 각종 필드 정보들과 메서드 정보, 데이터 Type 정보, Constant Pool, static변수, final class 등이 생성되는 영역입니다.

 

2. Heap 영역

> new를 이용해 객체를 생성하거나 배열의 요소들을 저장할 때 사용됩니다. 

> Garbage Collectoin은 참조가 해제 되거나 없는 객체들을 heap 메모리에서 정리합니다. 

 

3. stack 영역

> 특성상 해당 메서드의 호출이 종료되면 안에 선언된 변수는 사라지기때문에 금방 사용되고 끝나는 data가 저장됩니다. 

> 지역변수나 메서드의 매개변수, 임시적으로 사용되는 변수나 메서드가 저장되거나  heap에 존재하는 개체들의 참조가 포함됩니다. 

> LIFO(Last In First Out) 순서를 따릅니다. 즉, 가장 먼저 들어선 데이터의 처리 우선순위는 가장 낮습니다. 

> stack에서 더 이상 참조하지 않는 heap의 메모리는 GC의 대상이 됩니다. 

 

4. PC register 

> thread가 생성될 때마다 생성되는 영역으로, thread가 실행되는 부분의 주소와 명령을 저장하고 있는 영역입니다. 

 

5. Native Method Stack

> Java가 아닌 C/C++과 같은 다른 언어로 구성된 메소드를 실행할때 사용되는 영역입니다.

 

 

 

 


요약

1. JVM은 컴파일 플랫폼과 실행 플랫폼이 다를 때, 프로그램이 동작하지 않던 문제를 해결하기 위해 나왔다. 

2. 실행 플랫폼에서 각 운영체제에 맞는 JVM이 설치되어 있다면, 코드를 한 번만 작성하여도 코드가 어디서든 컴파일 하여 실행할 수 있다. (Write once, Run Anywhere)

3. 이는 네트워크에 연결된 모든 디바이스에서 작동하는 것이 목적이었다. 

4. 즉, 자바 코드는 운영체제에 독립적, JVM은 운영체제에 종속적이다. 

 

5. 프로그래머의 코드를 기계가 이해하기 위해서는 컴파일 과정이 필요하다. 

6. 자바 코드를 저장 -> *.java 파일 생성 ->  컴파일(javac) -> *.class 바이트코드(byte code) -> JVM  -> 기계어(binary code) -> 실행

 

7. JVM의 내부 구조

클래스 로더: 동적으로 클래스를 읽어와 .class 파일를 묶어서 JVM이 운영체제로부터 할당 받은 메모리 영역인 Runtime Data Area에 적재한다. 

실행엔진: 바이트코드를 명령어 단위로 읽어서 실행한다.

가비지 컬렉터: 더는 사용하지 않는 메모리를 자동으로 회수해 효율적인 메모리 관리가능, 메모리 누수 방지.

런타임 데이터 영역: 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역

 

8. Rum Time Area의 내부 구조

Method area: JVM이 시작될 때 생성되는 공간입니다. byte코드, 클래스 정보, 변수 정보, static으로 선언한 변수를 저장

Heap area: 객체나 배열이 저장되어 있음 이를 참조하는 변수는 스택에 저장된다. GC에 의해 메모리 관리가 된다. 

Stack area: 지역변수, 메서드의 매개변수가 저장되거나 heap에 존재하는 개체들의 참조가 포함된다. 

PC register: thread가 실행되는 부분의 주소와 명령을 저장하고 있는 영역

Native Method Stack: Java가 아닌 다른 언어로 구성된 메소드를 실행할때 사용되는 영역

 

'Java' 카테고리의 다른 글

정렬(Sort)  (0) 2022.07.19
객체 지향 vs 절차 지향 프로그래밍  (0) 2022.07.18
변수  (0) 2022.06.30
상수(final)  (0) 2022.06.30
리터럴(literal)  (0) 2022.06.30