[JVM 쿰척쿰척 시리즈.1] JVM의 메모리 동작과정 및 GC의 내부 동작의 이해

BlackBean99·2023년 4월 30일
10

JVM

목록 보기
1/1
post-thumbnail

JVM 메모리 사용방법

JVM 의 Stack Area, Heap Area 중심으로
Kotlin / JVM 메모리 사용방식

Run-Time Data Area 는 크게 Method Area, Heap Area, Stack Area, PC registers, Native Method Stack 이 존재합니다.

여기서 PC Register, Native Method Stack 영역은 Low Level 의 Operation 을 위한 공간입니다.

1. Method Area(Static Area)

이 영역은 인스턴스 생성을 위한 객체 구조, 생성자, 필드 등이 저장됩니다.
JVM 에서 하나만 생성되는 이 영역은 JVM 의 모든 Thread 가 Method Area를 공유하게 됩니다. 여기서 특정 객체나 메모리에 대한 요청이 오면 실제 물리 메모리로 변환해서 전달해주는 역할을 합니다.

RunTime 에 생성되고, 종료까지 유지되는 공통 영역입니다.

Method Area 에 저장되는 목록

Type Information

  • Type 이름 : Package Name + Class Name
  • Type 종류 : Class, Enum ,Interface, data Class, sealed Class 를 구분하기 위한 정보
  • Type 제어자 : (접근제어자 public, private, default, abstract, final .. )
  • 관련된 Interface 정보

Runtime Constant Pool
말 그대로 Type 의 상수 정보를 따로 저장하는 Pool 입니다. 상수는 Index 를 통해 접근이 가능합니다.
실제로 이는 직렬화 하기 위해 타입을 체크할 때도 필요합니다.
이 영역에서는 Type, Field, Method 등 메모리에 접근하기 위한 reference 정보를 가지고 있습니다.

다음은 String 내부의 serialVersionUID 값입니다.

모든 클래스는 직렬화 하기 위해 이 값들이 JVM 에서 default 로 선언해줍니다. 이 값으로 클래스를 비교를 합니다. 이 부분은 직렬화 포스팅에서 구체적으로 확인해보세요!

Field Information

  • Type 명, 접근 제어자 를 저장

Method Information

  • Method 명, 반환 타입, parameter 수와 순서, 타입 정보들을 가지고 있습니다.

2. Heap Area

흔히 객체가 저장되는 공간이라고 많이 알고 있죠. 문자열에 대한 String Pool, 인스턴스, 배열 등이 저장되고 JVM 내에서 하나만 생성됩니다. 이 데이터는 Stack 의 reference 에 의해 참조되고 Thread 공유 데이터입니다.

Heap영역은 Stack과는 달리 조금 속도가 느립니다. 또한 공유 자원이기 때문에 동시성 문제(Thread Safe 하지 않은 문제)가 발생할 수 있는데 synchronized 블록을 사용하거나 Lock을 이용하는 방법등 이런 방법들로 해결하는 것이 좋습니다.

그럼 이 영역에서 GC 가 어떻게 동작하는지 알아볼까여?

Heap Area 에서 GC의 동작과정

  • 최초의 객체는 Eden 에서 생성
  • Eden 이 꽉 차면 GC 가 한번 돌고, 살아남은 객체 Survivor 영역 중 From Space 로 이동
  • 다시 Eden 이 GC가 돌면 , From Space 의 객체는 To Space 로 이동하게 된다.
  • 다시 GC가 돌아도 살아남으면 From Space의 객체는 Old Generatione 영역으로 이동

여기서 Eden이 Full될 때마다 From 의 Lived 객체는 To Space로 이동하는데 이는 논리적으로 그렇고,
Minor GC 가 발생할 때마다 Survivor Space 의 포인터를 유지하고 있다가 From Space에서 To Space로 변경시켜주는 것이다. 이 후 Eden 이 Full ehlaus To Space에 있는 Lived 객체는 Old Generation 으로 복사됩니다.

정리하면?
1. if(Eden Full) -> GC -> Lived 객체 (Eden -> From)
2. if(Eden Full) Lived 객체 ( From -> To ) Space 로 이동
3. if(Eden Full) Lived 객체 ( From -> Old Generation ) Space 로 이동

여기서 보다 시피 주소만 바꿔주는 작업이기 때문에 매우 빠르고 효율적입니다. JVM Thread 가 멈추기 않게 동작하는 이유중 하나입니다.

왜 이렇게 가만히 두질 않고 자꾸 옮겨주는 걸까요? 이는 최대한 객체의 생명 주기를 짧게 가져가겠다는 JVM 의 의도입니다.

  • Primative Type 인 경우 값 자체가 저장됩니다.

Major GC

Major 는 모든 Thread 실행을 잠시 멈추고 살아있는 객체를 Check 해둡니다.
사용하지 않는 객체는 정리합니다. 이 작업을 Mark and Sweep 이라고 하는데 대체로 Minor GC 보다 10배 이상 많은 시간을 소요하게 됩니다.
이는 Old Generation 으로 가기 전에 Young Generation에서 제거되게끔 하고 오래된 객체의 경우 Old Generation에 상주시켜 Minor GC만으로 관리할 수있게끔 유도하는 것이 중요한데
Young Generation은 전체의 1/2 보다 작게
Survivor Space 는 전체의 1/8 크기면 적절하다고 한다.


위까지 Method Area, Heap Area는 쓰레드간 공유하는 공통 영역이고 아래부터 나타나는 PC Register, Stack Area, Native Method Stack 이 세가지는 각각의 쓰레드마다 별도로 생성하는 공간입니다.
그럼 3가지를 소개해볼까요?

3. Stack Area


기술 면접으로 나오는 질문중 하나가 JVM 메모리 영역이죠.

  • JVM 내에서 여러 프로세스가 공유하는 메모리 영역은 어디인가요? Method Area, Heap Area

Stack Area에서는 메소드가 호출될 때 할당되는 영역입니다. 메소드 내부의 지역 변수,
여기서는 메모리를 호출할 때마다 Function Frame를 추가(push)합니다. 메소드가 반환하면 Frame 은 Stack으로 부터 제거(pop)됩니다.
내부 구성 영역은 다음과 같습니다.

  • Local Variable, Operand Stack 그리고 Constant Pool Reference 로 구성이 되어 있습니다

  • Heap 영역에 생성된 객체의 reference 와 일치하지 않는 즉. 참조되지 않는 값들은 GC가 따로 처리합니다.

4. Native Method Stack

Java를 사용하면서 Java로 작성되지 않은 라이브러리나 API가 조재합니다. 이런 다른 프로그래밍 언어로 작성된 메소드를 Native Method 라고 합니다. 흔히 C Stacks 라고 불리는데 이 영역은 Native Method를 호출하거나 생성, 실행될 경우 Stack 영역에 쌓입니다.

5. PC Register

현재 진행중인 명령어의 reference 를 저장하고 사용하는 관리를 담당하는 주체입니다. Native Method 라면 undefined 라고 작성되고, 그렇지 않은 Java 명령어면 명령의 주소 값을 저장하게 됩니다.

요약 질문!

  1. JVM내에서 하나만 생성되는 공간은?? ->
  2. Java 로 작성하지 않은 메소드를 무엇이라고 하는가?
  3. GC 의 동작과정을 4가지 영역과 관련지어 설명해보시오!
  4. IP라고 불리는 Instruction Pointer 는 JVM 내에서 다음 CPU 내에 이 장치에 의해 관리된다. 이 장리는?

reference

profile
like_learning

0개의 댓글