노트코드
노트코드
노트코드

블로그 메뉴

  • 홈
  • 이력서
  • 이전 블로그
  • 글쓰기
  • 관리자페이지
  • 분류 전체보기 (57)
    • 코틀린 (2)
      • 실무 프로젝트로 배우는 Kotlin & Sprin.. (2)
    • JAVA (1)
      • 디자인패턴 (1)
      • 객체지향 5대원칙 (0)
    • SPRING (32)
      • JPA (11)
      • 스프링시큐리티 (1)
      • 스프링 (8)
      • QueryDsl (1)
      • 스프링배치 (11)
    • AZURE (0)
    • ETC (10)
      • MAVEN (0)
      • GIT (0)
      • ReMind (3)
      • Exception (1)
      • CS (6)
    • 책 (8)
      • 이것이 자바다 (8)

최근 글

최근 댓글

태그

  • 스프링
  • JPA
전체 방문자
오늘
어제
hELLO · Designed By 정상우.
노트코드

노트코드

ETC/ReMind

[GC] GC

2022. 2. 14. 17:52

면접 질문 단골인 GC에 대해 매일같이 탈탈 털리던중, 하루 날잡고 공부해야겠다 생각하였고, 그 내용을 정리하고자 한다. 다양한 블로그 글을 읽으며 간단하게 GC의 동작은 이렇게 동작하는거 같다고 생각한다.

Heap영역에서  참조되고 있지 않은 대상(가비지)을 찾아 삭제한다(힙의 메모리를 회수).

 

1. 그래서 그 대상을 어떻게 찾는건데....?

여기서 가비지인지 판단하기위해, 힙 영역에 객체를 사용되는(Reachable)것과 사용되지 않는 (Unreachable)의 상태로 구분하여 사용되지 않는 객체를 대상으로 판단하고 GC작업을 수행한다. 즉 사용되지 않는 상태라하면, 그 누구도 참조하지 않는 대상을 말한다.(혼자 메모리에 살아 있는놈)

 

힙에 있는 객체들에 대한 참조는 4가지중 하나

  1. 힙 내의 다른 객체에 의한 참조
  2. JAVA스택, 즉 JAVA 메서드 실행 시에 사용하는 지역 변수와 파라미터들에 의한 참조
  3. 네이티브 스택, 즉 JNI에 의해 생성된 객체에 대한 참조
  4. 메서드 영역의 정적 변수에 의한 참조

이들 중 힙 내의 다른 객체에 의한 참조를 제외한 나머지 3개가 root set으로 reachability를 판단하는 기준이 된다.

Root Set으로 부터 시작한 참조 사슬에 속한 객체들은 reachable 객체이고, 이 참조 사슬과 무관한 객체들이 unreachable로 GC대상으로 여기고 삭제한다.

root set : 유효한 참조 여부를 파악하려면 항상 유효한 최초의 참조가 있어야 하는데 이를 객체 참조의 root set이라고 한다.

 

이미지 출처 :   https://d2.naver.com/helloworld/329631

 

 

2. GC가 발생하는 Heap의 구조는?

Heap은 크게 Young Generation, OldGeneration으로 나누어지고, 이중 Young Generation은
Eden, Survivor space0,1(From survivor space와, to Survivor Space로도 불림)로 세분화된다.

이미지 출처 :https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

 

  • Eden space: 새롭게 생성된 객체가 Eden의 용량보다 큰 경우를 제외하고  할당되는 영역이다.
    • Eden 영역이 꽉차게 되면 Minor GC가 발생한다.
    • 대부분의 객체가 금방 Unreachable 상태가 되므로 많은 객체가 생성됬다가 사라지는 위치
    • GC가 발생하는 동안 Stop the World(GC를 제외한 모든 어플리케이션 스레드가 멈춤)가 발생
  • Survivor Space: Eden영역이 꽉차게 되면 GC가 발생하면서 제거된 객체 외의 살아 남은 객체가 이동하는 장소
    • 동일한 사이즈의 두개의 영역으로 구분되고 이름은 From,to(또는 S0,S1)이라고 불린다.
    • 단편화 문제를 해결하기위해 2개를 가지고 있다.
    • Survivor 두 영역중 하나는 반드시 비어있는 상태,  만약 두 영역에 모두 데이터가 존재하거나, 사용량이 0이라면 정상적인 상황이 아님
    • Minor GC가 발생한다.
  • Old(Tenured): Young영역에서 Reachable 상태를 유지하여 살아 남은 객체가 복사되는 영역이다.
    • Old영역은 Young영역보다 크게 할당되며, 크기가 큰 만큼 가비지는 적게 발생한다.
    • 해당 영역이 가능 차면 Major GC가 발생

 

3. Minor GC동작원리

 

1.  객체가 할당되면 Eden 영역에 하나씩 차곡차곡 쌓인다.

 

2. Eden영역이 꽉차게 되면 Minor GC가 동작하게 된다.

3.  Unreachable과 reachable을 구분하고, Unreachable 대상은 삭제하고, reachable대상은
survivorS0 위치로 이동시킨다. 이미지에 보이는 1은 generational count라고 불리는데, GC로 부터 살아 남을때마다 +1된다고 생각하면된다. (+1 프로세스를 aging이라고 부름)

(위 과정을 진행하면 Eden영역은 아무값도 없이 비어있게 됨)

4. 위 작업이후 또 다시 Eden영역에 할당을 하다가 가득차게 되면, 또다시 Minor GC가 동작하게 된다.

다만 이때는 Survivor Space영역도 탐색의 범위에 속하고, Survivor S0에서 Unreachable대상을 찾아 마킹을 하고,

reachable대상은 Survivor S1으로 이동시킨다. 그리고 Eden영역에서도 동일하게 reachable대상을 S1으로 이동시킨다.

+ (이 과정에서 survivor space S0에 있던 대상은 generation count가 2가 되고, Eden에서 넘어온 대상은 1이 된다.)

 

Eden영역에 꽉찼을때 말고도 Minor GC가 발생하는 경우가 있는데, Survivor Space에 더이상 객체를 할당 할 수 없을때도 해당된다.
(Eden영역에 꽉찼을때마다 GC가 동작하여 Survivor space영역의 빈공간을 채워 넣을거 같지만 실제론 그렇지 않음,

사용하지 않는 객체를 삭제하며 사용공간을 확보했지만, 연속된 사용 공간이 없는 메모리 단편화가 발생하기 때문)

5. 이 과정이 몇번 반복되다보면 generation Count가 높아지는 대상이 보이게 되는데,일정 count값을 넘어가는 대상은 Old space영역(Tenured)으로 이동하게 된다.

Old space영역으로 이동하는 경우?
1. Generation Count가 일정 숫자에 도달 하였을때
(적당한 나이는 -XX:InitialTenuringThreshold와 -XX:MaxTenuringThreshold 파라미터로 정할 수 있다.)
2. 메모리 할당이 너무 잦아 Survivor Space에 공간이 없을때
3. 새롭게 할당될 객체의 용량이 Eden Space의 용량보다 큰 경우
4. Minor GC 이후의 From Survivor Space의 사용률(%)을 제한하는 옵션인,
   XX:TargetSurvivorRatio(기본값 50)의 설정 사용률을 넘어갈 때

* Old Space영역으로 넘어과는 과정을 promotion이라고 하는데, 객체의 count가 일정 값을 넘어가 이동되는것을 뜻함

* Premature Protmotion도 존재하는데, old space로 이동하는 조건중 promotion에 해당하는 것을 제외한 나머지를 뜻함

(즉 특정 count에 도달하지 못했으나, 다른 이유로 old space로 이동할 수 밖에 없는 경우를 말한다)

- 

 

4. Major GC

  • Minor GC와 대상을 찾는 방법등은 동일하며, Old Space(Tenured)가 가득찼을때 발생한다.
  • Old Space에 있는 대상은 대체로 크기가 크기 때문에 GC시간이 길다.
    (GC의 시간이 길다는것은 Stop the World의 시간도 길다는것을 의미한다.)
  • Old Generation은 Young Generation과 같이 Survivor Space가 존재하는게 아니기 때문에 메모리 단편화도 신경 써야하고 관리해야할 객체도 많다보이 훨씬 알고리즘이 복잡하다.

 

 

5. (etc)메모리 단편화

Ram에서 메모리 공간이 작은 조각으로 나뉘어져 사용가능한 메모리가 충분히 존재하지만 할당이 불가능한 상태

  • 내부 단편화
    • 메모리를 할당할 때 프로세스가 필요한 양보다 더 큰 메모리가 할당되어서 프러세스에서 사용하는 메모리 공간이 낭비되는 상황
  • 외부 단편화
    • 메모리가 할당되고 해제되는 작업이 반복될 때 작은 메모리가 중간중간 존재하게 된다. 이때 중간중간에 생긴 사용하지 않는 메모리가 많이 존재해서 총 메로리 공간은 충분하지만 실제로 할당할 수 없는 상황을 뜻함

 

 

 

6. GC방식

  • Serial GC : 하나의 쓰레드로 동작하는 순차적 방식의 GC (Mark-Sweep-Compact)
    1. 사용되지 않는 객체를 식별하는 작업(Mark)
    2. 사용되지 않는 객체를 제거하는 작업(Sweep)
    3. 파편화된 메모리 영역을 앞에서부터 채워나가는 작업(Compact)
  • Parallel GC:  Serial GC를 멀티 스레드로 동작시키는 방식으로 속도가 더 빨라졌다
  • Parallel Old GC: Parallel GC에서 Old GC알고리즘을 개선한 버전이다
    • Old GC도 병렬로 수행될 수 있도록함
    • 기존 Mark-Sweep-Compact 알고리즘에서,  Mark-Summary-Compaction 알고리즘으로 변경
      Summary 단계에서서는 이미 GC가 수행된 영역에서 살아 있는 객체를 식별하는 작업을 진행한다는 점이 Sweep랑 다르다
  • CMS(Concurrent Mark Sweep) GC : 자주 참조되는 객체를 한번에 찾지 않고 4번에 걸쳐 찾는 방식을 사용
    해당 GC는 stop the world 시간을 최소화 하는데 그 목적이 있다. low-latency GC로도 알려져있다.
    단점
    • 다른 GC방식보다 메모리와 CPU를 더 많이 사용
    • Compaction 단계가 기본적으로 제공되지 않는다.

이미지 출처 https://d2.naver.com/helloworld/1329

  1. initalMark : 클래스 로더에서 가장 가까운 객체 중 살아 있는 객체만 찾는것으로 끝낸다. 
  2. Concurrent Mark: 방금 살아있다고 확인한 객체에서 참조하고 있는 객체들을 따라가면서 확인한다.
    다른 스레드가 실행 중인 상태에서 동시에 진행된다.
  3. Remark : Concurrent Mark단계에서 새로 추가되거나 참조가 끊긴 객체들을 확인한다.
  4. Concurrent Sweep: 쓰레기를 정리하는 작업을 실행한다. 이 작업도 다른 스레드가 실행되는 상황에서 진행한다.

 

  • G1 GC : 바둑판의 각 영역에 객체를 할당하고 GC를 실행한다. 그러다가 해당 영역이 꽉 차면 다른 영역에서 객체를 할당하고 GC를 실행한다. 
    • Eden, Survivor,Old 영역이 존재하지만 고정된 크기로 고정된 위치에 존재하는 것이 아니라, 전체 힙 메모리 영역을 Region이라는 특정한 크기로 나눠서 각 Region의 상태에 따라 그 Region에 역할이 동적으로 부여되는 상태
    • G1 Gc에서는 Humongous, Available/unused이 존재한다.
      • Humongous: Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간
      • Available/Unused: 아직 사용되지 않는 Region
        참조: https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html
    • G1 GC에서 Young GC 를 수행할 때는 STW(Stop-The-World) 현상이 발생하며, STW 시간을 최대한 줄이기 위해 멀티스레드로 GC를 수행한다. Young GC는 각 Region 중 GC대상 객체가 가장 많은 Region(Eden 또는 Survivor 역할) 에서 수행 되며, 이 Region 에서 살아남은 객체를 다른 Region(Survivor 역할) 으로 옮긴 후, 비워진 Region을 사용가능한 Region으로 돌리는 형태 로 동작한다.
    • G1 GC에서 Full GC 가 수행될 때는 Initial Mark -> Root Region Scan -> Concurrent Mark -> Remark -> Cleanup > Copy 단계를 거치게된다.
      • Initial Mark : Old Region 에 존재하는 객체들이 참조하는 Survivor Region 을 찾는다. 이 과정에서는 STW 현상이 발생하게 된다.
      • Root Region Scan: Initial Mark 에서 찾은 Survivor Region에 대한 GC 대상 객체 스캔 작업을 진행한다.
      • Concurrent Mark: 전체 힙의 Region에 대해 스캔 작업을 진행하며, GC 대상 객체가 발견되지 않은 Region 은 이후 단계를 처리하는데 제외되도록 한다.
      • Remark : 애플리케이션을 멈추고(STW) 최종적으로 GC 대상에서 제외될 객체(살아남을 객체)를 식별해낸다.
      • Cleanup:애플리케이션을 멈추고(STW) 살아있는 객체가 가장 적은 Region 에 대한 미사용 객체 제거 수행한다. 이후 STW를 끝내고, 앞선 GC 과정에서 완전히 비워진 Region 을 Freelist에 추가하여 재사용될 수 있게 한다.
      • Copy: GC 대상 Region이었지만 Cleanup 과정에서 완전히 비워지지 않은 Region의 살아남은 객체들을 새로운(Available/Unused) Region 에 복사하여 Compaction 작업을 수행한다.
        참조 : https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html

 

 


참조

https://d2.naver.com/helloworld/1329

https://d2.naver.com/helloworld/329631

https://perfectacle.github.io/2019/05/07/jvm-gc-basic/

https://beststar-1.tistory.com/15#%EA%B0%80%EB%B9%84%EC%A7%80_%EC%BB%AC%EB%A0%89%EC%85%98(Garbage_Collection) 

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html

 

'ETC > ReMind' 카테고리의 다른 글

[JPA] 엔티티의 생명주기  (0) 2022.02.08
[JPA] 영속성 컨텍스트  (0) 2022.02.08
    'ETC/ReMind' 카테고리의 다른 글
    • [JPA] 엔티티의 생명주기
    • [JPA] 영속성 컨텍스트
    노트코드
    노트코드
    노션 블로그에서 티스토리로 이전공사중

    티스토리툴바