개발 일기

[Java] JVM 메모리 구조(Method-Static, Heap, Stack) 본문

Back-End/Java

[Java] JVM 메모리 구조(Method-Static, Heap, Stack)

개발 일기장 주인 2024. 11. 6. 18:25

그전에 JVM에 대해 다시 떠올려보자.

 

JVM 이란 Java Virtual Machine의 약자이며, 자바 가상 머신이라고 한다.

  • 자바 애플리케이션을 실행하기 위한 가상 환경
  • 자바 바이트코드(.class 파일)를 특정 운영체제나 하드웨어 환경에 상관없이 실행할 수 있게 해준다.
  • JVM은 OS와 독립적이므로 한 번 작성된 자바 코드가 다양한 플랫폼에서 실행될 수 있는 "Write Once, Run Anywhere"의 핵심적인 역할을 한다.

https://lucas-owner.tistory.com/38

  1. 컴파일 단계: .java 파일을 자바 컴파일러(javac)를 통해 .class 파일로 컴파일한다.
  2. 클래스 로드: 컴파일된 .class 파일은 JVM의 ClassLoader에 의해 로딩된다.
  3. 메모리 할당: ClassLoader는 .class 파일을 Runtime Data Area(런타임 데이터 영역)에 할당하여 JVM 메모리에 올린다.

 

이 JVM 런타임 메모리 영역에 대해서 알아보자. 크게 5가지가 있다.

 


1. Method Area (Static Area)

  • 클래스 로더가 로딩한 클래스의 구조와 메서드(클래스 정보, 생성자, 메소드), static 변수들이 저장된다
  • 프로그램 내에서 어디서든 접근할 수 있으며, 프로그램 전체에서 공유되는 공간
  • JVM이 시작할 때 생성되고 프로그램이 종료될 때까지 유지된다.
  • 무분별한 사용 시 메모리 부족
  • 클래스 변수

2. Heap Area

  • new 키워드를 통해 생성된 객체(인스턴스)들이 저장됩니다.
  • 모든 참조형 데이터가 저장되는 곳으로, Stack 영역에서 이 데이터를 참조하는 주소값을 가진다.
  • Garbage Collector가 관리하여 참조되지 않는 객체는 자동으로 메모리에서 해제
  • Heap 영역은 모든 스레드가 공유하는 단일 영역이다.
  • 인스턴스 변수

3. Stack Area

  • 기본 자료형(원시 자료형)과 지역 변수, 메서드 호출 시 전달되는 매개변수가 저장된다.
  • 실제데이터가 존재하는 Heap 영역의 참조값
  • 각 스레드마다 독립적으로 존재하며, 메서드 호출 시 메모리에 할당되고 종료 시 제거된다.
  • LIFO(Last In First Out) 구조를 가지며, 메서드가 호출될 때마다 메모리 블록이 추가되고, 종료 시 제거
  • 지역 변수

4. PC Register

  • 각 스레드가 실행할 명령의 주소를 저장하는 공간
  • 현재 실행 중인 명령어의 주소를 추적하며, 각 스레드가 독립적인 PC Register를 가진다.

5. Native Method Stack

  • 자바 외의 다른 언어(C/C++ 등)로 작성된 네이티브 코드를 실행할 때 필요한 메모리
  • JNI(Java Native Interface)를 통해 호출된 메서드가 여기에 저장

 

Stack과 Heap 구분

new Account();를 통해 Account 객체의 인스턴스가 생성되면

  1. Heap 영역에 실제 인스턴스의 필드값 balance와 age값이 저장된다. ( new Account(10000); )
  2. Account account 부분인 참조 변수는 Stack영역에 참조값(주소)형태로 저장된다.
  3. 메소드(main)가 끝나게 되면 stack은 메모리를 비우게 되는데 이때 Heap의 멤버 변수(필드값)를 참조하고 있던 값이 없어지게된다.
  4. 그렇게 Heap영역의 특정 데이터들을 참조하고 있는 값이 없으면 가비지 컬렉터(GC)가 청소하게된다.
인스턴스 메소드 실행

이때 만약 deposit(), withdraw(), getBalance() 등 클래스 메소드 등은 Method 영역에 저장시켜 둔다.

그러고 실제로 account.deposit()등 실제 메소드 호출이 발생한 경우 메모리에 다시 로드되는 것이 아니다.

메소드 실행 시 필요한 데이터는 Heap에 저장되어 있으므로, Stack에서 참조값을 통해 Heap에 접근하여 값을 가져와서 메소드를 실행한다.