본문 바로가기

개발&새발/Java

[번역] 자바에서 가장 많이 일어나는 메모리 문제 - 1

http://apmblog.dynatrace.com/2011/04/20/the-top-java-memory-problems-part-1/



Memory and Garbage Collection problems are still the most prominent issues in any java application. One of the reasons is that the very nature of Garbage Collection is often misunderstood. This prompted me to write a summary of some of the most frequent and also most obscure memory related issues that I have encountered in my time. I will cover the causes of memory leaks, high memory usage, class loader problems and GC configuration and how to detect them. We will begin this series with the best known one – memory leaks.


메모리 및 가비지컬랙션 문제는 여전히 모든 자바 응용 프로그램에서 가장 눈에 띄는 문제입니다. 이유 중 하나는 가비지 컬렉션의 본질이 종종 오해된다는 점이다. 이것은 가장 빈번하고도 가장 알수없는 메모리 관련 문제 대문에 들인 내 시간때문에 나로 하여금 이 글을 작성하도록 자극했다. 나는 메모리 누수, 메모리 사용, 클래스 로더 문제와 GC의 구성 및 방법을 검토하는 방법을 다룰 것이다. 메모리 누수라는 우리에게 가장 잘 알려진 하나의 시리즈 이야기를 시작해 보자.


Memory Leaks

A memory leak is the most-discussed java memory issue there is. Most of the time people only talk about growing memory leaks, that is the continuous rise of leaked objects. They are in comparison, the easiest to track down as you can look at trending or histogram dumps.


'메모리 누수'는 가장 많이 논의된 자바 메모리 문제입니다. 논의된 시간중의 대부분, 사람들은 객체가 지속적으로 증가하는 누수 현상에 대한 이야기를 한다. 그것들을 비교해 보면, 우리는 추세 또는 히스토그램 덤프를 봄으로서 쉽게 추적할 수 있다.


이미지 - http://apmblog.dynatrace.com/wp-content/trending.png


In the picture we see the dynaTrace trending dump facility. You can achieve similar results manual by using jmap -histo <pid> multiple times and compare the results. Single object leaks are less thought about. As long as we have enough memory they seldom pose a serious problem. From time to time though there are single object leaks that occupy a considerable amount of memory and become a problem. The good news is that single big leaks are easily discovered by todays heap analyzer tools, as they concentrate on that.


사진에 dynaTrace라는 덤프를 통한 추세 기능을 참조해라. 당신은 jmap -histo <PID>를 여러 번 사용하여 비슷한 결과를 내고 그 결과를 비교함으로서 간단히 결과를 조작할 수 있다. 단일 개체 누수는 생각할 필요가 없다. 그 만큼 우리가 충분한 메모리를 가지고 있다면 거의 심각한 문제가 발생하지 않는다. 때때로 메모리의 상당한 양을 차지하며, 문제가 될 단일 개체 누수가 있기도 하지만. 좋은 소식은 그 집중적인 하나의 큰 누수는 쉽게 오늘날의 힙 분석 도구에 의해 발견된다는 것이다.


이미지 - http://apmblog.dynatrace.com/wp-content/big-leak.png


Lastly there is also the particularly nasty, but in reality rarely, seen version of a lot of small, unrelated memory leaks. It’s theoretically possible, but in reality it would need a lot of seriously bad programmers working on the same project. So let’s look at the most common causes for memory leaks.


마지막으로 이것은 불쾌하게도 계속 존재하긴 하지만, 실제로는 너무 작아서, 관련이 없는 메모리 누수가 일어나는 작은 많은 버전을 볼 수 있다. 그것은 이론적으로 가능하지만, 실제로는 심각하게 많은 나쁜 프로그래머들이 한 프로젝트에서 같이 일해야 나타난다. 그래서 이제는 메모리 누수에 대한 가장 일반적인 원인을 살펴 보러 가자.



Thread Local Variables

ThreadLocals are used to bind a variable or a state to a thread. Each thread has its own instance of the variable. They are very useful but also very dangerous. They are often used to track a state, like the current transaction id, but sometimes they hold a little more information. A thread local variable is referenced by its thread and as such its lifecycle is bound to it. In most application servers threads are reused via thread pools and thus are never garbage collected. If the application code is not carefully clearing the thread local variable you get a nasty memory leak.

These kind of memory leaks can easily be discovered with a heap dump. Just take a look at the ThreadLocalMap in the heap dump and follow the references.

 

ThreadLocals 은 하나의 쓰레드에서 변수 또는 상태를 결합하는 데에 사용되어진다. 각각의 쓰레드들은 변수의 자체 인스턴스를 가지고 있다. 그것들은 매우 유용하지만 또한 매우 위험하다. 그것들은 종종 상태(트랜잭션 ID와 같은) 를 추적하는 데에 사용된다. 그러나 때때로 그것들은 조금 더 많은 정보를 보유한다.

하나의 쓰레드로컬 변수는 그 쓰레드 그리고 쓰레드로컬변수로 결합된 라이프사이클에 의해 참조된다. 대부분의 어플리케이션 서버 쓰레드들은 쓰레드POOL에 의해서 재사용되고 그러므로 가비지 수집되지 않는다. 만약 어플리케이션 코드는 쓰레드지역변수를 주의깊게 지우지 않으면 당신은 끔찍한 메모리부족을 발생할 것이다.

이러한 종류의 메모리 부족은 쉽게 heap dump에서 발견할 수 있다. Heap Dump ThreadLocalMap 를 보거나 레퍼런스를 참고해라

 

http://apmblog.dynatrace.com/wp-content/threadlocal-600x359.png

 

You can then also look at the name of the thread to figure out which part of your application is responsible for the leak.

당신은 쓰레드의 이름으로 어플리케이션에서 누수를 담당하는 부분을 볼 수 있다.

 

Mutable static fields and collections

The most common reason for a memory leak is the wrong usage of statics. A static variable is held by its class and subsequently by its classloader. While a class can be garbage collected it will seldom happen during an applications lifetime. Very often statics are used to hold cache information or share state across threads. If this is not done diligently it is very easy to get a memory leak. Especially static mutable collections should be avoided at all costs for just that reason. A good architectural rule is not to use mutable static objects at all, most of the time there is a better alternative.

 

메모리누수의 가장 많은 이유는 잘못된 스태틱의 사용이다. 하나의 스태틱 변수는 클래스와 그 후 클래스 로더에 의해서 열린다. 하나의 클래스의 가비지가 수집될 수 있지만, 그것은 어플리케이션 라이프사이클동안 발생하지 않는다. 매우 종종 스태틱은 캐쉬정보를 보유하거나 쓰레드에서 상태를 공유할 때 사용된다. 만약 이러한 일들은 부지런히 수행하지 않을 경우, 매우 쉽게 메모리누수를 얻는다. 특히 static mutable collection들은 희생을 치르더라도 피해야 한다. 하나의 좋은 아키텍쳐 규칙은 mutable static objects을 절대로 사용하지 않는 것이다. 더 나은 방법이 있다.

 

Circular and complex bi-directional references

This is my favorite memory leak. It is best explained by example:

org.w3c.dom.Document doc = readXmlDocument();
org.w3c.dom.Node child = doc.getDocumentElement().getFirstChild();
doc.removeNode(child);
doc = null;

At the end of the code snippet we would think that the DOM Document will be garbage collected. That is however not the case. A DOM Node object always belongs to a Document. Even when removed from the Document the Node Object still  has a reference to its owning document. As long as we keep the child object the document and all other nodes it contains will not be garbage collected. Ive see this and other similar issues quite often.

 

이것은 내가 좋아하는 메모리누수이다. 예제의 의한 가장 좋은 설명이다.

코드마지막에서 우리는 DOM Document는 가비지 수집될 것이라고 생각한다. 그러나 그러한 케이스는 일어나지 않는다. 하나의 DOM Node 오브젝트는 항상 하나의 Document에 속한다. Document 에서 제거하는 경우에도 Node Object는 여전히 소유 document의 참조를 가지고 있다. document에서 child object와 모든 다른 node들을 유지하는 한 그것은 가비지 수집되는 것을 포함하지 않을 것이다. 나는 이것과 유사한 이슈들을 종종 참조했습니다.



JNI memory leaks

This is a particularly nasty form or memory leak. It is not easily found, unless you have the right tool and it is also not known to a lot of people. JNI is used to call native code from java. This native code can handle, call and also create java objects. Every java object created in a native method begins its life as a so called local reference. That means that the object is referenced until the native method returns. We could say the native method references the java object. So you dont have a problem unless the native method runs forever. In some cases you want to keep the created object even after the native call has ended. To achieve this you can either ensure that it is referenced by some other java object or you can change the local reference into a global reference.

A global reference is a GC root and will never be garbage collected until explicitly deleted by the native code. The only way to discover such a memory leak is to use a heap dump tool that explicitly shows global native references. If you have to use JNI you should rather make sure that you reference these objects normally and forgo global references altogether.

You can find this sort of leak when your heap dump analysis tool explicitly marks the GC Root as a native reference, otherwise you will have a hard time.

 

이경우는 특히나 심각한 상태거나 또는 메모리 누수입니다. 당신이 제데로된 툴을 가지고 있어도 쉽게 찾을수 없고 많은 사람들에게 알려져 있지 않다. JNI는 자바에서 네이티브 코드를 호출하는 데 사용된다. 이 네이티브 코드는 호출, 처리하고 또한 자바 객체를 생성 할 수 있습니다. 네이티브 메소드에서 생성 된 모든 자바 객체는 지역범위 기준으로 라이프사이클이 정해진다. , 오브젝트가 네이티브 메소드가 반환 될 때까지 참조되는 것을 의미한다. 우리는 네이티브 메소드가 자바객체를 참조한다고 말할 수 있습니다. 그래서 네이티브 메소드가 영원히 돈다할지라도 문제가 없다. 이를 위해 당신은 하나가 다른 자바 객체에 의해 참조되어 있는지 확인하거나 글로벌 기준으로 로컬 참조를 변경할 수 있습니다. 전역 참조는 GC 루트이고 네이티브 코드에 의해 명시적으로 삭제될 때 까지 결코 가비지컬랙팅 당하지 않을 것이다. 그러한 메모리 누설을 발견 할 수있는 유일한 방법은 명시 적으로 글로벌 고유 참조를 보여주는 힙 덤프 도구를 사용하는 것이다. 만약 당신이 JNI를 사용해야하는 경우 당신은 오히려 일반적인 이러한 개체를 참조하는지 확인하고 모든 글로벌 참조를 포기해야한다.

힙 덤프 분석 도구가 네이티브 참조의 GC Root 를 명시 적으로 보여줄 때 당신은 누수된 내용을 찾을 수 있다. 그렇지 않다면 당신은 힘든시간을 보내게 될 것이다.