자바의 메모리 구조에 대해 알아보기 전에, 우선 JVM에 대해서 이야기 할 필요가 있다.
자바의 가장 큰 특징 중 하나가 바로 플랫폼에 독립적이라는 것인데,
이것은 자바로 작성된 프로그램은 어떠한 운영체제에서도 실행시킬 수 있다는 의미이다.
리눅스에서 실행되던 C 프로그램을 윈도우에서 실행시키기 위해서는 프로그램의 소스 파일을 윈도우 환경에 맞게 다시 컴파일 및 빌드해야 합니다.
이는 리눅스와 윈도우가 서로 다른 운영체제이며, 각 운영체제에 맞는 시스템 호출, 라이브러리, 파일 시스템 등을 사용하기 때문입니다.
운영체제가 바뀔 때마다 소스 파일을 새로 컴파일하고 빌드해야 하는 이유는 이러한 차이점들을 반영하여 프로그램이 올바르게 동작하도록 보장하기 위함입니다.
리눅스 환경에서 실행 중인 자바 프로그램을 윈도우 환경에서 실행시키려면, 해야 할 일은 단 하나입니다. 바로, 윈도우에 알맞은 JVM(Java Virtual Machine)을 설치하는 것입니다.
그 후, 리눅스에서 사용하던 자바 프로그램의 실행 파일을 그대로 윈도우에 옮기기만 하면 됩니다. 즉, 자바 소스 파일을 새로 컴파일하고 빌드할 필요가 없습니다.
이것이 가능한 이유는 자바 프로그램이 JVM이라는 가상 머신 위에서 실행되기 때문입니다. 자바 소스 파일(.java)은 컴파일되어 바이트코드(.class)로 변환됩니다. JVM은 이 바이트코드를 읽어들이고, 해당 운영체제가 이해할 수 있는 기계어로 번역합니다.
자바의 메모리 영역은 크게 5개 영역으로 나누어 볼 수 있다.
- Method 영역
- Heap 영역
- Stack 영역
- PC register 영역
- Native Method stack 영역
Method 영역과 Heap 영역은 하나의 자바 프로세스 내에서 모든 스레드가 공유하는 영역입니다.
JVM이 실행되어 클래스가 로딩될 때 생성되는 Method 영역에는 다음과 같은 데이터가 저장됩니다:
타입 정보: 클래스명과 접근 제어자.
필드 정보: 클래스 내 필드의 접근 제어자, 필드 타입, 기타 제어자(static, final 등).
메서드 정보: 메서드 이름, 리턴 타입, 파라미터 리스트와 각 파라미터의 타입.
클래스 변수: static으로 선언된 변수.
런타임 상수 풀: 클래스, 필드, 메서드에 대한 모든 레퍼런스 데이터를 저장하며, JVM이 이를 이용해 실제 메모리상의 위치를 찾아 필드나 메서드에 접근.
Method 영역은 이러한 메타데이터들을 담고 있으며, 모든 스레드가 공유하는 공간입니다.
Heap 영역은 프로그램 실행 중 동적으로 생성되는 데이터들이 저장되는 공간입니다. 참조형 데이터 타입을 갖는 객체 인스턴스와 배열이 이 영역에 할당됩니다.
C/C++에서는 개발자가 힙 영역에 할당된 메모리를 직접 해제해야 하지만, 자바에서는 Garbage Collector(GC)가 자동으로 불필요한 메모리를 탐지하여 해제합니다.
Heap 영역은 모든 스레드가 공유하는 메모리 공간입니다. 따라서, 이 영역에서는 스레드 간의 동기화 문제가 발생할 수 있습니다.
메서드 호출 시 받아온 매개변수, 메서드 실행 중 생성되는 지역 변수, 리턴값 등은 Stack 영역에 저장됩니다. 메서드가 실행될 때마다 필요한 스택 메모리 공간을 스택 프레임이라고 부르며, 메서드가 실행될 때마다 스택 프레임 단위로 메모리가 할당되고, 메서드 실행이 끝나면 스택 프레임 단위로 메모리가 해제됩니다.
메서드의 지역 변수로 참조형 데이터 타입(객체나 배열)이 필요한 경우, 해당 인스턴스는 Heap 영역에 생성되고, Stack 메모리에는 그 주소값이 저장됩니다. 이는 C/C++에서 포인터 변수가 메모리 주소값을 저장하는 방식과 유사합니다.
Stack 영역은 쓰레드마다 개별적으로 할당되어, 각 쓰레드가 독립적으로 메서드를 수행할 수 있도록 보장합니다.
PC는 Program Counter의 약자로, 현재 스레드가 실행하고 있는 코드의 주소값을 저장하는 영역입니다. 자바 프로그램이 실행되는 동안, 현재 실행 중인 명령어의 주소를 보관하여 제어 흐름을 관리합니다.
자바 코드 실행 중에 네이티브 코드(C/C++ 등으로 작성된 라이브러리)를 호출하게 되면, PC 레지스터에 저장된 값은 정의되지 않습니다 (Undefined).
PC 레지스터는 각 스레드마다 독립적으로 할당되어, 여러 스레드가 동시에 자바 메서드를 수행할 수 있도록 보장합니다. 이는 Stack 영역이 스레드마다 개별적으로 할당되는 것과 유사한 방식입니다.
Native Method Stack은 자바 이외의 언어로 작성된 코드가 실행되기 위해 필요한 스택 영역입니다. JNI(Java Native Interface)를 통해 다른 언어로 작성된 메서드가 실행되는 경우, 해당 메서드의 스택 프레임은 일반 Stack 영역이 아닌 Native Method Stack 영역에 쌓이게 됩니다.
Native Method Stack도 Stack 영역과 PC 레지스터와 마찬가지로, 스레드마다 독립적으로 할당됩니다. 이는 각 스레드가 자바 메서드뿐만 아니라 네이티브 메서드도 독립적으로 수행할 수 있도록 보장합니다.