개발놀이터

JVM (Java Virtual Machine) 본문

Java

JVM (Java Virtual Machine)

마늘냄새폴폴 2022. 7. 14. 02:08

JVM의 대략적인 모습

위 그림은 자바 코드의 실행 과정을 간략하게 보여준다.

  • 프로그램이 실행되면 JVM은 OS로부터 프로그램이 필요로하는 메모리를 할당 받는다. (JVM은 이 때 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.)
  • 자바 파일(.java)이 자바 컴파일러에 의해 자바 바이트코드(.class)로 변환된다.
  • 클래스 로더를 통해 자바 바이트 코드를 JVM으로 필요한 시점에 로딩한다.
  • 해석된 바이트 코드는 런타임 데이터 영역에 배치되어 실질적인 수행이 이루어진다.
  • 실행 과정 속에서 JVM은 필요에 따라 GC와 같은 관리 작업을 수행한다.

자바 코드가 실행되는 과정을 살펴보면 C / C++과는 다르다는 것을 알 수 있다. C / C++은 운영체제별로 컴파일러가 존재하여 해당 운영체제가 인식할 수 있는 기계어로 변환된다. 반면 자바는 자바 컴파일러 하나만 존재하며, 기계어가 아닌 중간 단계의 바이트 코드로 변환된다. 또한, 운영체제가 아닌 JVM에 의해 실행된다.

 

바이트코드란?

바이트 코드란 JVM에서 작동하도록 만든 이진 코드이다. 즉, JVM이 이해할 수 있는 언어로 변환된 코드이며 명령어의 크기가 1바이트라서 자바 바이트 코드라고 불리고, 자바 코드를 배포하는 가장 작은 단위이다. 확장자는 .class이다.

 

 

JVM의 특징

*스택 기반의 가상 머신

-대표적인 컴퓨터 아키텍처인 인텔 x86 아키텍처나 ARM아키텍처와 같은 하드웨어가 레지스터 기반으로 동작하는데 비해 JVM은 스택 기반으로 동작한다.

*심볼릭 레퍼런스

-참고하는 클래스의 특정 메모리 주소를 참조 관계로 구성한 것이 아니라, 참조하는 대상의 이름만을 지칭한 것이다. 자바 바이트 코드가 JVM에 올라가게 되면 심볼릭 레퍼런스는 이름에 맞는 객체의 물리적인 주소를 찾아서 연결하는 작업을 수행한다.

-기본 자료형을 제외한 모든 타입을 명시적인 메모리 주소 기반의 레퍼런스가 아니라 심볼릭 레퍼런스를 통해 참조한다.

*가비지 컬렉션

클래스 인스턴스는 사용자 코드에 의해 명시적으로 생성되고 가비지 컬렉션에 의해 자동으로 파괴된다.

*기본 자료형을 명확하게 저으이하여 플랫폼 독립성 보장

-C / C++등의 전통적인 언어는 플랫폼의 따라 int형의 크기가 변한다. 하지만 JVM은 기본 자료형을 명확하게 정의하여 호환성을 유지하고 플랫폼 독립성을 보장한다.

*네트워크 바이트 오더

-자바 클래스 파일은 네트워크 바이트 오더를 사용한다. 인텔 x86아키텍처가 사용하는 리틀 엔디안이나 RISC 계열 아키텍처가 주로 사용하는 빈엔디안 사이에서 플랫폼 독립성을 유지하려면 고정된 바이트 오더를 유지해야하므로 네트워크 전송 시에 사용하는 바이트 오더인 네트워크 바이트 오더를 사용한다. 네트워크 바이트 오더는 빅 엔디안이다.

 

 

JVM의 구조

 

JVM의 구조

 

클래스 로더 (Class Roader)

자바는 동적 로드, 즉 컴파일 타임이 아니라 런타임에 클래스를 로드하고 링크하는 특징이 있다. 이 동적 로드를 담당하는 부분이 JVM의 클래스 로더이다. 클래스 로더에는 로딩, 링크, 초기화 단계로 나눠져 있는데, 간단히 설명하면 다음과 같다.

 

*로드

  • 각 자바 바이트 코드는 JVM에 의해 메소드 영역에 다음 정보를 저장한다.
  • 로드된 클래스를 비롯한 그의 부모 클러ㅐ스의 정보
  • 클래스 파일과 Class, Interface, Enum의 관련 여부
  • 변수나 메소드 등의 정보

*링크

  • 검증 : 일거 들인 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사한다.
  • 준비 : 클래스가 필요로 하는 메모리를 할당하고 ,ㅋ르래스에서 정의된 필드, 메소드, 인터페이스를 나타내는 데이터 구조를 준비한다.
  • 분석 : 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체한다.

*초기화

  • 클래스 변수를 적절한 값으로 초기화한다. 즉, static 필드들이 설정된 값으로 초기화 된다.

 

런타임 데이터 영역 (Runtime Data Areas)

런타임 데이터 영역은 JVM이 운영 체제 위에서 실행될 때, 할당 받는 메모리 영역이며 총 6개의 영역으로 나눌 수 있다. 이 영역들은 스레드가 공유하는 공간인지 아닌지로 나눈다.

 

스레드마다 하나씩 생성되는 공간

  • PC 레지스터
  • JVM 스택
  • 네이티브 메소드 스택

모든 스레드가 공유하는 공간

  • 메소드
  • 런타임 상수 풀

 

PC 레지스터

PC 레지스터는 각 스레드마다 하나씩 존재하며, 스레드가 시작될 때 생성된다. PC 레지스터는 메소드 안에서 바이트 코드 몇 번째 줄을 실행하고 있는지와 같은 정보를 갖고 있다.

 

JVM스택

JVM 스택은 각 스레드마다 하나씩 존재하며, 스레드가 시작될 때 생성된다. JVM 스택은 스택 프레임이라는 구조체로 이루어져 있는데, 새로운 메소드가 호출될 때마다 push, 메소드 실행이 끝나면 pop 동작을 수행한다. 각 스택 프레임은 지역 변수 배열, 피연산자, 프레임 데이터를 갖는다. 프레임 데이터는 현재 실행중인 메소드가 속한 클래스의 런타임 상수 풀, 이전 스택 프레임에 대한 정보, 현재 메소드가 속한 클래스, 객체에 대한 참조 등을 말한다.

 

네이티브 메소드 스택

네이티브 메소드 스택은 자바 바이트 코드가 아닌 다른 언어로 작성된 네이티브 코드를 위한 스택이다. 네이티브 메소드 스택은 성능 향상을 목적으로 작성되었다. JVM 스택과 네이티브 스택이 나눠여 있다 하더라도 자바 코드를 수행하다 JNI를 호출하면 JVM스택에서 네이티브 메소드 스택으로 확장할 뿐이다.

 

힙 (Heap)

힙은 모든 스레드가 공유하는 영역이다. 힙은 프로그램을 실행하면서 생성된 모든 인스턴스 또는 겍체를 저장하는 공간이다.

 

메소드 영역

모든 스레드가 공유하는 영역으로 JVM이 시작될 때 생성된다. 클래스 로더가 클래스 파일을 읽어 오면, 클래스 정보를 파싱하여 런타임 상수 풀, 필드와 메소드 정보, static 변수, 메소드의 바이트 코드등을 보관한다. 메소드 영역은 JVM 벤더마다 다양한 형태로 구현할 수 있으며, 오라클 핫스팟 JVM에서는 흔히 PermGen(자바 1.7이전), MetaSpace(자바 1.8이후)로 부른다.

 

런타임 상수 풀

런타임 상수 풀은 메소드 영역에 포함되는 영역이긴 하지만, JVM 동작에서 가장 핵심적인 역할을 수행하는 곳이기 때문에 JVM 명세에서도 따로 중요하게 기술한다. 각 클래스와 인터페이스의 상수 뿐만 아니라, 메소드와 필드에 대한 모든 레퍼런스까지 담고있는 테이블이다. 즉, 어떤 메소드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메소드나 필드의 실제 메모리 상 주소를 찾아서 참조한다.

 

 

실행 엔진 (Execution Engine)

실행 엔진은 메모리에 적재된 바이트 코드를 기계어로 변경하여 명령어 단위로 실행 및 바이트 코드를 운영체제에 맞게 해석해주는 역할을 수행한다. 실행 엔진이 바이트 코드를 명령어 단위로 읽어서 수행하는데에는 크게 두가지 방식이 사용된다.

 

인터프리터

런타임 중에 바이트 코드를 한 줄씩 읽고 실행한다. 그래서 컴파일보다 속도가 느리다.

 

JIT 컴파일러

인터프리터의 속도 이슈를 해결하기 위해 같이 사용한다. 자주 실행되는 바이트 코드 영역을 런타임 중에 기계어로 컴파일하여 사용한다.

 

 

 

정확히는 JIT 컴파일러가 번역 안할래 라고 하기 보다는 인터프리터가 해당 바이트 코드를 읽지 않아도 된다는 것을 의미한다.

 

 

 

JVM 구조 정리

JVM의 구조는 Class Loader, Execution Engine, Runtime Data Area, JNI, Native Method Library로 이루어져 있다. 
-Class Loader : JVM내로 클래스를 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈.
-Execution Engine : 바이트 코드를 실행시키는 역할을 수행.
--인터프리터 : 바이트 코드를 한줄 씩 실행.
--JIT 컴파일러 : 인터프리터의 효율을 높이기 위한 컴파일러로 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러가 반복되는 코드인 네이티브 코드로 바꿔준다.
--GC : 가비지 컬렉터로 힙 영역에서 사용되지 않는 객체들을 제거하는 작업을 수행. 
-Runtime Data Areas : 프로그램 실행 중에 사용되는 다양한 영역
--PC Register : Thread가 시작될 때 생성되며 현재 수행중인 JVM 명령의 주소를 갖고 있다.
--Stack Area : 지역변수, 파라미터 등이 생성되는 영역, 실재 객체는 Heap에 할당되고 해당 래퍼런스만 Stack에 저장된다.
--Heap Area : 동적으로 생성된 오브젝트와 배열이 저장되는 곳으로 GC의 영역이다.
--Method Area : 클래스 멤버 변수, 메서드 정보, Type정보, 상수 풀, static 변수, final 변수 등이 생성된다.
-JNI(Java Native Interface) : 자바 애플리케이션에서 C, C++, 어셈블리어로 작성된 함수를 사용할 수 있는 방법을 제공해준다.
-Native Method Library : C, C++로 작성된 라이브러리

 

 

Reference

https://steady-coding.tistory.com/587

 

[Java] JVM이란?

java-study에서 스터디를 진행하고 있습니다. 자바 코드 실행 과정 위 그림은 자바 코드의 실행 과정을 간략하게 보여 준다. 프로그램이 실행되면 JVM은 OS로부터 이 프로그램이 필요로 하는 메모리

steady-coding.tistory.com