[Java] JVM과 런타임 데이터 영역(Runtime Data Area)에 대해

Yunjisoo·2023년 5월 26일
0

Java

목록 보기
1/2

java는 프로그래밍 언어 중 하나로서 웹 애플리케이션 코딩에 널리 사용되는 가장 기초적인 언어이다. java를 이해하고 성능을 최적화시키기 위해서 JVM(Java Virtual Machine)을 자세히 알아보겠다.

먼저 자바의 실행과정에 대해 간단히 설명하자면,
자바는 컴파일러에 의해 바이트코드로 변환되어 클래스로더로 전송되고, JVM이 이를 운영체제에 맞는 시스템 언어로 번역하여 실행한다.

JVM 이란?

JVM(Java Virtual Machine)은 자바 프로그램을 실행하기 위한 가상 컴퓨터이다. JVM은 자바 프로그램을 실행하기 위해 필요한 메모리 관리, 스레드 관리, 가비지 컬렉션 등의 기능을 제공한다. JVM은 자바 소스 코드를 컴파일하여 생성된 바이트코드를 해석하고 실행하는 역할을 한다.

실행방법

  1. java 소스코드를 컴파일러를 통해 바이트코드로 변환하고
  2. 바이트코드를 JVM 내의 클래스로더로 불러들여 런타임 데이터 영역에 로드하고
  3. 이를 실행엔진을 통해 실행시킨다.

1-1. 컴파일러는 자바 소스 코드를 이해하고 해당 플랫폼에서 실행 가능한 기계어로 변환하는 역할을 한다. 자바 컴파일러(javac)는 자바 소스 코드 파일(.java)을 컴파일하여 바이트코드 파일(.class)로 변환한다. 이 바이트코드는 JVM이 이해할 수 있는 중간 언어이다.
1-2. 바이트코드는 운영체제에 독립적이다. JVM은 각 운영체제에 맞게 구현되어 있으며, 해당 운영체제에서 바이트코드를 실행할 수 있도록 처리한다. 자바 프로그램은 한 번 컴파일되면 어떤 운영체제에서든 JVM을 통해 실행될 수 있다. 따라서 자바는 운영체제에 독립적이며 플랫폼에 종속되지 않는 특징을 가지고 있다.

2-1. 클래스로더(Class Loader)는 JVM의 일부로서, 바이트코드를 읽고 JVM 내에서 사용할 수 있는 형식으로 변환하여 런타임 데이터 영역(Runtime Data Area)에 로드한다.
2-2. 클래스로더는 로드한 클래스를 JVM 내의 메모리에 저장하며 이때 클래스의 런타임 상수풀(Runtime Constant Pool), 필드, 메서드 등의 정보도 함께 준비한다. 런타임 데이터 영역은 JVM이 프로그램을 실행하는 동안 사용하는 메모리 영역이다.

3-1. 실행엔진은 인터프리터(Interpreter)나 JIT(Just-In-Time) 컴파일러를 사용해서 런타임 데이터 영역에 로드된 클래스의 바이트코드를 실행하는 역할을 한다.
3-2. 인터프리터는 바이트코드를 한 줄씩 읽어 해석하고 실행한다. 이 방식은 속도가 다소 느리다는 단점이 있지만 바이트코드를 즉시 실행할 수 있으므로 컴파일 단계가 필요하지 않다.
3-3. JIT 컴파일러는 인터프리터와 함께 사용되며, 실행 시점에 프로그램의 동작을 분석하여 최적화된 기계어 코드를 생성하며, 이를 통해 인터프리터의 성능 단점을 보완한다.

Runtime Data Area

런타임(Runtime)은 '어떤 프로그램이 실행되고 있는 동안의 동작'을 가리킨다.

JVM을 실행하기 위해 운영체제(OS)로부터 메모리 영역을 할당받는데, JVM을 실행 중에(==Runtime) 사용하는 메모리를 적재하는 공간을 의미한다.

Runtime Data Area의 구성 요소

  • Method Area
  • Heap
  • Stack
  • PC Register
  • Native Method Stack

스레드(Thread) : 소프트웨어적으로 정의된 스레드는 
               '하나의 프로그램에서 독립적으로 실행되는 단위'를 의미한다. 

여기서 스레드는 무엇을 공유하고 무엇을 별도로 가져갈까? 그림을 보면 알 수 있듯이, Stack 영역과 PC 레지스터 영역, 네이티브 메서드 영역은 스레드가 별도로 가지고 있고(per thread), 힙 영역과 메서드 영역만을 공유한다. 그러나 하나의 스레드는 다른 스레드의 내부 데이터에 접근할 수 없다.( 하나의 클래스 내에서 지역 변수의 동시성 문제를 걱정할 필요 없는 이유이다.)

  • 스레드가 공유하는 영역
    = 힙 영역
    = 메서드 영역
    (전역변수와 유사한 개념이라고 생각하면 이해하기 쉽다. 프로그램이 실행되는 동안 유지되며, 모든 스레드가 접근할 수 있는 영역으로 기억하면 된다.)

  • 스레드가 공유하지 않는 영역
    = Stack 영역
    = PC 레지스터 영역
    = 네이티브 메서드 스택
    (지역변수와 유사한 개념이라고 생각해보자. 해당 영역들은 각 스레드마다 독립적으로 관리되는 데이터 영역으로 스레드의 개별 실행 흐름과 관련된 데이터가 저장된다.)

각 영역이 갖는 데이터와 특징을 좀 더 자세히 설명해보자.

Heap

Heap은 JVM이 실행 중인 시스템 메모리 중 가장 큰 부분을 차지하며 객체의 동적 할당과 해제를 관리한다. 객체는 new 키워드를 사용하여 동적으로 생성되며, 참조하는 변수가 없으면 가비지 컬렉션(Garbage Collection)에 의해 자동으로 정리된다. 가비지 콜렉션 덕분에 개발자는 힙 영역에 직접 접근할 필요가 없다.(C/C++과는 다르게)

Method Area

쉽게 이해하자면, 일반적인 메모리 구조에서의 코드 영역과 유사하다고 볼 수 있다. JVM의 메서드 영역은 모든 스레드가 공유하는 영역으로 JVM이 시작될 때 생성되고, 클래스 로더에 의해 로드된 클래스 정보를 저장한다.

  • 상수 풀(Constant Pool) : 한번 선언되면 바뀌지 않는 것을 상수라고 한다.
  • 클래스 및 인터페이스의 필드
  • 메서드 코드:
  • 생성자
  • static 변수
  • static 메서드

PC Register

JVM 내에서 각 스레드가 현재 실행 중인 명령어의 주소 값을 저장하는 레지스터이다. PC Register는 스레드별로 독립적으로 관리되며, 각 스레드가 현재 실행 중인 메서드 또는 명령어의 주소를 추적하고 유지한다.
또한, PC Register는 JVM이 스레드를 다중 처리하고 동시에 실행하기 위해 필요한 기능이다. 각 스레드는 자신의 PC Register에 현재 실행 중인 메서드나 명령어의 주소를 저장하고, 해당 주소에 해당하는 다음 명령어를 실행한다.

Stack

스택 영역에는 메서드 호출 시 지역 변수, 매개변수, 함수 호출내역 등이 저장되는 영역이다. 각 스레드마다 개별적으로 생성되며, 메서드 호출 시 생성되었다가 메서드가 종료되면 사라진다.
스택에 저장되는 데이터들은 Frame 이라는 자료구조로 저장된다. 그리고 Frame 에는 아래와 같은 데이터들이 저장된다.

  • Frame data 지역변수, Operand stack 이외에도 반환 타입, 상수 풀(Constant Pool) 등과 같은 데이터들 포함
  • Local Variables 함수에서 쓰이는 매개변수 혹은 지역변수들이 저장
  • Operand stack 피연산자들을 stack 자료구조로 저장

Native Method Stack

Native는 Java가 아닌 다른 언어(C/C++등)들을 의미한다. Native Method Stack은 Java 언어가 아닌 다른 언어(C/C++ 등)로 작성된 네이티브 코드(원시 코드)를 실행하기 위한 스택이다. 네이티브 코드는 Java 언어의 제어를 벗어나서 시스템 라이브러리나 하드웨어와의 상호작용을 위해 사용된다.

참고문헌

profile
기초부터 시작

0개의 댓글