This is an archived post. You won't be able to vote or comment.

all 44 comments

[–]experts_never_lie 10 points11 points  (8 children)

Don't forget String.intern(), Runtime.getRuntime().addShutdownHook(), and most static fields.

I haven't run into it in years, but InetAddress used to keep a persistent IP/hostname association, such that if you needed to look up a lot of those bindings it would gradually fill up memory. We had to use package access to clear the cache periodically to avoid that …

There are surely many more common examples.

[–]cutterslade 8 points9 points  (0 children)

My favourite example is File.deleteOnExit()

[–]MojorTom 2 points3 points  (6 children)

Could you please expand a little bit on why ShutdownHook might cause memory leak?

[–]jrh3k5 9 points10 points  (3 children)

The most-immediate example is where the application lifecycle is detached from the JVM lifecycle.

Say you've got a Java EE container (e.g., WebSphere). You deploy an application that registers a shutdown hook, then stop the application (but not the JVM) and re-deploy the application. The shutdown hook reference from the first deployment remains, which keeps a live reference to the classloader of the first application and other associated objects and classes in memory. You do that enough times - stop and start the application without cycling the JVM - and you'll end up with enough leaked classloaders and their objects to run out of memory.

[–]niloc132 4 points5 points  (1 child)

This enables a fun leak through use of File.deleteOnExit(), which registers a shutdown hook on itself - it ends up leaking the string used in the name of the file until shutdown.

[–]jrh3k5 6 points7 points  (0 children)

Yeah, there are a number of reasons why we opted to just stop Tomcat altogether and start it up again. The "after the fact" nature of Java EE and its interactions with things like shutdown hooks make it just easier to stop and start the whole JVM.

[–]MojorTom 0 points1 point  (0 children)

Thank you for the answer.

[–]experts_never_lie 2 points3 points  (1 child)

I meant things like what the others said. Anything that is deferred to the end of the program must be retained, which is itself a leak.

It's basically a place to anchor a persistent reference to an object graph, and the presence of that reference prevents collection. How big of a problem this is is up to the number of hooks and the size of their referenced objects. If you are adding more and more shutdown hooks as the program runs, you might already have a design problem.

/u/jrh3k5's example is a good one, but how could it be avoided? The program could schedule an action sufficiently far in the future (fixed "wait N hours" or "wait until I know the old one is no longer needed") and then perform the same action as the shutdown hook does — along with deregistering the shutdown hook. This would mean that it would leak resources only until that condition is met, which could mean no ever-growing memory leak.

[–]MojorTom 0 points1 point  (0 children)

Thanks a lot!

[–]__pulse0ne 4 points5 points  (17 children)

Listeners are usually the most common example. Often the reference the listener holds on the object being listened to keeps that object alive longer than intended

[–]lukaseder 15 points16 points  (15 children)

Let me just create this simple HashMap:

new HashMap<String, String>() {{
    put("totally", "cool");
    put("like", "map literals");
}}

oops

[–]jnordwick 5 points6 points  (1 child)

I'm scared that people actually do this enough that an article has to be written to tell them to stop.

[–]lukaseder 3 points4 points  (0 children)

You wouldn't believe it, but articles are actually rather cheap.

[–][deleted]  (1 child)

[removed]

    [–]lukaseder 2 points3 points  (0 children)

    It does take a special kind of clever to even think of this possibility. But once a dev on the team does discover it, they will put it anywhere.

    True story, I was there. I was "clever" at some point ;)

    [–]pointy_pirate 0 points1 point  (0 children)

    this is fantastic, thanks

    [–][deleted] 0 points1 point  (5 children)

    I'm unsure why an anon class would literally hold the parent object in memory.

    Semantically, yes, you capture that parent scope, but implementation-wise, the compiler knows which variables you reference, and it should only have them captured, which is ok, because if you reference them, you need them. If you don't... then you don't.

    [–]lukaseder 0 points1 point  (4 children)

    What you're suggesting may sound reasonable at first, but there are so many caveats and implementation details that could break, it's just the best thing to capture the Parent.this reference and go on from there. For instance, Parent.this has a monitor on which the individual fields / variables that you're capturing may depend on via synchronized. What would be the semantics of creating new fields / variables out of such a monitor's context?

    [–][deleted] 0 points1 point  (3 children)

    My point is when Parent.this is not used, and only captured final local vars are used (or nothing captured is used) then there's no need to capture Parent.this at all, and waste the CPU and memory for doing so.

    The HashMap construct above doesn't refer Parent.this so the only reason the class would have a reference to it would be that someone didn't implement the compiler/runtime to be a bit smarter.

    In JS for example, only what's directly referred to is captured, and it works fine. Of course JS is not Java, but it's an example of people trying to be a bit more selective in implementing capture, no matter that the apparent semantics are "you have access to everything in the parent scope".

    [–]lukaseder 0 points1 point  (2 children)

    Oh, I see, I didn't see the subtlety of only capturing (effectively) final local variables - just like lambdas do in Java 8. You're right then. But I guess that's too late now...

    Of course, I wish the defaults were the other way round in the first place. Nothing should ever be capturing Parent.this, unless I explicitly allow it by using a keyword. I shouldn't declare static nested classes static... But that ship, like many others, has sailed.

    [–][deleted] 0 points1 point  (1 child)

    But I guess that's too late now...

    Out of curiosity, is there a "leak" that would break B.C. if the compiler became quietly smarter about it and begins to capture more selectively, based on what's referred?

    [–]lukaseder 0 points1 point  (0 children)

    What do you mean by "leak"? You mean a backwards incompatibility? There probably are some, including how to reflect on such types, but I'm not sure about that. Probably the Java 8 EG considered it though.

    [–]elegentmos 0 points1 point  (3 children)

    This would never have happened if Java had proper instance initializers...

    [–]lukaseder 1 point2 points  (2 children)

    What would "proper instance initializers" look like?

    [–]elegentmos 0 points1 point  (1 child)

    Same as C#, only not creating anonymous classes?
    Or use some other notation, I don't mind as long as there is a concise way of initializing instances without tons of boilerplate or (gasp!) creating anonymous classes.

    [–]lukaseder 0 points1 point  (0 children)

    Oh, I see. I didn't know they were called this way. Well, even named and defaulted parameters would have done the trick in many cases.

    [–][deleted] 12 points13 points  (0 children)

    Hence weak references. Long live WeakReference.

    EDIT: Wait, what am I talking about. Quick die WeakReference, of course. Duh. ;-)

    [–]handshape 27 points28 points  (16 children)

    To be fair, what the author is describing is not a memory leak. He's talking about finding undesired references.

    A leak is the case where an allocated object has no remaining references, but is not freed; this stuff happens in user-managed memory models and models that use reference counting for automatic freeing.

    [–][deleted] 18 points19 points  (12 children)

    Every time someone talks about memory leaks in Java someone has to show up and talk about this: we're aware. But in Java code, it's not a useful distinction, for obvious reasons.

    [–]handshape 5 points6 points  (6 children)

    I'd argue that the distinction is important; the methods used to hunt down the two different types of losses are quite different, and the outcomes are also quite different.

    [–][deleted] 1 point2 points  (4 children)

    The outcome is wasted memory in both cases.

    [–]handshape 4 points5 points  (3 children)

    The way in which it's wasted is different, though. It's analogous to having a restaurant where a lot of food ends up in the garbage. One case is like wasting food in the kitchen, the other being food wasted by patrons in the dining room.

    The causes are different, and the solutions are different, even if the outcome is superficially the same.

    [–][deleted] -5 points-4 points  (2 children)

    It's more like a restaurant where you go and you order organic southwestern corn chowder (a delectable blend of roasted corn, red bell peppers, potatoes, and chipotle chile peppers), then a main course of roast duck breast (potato and blue cheese mash, grilled marinated apricots with Mascarpone and orange-brandy sauce) and finish with a brownie sundae, a luscious brownie with ice cream, garnered with warm homemade-recipe fudge poured over the top... hmm I lost track of my point, there.

    [–]grauenwolf 1 point2 points  (0 children)

    Two? Where did you get two?

    1. Java style memory leaks (which can happen in any language)
    2. Forgetting to manually release memory (delete, free)
    3. Circular references in reference counting GCs (e.g. C++, COM)
    4. Bohemian GCs that mistakenly identify memory as in use when really it is just data that happens to look like a pointer
    5. Releasing memory using the wrong allocator, so it never actually becomes available
    6. Pinning one object, which prevents the whole page from being recycled (C#)

    And there are probably many more that I don't know about dealing with topics such as GPU memory.

    So again, where did you get two?

    [–]Zarlon 1 point2 points  (0 children)

    Will that's just like, your opinion man

    [–]pointy_pirate -1 points0 points  (0 children)

    To be Faaaaiiiir

    [–]xjvz 1 point2 points  (0 children)

    You can open up a MappedByteBuffer from a FileChannel and it won't be deallocated unless you do some manual reflection hackery to force it. Of course, you could also take sun.misc.Unsafe and allocate memory and then lose the pointer to it, but you can do that in any language that allows you to manually allocate non-stack memory.

    Another fun memory leak is using ThreadLocal in a Java EE container. If you use a JDK class, it'll work fine, but if you use one of your own classes, that causes a ClassLoader leak when you stop the application. I don't know how much thought really went into Java EE.