2009. 4. 11. 11:56
Java garbage collection Enjoy/JAVA2009. 4. 11. 11:56
Java garbage collection
Qualcomm 부스에서 기다렸더니 옆집 아저씨같은 분이 반갑게 맞이하며 두 개 질문 하더라. vTable이 뭐야? 하하하 이건 우리 인터뷰 과외전문 art.oriented 김선생이 귀뜸해준 것이 아닌가? 상속받은 클래스가 여러 개 있는데 virtual 키워드를 쓰게 되면 실제 호출할 때 엉뚱한 메소드를 호출할 수도 있다. 그 때를 위해 메소드들에 대한 포인터를 가지고 있는 것이 vTable이라고 영어로 버벅거리며 대답을 했다. 내 대답에 아저씨는 미심쩍은 표정을 지어주셨다.
두 번째 질문은 Java에서 언제 garbage collection을 하는 가였다. gc라고 garbage collector가 있지만 그걸 내가 실행시킨다고 바로 garbege collector가 작동하는 것도 아니고 JVM이 알아서 자동으로 하는 거라고 말해줬다. 그랬더니 그럼 자동으로 어떻게 하냐고 되묻는다. 퀄컴 오피스가 학교 근처에 있길래 편하게 인턴하겠다 싶어서 지원했는데 레주메 내는 것조차 쉽지가 않다. 그래도 대답을 해야겠기에 일반적인 이야기를 했다. 자바가 자동으로 memory를 관리하고 reference counter등을 가지고 있어서 메모리가 부족하면 garbage collection이 일어난다고. 아저씨 표정은 아까보다 더 않 좋게 변했다. 인터뷰 하나가 날아가는 소리가 들린다.
시험 볼 때 틀린 문제는 제대로 알아놓지 않으면 나중에 나왔을 때 또 틀리는 법! 그래서 잊어버리기 전에 한 번 적어보자. 구글신 가라사대
즉, 정해진 건 없다는 건데. 그럼 내가 말한 것처럼 레퍼런스 카운팅을 해서 구현해도 사실 문제가 없는 거잖아? 그 아저씨는 왜 이상한 표정을 지었을까. 그런데 저 위에 나온 문서는 96년에 Bill Venners라는 사람이 쓴 글이다. 그럼 좀 더 최신 문서를 찾아보면?
한 줄 요약하면, JVM 1.2이상 버전에서는 copying과 mark-compact 기술을 합친 generational garbage collection을 한다는 말이다. 내가 그 아저씨한테 reference counting을 한다고 했으니 아저씨 표정이 이랬지.
두 번째 질문은 Java에서 언제 garbage collection을 하는 가였다. gc라고 garbage collector가 있지만 그걸 내가 실행시킨다고 바로 garbege collector가 작동하는 것도 아니고 JVM이 알아서 자동으로 하는 거라고 말해줬다. 그랬더니 그럼 자동으로 어떻게 하냐고 되묻는다. 퀄컴 오피스가 학교 근처에 있길래 편하게 인턴하겠다 싶어서 지원했는데 레주메 내는 것조차 쉽지가 않다. 그래도 대답을 해야겠기에 일반적인 이야기를 했다. 자바가 자동으로 memory를 관리하고 reference counter등을 가지고 있어서 메모리가 부족하면 garbage collection이 일어난다고. 아저씨 표정은 아까보다 더 않 좋게 변했다. 인터뷰 하나가 날아가는 소리가 들린다.
시험 볼 때 틀린 문제는 제대로 알아놓지 않으면 나중에 나왔을 때 또 틀리는 법! 그래서 잊어버리기 전에 한 번 적어보자. 구글신 가라사대
The JVM specification says only that the heap of the Java virtual machine must be garbage collected. The specification does not define how the garbage collector must work. The designer of each JVM must decide how to implement the garbage-collected heap.
즉, 정해진 건 없다는 건데. 그럼 내가 말한 것처럼 레퍼런스 카운팅을 해서 구현해도 사실 문제가 없는 거잖아? 그 아저씨는 왜 이상한 표정을 지었을까. 그런데 저 위에 나온 문서는 96년에 Bill Venners라는 사람이 쓴 글이다. 그럼 좀 더 최신 문서를 찾아보면?
Last month, we looked at the classic garbage collection techniques of reference counting, copying, mark-sweep, and mark-compact. Each of these approaches has advantages and disadvantages in certain situations. For example, copying does well when a large proportion of objects are garbage, but does poorly with many long-lived objects (copying them repeatedly). Conversely, mark-compact does quite well with long-lived objects (copying them only once), but not so well with many short-lived objects. The technique used by the 1.2 and later JVMs, calledgenerational garbage collection, combines these two techniques to get the best of both worlds, and as a bonus provides very low object allocation overhead.
한 줄 요약하면, JVM 1.2이상 버전에서는 copying과 mark-compact 기술을 합친 generational garbage collection을 한다는 말이다. 내가 그 아저씨한테 reference counting을 한다고 했으니 아저씨 표정이 이랬지.
copying GC
블로그에 보니 알기 쉽게 설명이 잘 되어있다. 역시 나같은 사람은 그림을 보여줘야 이해가 빠르다. 간단하게 설명하자면 메모리를 두 개로 나눈다. 두 공간을 각각 ping과 pong으로 부르자. 처음에는 ping에만 stack처럼 메모리를 할당하고 stack의 top 포인터를 가장 마지막에 할당한 메모리를 가리키게 한다. 근데 계속 새로운 메모리를 할당하게 되면 ping이 꽉 차서 더 이상 메모리를 할당할 수 없게 된다. 그럼 그 때 GC를 실행시킨다. GC는 간단하다. pong에다가 현재 참조되고 있는 메모리를 다 옮겨놓는다. 그리고 ping은 다 지운다. 그리곤 pong이 다 찰 때까지 새로운 메모리는 pong에다가 계속 할당한다.
이 방법은 매우 빠르다는 장점이 있다. 그냥 top 포인터는 계속 증가하기만하면 되고, 한 쪽이 다 차면 다른 한쪽으로 옮기면 끝. GC를 할 시점인지 아닌지도 top 포인터만 체크하면 된다. 자 이제 단점 들어간다. 항상 두 배의 메모리가 필요하다. ping과 pong 두 개를 유지해서 하나가 꽉 차면 다른 한쪽으로 옮겨야하기 때문에 나머지 하나는 항상 놀고 있다.
mark-compact GC
이건 정말 짤막한 설명이어서 한 편으로는 머리속에 지식이 훅~ 하고 들어오지만, 한 편으로는 그림이 없어서 조금 애매하기도 하다. 알고리즘은 두 단계로 되어있다.
generational garbage collection
쉽게 말해 메모리를 여러 세대(generation)으로 나누고 GC에서 살아 남거나 하면 다음 세대(next older generation)으로 승격되는 시스템 되겠다. JVM은 모든 메모리 영역에 대해서 GC를 하는 것이 아니라 메모리를 여러 영역으로 나누고, 해당 영역이 꽉 차면 그 영역에 대해서만 GC를 한다. 전체 영역에 대한 GC(Full GC)를 할 필요가 없고 일부 영역에 대한 GC(Minor GC)만 수행하므로 수행 속도가 빨라진다.
JVM은 메모리를 젊은 세대(Young Generation, 이하 YG)과 나이든 세대(Old Generation, 이하 OG)으로 구분한다. YG는 다시 Eden, 두 개의 Survivor 나뉜다. 처음 object가 생성이 되면 일단 Eden에 들어가게 되고 대부분의 object는 Eden에서 생을 마감하게 된다. Eden이 꽉 차면 Minor GC를 수행하고 살아 남는 object들은 Survivor 지역으로 이동된다. Survivor에서 어느 정도 오래 살아 남으면 Tenured 지역으로 이동되고, Tenured 지역이 꽉 차면 다시 Full GC가 일어난다.
더 자세한 정보를 원한다면 링크1, 링크2를 보기 바란다.
블로그에 보니 알기 쉽게 설명이 잘 되어있다. 역시 나같은 사람은 그림을 보여줘야 이해가 빠르다. 간단하게 설명하자면 메모리를 두 개로 나눈다. 두 공간을 각각 ping과 pong으로 부르자. 처음에는 ping에만 stack처럼 메모리를 할당하고 stack의 top 포인터를 가장 마지막에 할당한 메모리를 가리키게 한다. 근데 계속 새로운 메모리를 할당하게 되면 ping이 꽉 차서 더 이상 메모리를 할당할 수 없게 된다. 그럼 그 때 GC를 실행시킨다. GC는 간단하다. pong에다가 현재 참조되고 있는 메모리를 다 옮겨놓는다. 그리고 ping은 다 지운다. 그리곤 pong이 다 찰 때까지 새로운 메모리는 pong에다가 계속 할당한다.
이 방법은 매우 빠르다는 장점이 있다. 그냥 top 포인터는 계속 증가하기만하면 되고, 한 쪽이 다 차면 다른 한쪽으로 옮기면 끝. GC를 할 시점인지 아닌지도 top 포인터만 체크하면 된다. 자 이제 단점 들어간다. 항상 두 배의 메모리가 필요하다. ping과 pong 두 개를 유지해서 하나가 꽉 차면 다른 한쪽으로 옮겨야하기 때문에 나머지 하나는 항상 놀고 있다.
mark-compact GC
이건 정말 짤막한 설명이어서 한 편으로는 머리속에 지식이 훅~ 하고 들어오지만, 한 편으로는 그림이 없어서 조금 애매하기도 하다. 알고리즘은 두 단계로 되어있다.
- 모든 참조되는 object들에 mark를 해 놓는다.
- 마크된 것들은 contiguous memory location에다가 옮겨놓는다.
generational garbage collection
A generational collector divides the heap into multiple generations.Objects are created in theyoung generation, and objects that meet some promotion criteria, such as having survived a certain number of collections, are then promoted to the next older generation.
쉽게 말해 메모리를 여러 세대(generation)으로 나누고 GC에서 살아 남거나 하면 다음 세대(next older generation)으로 승격되는 시스템 되겠다. JVM은 모든 메모리 영역에 대해서 GC를 하는 것이 아니라 메모리를 여러 영역으로 나누고, 해당 영역이 꽉 차면 그 영역에 대해서만 GC를 한다. 전체 영역에 대한 GC(Full GC)를 할 필요가 없고 일부 영역에 대한 GC(Minor GC)만 수행하므로 수행 속도가 빨라진다.
Sun의 자바 사이트에서 가져온 그림
JVM은 메모리를 젊은 세대(Young Generation, 이하 YG)과 나이든 세대(Old Generation, 이하 OG)으로 구분한다. YG는 다시 Eden, 두 개의 Survivor 나뉜다. 처음 object가 생성이 되면 일단 Eden에 들어가게 되고 대부분의 object는 Eden에서 생을 마감하게 된다. Eden이 꽉 차면 Minor GC를 수행하고 살아 남는 object들은 Survivor 지역으로 이동된다. Survivor에서 어느 정도 오래 살아 남으면 Tenured 지역으로 이동되고, Tenured 지역이 꽉 차면 다시 Full GC가 일어난다.