Java

JVM 자동 메모리 관리

taey 2025. 12. 11. 17:38

런타임 데이터 영역

1. 프로그램 카운터

PC : 현재 실행 중인 스레드의 '바이트코드 줄 번호 표시기'

  • 자바 가상 머신에서의 멀티 스레딩은 CPU 코어를 여러 스레드가 교대로 사용하는 방식으로 구현되기 때문에 특정 시각에 각 코어는 한 스레드의 명령어만 실행하게 된다.
  • 스레드 전환 후 복원하려면 스레드 각각에 고유한 프로그램 카운터가 필요하다.
  • 각 스레드의 카운터는 서로 영향을 주지 않는 독립된 영역에 저장된다. 이 영역을 스레드 프라이빗 메모리라고 한다.
  • 스레드가 네이티브 메서드를 실행 중일 때 프로그램 카운터 값은 Undefined이다. 

2. 자바 가상 머신 스택

자바 가상 머신 스택도 '스레드 프라이빗'하다.

  • 각 메서드가 호출될 때마다 자바 가상 머신은 스택 프레임을 만들어 지역 변수 테이블, 피연산자 스택, 도적 링크, 메서드 반환값 등의 정보를 저장한다. 그런 다음 스택 프레임을 가상 머신 스택에 푸시하고, 메서드가 끝나면 팝하는 일을 반복한다.
  • 자바를 힙 메모리와 스택 메모리로 구분하는 사람이 많은데, 이는 전통적인 C, C++ 프로그램의 메모리 구조에서 기인한 것으로, 자바 언어를 설명하기에는 무리가 있다.
  • 지역 변수 테이블에는 자바 가상 머신이 컴파일 타임에 알 수 있는 다양한 기본 데이터 타입, 객체 참조, 반환 주소 타입을 저장한다. 지역 변수 테이블을 구성하는 데 필요한 데이터 공간은 컴파일 과정에서 할당된다.
  • 스택 메모리 영역에서 발생할 수 있는 2가지 오류
    • 스레드가 요청한 스택 깊이가 가상 머신이 허용하는 깊이보다 크다면 StackOverflowError
    • 스택 용량을 동적으로 확장할 수 있는 JVM에서 확장 시점에 여유 메모리가 충분하지 않다면 OutOfMemoryError

3. 네이티브 메서드 스택

  • 가상 머신 스택과 비슷한 역할이다. 차이점은 네이티브 메서드 스택은 네이티브 메서드를 실행할 때 사용한다.
  • 가상 머신 스택처럼 네이티브 메서드 스택도 스택 허용 깊이를 초과하면 StackOverflowError를, 스택 확장에 실패하면 OutOfMemoryError를 던진다.

4. 자바 힙

  • 자바 애플리케이션이 사용할 수 있는 가장 큰 메모리이자, 모든 스레드가 공유하는 메모리이다.
  • 이 메모리 영역의 유일한 목적은 객체 인스턴스를 저장하는 것이고, 거의 모든 객체 인스턴스가 이 영역에 할당된다. 
  • 자바 힙은 크기를 고정할 수도, 확장할 수도 있게 구현할 수 있다. 주류의 가상머신들은 확장가능한 형태로 구현되어 있다.
  • 새로운 인스턴스에 할당해줄 힙 공간이 부족하고, 힙을 더는 확장할 수 없다면 OutOfMemoryError를 던진다.

5. 메서드 영역

  • 메서드 영역도 자바 힙처럼 모든 스레드가 공유한다. 
  • 메서드 영역은 가상 머신이 읽어들인 타입 정보, 상수, 정적 변수 그리고 JIT 컴파일러가 컴파일한 코드 캐시 등을 저장하는데 이용된다.

6. 런타임 상수 풀

  • 런타임 상수 풀은 메서드 영역의 일부로, 클래스 버전, 필드, 메서드, 인터페이스 등 클래스 파일에 포함된 설명 정보에 더해 컴파일 타임에 생성된 다양한 리터럴과 심벌 참조가 저장된다.
  • 자바 언어에서는 상수가 꼭 컴파일 타임에 생성되어야 한다는 규칙은 없다, 상수 풀의 내용 전부가 클래스 파일에 미리 완벽하게 기술되어 있는게 아니다.
  • 런타임에도 메서드 영역의 런타임 상수 풀에 새로운 상수가 추가될 수 있다.
  • 런타임 상수 풀은 메서드 영역에 속하므로 자연스럽게 메서드 영역을 넘어서까지 확장될 수 없다. 그래서 상수 풀의 공간이 부족하면 OutOfMemoryError를 던진다.

7. 다이렉트 메모리

  • NIO는 힙이 아닌 메모리를 직접 할당할 수 있는 네이티브 함수 라이브러리를 이용하며, 이 메모리에 저장되어 있는 DirectByteBuffer 객체를 통해 작업을 수행할 수 있다.
  • 자바 힙과 네이티브 힙 사이에서 데이터를 복사해 주고받지 않아도 돼서 일부 시나리오에서 성능을 개선할 수 있다.
  • 물리 메모리를 직접 할당하기에 자바 힙 크기와는 무관하지만, 실제 물리적 메모리 한계를 넘어서면 동적 확장을 시도할 때 OutOfMemoryError가 발생한다.

핫스팟 가상 머신 

객체 생성

  1. 자바 가상 머신이 new 명령에 해당하는 바이트코드를 만나면, 이 명령의 매개 변수가 상수 풀 안의 클래스를 가리키는 심벌 참조인지 확인한다. 그 다음, 이 심벌 참조가 뜻하는 클래스가 로딩, 해석, 초기화되었는지 확인한다.
  2. 로딩이 완료된 크래스라면 새 객체를 담을 메모리를 할당한다.  객체에 필요한 메모리 크기는 클래스를 로딩하고 나면 알 수 있다.
  3. 가상 머신은 가용 메모리 블록들을 목록으로 따로 관리하며, 객체 인스턴스를 담기에 충분한 공간을 찾아 할당한 후 목록을 갱신, 이 할당 방식을 여유 목록이라 한다.
  4. 멀티 스레딩 환경에서 메모리 할당 시작 포인터 지정 시, 스레드 안전하지 않기 때문에 문제가 생길 수 있다.
    • 메모리 할당을 동기화하는 방법
      • 비교 및 교환(CAS)과 실패 시 재시도 방식의 가상 머신은 갱신을 원자적으로 수행한다.
    • 스레드마다 다른 메모리 공간을 할당하는 방법
      • 스레드 각각이 자바 힙 내에 작은 크기의 전용 메모리를 미리 할당받아 놓는 것이다. 이런 메모리를 스레드 로컬 할당 버퍼(TLAB)라고 한다.
      • 로컬 버퍼에서 메모리를 할당 받아 사용하다가 버퍼가 부족해지면 그때 동기화를 해 새로운 버퍼를 할당받는 식으로 사용한다.
  5. 메모리 할당이 끝나면 가상 머신은 할당받은 공간을 0(기본값)으로 초기화한다.(객체 헤더 제외)
  6. new 명령어에 이어서 <init>() 메서드까지 실행되어 객체를 개발자의 의도대로 초기화해야 진짜 객체가 완성된다.

객체의 메모리 레이아웃

핫스팟 가상 머신은 객체를 3부분으로 나눠 힙에 저장한다. 객체 헤더, 인스턴스 데이터, 길이 맞추기용 정렬 패딩이다.

  • 객체 헤더
    • 마크워드, 클래스 워드, 배열 길이로 구성
  • 인스턴스 데이터
    • 객체가 실제로 담고 있는 정보
  • 정렬 패딩
    • 이 부분은 존재하지 않을 수도 있으며, 특별한 의미 없이 자리를 확보하는 역할만 한다.
    • 핫스팟 가상 머신의 자동 메모리 관리 시스템에서 객체의 시작 주소는 반드시 8바이트의 정수배여야 한다. 즉, 모든 객체의 크기가 8바이트 정수배여야 한다.
    • 객체 헤더는 8바이트의 정수배가 되도록 잘 설계되어 있다. 따라서, 인스턴스 데이터가 조건을 충족하지 못하는 경우에만 패딩으로 채운다.

 

객체 접근

대다수 객체는 다른 객체 여러개를 조합해서 만들어진다. 자바 프로그램은 스택에 있는 참조 데이터를 통해 힙에 들어 있는 객체들에 접근해 이를 조작한다.

  • 객체에 접근하는 방식 역시 가상 머신에서 구현하기 나름이며, 주로 핸들이나 다이렉트 포인터를 사용해 구현한다.
    • 핸들 방식 
      • 핸들 방식에서는 자바 힙에 핸들 저장용 풀이 별도로 존재할 것이다. 
      • 참조에는 객체의 핸들 주소가 저장되고, 핸들에는 다시 해당 객체의 인스턴스 데이터, 타입 데이터, 구조 등의 정확한 주소 정보가 담길 것이다.
      • 핸들 방식의 장점은 참조에 안정적인 핸들의 주소가 저장된다는 것이다.
      • 핸들을 이용하면, 객체의 위치가 바뀌는 상황에서도 참조 자체는 손댈 필요가 없다. 대신, 핸들 내의 인스턴스 데이터 포인터만 변경하면 된다.
    • 다이렉트 포인터 방식
      • 자바 힙에 위치한 객체에서 인스턴스 데이터뿐 아니라 타입 데이터에 접근하는 길도 제공해야 한다.
      • 스택의 참조에는 객체의 실제 주소가 바로 저장되어 있다.
      • 다이렉트 포인터 방식의 장점은 빠른 속도이다. 핸들을 경유하는 오버헤드가 없기 때문이다.
  • 핫스팟 가상 머신은 주로 다이렉트 포인터 방식을 이용한다.

'Java' 카테고리의 다른 글

WriteBytes의 한글 깨짐 해결 방법  (1) 2024.10.19
자바 예외 이해  (1) 2024.10.06