JVM을 공부해보자

Panda·2023년 2월 20일
0

Java

목록 보기
3/7
post-thumbnail

취업준비 하다가 제가 CS부분에서 많이 약한걸 깨닫고
자바도 기초부터 제대로 이해하고 넘어가려고 빡세게 공부하려고 합니다.

JVM 부터 알아볼까요?

JVM이란?

Java Virtual Machine의 약자
OS에 의존하지 않고 CPU가 Java를 인식 및 실행할 수 있게 하는 가상 컴퓨터

Java는 OS에 의존하지 않는 다는 특징을 다들 들어보셨는데요. 바로 JVM이 하는 역할때문에 그러한 특징을 가지고 있는 것입니다.
바로 왜 그러한 특징을 가지고 있는지 JVM이 어떤식으로 컴파일을 하는지에 대해 알아보겠습니다.

JVM 컴파일

Java 소스코드(원시코드)를 CPU가 인식하지 못하므로 기계어로 컴파일을 해줘야합니다.
하지만 Java는 JVM을 거쳐서 OS에 도달하기 때문에 한번에 바로 기계어로 컴파일 되는 게 아니라 JVM이 인식할 수 있는 Java Bytecode(.class file)로 변한됩니다.

단점이 있었는데요 없습니다

Java 어플리케이션은 JVM 거치고 해석(인터프리트)까지 되기 때문에 실행 시 속도가 느리다는 단점이 있었는데 요즘은 자바 바이트코드를 하드웨어 기계어로 바로 변환해주는 JIT(Just-In-Time) 컴파일러와 최적화 기술이 적용되어 느렸던 속도를 많이 향상시켰다고 합니다.
단점에 관한 부분은 옛날 옛적 이야기 같습니다.

JVM 컴파일 순서

  1. Java 소스코드 작성

  2. Java 컴파일러가 Java 소스파일을 컴파일하여 Java Bytecode 생성

  3. 컴파일된 Java Bytecode를 클래스로더에게 전달

  4. 클래스 로더는 동적로딩을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역, 즉 JVM의 메모리에 올립니다.

    1. 로드 : Java Bytecode(.class file)을 가져와서 JVM의 메모리에 로드
    2. 검증 : 자바 언어 명세 및 JVM 명세에 명시된 대로 구성되어 있는지 검사
    3. 준비 : 클래스가 필요로 하는 메모리 할당 (필드, 메서드, 인터페이스 등)
    4. 분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스다이렉트 레퍼런스로 변경 즉 실제 메모리 주소 값으로 변경
    5. 초기화 : 클래스 변수들을 적절한 값으로 초기화 (static 필드)
  5. 실행엔진(Execution Engine)은 JVM 메모리에 올라온 Java Bytecode들을 명령어 단위로 하나씩 가져와서 실행합니다. 이때, 실행 엔진은 두가지 방식이 있습니다.

    1. 인터프리터 : Java Bytecode 명령어를 하나씩 읽어서 해석하고 실행합니다. 명령어 하나 하나의 실행은 빠르지만 전체적인 실행 속도가 느리다는 단점이 있습니다.
    2. JIT 컴파일러 : 인터프리터의 단점을 보완하기 위해 도입된 방식으로 Java Bytecode 전체를 컴파일하여 바이너리 코드로 변경하고 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고, 바이너리 코드로 직접 실행하는 방식. Java Bytecode 전체가 컴파일된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠릅니다.

심볼릭 레퍼런스

기본 자료형(원시 타입)을 제외한 모든 타입을 명시적인 메모리 주소 기반의 레퍼런스가 아니라 심볼릭 레퍼런스를 통해 참조한다.

참고하는 클래스의 특정 메모리 주소를 참조 관계로 구성하지 않고 이름만 가지고 있는 것.

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

Runtime Data Area란 JVM이 프로그램을 수행하기 위해 OS로부터 할당 받는 메모리 영역을 의미합니다.

Runtime Data Area 영역

  • PC Register
  • JVM Stacks
  • Heap
  • Method Area
  • Native Method Stacks

PC Register

PC Register는 Thread가 생성될 때 마다 생기는 공간으로 Thread가 어떠한 명령을 실행하게 될지에 대한 부분을 기록하는 공간입니다.

JVM Stacks

JVM Stack은 Thread마다 할당되며 Thread와 동시에 생성되고 소멸됩니다.
JVM 스택은 Frame을 넣고 빼는 작업을 수행하는 곳으로 지역 변수와 부분적인 결과를 보유하고 메소드 호출과 반환에서 역할을 수행합니다.

메모리 Stacks은 메소드 호출 반환 담당이 국룰이죠!!

Frame이란?
Frame은 데이터와 부분적인 결과를 저장하고, 동적인 linking을 수행하고, 메소드를 위해 값을 리턴하고, 예외를 dispatch 하기 위해 사용되어집니다.
Frame은 메서드를 호출하는 스레드의 JVM Stack으로 부터 할당되어지고 메서드가 호출되어질 때 생성되고 메서드가 완료되어질 때 소멸합니다.
(결국 Frame 메소드를 위한 메소드를 위한 메소드를위한 것)

Native Method Stack

Native Method Stack이란 Native Method 즉, 자바 프로그래밍 언어 이외의 언어로 작성된 메서드를 지원하기 위한 스택이며 이를 호출할 시 JVM Stack은 동작하지 않고, Native Method Stack을 활용하여 Frame을 push, pop 한다.
(오 이부분은 되게 신기하네요 타 언어의 메서드를 위한 메모리 공간이라니!)

Method Area

Method Area란 모든 JVM Thread 사이에서 공유되어지는 영역으로 JVM이 시작할 때 생성되어지며 종료될 때 소멸합니다. Method Area는 Run-Time Constant Pool, 필드, 메소드, 생성자 등의 클래스별 구조를 저장합니다. 이러한 정보들은 ClassLoader에서 넘겨 받은 Class File의 관련 정보를 추출하여 저장합니다.
(Method Area는 클래스 구조를 저장하기 위한 메모리 공간)

Run-Time Constant Pool이란?
Run-Time Constant Pool은 클래스 파일에 있는 Constant_Pool Table에 대한 클래스별 또는 인터페이스별 Runtime 표현입니다.
클래스와 인터페이스의 모든 Constant 정보를 가지고 있는 곳이며 여기서의 Constant는 상수만을 의미하는 것이 아니라 Literal Constant, Type Field(Local Variable, Class Variable), Method로의 모든 Symbolic Reference 까지 확장된 개념을 의미한다.

즉 모든 상수 정보와 아까 심볼릭 레퍼런스도 저장을 합니다.
심볼릭 레퍼런스는 Constant Pool에 저장되며 객체에 접근할 필요가 있으면 Constant Pool에서 심볼릭 레퍼런스를 통해 해당 객체의 Memory Address를 찾아 동적으로 연결합니다.

Heap

Heap은 JVM의 모든 스레드 사이에서 공유되어지는 영역입니다. Heap은 JVM이 시작할 때 생성되고 종료될 때 소멸됩니다. Heap은 모든 클래스의 인스턴스와 배열에 대한 메모리가 할당되는 영역입니다. 자바에서 객체들은 절대 명시적으로 할당이 해제되지않으며 Garbage Collector에 의해서만 메모리가 회수됩니다.

힙메모리는 가비지콜렉터랑 매우 관계가 깊은데
힙 메모리도 가비지 컬렉터에 대해 글을 쓸 때 자세히 설명해보겠습니다.

Execute Engine

Execute Engine은 JVM의 다양한 메모리 영역과 통신합니다. Execute Engine은 클래스 로더를 통해서 JVM의 Run-Time Data Area에 할당되어진 바이트 코드를 실행하는 역할을 담당합니다. Execute Engine은 Java byte code를 실행하기 위해 3가지 주요 요소인 Interpreter, JIT Compiler, Garbage Collector을 포함한다.

Interpreter : Interpreter는 Java Byte Code를 읽고 OS의 Native Code로 변화하여 이들을 순차적인 방식으로 실행하는 구성요소를 의미합니다.
JIT Compiler : JIT Compiler는 컴파일러를 통해 Interpreter의 느린 실행의 단점을 상쇄하고 성능을 향상시킵니다.
Garbage Collector : 메모리를 자동으로 관리하는 도구를 의미하며 백그라운드에서 항상 실행하고있는 Daemon Thread 입니다.
Java Native Interface(JNI) : JNI는 자바 메소드 호출과 관련된 Native Libraries 와의 다리 역할을 해줍니다.

느낀 점

JVM 내용은 많이 유명하고 중요해서 대략적으로만 알고 있었는데 깊게 공부해보니까 어렵네요

클래스로더 같은 경우는 코딩하다가 에러 발생하면 자주 보았는데
검증 부분에서 에러가 난건가 보네요 이해하니까 신기합니다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

또 제가 자바에 대해서 제대로 알고있지 않는 것 같아서
앞으로 자바 공부를 열심히 하고 또 의욕이 나네요!!

profile
실력있는 개발자가 되보자!

0개의 댓글