[CS-JAVA] JVM(자바 가상 머신)

지영·2023년 8월 22일
0

CS

목록 보기
61/77

JVM이란

Java Virtual Machine으로, 자바 가상 기계

  • 자바 기술을 활용한 애플리케이션을 실행시키는 가상 기계를 뜻한다. 모든 자바 애플리케이션은 이 JVM위에서 동작한다. (*코틀린, 스칼라 등도 JVM위에서 실행된다.)
  • JVM은 특정 OS에 맞게 자바 언어를 변환한다. 결국 특정 OS에 마주하는 것은 JVM이므로 , 자바는 운영체제에 독립적이고, JVM은 운영체제에 종속적이라고 할 수 있다. (다른 애플리케이션과 달리 자바는 JVM을 거쳐서 속도가 느리다고들 하지만, 요즘에는 기술이 발전하여 굳이 그렇지도 않다!)

JVM의 등장배경

🤔 C/C++처럼 크로스 컴파일(타겟 플랫폼에 맟춰서 컴파일하는 것)해서 배포하면 되지 않을까?

WORA (Write Once Run Anywhere) : 우선 읽자!

  • 자바는 네트워크에 연결된 모든 디바이스에서 작동하는 것이 목적이다.
  • 하지만 디바이스마다 운영체제나, 하드웨어가 달라서 플랫폼에 의존하지 않는 언어가 필요해졌다. 그렇게 자바 바이트코드(bytecode), JVM이 등장하게 되었다.

JVM의 구성요소

클래스로더가 자바 바이트코드를 런타임 데이터 영역에 로드하고, 실행 엔진이 자바 바이트를 실행한다.


1. 클래스 로더(Class Loader)

클래스로더 작업은 런타임시 처음으로 클래스를 참조할 때 발생한다. 클래스 로드를 요청받으면, 클래스 로더 캐시 > 상위 클래스 로더 > 자기 자신 순으로 해당 클래스가 있는지 확인한다. 만약 클래스로더가 아직 로드되지 못한 클래스를 발견하면, (로드-링크-초기화)한다.

+) 로드 / 링크/ 초기화 자세히 알아보기

  • <로드> : 클래스를 파일에서 가져와서 JVM 메모리에 로드한다. 로딩은 특정 이름을 가진 클래스 또는 인터페이스 유형의 이진 표현을 찾고(finding) 해당 이진 표현에서 클래스 또는 인터페이스를 만드는(creating) 프로세스이다.

  • <링크> : 연결은 클래스 또는 인터페이스를 가져와 이들의 유형과 다이렉트 수퍼 클래스와 수퍼 인터페이스를 검사하고 준비하는 프로세스이다.

  • <초기화> : 클래스 변수들을 적절한 값으로 초기화한다. static initializer들을 수행하고, static 필드들을 설정된 값으로 초기화한다.

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

런타임 데이터 영역은 JVM이 운영체제 위에서 실행되면서 할당받는 메모리 영역이다. 이 영역은 크게 5가지로 나뉜다.

  • 모든 스레드가 공유하는 영역

    • 메소드 영역 : 클래스별 정보 저장 영역, JVM이 시작될 때 생성되고, JVM이 읽은 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드 및 메서드 코드, 정적 변수, 메서드의 바이트 코드 등을 보관함.
    • 런타임 상수 풀 : 심볼릭 레퍼런스(실제 물리적 위치를 가리키는 참조가 아닌, 논리적 참조를 의미)의 역할을 하는곳, 메소드 영역 내에 위치함.
    • : 인스턴스 또는 객체, 배열을 저장하는 공간으로 가비지 컬럭션의 대상이다. JVM성능 이슈에서 가장 많이 언급되는 공간이며 여러 스레드에서 공유하기 때문에 데이터가 스레드로 부터 안전하지 않다.
  • 스레드마다 하나씩 생성되는 영역

    • PC레지스터 : 현재 스레드의 메소드에서 실행되는 명령어의 주소를 가짐. PC를 통해 실행 중인 명령의 위치를 추적하고, 실제로 PC는 메소드 영역의 메모리 주소를 가리킴. PC레지스터는 스레드가 시작될 때 생성된다.
    • JVM 스택 : 스레드가 시작될 때 생성됨. 메소드가 호출될 때마다 스택 프레임이 만들어지는데, 해당 영역은 스레드간에 공유되지 않으므로 스레드로부터 데이터가 안전하다.
    • 네이티브 메소드 스택 : 자바가 아닌 언어로 작성된 네이티브 코드(실제 실행될 수 있는 기계어)를 위한 스택이다. JNI(Java Native Interface)를 통해서 호출하는 C/C++등의 코드를 수행하기 위해 언어에 맞는 C스택이나 C++스택이 생성된다.

📑 스택 프레임이란?

  • JVM 내에서 메서드가 수행될 때마다 하나의 스택 프레임이 생성되어 해당 스레드의 JVM스택에 추가되고 메서드가 종료되면 스택 프레임이 제거된다.
  • 각 스택 프레임은 지역 변수 배열(Local Variable Array), 피연산자 스택(Operand Stack), 현재 실행 중인 메서드가 속한 클래스의 런타임 상수 풀에 대한 레퍼런스를 갖는다.

3. 실행 엔진(Execution Engine)

실행엔진에서는 바이트코드를 실행한다. 이때 바이트코드의 명령어는 1바이트의 OpCode와 추가 피연산자로 이루어지고, 실행 엔진은 하나의 OpCode를 가져와 피연산자와 함께 작업한 후, 그 다음 OpCode를 수행하는 식으로 동작한다.

바이트코드는 JVM은 이해할 수 있지만 아직 기계가 이해할 수 없는 코드다. 따라서 실행 엔진은 바이트코드를 기계가 실행할 수 있는 코드로 변환한다. 변환 방식에는 2가지가 존재한다.

  1. 인터프리터 : 바이트코드 명령어를 한 줄씩 해석하고 실행한다. 해석은 빠르나, 인터프리팅 결과의 실행은 느리다.
  2. JIT 컴파일러(Just-In-Time Compiler) : 인터프리터의 단점을 보완하기 위해 도입되었다. 인터프리터 방식대로 실행하다가 적절한 때에 전체를 컴파일해서 네이티브 코드로 변환하고, 이후 네이티브 코드로 직접 실행한다. (네이티브 코드를 실행하는 것이 하나씩 인터프리팅하는 것보다 빠르고, 네이티브 코드는 캐시에 저장해서 한 번 컴파일된 코드는 계속 빠르게 수행된다.)

JVM의 메모리 관리 실행과정

  1. 프로그램이 실행되면, JVM은 OS로부터 이 프로그램이 필요로하는 메모리를 할당받음. JVM은 할당받은 메모리를 용도에 따라서 여러 영역으로 나누어 관리함.

  2. 자바 컴파일러(JAVAC)가 자바 소스코드를 읽고, 자바 바이트코드(.class)로 변환시킴.

  3. 변경된 class파일들은 클래스 로더를 통해 JVM메모리 영역으로 로딩함.

  4. 로딩된 class파일들은 실행엔진(Execution engine)을 통해 해석됨.

  5. 해석된 바이트코드는 메모리 영역에 배치되어 실질적인 수행이 이루어짐. 이러한 실행과정 속 JVM은 필요에 따라 스레드 동기화나 가비지 컬렉션 같은 메모리 관리 작업도 수행함.

profile
꾸준함의 힘을 아는 개발자가 목표입니다 📍

0개의 댓글