Find out when memory leaks are a concern and how to prevent them
Handling memory leaks in Java programs
Find out when memory leaks are a concern and how to prevent them
How memory leaks manifest themselves in Java programs
Most programmers know that one of the beauties of using a programming language such as Java is that they no longer have to worry about allocating and freeing memory. You simply create objects, and Java takes care of removing them when they are no longer needed by the application through a mechanism known as garbage collection. This process means that Java has solved one of the nasty problems that plague other programming languages -- the dreaded memory leak. Or has it?
Before we get too deep into our discussion, let's begin by reviewing how garbage collection actually works. The job of the garbage collector is to find objects that are no longer needed by an application and to remove them when they can no longer be accessed or referenced. The garbage collector starts at the root nodes, classes that persist throughout the life of a Java application, and sweeps through all of the nodes that are referenced. As it traverses the nodes, it keeps track of which objects are actively being referenced. Any classes that are no longer being referenced are then eligible to be garbage collected. The memory resources used by these objects can be returned to the Java virtual machine (JVM) when the objects are deleted.
So it is true that Java code does not require the programmer to be responsible for memory management cleanup, and that it automatically garbage collects unused objects. However, the key point to remember is that an object is only counted as being unused when it is no longer referenced. Figure 1 illustrates this concept.
【many times member variables of a class that point to other classes simply need to be set to null at the appropriate time】
Figure 1. Unused but still referenced

The figure illustrates two classes that have different lifetimes during the execution of a Java application. Class A is instantiated first and exists for a long time or for the entire life of the program. At some point, class B is created, and class A adds a reference to this newly created class. Now let's suppose class Bis some user interface widget that is displayed and eventually dismissed by the user. Even though class B is no longer needed, if the reference that class A has to class B is not cleared, class B will continue to exist and to take up memory space even after the next garbage collection cycle is executed.
When are memory leaks a concern?
If your program is getting a java.lang.OutOfMemoryError after executing for a while, a memory leak is certainly a strong suspect. Beyond this obvious case, when should memory leaks become a concern? The perfectionist programmer would answer that all memory leaks need to be investigated and corrected. However, there are several other points to consider before jumping to this conclusion, including the lifetime of the program and the size of the leak.
Consider the possibility that the garbage collector may never even run during an application's lifetime. There is no guarantee as to when or if the JVM will invoke the garbage collector -- even if a program explicitly calls System.gc(). Typically, the garbage collector won't be automatically run until a program needs more memory than is currently available. At this point, the JVM will first attempt to make more memory available by invoking the garbage collector. If this attempt still doesn't free enough resources, then the JVM will obtain more memory from the operating system until it finally reaches the maximum allowed.
Take, for example, a small Java application that displays some simple user interface elements for configuration modifications and that has a memory leak. Chances are that the garbage collector will not even be invoked before the application closes, because the JVM will probably have plenty of memory to create all of the objects needed by the program with leftover memory to spare. So, in this case, even though some dead objects are taking up memory while the program is being executed, it really doesn't matter for all practical purposes.
If the Java code being developed is meant to run on a server 24 hours a day, then memory leaks become much more significant than in the case of our configuration utility. Even the smallest leak in some code that is meant to be continuously run will eventually result in the JVM exhausting all of the memory available.
And in the opposite case where a program is relatively short lived, memory limits can be reached by any Java code that allocates a large number of temporary objects (or a handful of objects that eat up large amounts of memory) that are not de-referenced when no longer needed.
One last consideration is that the memory leak isn't a concern at all. Java memory leaks should not be considered as dangerous as leaks that occur in other languages such as C++ where memory is lost and never returned to the operating system. In the case of Java applications, we have unneeded objects clinging to memory resources that have been given to the JVM by the operating system. So in theory, once the Java application and its JVM have been closed, all allocated memory will be returned to the operating system.
Determining if an application has memory leaks
To see if a Java application running on a Windows NT platform is leaking memory, you might be tempted to simply observe the memory settings in Task Manager as the application is run. However, after observing a few Java applications at work, you will find that they use a lot of memory compared to native applications. Some Java projects that I have worked on can start out using 10 to 20 MB of system memory. Compare this number to the native Windows Explorer program shipped with the operating system, which uses something on the order of 5 MB.
The other thing to note about Java application memory use is that the typical program running with the IBM JDK 1.1.8 JVM seems to keep gobbling up more and more system memory as it runs. The program never seems to return any memory back to the system until a very large amount of physical memory has been allocated to the application. Could these situations be signs of a memory leak?
To understand what is going on, we need to familiarize ourselves with how the JVM uses system memory for its heap. When running java.exe, you can use certain options to control the startup and maximum size of the garbage-collected heap (-ms and -mx, respectively). The Sun JDK 1.1.8 uses a default 1 MB startup setting and a 16 MB maximum setting. The IBM JDK 1.1.8 uses a default maximum setting of one-half the total physical memory size of the machine. These memory settings have a direct impact on what the JVM does when it runs out of memory. The JVM may continue growing the heap rather than wait for a garbage collection cycle to complete.
So for the purposes of finding and eventually eliminating a memory leak, we are going to need better tools than task monitoring utility programs. Memory debugging programs (see Related topics) can come in handy when you're trying to detect memory leaks. These programs typically give you information about the number of objects in the heap, the number of instances of each object, and the memory being using by the objects. In addition, they may also provide useful views showing each object's references and referrers so that you can track down the source of a memory leak.
Next, I will show how I detected and removed a memory leak using the JProbe debugger from Sitraka Software to give you some idea of how these tools can be deployed and the process required to successfully remove a leak.
A memory leak example
This example centers on a problem that manifested itself after a tester spent several hours working with a Java JDK 1.1.8 application that my department was developing for commercial release. The underlying code and packages to this Java application were developed by several different groups of programmers over time. The memory leaks that cropped up within the application were caused, I suspect, by programmers who did not truly understand the code that had been developed elsewhere.
The Java code in question allowed a user to create applications for a Palm personal digital assistant without having to write any Palm OS native code. By using a graphical interface, the user could create forms, populate them with controls, and then wire events from these controls to create the Palm application. The tester discovered that the Java application eventually ran out of memory as he created and deleted forms and controls over time. The developers hadn't detected the problem because their machines had more physical memory.
To investigate this problem, I used JProbe to determine what was going wrong. Even with the powerful tools and memory snapshots that JProbe provides, the investigation turned out to be a tedious, iterative process that involved first determining the cause of a given memory leak and then making code changes and verifying the results.
JProbe has several options to control what information is actually recorded during a debugging session. After some experimentation, I decided that the most efficient way to get the information I needed was to turn off the performance data collection and concentrate on the captured heap data. JProbe provides a view called the Runtime Heap Summary that shows the amount of heap memory in use over time as the Java application is running. It also provides a toolbar button to force the JVM to perform garbage collection when desired. This capability turned out to be very useful when trying to see if a given instance of a class would be garbage collected when it was no longer needed by the Java application. Figure 2 shows the amount of heap storage that is in use over time.
Figure 2. Runtime Heap Summary

In the Heap Usage Chart, the blue portion indicates the amount of heap space that has been allocated. After I started the Java program and it reached a stable point, I forced the garbage collector to run, which is indicated by the sudden drop in the blue curve before the green line (this line indicates a checkpoint was inserted). Next, I added, then deleted four forms and again invoked the garbage collector. The fact that the level blue area after the checkpoint is higher than the level blue area before the checkpoint tells us that a memory leak is likely, as the program has returned to its initial state of only having a single visible form. I confirmed the leak by looking at the Instance Summary, which indicates that the FormFrame class (which is the main UI class for the forms) has increased in count by four after the checkpoint.
Finding the cause
My first step in trying to isolate the problems reported by the tester was to come up with some simple, reproducible test cases. For this example, I found that simply adding a form, deleting the form, and then forcing a garbage collection resulted in many class instances associated with the deleted form to still be alive. This problem was apparent in the JProbe Instance Summary view, which counts the number of instances that are on the heap for each Java class.
To pinpoint the references that were keeping the garbage collector from properly doing its job, I used JProbe's Reference Graph, shown in Figure 3, to determine which classes were still referencing the now-deleted FormFrame class. This process turned out to be one of the trickiest aspects of debugging this problem as I discovered many different objects were still referencing the unused object. The trial-and-error process of figuring out which of these referrers was truly causing the problem was quite time consuming.
In this case, a root class (upper-left corner in red) is where the problem originated. The class that is highlighted in blue on the right is along the path that has been traced from the original FormFrame class.
Figure 3. Tracing a memory leak in a reference graph

For this specific example, it turned out that the primary culprit was a font manager class that contained a static hashtable. After tracing back through the list of referrers, I found that the root node was a static hashtable that stored the fonts in use for each form. The various forms could be zoomed in or out independently, so the hashtable contained a vector with all of the fonts for a given form. When the zoom view of the form was changed, the vector of fonts was fetched and the appropriate zoom factor was applied to the font sizes.
The problem with this font manager class was that while the code put the font vector into the hashtable when the form was created, no provision was ever made to remove the vector when the form was deleted. Therefore, this static hashtable, which essentially existed for the life of the application itself, was never removing the keys that referenced each form. Consequently, the form and all of its associated classes were left dangling in memory.
Applying a fix
The simple solution to this problem was to add a method to the font manager class that allowed the hashtable's remove() method to be called with the appropriate key when the form was deleted by the user. The removeKeyFromHashtables() method is shown below:
|
1
2
3
4
5
6
|
public void removeKeyFromHashtables(GraphCanvas graph) { if (graph != null) { viewFontTable.remove(graph); // remove key from hashtable // to prevent memory leak }} |
Next, I added a call to this method to the FormFrame class. FormFrame uses Swing's internal frames to actually implement the form UI, so the call to the font manager was added to the method that is executed when an internal frame has completely closed, as shown here:
|
1
2
3
4
5
6
7
8
9
|
/*** Invoked when a FormFrame is disposed. Clean out references to prevent * memory leaks.*/public void internalFrameClosed(InternalFrameEvent e) { FontManager.get().removeKeyFromHashtables(canvas); canvas = null; setDesktopIcon(null);} |
After I made these code changes, I used the debugger to verify that the object count associated with the deleted form decreased when the same test case was executed.
Preventing memory leaks
You can prevent memory leaks by watching for some common problems. Collection classes, such as hashtables and vectors, are common places to find the cause of a memory leak. This is particularly true if the class has been declared static and exists for the life of the application.
Another common problem occurs when you register a class as an event listener without bothering to unregister when the class is no longer needed. Also, many times member variables of a class that point to other classes simply need to be set to null at the appropriate time.
Conclusion
Finding the cause of a memory leak can be a tedious process, not to mention one that will require special debugging tools. However, once you become familiar with the tools and the patterns to look for in tracing object references, you will be able to track down memory leaks. In addition, you'll gain some valuable skills that may not only save a programming project, but also provide insight as to what coding practices to avoid to prevent memory leaks in future projects.
Find out when memory leaks are a concern and how to prevent them的更多相关文章
- Identify Memory Leaks in Visual CPP Applications —— VLD内存泄漏检测工具
原文地址:http://www.codeproject.com/Articles/1045847/Identify-Memory-Leaks-in-Visual-CPP-Applications 基于 ...
- 解决:Detected memory leaks
最近在一个项目中,程序退出后都出现内存泄漏: Detected memory leaks!Dumping objects ->{171} normal block at 0x05785AD0, ...
- [Angular2 Router] Exiting an Angular 2 Route - How To Prevent Memory Leaks
In this tutorial we are going to learn how we can accidentally creating memory leaks in our applicat ...
- The Introduction of Java Memory Leaks
One of the most significant advantages of Java is its memory management. You simply create objects a ...
- 【转】简单内存泄漏检测方法 解决 Detected memory leaks! 问题
我的环境是: XP SP2 . VS2003 最近在一个项目中,程序退出后都出现内存泄漏: Detected memory leaks! Dumping objects -> {98500} n ...
- On Memory Leaks in Java and in Android.
from:http://chaosinmotion.com/blog/?p=696 Just because it's a garbage collected language doesn't mea ...
- _CrtSetBreakAlloc简单内存泄漏检测方法,解决Detected memory leaks!问题
我的环境是: XP SP2 . VS2003 最近在一个项目中,程序退出后都出现内存泄漏: Detected memory leaks! Dumping objects -> {98500} n ...
- 内存泄露 Memory Leaks
什么是内存泄露 内存管理一直是Java 所鼓吹的强大优点.开发者只需要简单地创建对象,而Java的垃圾收集器将会自动管理内存空间的分配和释放. 但在很多情况下,事情并不那么简单,在 Java程序中总是 ...
- Activitys, Threads, & Memory Leaks
Activitys, Threads, & Memory Leaks 在Android编程中,一个公认的难题是在Activity的生命周期如何协调长期运行的任务和避免有可能出现的内存泄漏问题. ...
随机推荐
- 标准C程序设计七---45
Linux应用 编程深入 语言编程 标准C程序设计七---经典C11程序设计 以下内容为阅读: <标准C程序设计>(第7版) 作者 ...
- C++的静态联编和动态联编详解
一.概述: 通常来说联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程.按照联编所进行的 ...
- iOS开发之手势gesture详解(一)
前言 在iOS中,你可以使用系统内置的手势识别(GestureRecognizer),也可以创建自己的手势.GestureRecognizer将低级别的转换为高级别的执行行为,是你绑定到view的对象 ...
- POJ 2577: Interpreter
简略解题报告 Description A certain computer has 10 registers and 1000 words of RAM. Each register or RAM l ...
- Hibernate游记——装备篇《二》(基础配置示例)
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hi ...
- 牛客网 牛客小白月赛1 A.简单题-控制输出格式setiosflags()函数+setprecision()函数
水一水博客,都不好意思写这篇博客,毕竟已经不是大一的了. 难得能把一整套题都写出来(日常智障).但是在这里不写G题あなたの蛙は旅⽴っています的题解. 有毒,G题关了流同步只能过94%的样例,说我运行超 ...
- Codeforces 934 C.A Twisty Movement-前缀和+后缀和+动态规划
C. A Twisty Movement time limit per test 1 second memory limit per test 256 megabytes input standa ...
- MyBatis_SelectKey使用oracle 序列插入主键
mapper 如下: 使用<selectkey>实现 也可以使用oracle的row 级触发器trigger实现: <?xml version="1.0" enc ...
- array_map常用技巧
array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组. 简单来说 “array_map” 会对数组中的每一项进行处理,并返回处理后的数据. 定 ...
- hough变换检测直线和圆
图像测量和机器视觉作业: 提取图像中的直线和点的位置坐标,将其按一定顺序编码存入一文本文件,并在原图像上叠加显示出来. 下午实验了一下: 程序环境:vs2013(活动平台为x64)+opencv3.1 ...