개발쿠키

[JAVA]JVM 본문

개발/java

[JAVA]JVM

쿠키와개발 2023. 7. 6. 17:16

JVM과 자바 어플리케이션

JVM은 자바 프로그램을 실행하기 위한 소프트웨어이다. 다시 말해 결국 JVM도 일종의 프로그램이다. 그렇기 때문에 자바 어플리케이션을 실행 시키면 우선 JVM이 OS로부터 메모리를 할당을 받는다.

 

이후 .java 파일의 소스 코드를 컴파일 하여 나오는 바이트 코드를 해석하고 실행하는 역할을 한다. 

public class Main {

    public static void main(String[] args) {
        Audio audio = new Audio();
    }
}



class Audio {
    final static String color = "WHITE";
    int volume;
    
    void volumeUp() {
        this.volume += 1;
    }

    void volumeDown() {
        this.volume -= 1;
    }
    public String getColor() {
        return color;
    }
}

위와 같이 Main.java에 작성된 소스코드를 아래와 같이 바이트 코드(.class)로 변환해놓으면 이걸 JVM의 Class Loader에게 전달하면 바이트 코드를 현재 사용하고 있는 운영체제가 이해할 수 있는 기계어로 실행하는 역할을 한다. 

public class javastudy/Main {
  ...
  
  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 6 L0
    NEW javastudy/Audio
    DUP
    INVOKESPECIAL javastudy/Audio.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 7 L1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL javastudy/Audio.getColor ()Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)
    
   ...
}

 

 

이러한 바이트 코드들은 Class Loader를 통해 런타임 데이터 영역에 로딩되고 실행 엔진을 통해 해석되고 실행 된다.


JVM의 구조

그럼 JVM의 구조를 그림으로 보면서 알아보자  혹시 머리에 잘 들어오지 않으면 직접 그려보는걸 추천합니다! 

JVM의 크게 런타임 데이터 영역(Runtime Data Area), 클래스 로더(Class Loader), 가비지 컬렉터(Garbage Collector), 실행 엔진(Execution engine)로 구성되어 있다. 그럼 하나하나 간략하게 살펴보자.(런타임 데이터 영역에는 스레드가 3개 있다라는 것을 표현하려고 해당 그림처럼 그렸다)


1. Execution Engine(실행 엔진)

위에서 실행 엔진은 런타임 데이터 영억의 Method 영역에 로딩된 바이트 코드를 해석하고 실행시킨다고 했다. 이때 바이트 코드를 실행 시키는 2가지 방법이 있다. 

  • Interpreter
  • JIT(Just In Time)

Interpreter 

인터프리터는 자바 바이트 코드를 한 줄씩 해석하고 실행하는 방식이다. 해당 방식은 코드를 즉시 실행할 수 있지만 반복적인 실행이 있는 경우에는 속도가 느려질 수 있다. 예를 들어 동일한 메소드를 여러 번 수행하면 그때마다 해석하는 과정을 거치기 때문에 효율성 측면에서 떨어진다. 이를 보완하기 위해 나온 것이 JIT 방식이다.

 

JIT(Just In Time)

JIT 컴파일러는 일반적으로 인터프리터와 함께 사용되며 인터프리터가 반복적으로 실행되는 부분을 식별해서 해당 바이트 코드를 컴파일하여 기계어로 번역한다. 이후부터는 해당 코드를 인터프리터가 아닌 기계어로 컴파일되어 있는 코드를 직접 실행하여 빠른 속도로 실행 될 수 있다. 

 

2. Class Loader(클래스 로더)

자바는 프로그램이 컴파일이 아닌 런타임 중일 때(바이트 코드를 실행 중일 때)필요한 클래스를 로드하고 링크는 특징이 있다. 하지만 난 이 말이 처음에 도저히 이해가 안갔다...그래서 gpt한테 물어봤다. 

 

내가 처음에 생각했던건 Main.class를 JVM에 넘기면 JVM에서는 main 메소드에서 사용되는 모든 클래스들을 미리 불러와서 메소드 영역에 올려놓는건줄 알았는데 그게 아니였다. 순서를 간략히 보자면(stack 영역까지는 생략하겠다...)

 

1. Main.java -> Main.class로 변환

2. Class loader가 Main.class의 바이트 코드를 메소드 영역에 로딩 

3. 실행 엔진이 한줄 해석 후 실행 또 한줄 해석 후 실행

4. 해석 중 예시로 Person p = new Person()이라는 코드가 있을 때 해당 클래스가 로딩되어 있지 않을 경우 클래스 로더에게 요청 후 해당 Person.class 파일을 로딩 

 

여기서 추가적으로 만약에 Person p;라고 하고 해당 객체를 인스턴스화 하지 않거나 초기화 하지 않은 경우에는 클래스 로딩이 일어나지 않는다.

 

아무튼 이러한 과정 중에 클래스 로더는 순서를 보면 알 수 있듯 런타임 데이터 영역 중 메소드 영역에 동적으로 클래스를 로드하는 역할을 하며 로딩, 링크, 초기화 단계를 수행하여 클래스를 로딩하고 해당 정보를 메소드 영역에 넣는다. 

 

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

런타임 데이터 영역은 JVM이 프로그램을 수행하기 위해 os로 부터 별도로 할당받은 메모리 공간이다. 다시 말해 실질적인 데이터들이 메모리에 저장되는 곳이라고 볼 수 있다. 

 

런타임 데이터 영역은 Heap, Method, Pc Register, Stack, Native Method Stack으로 나뉘어지며 Heap, Method는 모든 스레드가 공유하는 영역이고 그 외는 스레드 별로 하나씩 가지고 있다. 이해가 안된다면 위의 그림을 보면 좀 더 이해가 될거다. 

 

Method 

메소드 영역은 static으로 선언된 변수들을 포함하여 Class 레벨의 모든 데이터가 저장된다.

Method Area에는 Runtime Constant Pool이라는 별도의 영역이 존재하며 상수 자료형을 저장하며 참조하는 역할을 한다. 

저장되는 정보는 아래와 같다.

  • Field Info - 멤버 변수 이름, 타입, 접근 제어
  • Method Info - 메소드 이름, Return 타입, 매개변수, 접근 제어
  • Type Info: Class 또는 Interface 등의 여부, Type 속성, Super Class 이름

Heap

프로그램 상에서 실제 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역이다.

new 연산을 통해 생성된 객체 또는 인스턴스, 배열을 저장하기 위한 메모리 영역이며 해당 영역은 GC의 관리 대상이 되는 공간이다. 추가적으로 string constant pool 또한 해당 영역에서 존재한다.

Heap 영역에 생성된 변수는 해당 객체가 소멸되기 전이나 가비지 컬렉터가 정리하기 전에는 남아있다. 

 

Heap 영역은 효율적인 GC를 위해 크게 Young Generation과 Old Gneration 영역으로 나뉜다. Young Generation 영역은 다시 eden, Survivor0, Survivor1영역으로 구분되고 Old Gneration은 그냥 Old(Tenured) 영역만 존재한다. 

 

Young Generation영역은 일시적으로 사용하거나 하는 생명 주기가 짧은 객체를 GC 대상으로 하는 영역이다. Old Gneration은 반대로 생명 주기가 긴 객체를 GC 대상으로 하는 영역이다. 

 

Eden - 객체가 최초로 Heap에 할당되는 장소

Survivor - eden 영역에서 넘어온 객체들이 존재하는 영역

Old(Tenured) - 앞서 Young Generation영역에서 넘어온 객체들이 존재하는 영역

 

Stack

스택 영역은 메소드 호출과 관련된 정보를 저장하는 자료구조이며 메소드의 정보, 지역 변수나 매개변수 등이 저장된다.  

스택 영역에서 원시 타입의 경우는 스택 영역에 값이 그대로 저장되지만 참조타입의 경우 실제 값은 Heap 영역에 저장되고 해당 변수는 참조값을 가지고 있는다. 

public static void main(String[] args) {
    Audio audio = new Audio();
    System.out.println(audio);
}

해당 코드를 실행하면 javastudy.Audio@776ec8df라는 값이 나온다(참조 값은 다 다릅니다 저는 저렇게 나왔어요)

 

각 스레드에서 만약 실행중에 메소드 호출이 발생하면 스택 에는 Frame라는 스택 단위가 쌓인다. 만약 호출한 메소드의 로직이 완료되어 결과를 반환하면 해당 Frame은 pop 되어 빠지고 호출했던 프레임으로 다시 돌아간다. 

 

그럼 위의 코드에서는 총 몇개의 스택 프레임이 생기고 종료될까? 정답은 3개다.

1. main 스택 프레임 - 메인 메소드를 실행하면서 스택 프레임이 하나 생긴다. 

2. 생성자 호출 스택 프레임 -  객체 생성 시 생성자 호출을 하게 되면 스택 프레임이 한 개 더 생긴다.

3. println 호출 스택 프레임 - println  메소드를 호출할 때 스택 프레임이 마지막으로 생기게 된다.

 

Native Method Stack 

바이트 코드가아닌 실제 실행할 수 있는 다른 언어로 작성된 코드를 위한 공간이다.

 

 

Pc Register

스레드가 시작될 때 생성되며 현재 수행중인 상태 정를 저장하는 공간이며 스레드별로 어떤 명령을 실행해야 할지에 대한 기록을 담당하는 영역이다. 

 

4. GC(가비지 컬렉)

GC는 따로 작성하려고 합니다. 글이 추가되면 링크 걸어놓겠습니다

 

 

 


참고

https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/

https://www.youtube.com/watch?v=AWXPnMDZ9I0&t=419s 

https://m.blog.naver.com/heartflow89/220954420688

'개발 > java' 카테고리의 다른 글

[JAVA]클래스, 객체, 인스턴스  (0) 2023.07.20
[JAVA]Call By Reference, Call By Value  (0) 2023.07.14
[JAVA]String  (0) 2023.07.04
[JAVA]JVM, JRE, JDK  (0) 2023.07.03
[Java]Map getOrDefault  (0) 2023.06.27