Memory leaking without objects growing in number or size

拜拜、爱过 提交于 2019-12-03 09:38:31

Having eliminated some careless memory waste of memory (though not any leaks) in my program, and tuned the GC better for our workload, I have brought down the runaway memory use to a tolerable level.

However, in the process I have demonstrated that the IBM J9 JVM used on the AS/400 (aka iSeries, Systemi, i5, et al) has an 1336 byte/minute leak totaling 2 MB/day. I can observe this leak with a variety of programs from a "one-line" test program all the way up to our application server.

The one-line test program is this:

public class ZMemoryLeak2
extends Object
{

static public synchronized void main(String... args) {
    try { ZMemoryLeak2.class.wait(0); } catch(InterruptedException thr) { System.exit(0); }
    }

}

And a separate test program that did nothing except monitor memory use via the JMX API showed conclusively that 1336 B is leaked at exactly 1 minute intervals, never to be reclaimed (well, not reclaimed after 2 weeks of running). OP Note: It was actually slightly different amounts on each variation of the JVM.

Update 2012-04-02: This was accepted as a bug by IBM a few weeks ago; it was actually found and patched in Java 5 about the middle of last year, and the patch for Java 6 is expected to be available in the next week or two.

Great question. Thought I'd turn some of my comments into an answer.

  1. You mention that an idle system grows in terms of memory. This is an important bit of information. Either there is some internal scheduled jobs (automations, timers, etc.) or external process monitoring that is causing object bandwidth. I would consider turning off monitoring to see if the graphs are affected. This may help you figure out which objects are part of the problem.

  2. When the object is under load, I suspect there is a certain amount of object bandwidth. Your ultimate problem may be that the IBM JVM is not handling memory fragmentation as well as the other JVMs -- I'm surprised by this though. I would work with them to try various other GC options to see how you can address this. I would think that this would be easy to simulate if you wrote a test server that did a whole bunch of memory operations and see if over days the memory usage grows. This may demonstrate that it is time to migrate away from IBM JVMs. Again, this would surprise me but if what you say is true and the number or size of objects is not growing...

  3. I would look at the graphs of the various memory sections. I suspect you are seeing old-gen space go up and down and survivor trickle up steadily. If it is true that the number of objects is not changing then @Stephen must be right about their internal size or something else it at work. Maybe the object accounting is failing to report them all for some reason.

  4. I find that the gc JMX button on the memory tab does a more complete sweep. It should be equivalent to using System.gc() which you have tried. Just FYI.

  5. It would be good to turn on GC logging output to see if you can see any patterns: http://christiansons.net/mike/blog/2008/12/java-garbage-collection-logging/ and http://java.sun.com/developer/technicalArticles/Programming/GCPortal/

  6. Any chance you can increase the transaction throughput on the server without changing monitoring or internal automations? If you see the memory graphs change in slope then you know that it is transaction based. If not then your problems are elsewhere. Again, this is to help you find which objects may be causing problems.

Hope something in here is helpful.

One possible explanation is that you are seeing the build up of objects in a cache implemented using WeakReference or similar. The scenario goes like this:

  • The GC cycles that you see in the graph are collections of the new space, and are not causing the references to be broken. So the cache is continuing to grow and use more heap space.

  • When you take a snapshot, this causes a full GC to be run which (maybe) breaks the references, and frees up the cached objects.

(Note the "maybe". I'm not sure that this explanation holds water ...)


Another possible explanation is that your application has the same number of objects, but some of them are larger. For instance, you might have an array of some primitive type that you keep reallocating with a larger size. Or a StringBuilder / StringBuffer the keeps growing. Or (in some circumstances) an ArrayList or similar that keeps growing.


You know, you could be chasing a phantom here. It may be that the system dump is telling the truth and there is no storage leak at all. You could test that theory by reducing the heap size to a point where a real memory leak is likely to provoke an OOME relatively quickly. If I couldn't provoke an OOME that way, I'd be inclined to write this off as an interesting curiosity ... and move on to a real problem.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!