JVM(Java Virtual Machine)

Geunhyung Pyun·2023년 1월 5일
0

Java

목록 보기
1/5
post-custom-banner

Java는 어느 특정 OS에 종속되지 않고 어느 곳에서나 사용이 가능하다. 그 이유는 OS 위에서 Java를 실행시키기 위한 어느 무언가가 있기 때문인데 이것이 JVM이다.

Java 소스코드(*.java)는 JVM을 거쳐서 OS에 도달하게 된다. Java 소스코드는 기계어가 아니기에 컴파일의 과정이 필요하다.
그 과정을 Java Compiler가 담당하고 Java 소스코드를 Java bytecode(*.class)로 변환한다.

참고로 Java compiler는 javac.exe를 말한다.

해당 바이트코드는 기계어가 아니기에 OS에서 실행이 불가능하다. 그렇기에 한 번 더 해석하는 과정이 필요한데 이 과정을 JVM이 담당한다.

JVM은 자바 가상 머신으로 자바 바이트 코드를 OS에 특화된 코드로 변환(JIT 컴파일러, 인터프리터)하여 실행한다. 바이트 코드를 실행하는 표준이자 구현체다.

특징으로는 다음과 같다.

  • 스택 기반
  • 가비지 컬렉션 사용
  • 단일 상속 형태의 객체 지향 프로그래밍을 VM 수준에서 구현
  • 포인터는 지원하나 주소 값을 임의로 조작이 가능한 포인터 연산 불가능
  • 모든 기본 타입의 정의를 명확히 하여 플랫폼 독립성 보장
  • 스택에서 가져올 피연산자의 타입을 명령어에 지정
  • 데이터 흐름 분석에 기반한 자바 바이트코드 검증기를 통해 stack overflow, 명령어 피연산자의 타입 규칙 위반, 지역 변수 초기화 전 사용 등 문제들을 실행 전에 검증

구성 요소

클래스 로더(Class Loader)

  • JVM 내로 자바 클래스를 동적 로드(프로그램이 메모리로 라이브러리를 적재/링크)하는 JRE의 일부이다.
  • 바이트 코드를 읽고 메모리에 저장한다.
  • 로드 : 클래스를 읽어오는 과정. 바이트코드를 읽고 그 내용에 따라 적절한 바이너리 데이터를 만들고 Method Area에 저장한다. JVM 로드 시 3가지의 클래스 로더들이 사용된다.
    • 부트스트랩 : JAVA_HOME\lib 경로에 있는 핵심 자바 라이브러리들을 불러들이는 역할. 네이티브 코드로 작성되어 있다. 최상위 우선순위를 가진 클래스 로더다.
    • 플랫폼 : 확장 디렉터리(JAVA_HOME\lib\ext)에 코드를 로드한다.
    • 애플리케이션 : CLASSPATH 환경변수에서 클래스를 읽는다. 가장 하위 우선순위다.
    • 애플리케이션에도 클래스를 못 찾으면 ClassNotFoundException이 발생
  • 링크 : 레퍼런스를 연결하는 과정. 3가지의 단계가 있다.
    • verify : 바이트 코드 파일 형식이 유효한지 체크
    • prepare : 클래스 변수(static 변수)와 기본값에 필요한 메모리를 준비
    • resolve : 심볼릭 메모리 레퍼런스(논리적인 레퍼런스)를 Method Area에 있는 실제 레퍼런스로 교체한다.
  • 초기화 : static 값들 초기화 및 변수에 할당

실행 엔진(Execution Engine)

JIT 컴파일러

JIT 컴파일러는 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일러로 인터프리터 방식의 단점을 보완하기 위해 도입하였다.
바이트코드를 읽어서 빠른 속도로 기계어를 생성할 수 있고 이 과정이 실시간으로 일어난다.
실행 시점에서 인터프리트 방식으로 기계어 코드를 생성하고 캐싱한다. 같은 함수가 여러 번 불릴 때에 매번 기계어 코드를 생성하는 것을 방지한다. 적절한 시점 이후에는 바이트 코드 전체를 컴파일해서 기계어로 변경한다.
또한 JIT 컴파일러가 컴파일하는 과정은 바이트 코드를 인터프리팅하는 것보다는 오래 걸리므로 한 번만 실행되는 코드일 때는 인터프리팅이 유리하다. 그렇기에 JVM은 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고 일정 정도를 넘을 때만 컴파일을 수행한다.
최적화를 미리 해주기 때문에 번역이 빠르게 진행되어 시간적으로도 이점을 갖게 되었고 가상 머신이 설치되어 있으면 빠르게 실행할 수 있다는 점 때문에 이식성이 뛰어나다고 할 수 있다.

Interpreter

인터프리터는 한 줄씩 번역하여 동시에 즉시 실행시키는 프로그램이다. 시분할 시스템에 유용하며 수정이 쉽다는 장점이 있다. 다만 실행 속도가 느리다는 단점이 있다. 목적 프로그램은 따로 생성하지 않는다.

Garbage Collector

메모리 관리 기법 중 하나. 동적으로 할당했던 메모리 영역 중 필요없게 된 영역을 해제하는 기능이다. 장점으로는 프로그래머가 동적으로 할당한 메모리 영역을 전체적으로 완벽하게 관리할 필요가 없어진다는 점이 있다.(Through-put, stop-the-world)

이 친구는 아예 따로 한 번 다뤄야 될 것 같다. 양이 좀 있다.

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


JVM이 프로그램을 수행하기 위해 OS로부터 별도로 할당받은 메모리 영역이다. PC Register, Navite Method Stack, JVM Stack은 각 쓰레드 별로 할당을 받고 Method Area, Heap은 쓰레드들이 공유한다.

PC Register

현재 수행 중인 JVM 명령의 주소이다. 쓰레드가 시작될 때 생성되고 쓰레드마다 하나씩 존재한다. 현 쓰레드가 어떤 부분을 어떤 명령으로 실행해야할 지에 대한 기록을 한다.

Native Method Stack

Native Method를 호출하는 코드를 수행하기 위한 스택으로 자바가 아닌 언어에서 제공되는 메서드를 수행하기 위함이다. Java Native Interface를 통해서 바이트 코드로 전환하여 저장하게 되고 독자적으로 프로그램을 실행시키는 영역이다.

JVM Stack

쓰레드가 시작될 때 생성이 되고 메소드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장하기 위한 영역이다. 즉, 지역 변수, 파라미터, 기본 자료형, 리턴값 등 연산에 임시로 사용되는 정보들이 저장되는 영역이다. 또한 참조변수의 메모리 주소도 저장된다.
메서드가 수행될 때마다 하나의 스택 프레임이 생성되어 JVM Stack에 추가되고 종료되면 스택 프레임이 제거된다.
참고로 stack frame은 메서드가 호출될 때 그 메서드만의 스택 영역을 구분하기 위해 생성되는 공간을 말한다. JVM은

Method Area

Class Area, Static Area라고도 한다. 클래스 정보를 처음 메모리 공간에 올릴 때 초기화되는 대상을 저장하기 위한 메모리 공간이다. 즉, 메소드 정보, 클래스 정보, static 변수, final class, constant pool 등이 저장된다. 안에 Runtime Constant Pool이라는 영역이 따로 있는데 이 영역은 상수 자료형을 저장하여 참조하고 중복을 막는 역할을 수행한다.

Heap

인스턴스 변수의 데이터와 참조 자료형이 저장된다. Method Area에 로드된 클래스로만 객체 생성이 가능하다. JVM이 종료될 때까지, 가비지 컬렉터에서 지워질 때까지 메소드 호출이 끝나도 메모리에 유지된다.
해당 영역은 3 구역으로 나뉘어진다.

  • Permanent Generation : 생성된 객체들의 정보의 주소값이 저장된 공간이다. 클래스 로더에 의해 로드되는 클래스, 메서드 등에 대한 메타 정보가 저장되고 JVM에 의해 사용된다.
  • New/Young : 이곳의 인스턴들은 가비지 컬렉터에 의해 사라진다. 생명 주기가 짧은 젊은 객체를 GC대상으로 하는 영역이다. Minor GC가 발생한다.
    • Eden : 객체들이 최초로 생성되는 공간으로 객체가 가득차게 되면 Survivor1 영역에 값들을 복사하고 가비지 컬렉트가 발생한다.
    • Survivor 0, 1 : Eden에서 참조되는 객체들이 저장되는 공간이다.
  • Old : 이곳의 인스턴들은 가비지 컬렉터에 의해 사라진다. 생명 주기가 긴 오래된 객체를 GC 대상으로 하는 영역이다. Major GC가 발생한다. Minor GC에 비해서는 느리다. New/Young 영역에서 일정시간 참조되고 있는 객체들이 저장된다.

JNI(Java Native Interface)

Native 키워드를 사용한 메서드 호출 구역으로 C, C++, 어셈블리 함수를 사용할 수 있는 방법을 제공한다.

알아둘 개념

Java bytecode

JVM이 이해할 수 있는 언어로 변환된 자바 소스코드를 의미한다. 코드의 명령어 크기가 1바이트라 바이트 코드라고 한다. JIT 컴파일러 혹은 실시간 번역기에 의해 바이너리 코드로 변환된다.

JDK (Java Development Kit)

Java를 사용하기 위한 기능을 갖춘 SDK이다. JRE + 개발 툴 구성이다. 소스 코드를 작성할 때 사용하는 자바 언어는 플랫폼에 독립적이다. 오라클은 자바 11부터 JDK만 제공하고 JRE를 따로 제공하지 않는다. 그 이유는 자바 9부터 모듈을 제공하기 때문에 그렇다.

JRE (Java Runtime Enviornment)

Java 애플리케이션을 생성하고 실행하기 위한 구성요소로 JDK의 일부이다. JVM + Java 클래스 라이브러리로 구성된다. 개발 관련 도구는 포함하지 않고 JDK에서 제공한다. JVM, 핵심 라이브러리 및 자바 런타임 환경에서 사용하는 프로퍼티 세팅이나 리소스 파일을 가지고 있다.

Java

  • JDK의 컴파일러(javac)을 사용해 바이트 코드로 컴파일 할 수 있다.
  • 유료화 : 오라클에서 만든 Oracle JDK 11 버전부터 상용으로 사용할 때 유료이다.

참고

profile
개발자를 원하는 사람.
post-custom-banner

0개의 댓글