[CS] 5) JVM의 개념과 구조(JDK, JIT, GC)

songh·2024년 2월 19일
0

CS지식

목록 보기
6/35

JVM은 자바 가상 머신의 약자로, 자바를 실행하기 위한 가상기계라고 할 수 있다.
자바는 앞서 컴파일 과정에서 얘기했지만 OS에 종속적이지 않다는 특징이 있다. OS 위에서 자바를 실행시킬 무언가가 필요한데, 이것이 바로 JVM이다.

➡️ JVM은 OS에 종속되지 않고 CPU가 자바를 인식, 실행할 수 있게 하는 가상 컴퓨터다.

JVM 자체는 플랫폼 종속적이다. 따라서, 아키텍쳐가 무엇이냐에 따라 JDK를 다르게 받아야함을 알 수 있다.

JDK(Java Development Kit) 안에 JRE이 포함되어있는데 JRE 안에 JVM과 library가 포함되어있다. 따라서 플랫폼 종속적인 JDK를 받으면 해당 플랫폼의 바이너리 코드로 변환할 수 있는 JVM이 들어있다는걸 알 수 있다.

자바 소스코드(원시코드, .java)는 cpu가 인식을 못하므로 기계어로 컴파일해줘야 한다. 컴파일러가 소스코드를 컴파일하여 생성된 바이트 코드(.class)는 JVM의 인터프리터를 거쳐 OS가 인식할 수 있는 기계어로 컴파일되어 OS에 도달한다.

개발자 -> 자바 소스코드(.java) -> 컴파일러 -> 바이트코드(.class) -> JVM -> 인터프리터 -> 기계어 -> OS

자바 컴파일러는 .java 자바 소스코드를 .class라는 자바 바이트 코드로 변환한다. 하지만 이는 기계어가 아니므로 OS에서 바로 실행되지 않는다. JVM이 OS가 바이트코드를 이해할 수 있도록 해석하여 바이너리코드가 된다. 따라서 OS에 종속적이지 않고 자바 파일만 만들면 어느 디바이스든 실행될 수 있는 것이다. 이 바이트코드는 다시 인터프리터/JIT 컴파일러에 의해 컴파일되어 바이너리코드로 변환된다.

  • 자바 바이트코드 : JVM이 이해할 수 있는 언어로 변경된 자바 소스코드
  • 바이너리 코드(이진코드) : 컴퓨터가 인식할 수 있는 0과 1로 구성된 이진코드
  • JIT 컴파일러 : 프로그램 실행시점에 기계어로 변역하는 컴파일러

물론 JIT 컴파일러가 컴파일하는 과정은 바이트코드를 인터프리팅하는 것보다 훨씬 오래걸리므로, 한번만 실행되는 코드라면 컴파일하지 않고 인터프리팅하는 것이 유리하다.

➡️자바에서 자바컴파일러가 소스코드를 바이트코드로 변환

➡️이후 실제 바이트코드를 실행하는 시점에 자바가상머신(JVM)이 JIT 컴파일을 통해 바이트코드->기계어로 변환한다.

JVM의 핵심은 메모리 영역을 가지고 있다는 것과 바이트 코드를 기계어로 해석해서 실행해준다는 점이다.

JVM은 크게 3가지로 나뉜다.
클래스 로더, 실행엔진, 런타임 데이터 영역(JVM 메모리)
*실행엔진 구성요소 : 인터프리터, JIT 컴파일러, 가비지콜렉터

1) 클래스 로더

준비가 끝나면 JRE 클래스로더가 프로그램 실행에 필요한 바이트 코드를 JVM 메모리 영역에 올려준다. 필요할때마다 동적으로 클래스를 로딩시켜주기 때문에 가장 먼저 메인 메소드가 메모리에 올라가게 된다.

JVM 내에서 클래스 파일(.class)를 로드, 링크를 통해 배치하는 작업을 수행하는 모듈이다. 런타임 동작시 동적으로 클래스를 로드하고 jar 파일내 저장된 클래스들을 jvm 위에 탑재한다.

➡️클래스를 처음 참조할때, 해당 클래스를 로드하고 링크, 초기화하는 역할을 한다.

로딩 - 링크 - 초기화를 진행한다.

  • 로딩 : 클래스를 파일에서 가져와서 JVM 메모리에 로드하는 역할
  • 링크 : 레퍼런스를 연결하는 역할
  • 초기화 : static 값을 초기화하는 역할

1. 로딩

클래스 로더의 종류는 아래와 같고 실행 순서는 차례대로 진행된다.
BootStrap Class Loader -> Extension Class Loader -> Application(System) Class Loader 순으로 실행된다.

✔ BootStrap Class Loader : lib/RT.jar에 있는 클래스를 로딩
✔ Extension Class Loader : lib/ext/*.jar에 있는 클래스를 로딩
✔ Application(System) Class Loader :

- 클래스 로더의 특징
계층적이고 가시적인 규약을 가지며(자식 클래스 로더에서 찾지 못한 클래스는 부모 클래스 로더에서 찾을 수 있다 = 위임형 로드요청)

jvm 메모리 영역에 찾고자 하는 클래스가 없다면 System Class Loader에게 호출을 위임->Extension Class Loader에게 호출을 위임 -> Bootstrap Class Loader에게 호충을 위임 -> 만약 BootStrap Class Loader에서 해당 클래스를 찾는 다면 반환-> 없다면 Extension Class Loader에서 찾고 반환 -> 없다면 System Class Loader에서 찾고 없다면 ClassNotFoundException이 발생한다.

- 클래스 로더는 두 가지 방식으로 클래스를 로딩한다.
자바는 동적
1. 로드타임 동적로딩

  1. 런타임 동적로딩

2. 링킹 (검증-준비-분석)

검증 - .class 파일이 유효한지 검증
준비 - 메모리 준비과정으로 static 변수와 기본값에 필요한 메모리를 준비
분석 - 심볼릭 메모리 레퍼런스를 실제 레퍼런스로 교환

2) 메모리 영역(런타임 데이터 영역)

프로그램 수행을 위해 os에서 할당받은 메모리 공간을 말한다. 총 5가지 영역으로 나뉜다. 메모리 영역은 크게 모든 쓰레드가 공유하는 영역과 쓰레드별 하나씩 생성되는 영역으로 나뉜다.

  1. 모든 쓰레드가 공유하는 영역 : 메소드 영역, 힙 영역
  2. 쓰레드 별 하나씩 생성되는 영역 : 스택영역, 네이티브 메서드 영역, pc 레지스터

run time data area

  • jvm은 하나의 애플리케이션이 여러 개의 스레드를 갖는걸 허용한다.
    따라서 main() 스레드 생성되면 gc 스레드 외에도 여러 스레드가 생성될 수 있는 것
    이름 그대로 프로그램 실행 중 데이터를 저장하는 영역이다.

📗 프로세스
단순히 실행중인 프로그램을 말하며, 사용자가 작성한 프로그램이 os에 의해 메모리 공간을 할당받아 실행중인 것을 말한다.

📗 스레드
프로세스 내에서 실제로 작업을 수행중인 주체를 의미한다. 모든 프로세스에는 한 개이상의 스레드가 존재하여 작업을 수행한다. 또한 두 개이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스라고 한다,

  • 힙 영역 : 런타임에 동적으로 할당되는 데이터가 저장되는 영역으로 인스턴스 변수, 객체, 필드가 여기에 해당하며 더이상 참조되지 않는 인스턴스는 gc에 의해 제거된다. 허용 공간 초과시 outofmemoryerror
  • 메서드영역 : JVM이 시작될때 생성되고 JVM이 읽은 클래스 구조 저장
    클래스 구조 => 클래스명, run time constant pool, 필드 데이터, 생성자 등
    허용 공간 초과시 outofmemoryerror
  • run time constant pool => 런타임시 사용되는 리터럴을 constant_pool 테이블로 참조
  • PC 레지스터 : 스레드가 어떤 명령어로 실행해야할지 JVM 명령의 주소를 저장

현재 실행중인 명령어의 주소값을 저장(method area의 메모리 주소)
만약 실행중인 명령어가 native 메소드면 pc register값은 undefined

  • native method => c, c++ 등 다른 언어로 작성된 메소드
  • JVM 스택 : 지역변수, 매개변수, 메서드 정보, 임시데이터 저장

스레드 한개당 하나씩 스택이 생성된다. 스택에는 frame들을 담는다.
이 frame은 메소드 호출시 생성(push)되고 명령 마치거나 예외 발생시 종료(pop)
frame에는 지역변수, 리턴값 등 저장
스레드에 더 이상 frame 저장할 공간이 없을때 stackoverflowerror 발생
JVM -Xss 옵션 -> 스택 사이즈 조절가능하나, 스레드 너무 많이 생기면 outofmemoryerror 발생

  • native method stacks : native method를 위해 사용되는 영역
  • 네이티브 메서드 스택 : 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행하는 영역

(static 변수)
힙 영역 중 java 7까지는 permgen, 8부터는 metaspace에 저장된다.

3) 실행엔진

코드 메모리에 올라온 코드는 실행엔진에서 한줄씩 기계어로 해석해서 실행해준다. 바이트 코드를 한줄씩 기계어로 번역해서 실행하는 방식을 인터프리터라고 하고, 파일 전체를 한번에 기계어로 번역하는 방식을 JIT라고 한다. 이 두 방식을 적절하게 섞어서 실행시켜준다.

  • 가비지 컬렉션 : 자바 이전에는 프로그래머가 모든 프로그램 메모리를 관리했으나, 자바에서는 JVM이 프로그램 메모리를 관리한다. JVM은 가비지 컬렉션이라는 프로세스를 통해 메모리를 관리한다. 자바 프로그램에서 사용되지 않는 메모리를 지속적으로 찾아서 제거하는 역할을 한다.

Static 메서드와 필드는 클래스 파일이 최초 호출될때 메모리에 올라가고 프로그램 종료가 될때까지 계속 남아있게 된다. 그 외 코드들은 필요할때만 메모리에 올라갔다가 사용후 GC에 의해 없어진다. 이렇게 평소 메모리에 없다가 객체 생성 후 해당 클래스의 바이트 코드를 메모리에 올려주고 삭제되는 작업이 반복된다.

실행순서 : 참조되지 않는 객체들을 탐색 후 삭제 -> 삭제된 객체의 메모리 반환 -> 힙 메모리 재사용

0개의 댓글

관련 채용 정보