5

I stumbled across some posts on the main stackoverflow community about whether you can have a 'memory leak' in Java and it (maybe unsurprisingly) creates a lot of contentious debate. At some point I had decided that Java applications don't have memory leaks (or have them all the time depending on how you look at it) but rather have 'space leaks'.

I can't recall where I got this term from did a quick search to make sure I hadn't dreamed it up or something and it seems to be used with regard to Haskell and lazy evaluation. Here, I found a concise definition that appears to be authoritative.

A space leak occurs when a computer program uses more memory than necessary. In contrast to memory leaks, where the leaked memory is never released, the memory consumed by a space leak is released, but later than expected.

The concept of a 'memory leak' as I was taught back in the previous century was caused by losing track of memory without deallocating it. It's something you can detect without understanding the program which is why it's the exact condition that a GC uses to know what is garbage. What people refer to as 'memory leaks' in Java seem to me to be more closely aligned with the term 'space leak' because the application is still referencing the offending memory when it really doesn't need to.

I feel like this distinction would help bring some clarity to discussions about managed memory languages but I'm not sure if it's too much of a stretch? Maybe it's too late but I think it would be best to keep the term 'memory leak' specific and not overload it to also apply to something that produces similar results but has different causes.

To clarify, I am not suggesting that leaks don't happen in a managed application. I'm simply pondering about how we talk about them. There are some highly upvoted discussions about memory leaks and Java that contain flame wars with 'fanboy' slurs and such that center around this exact question. Actually given that even my question is misunderstood tells me that this is an issue of terminology.

  • 8
    Garbage collection alone does not get rid of memory leaks. In Java, common ways to get a leak are: class loading of classes with static data, string interning, observer implementations without weak references, unclosed JDBC connections, data referenced but not used in other threads, …. Your definition of memory leaks as only applying to unreferenced memory is too restricted for any language other than C. Instead, memory leaks are almost always a design bug, but space leaks are only a performance issue. – amon Dec 30 '15 at 23:17
  • Using too much memory is generally caused by trying to do too many things. It's usually not so much a bug as it is the inevitable consequence of a bloated feature set. Trying to be more memory efficient without reducing features is usually considered a kind of optimization, not a kind of bugfix, which is why calling it a "leak" would be misleading. – Ixrec Dec 31 '15 at 0:44
  • @lxrec I think you misunderstand the kind of thing I am talking about. An example would be a long running application that runs for days and continually increases it's (live) memory footprint with uniform load. This is usually caused by adding things to collections and not removing them when they are not needed. A classic example is adding Listeners as hard references which prevents those Listener objects from being collected when they are no longer needed. This is a much thornier problem than C-style memory leaks as it's not (in general) possible to for software to detect this situation. – JimmyJames Dec 31 '15 at 15:18
  • I tend to use one of "unintentional liveness" (there's one or more references to an object, keeping it live, even though it is no longer needed) or "GC bug" (if the object is no longer reachable, but still cannot be collected). – Vatine Dec 31 '15 at 16:57
16

Does it make sense to use the term “Space Leak” with regard to Java?

Frankly, no.

While the term is well-founded, and it is (or may be) understood by the Haskell community, the distinction between "space leak" and "memory leak" is NOT widely understood by Java developers or the wider software engineering community1.

Therefore, making a distinction between "space leak" and "memory leak" is likely to cause confusion rather than illumination.

As to the question of whether this term "brings clarity" ... IMO, not necessarily:

  1. You can be equally clear without using this "new" terminology. Possibly even more so ... if you set out fully what you are talking about.

  2. Using terminology that people don't understand does not bring clarity.


1 - For instance, I've been programming for 40+ years, and in Java for 15+ years, and I can't ever recall seeing "space leak" used in this way ... until now.

  • 3
    "Using terminology that people don't understand does not bring clarity": It's a bit of a chicken or egg problem though, unfamiliar terminology. It's unfamiliar because it's not used. If you take your argument to it's logical conclusion, no new terminology should be created if a more general term exists. That's not what you are saying but do you see where I am going? – JimmyJames Dec 31 '15 at 14:54
  • +1 for "You can be equally clear without using this "new" terminology." If it walks like a memory leak and quacks like a memory leak... – Mason Wheeler Dec 31 '15 at 15:05
  • I'll bow to the overwhelming lack of support for this idea. I choose this answer because it makes the popular case while being very courteous. – JimmyJames Jan 4 '16 at 15:19
30

No.

Pretty much all modern programs use more space than is necessary. If your Java program is keeping objects alive when the programmer thinks it should be eligible for collection, it is a memory leak. It is a common, well known term. Yes, it will vary a bit by context, but the underlying concept is close enough that the mental jump is very short.

Pedantry provides no benefit here.

  • 3
    If anything, in the case of JVM-based languages, it is the "typical" memory leak (e.g. one caused due to not playing safe with Unsafe) that should be distinguished with a special name. – mikołak Dec 31 '15 at 10:29
  • When trying to solve a problem where a VM is eating up too many resources and actually causing an identifiable problem it can be useful to know if the cause is unreachable references or just a VM with lots of active objects. But, otherwise, I agree that it is pedantry until we are having that conversation. Though, the phrase "space leak" makes me want to System.out.punch() things. – user156792 Dec 31 '15 at 14:50
  • @jdv unreachable references should be collected so perhaps reconsider whether more specific terminology might be helpful to understanding. – JimmyJames Dec 31 '15 at 14:58
  • @JimmyJames unreachable as described by tools that follow the refs from the root, which the best way I've found to distinguish between a "leak" and a "bloat". This is very specific language in all the tools I've used to do this. – user156792 Dec 31 '15 at 15:09
  • @jdv Right. 'Unreachable' is the precise condition that allows for an object to be collected. Per the spec, all unreachable objects should be collected prior to a JVM throwing an OutOfMemoryError. I sense you are referring to something other that Java here so that might the disconnect. – JimmyJames Dec 31 '15 at 15:21
7

Perhaps the difference here can be described as physical vs. logical leaks. GC protects excellently against the former.

Yet just talking about "memory leaks" in general, I'd suggest that GC doesn't really offer very strong protection and might even make it harder to avoid them. This is kind of controversial so I'll try to explain it carefully (and also the safety negatives associated when GC is lacking which definitely exist).

To me the greatest benefit GC provides is not protecting against leaks but in providing safety in cases where leaks aren't the biggest deal.

Take an example of a video game. Probably people have tried some games out there written by aspiring game developers in languages that provide garbage collection which leak resources like crazy. Within a half hour of playing the game, the memory usage of a simple 2D game has risen to gigabytes, and the game is increasingly getting slower and slower with prolonged usage until it is restarted. 1 year of development and 16 updates later, the game is still leaky as ever.

These types of leaky games almost always tend to be written against garbage collectors, yet they're among the leakiest applications out there. How could that be if GC is considered by some to be a silver bullet against memory leaks?

The comparison may not be fair since these games are often developed by not-so-experienced developers. Yet I would suggest these game devs might write less leaky (but more crashy) software if they didn't use garbage collection.

Resource Mismanagement

The reason these leaks occur is because of rooted resources. More generally speaking, they're the result of resource mismanagement which is a problem that can occur with or without GC. A game might allocate sprites repeatedly during gameplay, but multiple areas of the codebase may reference those sprites. Failure to manually release the reference in any one of these places then leads to that dreaded logical leak with rooted resources that aren't freed at the time they are no longer needed, and not freed at all until the application is shut down.

Safety

Yet the game continues to run without crashing. It might unnecessarily loop over sprites that are no longer needed or being drawn, getting slower and slower and using up more and more memory with the accumulation of unneeded sprites. Nevertheless, it's worth noting how stable (crash-free) even these amateurish indy games are, and that's where I see GC offering a great benefit.

Dangling Pointers vs. Unfreed Resources

In a similar scenario given a native codebase that doesn't use GC, the resource would be freed by an explicit request to free the resource through any one of its references (pointers). That then causes the other references (pointers) to become invalidated (become dangling pointers). Subsequent access of those dangling pointers in some part of the game then leads to very deadly undefined behavior, typically a hard crash resulting from a segfault/access violation.

A segfault here would be a very showstopping-bug, introducing a crash: a jarring, very frustrating interruption to the gameplay. Nevertheless, it would be blatantly obvious and easy to detect and correct. That's why I think the edge in ultimately avoiding memory leaks actually goes to languages that don't use garbage collection (albeit some patches later). The easiest bugs to correct are the easiest to notice/reproduce. Instead these languages without GC pay for it with more dangerous code and undefined behavior resulting from resource mismanagement at a lower memory management level. GC languages can pay for resource mismanagement in the form of safe but deceptive and subtle leaky behavior which can fly under the radar of testing.

This can be especially annoying in extensible plugin architectures where loading a third party plugin then causes that plugin to acquire a reference to a resource in your system, say some gigantic image that spans 300 megabytes. When that plugin code (which isn't even under your control) fails to manually release the reference, the mere presence of that plugin causes the codebase to mysteriously leak 300 megabyte resources here and there at no fault of your own, only the mere presence of this third-party plugin.

Silver Bullets

Anyway, I would suggest not trying to distinguish various types of leaks so much. They're all equally annoying (actually, logical leaks tend to be the most annoying). Instead I'd try to focus on the fact that garbage collection is not a silver bullet against memory leaks. To buy into this kind of illusion is often inviting very leaky software. It pays to give special attention to concepts like soft/weak/phantom references in languages with GC upfront if there's a strong motivation to avoid memory leaks. Resource management doesn't necessarily get much easier with GC and may even get harder in some cases, it's just that mismanagement tends to lead to less deadly behavior (which can actually be both good and bad depending on the context).

It's also worth noting that the physical leaks which GC definitely avoids outright are some of the easiest to avoid, with leak detection tools like valgrind immediately showing which line of code allocated memory that wasn't freed even after application shutdown. The logical leaks are typically far more difficult, and the ones that GC and other tools can't so effectively protect us against, only a careful design and implementation when complex resource management is involved.

  • 2
    Cue Rust, no GC and no crash => a GC is not mandatory to avoid crashes, it just is the easiest way (for the implementer and potentially users). – Matthieu M. Dec 31 '15 at 12:26
  • 1
    @Ike: Clearly, GCs are more about protecting against dangling pointers/references (as you noted) than protecting against leaks. Rust also acknowledged this and a leak is considered safe (it's just annoying!). – Matthieu M. Dec 31 '15 at 12:33
  • 1
    Just to be clear, there is nothing in my post that suggests that GC is a silver bullet for anything. The fact remains that a properly working GC will collect all "C-style" memory leaks and nothing else. – JimmyJames Dec 31 '15 at 14:37
  • 2
    @Ike One of the purported benefits of GC is that it allows for quicker object creation and destruction. This is probably counter intuitive to a lot of people especially because your description of applications running on GC is so familiar. Gil Tene has a lot of good articles and presentations about GC that explain the benefits that are freely available. – JimmyJames Dec 31 '15 at 15:47
  • 1
    @Ike No worries. I just saw a bunch of comments about how GC doesn't eliminate leaks when I skimmed through the responses. – JimmyJames Dec 31 '15 at 15:49
5

The main difference is in terms of time. A space leak as described in your linked article occurs before the user is finished with the result, while a memory leak occurs after. As soon as the user is done with the result in a space leak situation, it is garbage collected, it just temporarily used more memory than necessary.

A memory leak never lets go of the memory, it just continues to grow until eventually the program crashes, even after the user is done with the given results. This sort of problem is indeed possible in garbage collected languages. Technically, you're still referencing the memory somewhere, but not intentionally in a user-visible way.

  • You landed your arrow at in the weakest point of the argument. Kudos sir. But, I think there's wiggle room here. The definition with regard to Haskell is fairly narrow and I think it can be stretched. – JimmyJames Dec 31 '15 at 15:29
4

Maybe. A "memory leak" in the presence of GC can mean one of two things:

  1. A flaw in the garbage collector or language runtime where memory escapes the notice of the garbage collector without actually being freed at the OS level. Nobody knows it exists anymore, so it will never be freed (at least not until the process exits).

  2. A flaw in user code where objects continue to be referenced (maybe indefinitely) after they're no longer useful, for example through circular references or lexical closure. The GC sees that the objects are still referenced, so it won't free them. The article you reference calls this a "space leak", but I think that most reasonable people would consider it a straightforward example of a memory leak.

However, there are a couple of other cases where neither of these things happened, and where the loss isn't quite so permanent:

  1. An object is properly managed by the GC, and has been properly unreferenced, but hasn't actually been collected yet, probably because whatever condition triggers the GC hasn't happened yet, or because some condition is temporarily preventing GC. The memory is theoretically available in the future, but it isn't right now, and the low-level memory usage of the program exceeds its actual working set by a possibly large amount.

  2. The program allocated a lot of memory, then finished with it, and the GC properly freed it, but it hasn't been returned to the OS yet, either because the condition for doing that hasn't been satisfied yet, because it simply isn't possible on a given OS, or because the runtime hasn't been written to make it possible. The memory is available for future allocations by the same program, but it isn't available for other programs — from the outside, the program's memory usage goes up but not down.

These cases are usually much less problematic than proper memory leaks, because they tend to sort themselves out eventually — until one day you find yourself with a program that crashes with out-of-memory errors because it triggers one of these conditions by allocating in a tight loop or doing large transient allocations or whatever happens to be the magic combination for a given language/runtime/OS. Then you have a problem that you have to deal with.

In short: memory leaks aren't banished by GC, they're still a concern. They're just harder to create. But there's a whole other class of problems that you might also run into someday (although hopefully not). I think that "space leak" would actually be a reasonable name for them, but that's in conflict with the article you linked, so perhaps something else.

  • 1
    1.2: circular references (alone) will not prevent collection in a properly functioning JVM. – JimmyJames Dec 31 '15 at 15:02
  • 2.1 This is is the area where i see so much confusion and I feel more explicit terminology might help. You can find innumerable questions and answers on stackoverflow about out of memory issues caused by the GC not running or that you need to set references to null etc. All of this suggests to me that people are not understanding what exactly allows an object to be collectable and confusing this with the classic concept (like in CS textbooks) of memory leak. This leads to bloated programs due to misunderstanding of core concepts. – JimmyJames Dec 31 '15 at 15:09
1

In C you can allocate memory for an object, discard all references to it, and then your program can never reliably recover that memory. Programs that do this repeatedly end up consuming a lot of memory—typically more and more as time goes by.

In Java that cannot happen, because garbage collection will recover the memory of all unreachable objects. What does happen often in Java is that an object will retain references to others that the program no longer needs, and these will accumulate with time until the program gets an OutOfMemoryError.

Both of these scenarios are popularly called "space leaks" by thousands and thousands of programmers, but it's important to note that this is not a dispute over facts. There are countless thousands of knowledgeable people who understand the factual difference here and yet use the same term for both.

So your question is entirely terminological, and the best advice I can give is that whenever you see the term "space leak" you have to judge which of these two situations the author is talking about, based on the context.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.