자바의 대표적인 특징으로

 플랫폼의 독립적이다, 객체지향적이다, 자동으로 메모리를 관리해준다 라는 특징이 있다.


 여기서 3번째 언급한 자동 메모리 관리는 흔히 알고있는 가비지 컬렉터가 더 이상 쓸모없어진 객체들을 자동으로 지워주는 기능을 뜻한다.


 처음에 자바 공부를 할 때에 별로 와닿지 않았는데 나는 개발자가 직접 메모리 관리를 해주어야하는 다른 언어를 배운적도 없고 

 자동으로 관리를 해주는데 신경 쓸 필요가 있나? 라는 생각이 들어 그냥 그렇게 넘어 갔었다.


 하지만 JVM에 대해 공부하면서 항상 언급되는 heap영역의 설명 중 'JVM의 성능 등의 이슈에서 가장 많이 언급되는 공간이다' 라는 설명을 항상 보았고 의문이 생겨 알아보게 되었다.


이 글을 보기전에 먼저 JVM 메모리구조에 대해 공부하는 것이 좋다. 검색하면 많이 나온다. (내 블로그에도 있지만.. 미약하다)



본론



 heap 영역은 크게 Young Generation 영역과 Old Generation 영역 으로 구성되며 Young 영역은 Eden과 두개의 SS(survivor space)로 나뉘어진다.


heap에 객체가 적재되는 과정


1. 객체가 새로 생성되면 먼저 Eden에 속하게 된다. 그러다 꽉 차게되면 


2. 필요없어진 객체를 비우고 살아남은 객체들을 SS중 한 곳으로 보낸다


3. 1,2의 과정을 반복하다 SS도 차게되면 역시 필요 없어진 객체를 비우고 살아남은 객체들을 남아있던 SS 로 보낸다. (반드시 한 SS는 비워진 상태여야 한다.)


4. 이 과정을 거치며 더 이상 SS에도 공간이 없어지면 살아남은 객체를 Old영역으로 보낸다.





Generation GC가 탄생한 배경


왜 heap영역을 저렇게 나누고 이리저리 옮겨가며 저장을 하는 것일까???



그 이유를 알기전에 먼저 알아야할 개념이 있다.


Stop the World (STW)


 말 그대로 GC발생시 JVM은 실행중인 어플리케이션을 멈추게 한다. GC를 수행하는 쓰레드를 제외한 나머지에 모든 쓰레드의 작업을 중지시킨다. 

그래서 heap 영역의 성능 튜닝 즉 GC튜닝이란 이 STW의 시간을 최소한으로 줄이는 것이다.



David ungar라는 사람이 weak generational hypothesis 라는 가설을 세운다. 그 내용은


     대부분의 객체는 금방 죽는다.  


무슨 말이냐면 대부분의 생성된 객체는 금방 쓸모가 없어진다. 즉 GC의 대상이 된다는 것이다


그래서 heap 전체를 정리해줄 필요 없이 Young영역만을 주기적으로 관리해주고 가끔씩만 Old영역을 정리해주면 되는 것이다.


여기서 Young영역에서 일어나는 GC를 마이너GC라 부르며 상대적으로 속도가 빠르다. (1초이내)

Old 영역에서 일어나는 GC는 메이저GC라 부르며 속도가 느리다. (STW시간이 길다) 


이 heap영역의 Young영역과 Old영역의 메모리는 설정이 가능한데 최대한 마이너GC만으로 메모리가 유지 될 수 있도록 적절하게 설정해주는 것이 필요하다고 생각한다.



GC의 종류


결국에는 Old 영역도 채워질 것이고 그러면 메이저GC가 발생 할 것이다. 이 Old영역의 GC는 채택하고 있는 방식에 따라 달라지므로 다음 5가지 방식에 대해 알아보자.


1. Serial GC (-XX:+UseSerialGC)


1) old 살아있는 개체 식별(mark)
2) heap 앞부분부터 확인해 살아있는것만 남김(sweep)
3) 각 객체들이 연속되게 쌓이도록 heap 의 가장 앞 부분부터 적재(compact)


이렇게 mark-sweep-compact 알고리즘을 사용하며 단일코어 싱글 쓰레드를 사용하여 부담이 적지만 성능이 안좋다.



2. Parallel GC  (-XX:+UseParallelGC)


Serial GC와 같은 알고리즘을 사용하지만 멀티 쓰레드를 사용하여 속도가 더 빠르다는 특징이 있다. 





3. Parallel Old GC


업뎃 예정...



4. CMS (Concurrent Mark-Sweep) GC  (-XX:+UseConcMarkSweepGC)


내용이 어렵지만 쉽게 말하면 

백그라운드 쓰레드를 작동시켜 Old 영역의 쓰레기들을 지속적으로 없애 줍니다. 

그래서 STW가 거의 없다는 장점이 있습니다.


하지만 2가지 단점이 있는데 

 - 백그라운드에서 항상 쓰레드가 도니 CPU를 많이 잡아먹고

 - 기본적으로 Compact 단계를 제공하지 않아 메모리 파편화가 생깁니다 

그래서 결국 파편화가 심해지면 이를 정리해주는 시간이 다른 방식의 GC를 사용할 때 생기는 STW시간보다 길어 질 수 있습니다.



5. G1 GC (-XX:+UseG1GC)


heap을 Young Old로 구조적으로 나누지 않고 개념적으로만 나눈다. 


이렇게 통으로 바둑판식으로 관리한다. 


CMS GC 방식처럼 백그라운드 쓰레드로 Old영역을 관리하며 필요없어진 부분을 통째로 날리며 살아남은 객체는 다른 부분으로 옮겨주어

CMS 의 단점이던 메모리 파편화를 해결한 방식 입니다.




각 jdk 별로 기본적으로 채택한 GC 방식은 다음과 같습니다.


  • Java 7 - Parallel GC
  • Java 8 - Parallel GC
  • Java 9 - G1 (proposed)




마냥 자바의 메모리 관리는 가비지 컬렉터가 알아서 해준다~ 라고 생각 하고만 있었지만

이제는 적어도 적절한 GC를 선택 하고 튜닝을 해줄 수 있고 필요하다는 걸 알게 되었다.



참고,출처

https://okky.kr/article/379036

https://yckwon2nd.blogspot.com/2014/04/garbage-collection.html

https://www.slipp.net/wiki/pages/viewpage.action?pageId=26641949

http://www.holaxprogramming.com/2013/07/20/java-jvm-gc/

http://12bme.tistory.com/57

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

http://lyb1495.tistory.com/3



+ Recent posts