GC는 무엇인가?

2022. 11. 11. 16:46카테고리 없음

GC:

메모리 관리 기법 

안 쓰는 메모리 프로그램이 동적으로 할당했던 메모리 영역(heap영역)중 필요없게된 영역(어떤 변수도 가리키지 않는)것을 알아서 해제 시킨다.

 

 

 

 

GC과정,어떻게 안쓰는 건지 알고 지우는가?

 

  1. Garbage Collector가 Stack의 모든 변수를 스캔하면서 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.
  2. Reachable Object가 참조하고 있는 객체도 찾아서 마킹한다 Reachable Object가 참조하고 있는 객체도 찾아서 마킹한다.
  3. 마킹되지 않은 객체를 Heap에서 제거한다 

 

파란색: Mark

빨간색:Sweep

 

Mark and Sweep 과정이라고 한다.

 

GC언제 발생하는가?

 

 

  1. 새로운 객체는 eden에 할당된다. 메모리 다 사용시 gc발생 
  2. mark and sweep과정이 일어나고 Reachable 객체는 Surviver0으로 이동
  3. 이 과정이 계속 반복  만약 Surviver0다 차면  mar and sweep과정을 통해 살아남은 객체는 
  4. Surviver1으로 이동한다 , 이동시 객체 age증가 
  5. 이미 객체가 찬곳에만 쌓인다.
  6. age값이 일정이상 올라가면 Olde Generation으로 가게 된다.

 

 

 

ex) 

main() {
  f1();
  f3();
}

func f1() {
  int k = 3; 
  Object a = new A();
 f2();
}

func f2() {
  int j = 2; 
  Object a = new A();
  return
} 

func f3() {
  int i = 1; 
  Object a = new A();
}

Q프로그램이 시작되서 끝날때 까지 stack에 저장되는 것들
heap에 저장되는 것들을 메소드 순서대로 적어보세요.

 

  1. 먼저 f(1)이 실행이되고 거기서 int k=3이 stack에 쌓이다.그다음 heap영역에 a 가 올라가고 f(2)호출
  2. f(2)에서  int j=2가 stack에 쌓이고 ,그다음 heap영역에 또 다른 a가 올라가고 return하여  실행 된 위치인 main()로 돌아간다. 그다음 코드 실행
  3. f(3)을 실행시키고  int i=1이 stack에 쌓이고 , 그다음 heap영역에 또또다른 a가 올라간다 .

 

Q.  f(1),f(2),f(3) 순서대로 호출이 되고 메인메소드가 아직 종료가 안된 상태에서  동작이 되는 경우에 heap영역에는 몇가지 상황들이 있을수 있는데 heap에 올라간 a 객체는 현재 3개가 있다.  다 없어졌는 가 아니면 마지막 a만 남았는가 

예상 되는 상황을 말해봐라.

 

즉!참조형 변수가 언제 해제되는지 ???? 또한 메인 메소드가 끝났다고 해서 참조변수딜이 다 날아 갔다고 확신 할 수

있는가?

 

 질문의도: 참조변수를 없애서 메모리를확보하는 방법  , 사용하지 않는 메모리를 버려서 메모리를 확보하는 방법 즉 GC가 언제  발동하나?

 

답: 동작했을지 안했을지 모른다, GC는 보통 메모리 공간이 상당히 많이 상당히 많이 차올랐을때 gc를 실행시켜서 초기화를한다.

 

 

Q.왜 자주 하지 않는가?

답: 모든 스레드가 멈춰야 한다 stop-the-word
자주 동작하지 않는다 . 

 

Q.그런 현상이 생기는 이유?

 

ex) 셋이서 같이 공유하는 공간이다, 근데 누군가 계속
물건을 가져오면 이물건을 버려도되는지 남겨 놓아야 하는지
봐야 하는데 계속물건이 들어오면  ?  버려야 새로운것이 들어오는데
이렇게 되면 프로그램이 죽는다, 새로운 프로그램이 들어오면 된다.
이것은 사실 공유 자원에 대해서 얘기하는 것이다 
메모리 공유자원에 넣고뺄수 있기 위해서는  얼려놓아야
깔끔하게 치울 수 있다..

 

Q. 어떻게  stop-the-word 시간을 줄일수 있을까?

 

mark and sweep : 체크해야 하는 객체들이 많을수록 시간이 오래걸린다, 
체크한 객체들이 적으면 그 과정도 빨리 끝난다
프로그램에 따라서 적절한 gc를 쓰는 것도 중요하다.

 

GC 종류 

 

CMSGC

 

  • Initial Mark: GC Root가 참조하는 객체만 마킹 (stop-the-world 발생)
  • Concurrent Mark: 참조하는 객체를 따라가며, 지속적으로 마킹. (stop-the-world 없이 이루어짐)
  • Remark: concurrent mark 과정에서 변경된 사항이 없는지 다시 한번 마킹하며 확정하는 과정. (stop-the-world 발생)
  • Concurrent Sweep: 접근할 수 없는 객체를 제거하는 과정 (stop-the-world 없이 이루어짐)

위와 같이 stop-the-world가 최대한 덜 발생하도록 하여, Java Application이 멈추는 현상을 줄임

결론적으로 최대한 STW 시간을 줄이고자 , 사전에 가비지를 정확히 확인하는 과정을 걸친 후 GC로 제거를 한다.
하지만 사전에 가비지를  찾고 마킹하는데 CPU에 사용량이 많이 든다는 단점이 있다,
또한 메모리 단편화의 이슈가 있다.

 

 

3.5. G1 GC (Garbage Frist GC) (-XX:+UseG1GC)

  • Java 9+ 의 default GC
  • 현재 GC 중 stop-the-world의 시간이 제일 짧음
  • CMS GC 를 개선하여 만든 GC로 위에서 살펴본 GC와는 다른 구조를 가진다. (아래 그림 참고)
출처: https://mirinae312.github.io/develop/2018/06/04/jvm_gc.html
  • Heap을 Region이라는 일정한 부분으로 나눠서 메모리를 관리한다.
  • 전체 Heap에 대해서 탐색하지 않고 부분적으로 Region 단위로 탐색하여, 각각의 Region에만 GC가 발생한다.

장점: 별도의 STW없이도 여유 메모리 공간을 압축하는 기능을 제공한다, 또한 전체 Old Gemeration혹은 Young Generation 통째로 Compaction을 할 필요 없고, 해당 Generation의 일부분 Region에 대해서만 Compaction을 하면된다.

결론은 CMS GC의 단점인 메모리 단편화문제를, Region영역을 통해 효율적으로 해결 할수 있고, 
마이너 GC 메이저 GC를 하고도 공간이 부족시 단점으로는 FUll GC 는 HEAP전반적인 GC가 발생하는 것을 의미하는데 이때 GC가 싱글스레드로 동작을 한다 .그래서  HEAP 공간이 작은 어플레세이션에서는FULL GC가 발생하기 쉽다 , HEAP영역이 큰 어플리케이션에 적합하다 .

 

  STW 현상 은 왜 생기는가?

 

STW가  가비지를 쓸어담을때 다른 스레드는 가만히 있게 되는데 STW에서 삭제하고 처리하는 

스레드만 움직이기 때문에 발생을 한다.

 

왜? 가비지 처리하는 스레드만 움직이는가?

HEAP영역은 공유 자원 ,스레드에 공유 자원이다 공유자원이 진행되고 있을때 다른 스레드가 접근하게 되면 공유자원이 훼손될 가능성이  있기에 접근을 하지 못하게 막는다.

 

요즘 사용하는 것  G1GC를 사용하는가?

 

CPU의 성능을 높이면 ,GC가 일어날때 CPU 부하의 문제를 조금 개선 시킬수는 있으나,CPU는 가격이 너무 비싸다.

그래서 차선책으로  비용이 적게 드는 메모리 (RAM)의 크기를 늘려서 이 문제를 해결 할 수 있다.

그러기 위해서는 큰 메모리 환경에서 효과적으로 관리하는 방법이 필요했고, 그것이 G1GC 가 메모리가 큰 

어플리케이션에 적합하다보니  G1GC를 사용한다.

 

이와 비슷한 원리가 Concurrent HashMap이다.

 

Hashtable 클래스의 대부분의 API를 보면 위와 같이 메소드 전체에 synchronized 키워드가 존재하는 것을 볼 수 있습니다.(메소드 전체가 임계구역으로 설정 됩니다.) 그렇기 때문에 Multi-Thread 환경에서는 나쁘지 않을 수도? 있습니다

하지만 동시에 작업을 하려해도 객체마다 Lock을 하나씩 가지고 있기 때문에 동시에 여러 작업을 해야할 때 병목현상이 발생할 수 밖에 없습니다.(메소드에 접근하게 되면 다른 쓰레드는 Lock을 얻을 때까지 기다려야 하기 때문입니다.)

 

HashMap 클래스를 보면 synchronized 키워드가 존재하지 않습니다. 그렇기 때문에 Map 인터페이스를 구현한 클래스 중에서 성능이 제일 좋다고 할 수 있습니다. 하지만 synchronized 키워드가 존재하지 않기 때문에 당연히 Multi-Thread 환경에서 사용할 수 없다는 특징을 가지고 있습니다.

 

 

 ConcurrentHashMap

Hashtable 클래스의 단점을 보완하면서 Multi-Thread 환경에서 사용할 수 있도록 나온 클래스 입니다.(JDK 1.5에 검색과 업데이트시 동시성 성능을 높이기 위해서 나온 클래스 입니다.)

 

위의 코드는 ConcurrentHashMap 클래스의 일부 API 입니다. ConcuurentHashMap에는 Hashtable 과는 다르게 synchronized 키워드가 메소드 전체에 붙어 있지 않습니다. get() 메소드에는 아예 synchronized가 존재하지 않고, put() 메소드에는 중간에 synchronized 키워드가 존재하는 것을 볼 수 있습니다.

 

이것을 좀 더 정리해보면 ConcurrentHashMap은 읽기 작업에는 여러 쓰레드가 동시에 읽을 수 있지만, 쓰기 작업에는 특정 세그먼트 or 버킷에 대한 Lock을 사용한다는 것입니다.

 

결론:

버킷의 수 == 동시작업 가능한 쓰레드 수인 이유는 위에서 말했던 것처럼 ConcurrentHashMap은 버킷 단위로 lock을 사용하기 때문에 같은 버킷만 아니라면 Lock을 기다릴 필요가 없다는 특징이 있습니다.(버킷당 하나의 Lock을 가지고 있다라고 생각하면 될 것 같습니다.)즉, 여러 쓰레드에서 ConcurrentHashMap 객체에 동시에 데이터를 삽입, 참조하더라도 그 데이터가 다른 세그먼트에 위치하면 서로 락을 얻기 위해 경쟁하지 않습니다.

 

이와 같이  G1 GC는  전체 YOUNG, Old 영역까지  전체 HEAP 영역을 관리할 필요가 없고 해당  Region을 관리하다 보니 관리하기 용이하고 락스트라이핑을 이용해서 그  쪼개진 단위( 버킷) 같은 곳에서 스레드가 한개씩 rock을 가지고 들어가다보니 동시성 이슈 ( 원자성)을  해결 할수 있다.

그렇게 되면 스레드에  STW 시간을 줄일수있다.

 

참고:https://devlog-wjdrbs96.tistory.com/269

https://applefarm.tistory.com/100

 

[Java] ConcurrentHashMap 이란 무엇일까?

들어가기 전에 HashTable, HashMap, ConcurrnetHashMap은 많이 유사한 특징들을 가지고 있습니다. 하지만 세부적으로 보면 조금씩 꽤나 차이가 있는데요. 간단하게 어떤 차이가 있는지 알아보면서 시작하

devlog-wjdrbs96.tistory.com

(트레이드 오프) 찾아서 공부