`

The Top Java Memory Problems – Part 1

阅读更多

转载:http://blog.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.

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.

Memory Trending Dumps

Memory Trending Dump that shows that the number of objects of the same type increase

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.

Single Object which is responsible for a big portion of the memory being leaked

Single Object which is responsible for a big portion of the memory being leaked

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.

 

The heapdump shows that over 4K objects which amount to about 10MB are held by thread locals
The heapdump shows that over 4K objects which amount to about 10MB are held by thread locals

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.

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. I've see this and other similar issues quite often.

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 don't 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.

Wrong implementation of equals/hashcode

It might not be obvious on the first glance, but if your equals/hashcode methods violate the equals contract it will lead to memory leaks when used as a key in a map. A HashMap uses the hashcode to lookup an object and verify that it found it by using the equals method. If two objects are equal they must have the same hashcode, but not the other way around. If you do not explicitly implement hashcode yourself this is not the case. The default hashcode is based on object identity. Thus using an object without a valid hashcode implementation as a key in a map, you will be able to add things but you will not find them anymore. Even worse if you re-add it, it will not overwrite the old item but really add a new one. And just like that you have memory leak. You will find it easily enough as it is growing, but the root cause will be hard to determine unless you remember this one.

The easiest way to avoid this is to use unit testcases and one of the available frameworks that tests the equals contract of your classes (e.g. http://code.google.com/p/equalsverifier/).

Classloader Leaks

When thinking about memory leaks we think mostly about normal java objects. Especially in application servers and OSGi containers there is another form of memory leak, the class loader leak. Classes are referenced by their classloader and normally they will not get garbage collected until the classloader itself is collected. That however only happens when the application gets unloaded by the application server or OSGi container. There are two forms of Classloader Leaks that I can describe off the top of my head.

In the first an object whose class belongs to the class loader is still referenced by a cache, a thread local or some other means. In that case the whole class loader, meaning the whole application cannot be garbage collected. This is something that happens quite a lot in OSGi containers nowadays and used to happen in JEE Application Servers frequently as well. As it is only happens when the application gets unloaded or redeployed it does not happen very often.

The second form is nastier and was introduced by bytecode manipulation frameworks like BCEL and ASM. These frameworks allow the dynamic creation of new classes. If you follow that thought you will realize that now classes, just like objects, can be forgotten by the developer. The responsible code might create new classes for the same purpose multiple times. As the class is referenced in the current class loader you get a memory leak that will lead to an out of memory in the permanent generation. The real bad news is that most heap analyzer tools do not point out this problem either, we have to analyze it manually, the hard way. This form or memory leak became famous due to an issue in an older version of hibernate and its usage of CGLIB.

Summary

As we see there are many different causes for memory leaks and not all of them are easy to detect. In my next post I will look at further java memory problems, so stay tuned.

分享到:
评论

相关推荐

    JSR-133-Java Memory Model Specification 中文版+英文版+参考资料

    j-jtp02244-Java.theory.and.practice-Fixing.the.Java.Memory.Model.Part1/2.pdf Java Memory Model Pragmatics (transcript).pdf 读JSR133笔记 - 十年磨一剑 - ITeye博客.pdf The JSR-133 Cookbook.pdf jsr-133-...

    The Java Memory Model.pdf

    《Java Memory Model》是一篇关于Java内存模型的重要论文,由Jeremy Manson、William Pugh以及Sarita V. Adve共同撰写。该论文主要介绍了Java 5.0版本中更新后的Java内存模型(JMM),并详细阐述了它在多线程程序中...

    The Art of Memory Forensic

    The next era of malware and security breaches are more sophisticated and targeted, and the volatile memory of a computer is often overlooked or destroyed as part of the incident response process. The ...

    java memory model

    Java内存模型(Java Memory Model,简称JMM)是Java虚拟机规范中的一个重要组成部分,它定义了程序中各种变量(线程共享变量)的访问规则,以及在并发环境下变量的存储与读取假设。本文将基于JSR-133规范,深入探讨...

    The Art of Memory Forensics 无水印pdf

    The Art of Memory Forensics 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者...

    MemoryAnalyzer-1.9.1.20190826-win32.win32.x86_64_.zip

    假如你机器的内存不大,改大该参数的值,会导致MemoryAnalyzer启动时,报错:Failed to create the Java Virtual Machine。 2.当你导出的dump文件的大小大于你配置的1024m(说明1中,提到的配置:-vmargs– Xmx1024m...

    java堆内存分析工具EclipseMemoryAnalyzer

    1、MemoryAnalyzer使用说明文档/使用指南 2、MemoryAnalyzer 1.8.1下载: Eclipse Memory Analyzer 是一个功能丰富且轻量的 Java 堆内存分析工具,可以用来辅助发现内存泄漏减 少内存占用。 使用 Memory Analyzer 来...

    javamemory_JAVA内存监视器_java_

    为了更好地评估内存消耗,还可以使用内存分析工具,例如MAT(Memory Analyzer Tool)或YourKit Java Profiler,它们可以深入分析堆转储文件,帮助定位内存占用高的对象和引用链,找出导致内存泄漏的原因。...

    Memory Management in the Java HotSpot Virtual Machine.pdf

    本文档提供了Java HotSpot虚拟机(JVM)中内存管理的广泛概述,特别是在Sun公司的Java 2平台标准版(J2SE)5.0版本的发布中。文档描述了可供使用的垃圾收集器(Garbage Collectors),给出了关于如何选择和配置收集...

    The Art of Memory Forensics

    The next era of malware and security breaches are more sophisticated and targeted, and the volatile memory of a computer is often overlooked or destroyed as part of the incident response process. The ...

    Data Structures and Algorithms in Java, 5th Edition (Part 3/3)

    Now revised to reflect the innovations of Java 5.0, Goodrich and Tamassia’s Fourth Edition of Data Structures and Algorithms in Java continues to offer accessible coverage of fundamental data ...

    翻译《Memory Management in the Java HotSpot™ Virtual Machine》

    《Memory Management in the Java HotSpot™ Virtual Machine》一文深入探讨了Java HotSpot虚拟机中的内存管理机制,这是Java性能优化的关键领域。HotSpot虚拟机是Oracle JDK和JRE的一部分,以其高性能和优化能力而...

    The Cambridge Face Memory Test

    The Cambridge Face Memory Test: Results for neurologically intact individuals and an investigation of its validity using inverted face stimuli and prosopagnosic participants

    Data Structures and Algorithms in Java, 5th Edition (Part 1/3)

    Now revised to reflect the innovations of Java 5.0, Goodrich and Tamassia’s Fourth Edition of Data Structures and Algorithms in Java continues to offer accessible coverage of fundamental data ...

    Data Structures and Algorithms in Java, 5th Edition (Part 2/3)

    Now revised to reflect the innovations of Java 5.0, Goodrich and Tamassia’s Fourth Edition of Data Structures and Algorithms in Java continues to offer accessible coverage of fundamental data ...

    the art of memory forensic

    Memory forensics is the art of analyzing computer memory (RAM) to solve digital crimes. As a follow-up to the best seller Malware Analyst's Cookbook, experts in the fields of malware, security, and ...

    Troubleshooting Memory Problems-Poonam Parhar.pdf

    1. Misconfiguration of Memory Pools:内存池的misconfiguration是最常见的内存问题之一。它可以导致Java应用程序中的内存使用率不均匀,导致应用程序崩溃。 2. Excessive use of Finalizers:Finalizer是一种特殊...

    Top_10_Java_Performance_Problems

    ### Top 10 Java Performance Problems #### Introduction 在Java应用程序开发过程中,性能问题是一个常见的挑战。这些问题不仅影响应用的响应时间和资源利用率,还可能影响用户体验和业务效率。本文将详细介绍十个...

Global site tag (gtag.js) - Google Analytics