[Java] - GC와 동작과정

링딩·2023년 4월 10일
0

Computer Science

목록 보기
27/49




GC


1. GC의 개념

메모리 관리 기법 중 하나로, 동적으로 할당했던 메모리 영역 중 필요 없게 된 영역을 해제하는 기능이다.

여기서 동적으로 할당했던 메모리 영역은 프로그램 런타임에 사용되는 Heap 영역 메모리를 뜻하고, 필요 없게 된 영역은 어떤 변수도 가리키지 않게 된 영역을 뜻한다.


GC의 장단점

C/C++에서는 Heap 영역의 메모리를 사용하려면 직접 동적 메모리의 영역에서 할당 받고 해제하는 작업을 해줬어야 했다.
그러나 Java에서는 동적 메모리 영역을 GC가 알아서 수행해주므로 개발자 입장에서는 편하게 개발에 집중할 수 있다.

+)

참고로 GC를 의도적으로 System.gc() 를 사용하여 호출할 수 있지만, 오버헤드가 굉장히 크므로 GC에게 메모리 해제를 믿고 맡기는 것을 추천한다.

◽ 장점

  • 메모리를 수동으로 관리하던 것에서 비롯된 에러를 예방할 수 있다.
  1. 개발자의 실수로 인한 메모리 누수,
  2. 해제된 메모리를 또 해제하는 이중 해제
  3. 해제된 메모리에 접근

◽ 단점

  • GC의 메모리 해제 타이밍을 개발자가 정확히 알기 어렵다.
  • 어떠한 메모리 영역이 해제의 대상이 될 지 검사하고, 실제로 해제하는 일모두 오버헤드

2. GC 메모리 해제 과정

GC는 스스로 메모리를 알쓸깔딱 하게 해제한다는데 그 과정부터 먼저 알아보자.

✨ Mark & Compact 알고리즘

1. Marking

  • 메모리가 사용되는지 안되는지를 구분한다.
    위 사진에서는 참조되지 않고 있는 객체를 주황색으로 마킹한 것이다. 모든 객체를 하나씩 탐색하며 마킹하기 때문에 오랜 시간을 소모하게 된다.


2. Normal Deletion

  • 1번에서 마킹을 했으니, 마킹한 객체 (참조되지 않고 있는 객체) 를 제거하고 메모리를 반환한다. => Memeory Allocator 는 반환되어 비어진 곳의 주소를 저장해뒀다가, 새로운 객체가 선언되면 이 곳에 할당되도록 한다.
  • 문제 🤔
    이렇게 하면 메모리가 단편화되어 비효율적으로 사용될지도 모른다 그렇기 때문에 흩어진 객체들의 영역을 한 곳으로 모으는 과정이 필요하다.

3. Compacting

  • 참조되는 객체들을 한 곳으로 집약
    -> 남은 공간을 극대화함으로써 새로운 메모리 할당 시 훨씬 더 쉽고 빠르게 진행할 수 있다.

지금까지의 과정은 모든 객체를 탐색해가며 일일히 참조되지 않고 있는 객체를 마킹하고 메모리 해제하고 다시 메모리 압축하고 난리이다.도 아니다. 상당히 비효율적인 알고리즘이다.😥😥



Weak Generational Hypothesis

대부분 객체는 금방 접근 불가능 상태가 되는데...
'신규'로 생성한 객체의 대부분은 머지않아 사용하지 않는 상태가 되고, '오래된 객체'에서 신규 객체로의 참조는 매우 적게 일어난다는 가정
=> Weak Generational Hypothesis 이다.

  • Y 축: 할당된 바이트의 수
  • X 축은 바이트가 할당될 때의 시간이다.
    => 시간이 가면 갈 수록 적은 객체가 남게 된다.

이러한 가정하에 Young 영역과 Old 영역으로 메모리를 분할하여, 새롭게 생성되는 객체는 Young 영역에 보관하고, 점차 나이를 들게 한 다음 어느정도 나이를 먹은 객체는 Old 영역에 보관하는 알고리즘을 고안해냈다.

이것을 Generational Gabage Collection 이라고 한다.

.
.

Generational Garbage Collection 알고리즘

1. Young Generation 영역

새롭게 생성한 객체들은 대부분 이곳에 자리잡게 된다. Weak Generational Hypothesis 에 의거하면, 대부분 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 이 영역에서 생성되었다가 사라지게 된다.
=> 해당 영역에서 객체가 메모리 해제되는 경우를 Minor GC 가 발생했다고 말한다.

2. Old Generation 영역

금방 접근 불가능 상태가 된 녀석들과 달리, 꽤 오랫동안 메모리에 남아있던 객체들이 이 영역으로 복사된다.

  • 대부분 Young 영역보다 크게 할당
    -> 크기가 큰 만큼 GC 는 Young 영역에 비해 적게 발생하게 된다.
  • 해당 영역에서 객체가 메모리 해제되는 경우 Major GC 가 발생했다고 말한다.

3. Permanent 영역

  • JVM 이 클래스들과 메소드들을 설명하기 위해 필요한 다양한 메타데이터들을 포함하고 있다. JDK8 부터는 Metapsace 로 교체된다.



1. 새로운 객체가 생성되면 Young 영역의 Eden Space(최초로 생성된 객체들을 위한 공간)에 할당


2. Eden Space 가 가득차면, Minor GC 수행


3. 참조되는 객체들은 첫 번째 Survivor 영역 (S0 영역) 으로 이동하고, 참조되고 있지 않는 (접근 불가능한) 객체들은 Eden Space 가 클리어 될 때 같이 반환


4. 다음에 일어나는 Minor GC 때, Eden Space 에선 똑같은 일이 일어남

  • 비 참조 객체는 사라지고, 참조 객체는 마찬가지로 Survivor 영역으로 이동한다. 하지만 이번에는 두 번째 영역인 S1 영역으로 이동한다.

그리고 최근 Minor GC 에서 S0 영역으로 이동된 객체들도 Age 가 증가하고, S1 영역으로 이동하게 된다. 모든 객체들이 S1 영역으로 이동하게 되면, Eden Space 와 S0 영역은 모두 클리어된다.

이제 우리는 각기 다른 Age 를 가진 객체들을 Survivor 영역에 가지게 된다.


5. 다음 Minor GC 때 역시 같은 과정이 반복

  • 하지만 이번 과정에선 Survivor 영역이 서로 스위치된다.
    참조되는 객체들은 S0 으로 이동하고, 살아남은 객체들은 Age 가 증가한다.
    => 그리고 Eden Space 와 S1 영역은 클리어 된다.

6. Minor GC 이후 일정한 Age 임계값을 넘은 Age 를 가진 객체들이 Old Generation 으로 프로모션됨 (아래의 경우 Age Threshold : 8)


7. Minor GC 가 계속될수록 틀딱 객체들이 Old Generation 영역으로 이동

정리하자면...

  1. Eden 영역에 최초로 객체 생성
  2. Survivor 영역을 통해 오래 살아남은 객체가 Old 영역으로 이동
    -> Old Generatiaon 영역도 똑같이 메모리가 가득 차게 되면 Major GC가 실행된다고 한다.




출처

haero_kim님의 글을 참조하여 작성하였습니다.

profile
초짜 백엔드 개린이

0개의 댓글