Java Virtual Machine

최창효·2022년 2월 12일
0

자바 이해하기

목록 보기
1/8
post-thumbnail
post-custom-banner
요약
1. JVM은 Java ByteCode를 읽고 해석하는 가상의 머신
	1-1. .java파일(소스코드) - compiler(javac) -> .class(자바 바이트코드) - JVM -> 기계어
	1-2. OS에 맞는 JVM만 있으면 자바 실행이 가능하기 때문에 OS독립적임
2. JVM은 Class Loader, Runtime data area, Execution Engine로 구성되어 있음
	2-1. Class Loader - .class파일을 가져와 method area에 올림
    	2-1-1. Loading - .class를 메모리에 올리는 작업
        	Bootstrap - rt.jar를 로딩
            Execution - $JAVA_HOME/jre/lib/ext에 있는 클래스를 로딩
            Application - $CLASSPATH에 있는 클래스를 로딩
        2-1-2. Linking - load한 .class를 검증하고 사용할 수 있게 준비하는 과정
        	Verify - .class파일이 유효한지 확인
            Prepare - 메모리 할당, 기본값 설정
            Resolve - symbolic memory reference를 실제 reference로 교체
        2-1-3. Initialization - static변수 초기화 및 값 할당
	2-2. Runtime Data Area - JVM이 OS위에서 실행되면서 할당받는 메모리 영역
    	2-2-1. Method Area - 생성자, 클래스의 바이트코드, Static변수가 저장되는 공간
        	Runtime Constant Pool - JVM이 해당 메서드나 필드의 실제 메모리상의 주소를 찾기 위해 참조하는 곳
        2-2-2. Heap - 새롭게 생성된 객체가 저장되는 공간, GC의 대상이 됨
        	String Constant Pool - literal값이 저장되는 곳
        2-2-3. Stack - 임시로 사용되는 변수들이 저장되는 공간
        2-2-4. PC Register - 현재 수행중인 JVM의 명령어 주소를 저장하는 공간
        2-2-5. Native Method Stack - JNI를 통해 호출되는 코드를 수행하기 위한 공간
	2-3. Execution Engine - 바이트코드가 기계어로 변환되는 공간
    	2-3-1. Interpreter - 바이트코드를 한줄씩 읽으며 해석
        2-3-2. JIT Compiler - 번역된 메서드를 캐싱한 뒤 똑같은 메서드가 여러번 사용되면 캐싱해둔 값을 활용
        2-3-3. GC - 메모리를 자동으로 삭제
        2-3-4. JNI - 자바 이외의 다른 언어로 작성된 라이브러리 호출

JVM이란

Java ByteCode를 읽고 해석하는 가상 머신
자바 애플리케이션을 실행하기 위한 가상의 공간
자바를 (자바API와 함께)실행시켜 주는 것

자바는 JVM덕분에 OS독립적이며, 메모리를 효율적으로 관리할 수 있습니다.

자바 바이트코드란?

컴파일을 통해 JVM이 이해할 수 있게 번역된 자바 소스 코드

  • cf) 어셈블리 언어: 컴퓨터가 알아들을 수 있는 기계어와 1:1로 대응되는 언어, 중간 언어라도고 불림

등장 배경

C/C++은 컴파일 플랫폼과 타겟 플랫폼이 다른 경우 프로그램이 동작하지 않습니다.
그래서 C/C++은 크로스 컴파일(타겟 플래폼에 맞춰 컴파일하는) 과정이 필요합니다.

하지만 자바는 처음부터 다양한 플랫폼에서의 사용을 고려해 개발되었습니다. - Write Once Run Everywhere
그래서 자바는 C/C++과 달리 플랫폼 독립적인 개발이 가능하고, JVM이 이를 가능하게 만들어주고 있습니다.

C/C++에서 한번에 실행하던 컴파일 과정(인터프리터 언어 -> 어셈블리어 -> 기계어)을 자바는 두 단계로 나눴습니다.

  • .java - compiler(javac) -> .class(자바 바이트 코드) // (소스코드 -> 중간언어)
  • .class - JVM -> 기계언어 // (중간언어 -> 기계언어)

자바 바이트코드 언어를 기계어로 바꾸는 과정을 JVM이 담당하고 있습니다.
그렇기 때문에 우리는 OS마다의 자바코드가 아니라 JVM에서 동작하는 자바코드를 만들기만 하면 됩니다.

???: 야 우리가 지금 window용을 만들고 있는거야? 아니면 Linux용을 만들고 있는거야?
???: 응 둘다 아니야. 우리는 그냥 JVM용을 만들고 있는거야

대신 OS마다의 JVM이 필요하게 됩니다.

어디서나 쓸 수 있는 청소기를 만들고 싶어.
그럼 110v청소기, 220v청소기, 330v청소기를 각각 만들자 -> X (어디서나 쓸 수 없다)
110v, 220v, 330v 콘센트를 모두 가진 청소기를 만들자 -> X (나중에 440v가 등장하면?)
청소기는 220v로만 만들고 220v에서 다른v로 바꿀 수 있는 변압기(JVM)를 만들자 -> O
대신 110v to 220v 변압기, 330v to 220v 변압기, 440v to 220v 변압기가 각각 필요해.

동작 원리

JVM은 3부분으로 나눠져 있습니다.
1. 클래스를 가져오는 부분 (Class Loader)
2. 클래스의 요소들이 존재하게 되는 부분 (Runtime data area)
3. 클래스를 해석하는 부분 (Execution Engine)

Class Loader가 가져온 .class파일은 Runtime data area의 Method Area에 로딩되고, Execution EngineRuntime data area위의 .class파일을 해석합니다.

Class Loader

Class Loader는 자바 클래스(.class파일)를 가져와 메모리(Method Area)에 올리는 역할을 수행합니다.
Class Loader내부적으로 세 가지 프로세스가 존재합니다.

Loading

.class 파일을 읽어 바이트 코드를 메소드 영역에 올립니다.

  • Bootstrap class loader
    • 가장 최상위 클래스 로더로 최우선적으로 로드됩니다.
    • jre의 lib폴더에서 rt.jar 파일을 찾아 자바의 기본 API를 메모리에 로딩합니다.
    • rt는 runtime의 약자로 런타임 시 제공되는 모든 라이브러리(java.lang,System)가 포함되어 있습니다.
  • Extension(Platform) class loader
    • Bootstrap class loader의 자식입니다.
    • $JAVA_HOME/jre/lib/ext경로에 위치한 클래스를 로딩합니다.
    • JDBC등의 외부 라이브러리를 로딩합니다.
  • Application(System) class loader
    • Extension class loader의 자식입니다.
    • $CLASSPATH경로에 위치한 클래스를 로딩합니다.
    • 사용자가 직접 만든 .class파일은 Application class loader에 의해 로딩됩니다.

자료를 모아봅시다
인터넷 기사는 A가 모아오고
책에 있는 내용은 B가 모아오고
논문에 있는 내용은 C가 모아오세요

Bootstrap, Extension, Application은 모두 상속관계이며 delegate(위임)방식으로 작업을 진행합니다.

  • X 클래스에 대한 로딩을 요청하면
      1. ApplicationClassLoader가 상위 클래스인 Extension에게 위임합니다.
      1. ExtensionClassLoader가 상위 클래스인 Bootstrap에게 위임합니다.
      1. BootstrapClassLoader는 rt.jar에 X 클래스가 있는지 찾아서 있으면 반환합니다.
      1. Bootstrap에서 찾지 못했으면 Extension이 $JAVA_HOME/jre/lib/ext경로에 X 클래스가 있는지 찾아서 있으면 반환합니다.
      1. Extension이 찾지 못했으면 Application이 $CLASSPATH에서 X 클래스가 있는지 찾아서 있으면 반환합니다.

(위에서부터 훑으면서 내려오는 느낌)
모든 클래스를 한번에 로딩시켜주는 게 아니라 부분적으로 탐색하고 로딩시켜줌

Linking

사전준비 과정

  • Verify(검증)

    • Loading에서 가져온 클래스 파일이 JVM스펙, 자바 언어 스펙을 지켰는지 검증하는 과정입니다.
    • 가장 오랜 시간이 소요됩니다.
  • Perpare(준비)

    • static변수를 위한 메모리 할당과 해당 메모리를 기본값으로 초기화하는 과정입니다.
      • static변수란 메모리에 한번 할당되어 프로그램이 종료될 때 해제되는 변수를 말합니다.
  • Resolve(해석)

    • 심볼릭 메모리 레퍼런스를 메소드 영역에 있는 실제 레퍼런스로 교체합니다.
      ('A의 집'이라고 적어뒀던 주소를 'XX시 XX구 XX아파트'로 바꾸는 과정)

Initialization

모든 static변수들을 지정해둔 값(코드에서 선언한 값)으로 초기화하는 과정

Runtime data area

JVM이라는 프로그램이 실행되면서 할당받게 되는 메모리 영역입니다.

Method Area

  • 모든 쓰레드가 공유하는 영역입니다.
  • JVM이 시작될 때 생성됩니다.
  • 인스턴스 생성을 위한 객체 구조, 생성자, 필드(멤버 변수)가 저장됩니다.
  • 클래스의 바이트 코드, 메서드, static으로 선언한 변수(Static 변수)가 저장됩니다.
  • Runtime Constant Pool은 Method Area에 존재합니다.

Runtime Constant Pool

  • 클래스, 인터페이스 상수, 메소드,필드에 대한 모든 레퍼런스를 저장합니다.
  • JVM은 Runtime Constant Pool을 통해 해당 메소드나 필드의 실제 메모리상 주소를 찾고 그 값을 참조합니다.
    • cf) String Constant Pool
      • literal로 선언한 문자열이 저장,참조되는 공간입니다.
      • 힙에 존재합니다.
      • String literal과 new의 차이에서 String Constant Pool의 용도를 확인하실 수 있습니다.

Heap Area

  • 모든 쓰레드가 공유하는 영역입니다.
  • 동적으로 생성된 객체와 인스턴스 변수가 저장되는 영역입니다.
  • new연산자로 생성된 객체(원시 데이터를 제외한 Object 타입의 데이터)나 배열 등이 저장됩니다.
  • GC의 대상이 되는 공간입니다.
  • Heap은 다시 5가지 공간(에덴, 서바이버0, 서바이버1, Old Generation, Meta Space)으로 나뉩니다.
  • String Constant Pool은 Heap에 존재합니다.

Stack Area

  • 쓰레드마다 생성되는 영역입니다.
  • 메서드를 호출할때마다 Frame을 스택에 쌓고 메서드가 종료되면 Frame을 제거합니다.
  • 임시적으로 사용되는 변수들이 저장되는 공간입니다. 지역변수, 연산자 스택, 프레임 데이터가 저장됩니다.

PC Register

  • 쓰레드마다 생성되는 영역입니다.
  • 현재 수행중인 JVM의 명령어 주소를 저장하는 공간입니다.

Native Method Stack

  • 쓰레드마다 생성되는 영역입니다.
  • 자바가 아닌 다른 언어로 작성된 코드를 위한 공간으로, JNI를 통해 호출되는 C/C++등의 코드를 수행하기 위한 공간입니다.

모아온 자료를 분류해 봅시다.
X에 대한 내용은 여기에 놓고,
Y에 대한 내용은 여기에 놓고,
다른 팀에 요청했던 내용은 여기에 넣어두세요.
작업이 많으니 다른작업 해야되면 우리가 어느부분 작업하고 있는지 까먹지 않게 책갈피도 꼽아두세요.

Execution engine

  • 바이트코드(.class)가 기계어코드(네이티브 코드)로 변환되는 공간입니다.
  • Execution engine은 Interpreter방식과 JIT Compiler 두 가지 방법을 혼합적으로 사용해 바이트코드를 명령어 단위로 읽고 실행합니다.

Interpreter

  • 바이트코드를 한 줄씩 해석하며 실행하는 방식입니다.

JIT Compiler

  • 한 줄씩 해석해서 느리다는 Interpreter의 단점을 보완하기 위해 도입된 개념입니다.
  • JIT Compiler는 런타임 시점에 컴파일이 진행됩니다.
  • JIT Compiler는 번역된 메서드를 캐싱(저장)해둔 다음, 똑같은 메서드가 여러번(특정 임계치 이상) 사용된다면 번역하지않고 캐싱해둔 값을 사용합니다.
    • 컴파일 임계치: JVM 내에 있는 메서드가 호출된 횟수 + 메서드가 루프를 빠져나오기까지 반복한 횟수

GC

더 이상 사용하지 않는 메모리를 자동으로 삭제하는 역할을 담당합니다.
가장 처음 자바가 메모리 효율적이라 설명했던 이유가 이 GC덕분입니다.
GC에 대한 자세한 설명은 여기에서 진행하겠습니다.

Java Native Method Interface(JNI)

다른 언어들로 작성된 라이브러리들을 호출하거나 반대로 호출되는 것을 가능하게 하는 프로그래밍 프레임워크 입니다.
JNI에서 호출되는 다른 코드를 Native Method Stack공간에서 처리합니다.

자료를 다 모았으니 이제 해석을 해 봅시다.
한줄한줄 열심히 읽어보고,
이전에 읽었던 내용은 다시 안읽고 바로 활용하기도 하고
더 이상 읽을 필요가 없는 자료는 버리기도 합시다.

기타

JDK와 JRE의 차이

JRE

  • JVM+라이브러리

JDK

  • JRE+개발시 필요한 여러 도구들(javac,java등)

JVM 전체 구조

References

profile
기록하고 정리하는 걸 좋아하는 개발자.
post-custom-banner

0개의 댓글