I just had an interview, and I was asked to create a memory leak with Java. Needless to say I felt pretty dumb having no clue on how to even start creating one.
What would an example be?
I just had an interview, and I was asked to create a memory leak with Java. Needless to say I felt pretty dumb having no clue on how to even start creating one.
What would an example be?
Threads are not collected until they terminate. They serve as roots of garbage collection. They are one of the few objects that won't be reclaimed simply by forgetting about them or clearing references to them.
Consider: the basic pattern to terminate a worker thread is to set some condition variable seen by the thread. The thread can check the variable periodically and use that as a signal to terminate. If the variable is not declared
volatile
, then the change to the variable might not be seen by the thread, so it won't know to terminate. Or imagine if some threads want to update a shared object, but deadlock while trying to lock on it.If you only have a handful of threads these bugs will probably be obvious because your program will stop working properly. If you have a thread pool that creates more threads as needed, then the obsolete/stuck threads might not be noticed, and will accumulate indefinitely, causing a memory leak. Threads are likely to use other data in your application, so will also prevent anything they directly reference from ever being collected.
As a toy example:
Call
System.gc()
all you like, but the object passed toleakMe
will never die.(*edited*)
Below there will be a non-obvious case where Java leaks, besides the standard case of forgotten listeners, static references, bogus/modifiable keys in hashmaps, or just threads stuck without any chance to end their life-cycle.
File.deleteOnExit()
- always leaks the string,if the string is a substring, the leak is even worse (the underlying char[] is also leaked)- in Java 7 substring also copies thechar[]
, so the later doesn't apply; @Daniel, no needs for votes, though.I'll concentrate on threads to show the danger of unmanaged threads mostly, don't wish to even touch swing.
Runtime.addShutdownHook
and not remove... and then even with removeShutdownHook due to a bug in ThreadGroup class regarding unstarted threads it may not get collected, effectively leak the ThreadGroup. JGroup has the leak in GossipRouter.Creating, but not starting, a
Thread
goes into the same category as above.Creating a thread inherits the
ContextClassLoader
andAccessControlContext
, plus theThreadGroup
and anyInheritedThreadLocal
, all those references are potential leaks, along with the entire classes loaded by the classloader and all static references, and ja-ja. The effect is especially visible with the entire j.u.c.Executor framework that features a super simpleThreadFactory
interface, yet most developers have no clue of the lurking danger. Also a lot of libraries do start threads upon request (way too many industry popular libraries).ThreadLocal
caches; those are evil in many cases. I am sure everyone has seen quite a bit of simple caches based on ThreadLocal, well the bad news: if the thread keeps going more than expected the life the context ClassLoader, it is a pure nice little leak. Do not use ThreadLocal caches unless really needed.Calling
ThreadGroup.destroy()
when the ThreadGroup has no threads itself, but it still keeps child ThreadGroups. A bad leak that will prevent the ThreadGroup to remove from its parent, but all the children become un-enumerateable.Using WeakHashMap and the value (in)directly references the key. This is a hard one to find without a heap dump. That applies to all extended
Weak/SoftReference
that might keep a hard reference back to the guarded object.Using
java.net.URL
with the HTTP(S) protocol and loading the resource from(!). This one is special, theKeepAliveCache
creates a new thread in the system ThreadGroup which leaks the current thread's context classloader. The thread is created upon the first request when no alive thread exists, so either you may get lucky or just leak. The leak is already fixed in Java 7 and the code that creates thread properly removes the context classloader. There are few more cases (like ImageFetcher, also fixed) of creating similar threads.Using
InflaterInputStream
passingnew java.util.zip.Inflater()
in the constructor (PNGImageDecoder
for instance) and not callingend()
of the inflater. Well, if you pass in the constructor with justnew
, no chance... And yes, callingclose()
on the stream does not close the inflater if it's manually passed as constructor parameter. This is not a true leak since it'd be released by the finalizer... when it deems it necessary. Till that moment it eats native memory so badly it can cause Linux oom_killer to kill the process with impunity. The main issue is that finalization in Java is very unreliable and G1 made it worse till 7.0.2. Moral of the story: release native resources as soon as you can; the finalizer is just too poor.The same case with
java.util.zip.Deflater
. This one is far worse since Deflater is memory hungry in Java, i.e. always uses 15 bits (max) and 8 memory levels (9 is max) allocating several hundreds KB of native memory. Fortunately,Deflater
is not widely used and to my knowledge JDK contains no misuses. Always callend()
if you manually create aDeflater
orInflater
. The best part of the last two: you can't find them via normal profiling tools available.(I can add some more time wasters I have encountered upon request.)
Good luck and stay safe; leaks are evil!
I recently encountered a memory leak situation caused in a way by log4j.
Log4j has this mechanism called Nested Diagnostic Context(NDC) which is an instrument to distinguish interleaved log output from different sources. The granularity at which NDC works is threads, so it distinguishes log outputs from different threads separately.
In order to store thread specific tags, log4j's NDC class uses a Hashtable which is keyed by the Thread object itself (as opposed to say the thread id), and thus till the NDC tag stays in memory all the objects that hang off of the thread object also stay in memory. In our web application we use NDC to tag logoutputs with a request id to distinguish logs from a single request separately. The container that associates the NDC tag with a thread, also removes it while returning the response from a request. The problem occurred when during the course of processing a request, a child thread was spawned, something like the following code:
So an NDC context was associated with inline thread that was spawned. The thread object that was the key for this NDC context, is the inline thread which has the hugeList object hanging off of it. Hence even after the thread finished doing what it was doing, the reference to the hugeList was kept alive by the NDC context Hastable, thus causing a memory leak.
Create a static Map and keep adding hard references to it. Those will never be GC'd.
there are many different situations memory will leak. One i encountered, which expose a map that should not be exposed and used in other place.
Everyone always forgets the native code route. Here's a simple formula for a leak:
malloc
. Don't callfree
.Remember, memory allocations in native code come from the JVM heap.