4

This question already has an answer here:

I understand and enjoy the benefits of the Garbage Collection in Java. However I don't understand why there is no way in Java to explicitly (and quickly) destroy an object. Surely this could be useful in some cases, I assume performance-critical software.

It's true that in Java the GC will delete an object with no existing reference to it, so if I want an object deleted I can set the reference to it to null. But if I understand correctly, it isn't ensured that the GC will indeed delete the object, at least not immediately. And that's out of the programmer's control.

Why is there no way in Java to explicitly destroy objects?

While I understand that Java was designed to be used as a high-level language, that abstracts away some of the technical details from the programmer to make things easier: Java has become one of the most widely used languages, and is used in huge projects. I assume that in huge projects, performance is often an issue. Since Java had grown to become what it is, why wasn't explicit object destruction added to the language?

marked as duplicate by gnat, jwenting, user40980, GlenH7, Jan Hudec May 23 '14 at 9:10

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • Partially because Java was intended to be a high-level language that abstracted manual memory management, among other things, away from the programmer. Allowing explicit memory management would pretty much go against one of the reasons Java was made in the first place. – awksp May 21 '14 at 21:16
  • 1
    "Gosling did not get the significance of RAII at the time he designed Java. In his interviews he often talked about reasons for leaving out generics and operator overloading, but never mentioned deterministic destructors and RAII. Funny enough, even Stroustrup wasn't aware of the importance of deterministic destructors at the time he designed them..." (possible duplicate) – gnat May 21 '14 at 21:22
  • 2
    this was asked and answered in Why can't Java/C# implement RAII? and explored in some more details in Disadvantages of scoped-based memory management – gnat May 21 '14 at 21:28
  • 1
    @Prog - think about what would need to happen to get a 'destroy now' into Java - you'd need syntax for it, but you'd also need some way to make sure that the programmer didn't access the thing after destruction. Which includes making sure that there are no other references to the object being 'destroyed'. Think about what that would have to do to the garbage collection system (already a non-trivial piece of code). – Michael Kohne May 21 '14 at 21:52
  • 2
    @Bobson no need to imagine, just look at your average C and C++ program... – jwenting May 22 '14 at 10:43
7

We have to decouple two concepts here.

As of Java 7, the language does have RAII. It's called the try-with-resources statement.

static String readFirstLineFromFile(String path) throws IOException {
  try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }
}

This guarantees that the reader will be closed no matter how the block exits.

Even without using try-with-resources, you can deterministically close a stream by calling br.close() when you want.

What's not deterministic is deleting the object from memory. That has to wait until there are no more references to the object and the garbage collector notices that. Trying to make that deterministic depends on potentially a lot of the program (e.g. does a listener, the debugger, or a logger still reference the object?) and the garbage collector's operational details (which GC algorithm is in use? is it a concurrent collector? which generation is the object in? how urgent is collection needed right now? ...).

  • 1
    This is a point I try to get across to people often. RAII happening at the point memory is reclaimed is an implementation detail. The important bit is the semantics, and those semantics can be achieved without the deterministic memory reclamation. – Phoshi May 22 '14 at 9:32
  • @Phoshi Yes! And the semantics are nicer since the object can still hold useful state after doing its cleanup, e.g. performance and debugging data, and its deallocation can be deferred while other code still references it. – Jerry101 May 23 '14 at 0:17
  • 3
    @Phoshi: True destructor-based RAII still has a huge advantage over try-with-resources/using in that it composes reliably. In C++ if you need an object to use several resources, you simply include appropriate subobjects and the compiler takes care of composing the destructors while with IDisposable you still have to write the Dispose method where it's still easy to miss something. – Jan Hudec May 23 '14 at 6:58
  • @JanHudec: In the IDisposable case, you have to write Dispose. In the dtor case, you have to write ~T. In the case your dtor has no important semantics (I.E., is all purely memory management) then the GC replaces that anyway. – Phoshi May 23 '14 at 8:38
  • 2
    @Phoshi: But not when I compose. Consider a class that manipulates three files for which it has members. In the IDisposable case the Dispose has to explicitly chain to Dispose of the members, in the destructor case it happens automatically. That is, the destructor does have important semantics, but it gets is purely as composition of the member destructors, so nothing needs to be written by hand (and don't forget all the nasty cases of exceptions thrown from constructor which proper use of RAII takes care of). – Jan Hudec May 23 '14 at 9:00
5

I have often pondered the same question.

I was a C++ programmer for many years and an Objective C programmer for many years before that. That experience taught me the discipline to track every object creation and achieve the nirvana of code - to logically assure the ultimate destruction of every created object and buffer.

And what a discipline it was - I could guarantee that I never leaked memory anywhere in my code. But memory leaked! Any library I used was a chink in this armor. Threading was a nightmare!! The discipline was valuable but it just did not solve the problem. Even the most carefully written code would require a restart after a few weeks of continuous running and many apps could not run stably for more than about 24 hours.

The solution is Garbage Collection. It makes everything so easy - you just let go of the memory and it floats off like a Helium-filled balloon to be gathered and recycled.

There are two primary benefits that make it a winner:

  1. It is simple - just let go of the object.
  2. The GC process also compacts your heap.

Now those two alone creates an order of magnitude improvement in the stability of your code and the ease of writing and debugging. The only downside is you have to suffer the indignity of occasionally putting up with the inevitable delay of the GC.

A quick consideration of allowing a mixture of immediate and postponed discard also proves pointless.

Say you have a Map tha you have filled with some data and now you know without a doubt that it will not be needed again. Surely manually freeing it would save you peanuts - just the nodes, not the keys or the values. A tiny gain of some fragmented values.

Bottom line - it's so simple even an incompetent programmer can write leak-free code.

  • 3
    Well, technically you can still leak. If your code is poorly organized and you unintentionally keep references around, you have what's still functionally a memory leak. – N Jones May 22 '14 at 2:40
  • @NJones - Indeed you can but you have to do it in a specific way to make it happen. Just leaving things attached to other things isn't enough - you have to leave them attached to a running thread or class loader. Just letting go is almost always enough. – OldCurmudgeon May 22 '14 at 7:44
  • How exactly does one go about recycling helium from a balloon floating off? – Cerad May 22 '14 at 19:12
  • @Cerad - When the balloon bursts in the upper atmosphere the Helium is returned to nature. How arrogant of you to suggest that mankind should retain control - surely that would be the leak! :D – OldCurmudgeon May 22 '14 at 22:59
  • @OldCurmudgeon when the balloon bursts in the upper atmosphere the helium continues drifting off into space. Helium is never recovered and one day we will run out. – gbjbaanb Jun 26 '14 at 7:29
3

Going off of here, which lists the design goals of the Java programming language (emphasis mine):

1.2.2 Robust and Secure

The Java programming language is designed for creating highly reliable software. It provides extensive compile-time checking, followed by a second level of run-time checking. Language features guide programmers towards reliable programming habits. The memory management model is extremely simple: objects are created with a new operator. There are no explicit programmer-defined pointer data types, no pointer arithmetic, and automatic garbage collection. This simple memory management model eliminates entire classes of programming errors that bedevil C and C++ programmers. You can develop Java code with confidence that the system will find many errors quickly and that major problems won't lay dormant until after your production code has shipped.

The bolded portion, I think, answers your original question pretty well.

As for the second part of your question, I don't think I can personally answer that sufficiently. However, googling led me to this answer by Jon Skeet on Stack Overflow. He mentions that deterministic destructors tend to involve reference counting, which impacts performance and fails for cycles, and refers readers to this email by Brain Harry on resource management. I cannot verify myself whether these answers are true, but they both point towards deterministic destructors not being worth implementing, as their flaws outweighed the potential benefits of implementation.

(By the way, I think that the linked sources would provide too much information to be reasonably quoted here. If I should quote them anyways, let me know)

3

The GC attempts to emulate a world where we never run out of memory, and that's where the advantages in terms of speed and maintainability come from.

In theory, the garbage collector should know more about whether now is a good time to destroy objects than you will while programming it. Often, programs in languages with less abstraction free memory as soon as possible. However, that's not always good for performance. So you might free memory later, where the place that the objects are allocated and destroyed is far removed. This increases the potential that the programmer will miss something. So it can be bad for memory usage, since things should be destroyed when the system gets some breathing room rather than in the middle of a high speed loop. But it can be faster that way.

If the garbage collector is able to run in a separate thread and do its work without corrupting the main thread's memory, then it will be faster than if the main thread handles its memory manually.

In practice, it depends on a lot of different things. Can the process be multithreaded, how much memory is available (less means more collections, more means faster speed), etc. While you can also force garbage collection, that's a very often misused feature. It may clean up those objects, but many times there are references the programmer didn't realize they were still using. So forcibly destroying those objects could create problems that wouldn't become apparent until much later, where the GC knows those objects are still in use.

Combined with recycling, so that the memory for objects doesn't need to be reallocated unless necessary, you can get much faster processing with a lot less work and a lot better maintainability.

  • In what circumstances is it possible for the garbage collector to free objects which may be used later? AFAIK if there is still a reference to the object that the programmer can use at that point in the code the object will not be garbage collected. – Andris May 22 '14 at 4:38
  • The statement that GC attempts to emulate infinite memory is suspect. If you try to create an object and there's no memory left, allocation will fail; just like malloc or new. And it'll fail with an exception that can be caught and handled if you really want to - just like you can handle malloc or new failures. There's never been any pretense of infinite memory. – Doval May 22 '14 at 13:23
  • @Andris Yes, as I said the GC must know when an object has the potential of being called on again in order to prevent "programmer forgot X was still in use" errors. Object pooling is something that has to be programmed, using things like weak references and such. By indicating to the GC that objects may be reused, the GC can still dispose of them if it needs the space or they can be rehydrated and strongly referenced again without incurring an allocation cost. – jzx May 22 '14 at 16:15
  • @Doval Yes, if you run out of memory then you're out. But we shouldn't need to worry about how much memory we have, because the available memory is something that can be optimized at runtime according to system state. For example, in a variation of GC, large underutilized objects could be jogged to slower media depending on usage to make room for new objects, and then restored to faster media when necessary. It just so happens that (in normal GC) unused objects are moved to oblivion, and sometimes we recreate them exactly the same when needed. – jzx May 22 '14 at 16:26
  • @jzx Nothing stops you from having those kinds of things in manual memory management, since no rule says a pointer is a literal memory address (or even that pointers to objects and pointers to functions have the same representation). And memory obtained from malloc can still come from memory pages on disc. There's obvious differences in both systems regarding when memory is freed, but there's never been a pretense of infinite supply. – Doval May 22 '14 at 16:32
-1

Java makes auto cleanup:

As we all know Garbage collector do the clean up job automatically but WHEN? Every object is eligible for cleanup when it has no links.

Car c1=new Car();---Object 1
c1=new Car(); ---Object 2

In line 2, Object 1 is eligible for garbage collection but this would be collected or not, it depends on many other factors. There are three heap spaces Eden, Survivor, Tenured Generation and JVM always wants a proper balance of memory(a proper saw tooth graph). Every object born in Eden space and follow other heap spaces according to it's age in program. So when any of these heap spaces fulls GC searches for objects with no links and make the cleanup for those object and shifts the remaining object to next heap space.

That's why in JDBC if we don't close Connections, Resultsets, Statements then there is a condition of OutOfMemory.

So JVM follow this type of cleanup management which it successful in most of the cases and there is no need of customized memory management. Even for preventing the cases like JDBC outofmemory problem java introduces try with resources, which frees the object at the end of try's scope.

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
  }

Don't confuse with finalize() method it just calls before garbage collection invocation, it has no connection to customized memory clean like destructors in C++.

  • and don't forget that finalize() is never guaranteed to be called... – jwenting May 23 '14 at 6:29
  • Yes definitely. :) – Deepak Tiwari May 23 '14 at 6:30
  • this seems to merely repeat points made and explained yesterday, in this answer – gnat May 23 '14 at 6:58
  • @gnat If you read both answers then you will get the difference. In latter one there is no concept of Heap Mechanism for memory cleanup. I just gave the try with resource example to overcome the problem of OutOfMemory. Still you think this answer should be downvoted then it's up to you. Thanks :) – Deepak Tiwari May 23 '14 at 7:02
  • as far as I can tell, "concept of heap mechanism" has also been explained in good details, in other two answers posted yesterday: here and here – gnat May 23 '14 at 7:05

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