Technical Note/SERVER PERFORMANCE

Memory Leak을 다루는 방법을 강구하기 전에 Root Set에 대해 좀 더 알아볼 필요가 있습니다.  이 그림은 구동 중인 JVM의 메모리 구조입니다. 간략하게 설명하자면 JVM은 실행 데이터 영역과 그 밖의 JVM의 엔진 역할을 하는 모듈 등으로 구성됩니다.

실행 데이터 영역은 다시 Stack과 같이 Thread에게 속해 있는 메모리 영역과 Thread가 공유하는 Heap, Method Area 같은 것이 있습니다


Java에서 Object를 생성하는 메소드를 수행하면 해당 Thread Stack에 이 메소드에 해당하는 Stack Frame이 생성되죠.

그리고 이 reference를 따라가 Heap Object가 생성됩니다. 그래서 Stack Root Set이 됩니다. 또 Object를 생성하는 또 한가지 방법은 Method Area에서 Static 변수에 의해 참조되는 것입니다. Method Area Sun JVM의 경우 Permanent Area라는 영역 안에 들어 있는 Class의 메타 정보를 의미합니다.


IBM JVM의 경우는 Area의 구분 없이 Class Object라는 이름을 가지고 있는 것입니다. Static 변수에 의해 참조되는 경우 이 Method Area Root Set이 됩니다. 또한 같은 방식으로 JNI를 이용하면서 Object를 생성할 수도 있습니다. 이 경우에 JNI 코드가 Root Set이 되는 것입니다.


그렇다면 이 그림에서 Live, Reachable Object를 구별해 볼까요? Root Set에서 직접 연결되는 것은 Live Object입니다.

그리고 Live Object에서 참조되는 이 Object Live Object입니다. 그리고 아무데서도 참조되지 않는 이 Object Not Live 상태입니다. 아마 다음 번 Garbage Collection 때 제거될 것으로 보입니다.


그렇다면 Unreachable Object는 어떤 것일까요? 모두 일수도 있고 전부다 아닐 수도 있습니다.

이것은 명확한 개념이 아니고 만약 참조가 되어 Garbage가 아니면서도 거의 사용되지도 않고 필요가 없는 Object Unreachable이라고 합니다. 그러니 참조 구조만으로 알 수 없는 것이죠. 그런데 Root Set이 어느 것이냐에 따라 이것이 Memory Leak으로 될 수도 있고 안될 수도 있습니다.


우선 Stack의 경우를 살펴 봅시다.

Stack Thread에 속해 있기 때문에 이 Thread가 작업이 끝나면 이 연결되어 있는Object는 모두 Garbage가 됩니다.

보통 WAS같은 데서는 User Thread는 수명이 짧죠이럴 경우는 문제되지 않습니다.

그러나 Application의 수명이 계속되는 동안 유지되는 Thread의 경우는 Live but Unreachable Object가 있다면 큰 문제가 됩니다.


Static 변수가 참조하는 Object는 어떨까요?

Static 변수에서 Memory Leak이 발생하는 경우 Class unloading될 때 까지는 계속 메모리를 점유하고 있기 때문에 문제가 될 소지가 큽니다. 특히 공통 모듈 같이 빈번하게 사용되는 Class의 경우라면 더욱 그러할 것입니다.

JNI의 경우는 Global 객체일 경우 문제가 됩니다.

Local 객체인 경우는 이 JNI를 호출한 Thread가 종료되면 Garbage가 되겠지만 Global객체로 생성되면 오랜 시간 동안 참조가 유지되기 때문입니다.

 

Memory Leak이 발생하게 되면 어떻게 되는지는 여러분들이 더 잘 아실 것입니다.

한정된 메모리 공간을 쓸데없이 사용하게 되기 때문에 그만큼 공간이 줄어들게 되겠지요.

결국 메모리가 없어서 Application을 수행하지 못하게 될 것이라는 메시지가 뜨게 될 것입니다.

 

이 익셉션이 발생하면 사실 WAS또는 JVM을 새로 기동하는 것 외에는 방법이 없습니다.

그렇기 때문에 이에 대한 탐지와 이에 따른 코드 수정은 불가피 합니다.

Java Memory Leak의 문제는 사실 그 역사가 비슷합니다.

그럼에도 불구하고 아직까지 뾰족한 해결책은 나오지 않았습니다.

그래서 아직까지 Memory Leak을 탐지하기 위한 방법은 크게 달라지지 않았습니다.


첫번째는 메모리의 추이를 살펴보는 것이죠.

Garbage Collection이 수행될 때마다 가용 메모리는 줄었다 늘었다를 반복하죠그러나 전체적으로 보았을 때 점점 가용 메모리가 줄고 있는 것 같으면 그리고 OOME가 발생하면 Memory Leak을 의심해 보아야 합니다. 그렇다면 어디에서 이 Memory Leak이 유발되었는지를 파악해야 합니다. 그래서 JVM벤더에서는 Heap Dump라는 것을 제공하죠. JVM에서 떨어트린 Heap Dump는 사실 좀 알아보기 쉽지 않기 때문에 Hat이나 Heap Analyzer와 같은 툴을 이용할 수 있습니다.


그러나 이러한 방식도 어느 Object가 문제가 있다는 것을 알려줄 뿐 그 연결 고리를 찾는 것은 쉽지 않은 일입니다.

특히 WAS의 경우 어느 Servlet에서 어떤 경로로 이 Object가 문제가 되는지를 알기란 쉽지 않은 일이죠.

그래서 전문 모니터링 툴이 점점 발전하고 있습니다가장 유명한 것이 JProbe입니다.

이것은 굳이 Heap Dump를 떨구지 않더라도 실시간으로 Object의 상황을 알아볼 수 있지만 역시 연결고리의 문제가 남게 됩니다InterMax의 경우는 기존의 Profiling 방식에Servelet의 연결고리까지 추가하여 보여주기 때문에 가장 진보한 형태입니다.

 

지금까지 Memory Leak에 대해 알아보았습니다.

사실 지금까지 Memory Leak에 대한 확실한 해결법은 나오지 않은 상태입니다.

지금까지의 최선은 검증된 프레임 웤등을 사용하여 개발하고 계속해서 모니터링을 하는 것으로 생각됩니다.