49

It's been at least 5 years since I worked with Java, and back then, any time you wanted to allocate an object that needed cleaning up (e.g. sockets, DB handles), you had to remember to add a finally block and call the cleanup method in there.

By contrast, in C++ (or other languages where object lifetimes are deterministic, e.g. Perl), the class implementor would define a destructor function that performs the cleanup whenever an object of that class goes out of scope. The advantage of this approach is that the user of the object can't forget to clean it up -- the destructor gets called automatically, even if an exception is thrown. This approach goes by the fairly awful name of RAII -- "Resource Acquisition Is Initialisation".

It's been my experience that doing things "the RAII way" has saved me a lot of mental overhead in terms of not having to worry about whether and when resource deallocations occur. We are considering using Java for a medium-sized project, and I'm wondering if some sort of deterministic destruction is among the many new features added to the language since I last looked at it. (I'm hopeful as my complaint that "Java has no RAII" was rebuked on this thread, but so far I haven't been able to find any details by googling.)

So if someone could point me to some introductory material on how to go about this in Java that would be great!

2
  • 1
    possible duplicate of Is there a destructor for Java? Commented Apr 23, 2015 at 6:04
  • @Raedwald: Although my question is indeed essentially a duplicate of the one you linked to, I noticed that the highest-voted and accepted answer on that one claims that because Java uses a GC, it could not have deterministic destruction. But the comments under Jon Skeet's answer here show that the two things are not contradictory, which I think is interesting and useful. (Of course, it could just be my self-interest talking ;-) Commented Apr 23, 2015 at 11:56

5 Answers 5

32

EDIT: The answer below was written in early 2009, when Java 7 was very much still in flux.

While Java still doesn't provide guarantees around finalization timing, it did gain a feature like C#'s using statement: the try-with-resources statement.


No, Java hasn't changed at all in that respect. You still need to use try/finally.

There's been discussion of adding the equivalent of C#'s "using" statement (which is syntactic sugar over try/finally) to Java, but I don't think that's going to be part of Java 7 any more. (Most of the language improvements seem to have been dropped.)

It's worth understanding that there are reasons why deterministic destruction hasn't been implemented in Java and .NET in the form of a reference-counted garbage collector, by the way - that a) impacts performance and b) fails with circular references. Brian Harry wrote a detailed email about this - it's about .NET and it's rather old, but it's well worth a close read.

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks Jon. Interesting that supporting some deterministic classes/objects would necessitate determinism "infecting" everything. It's the "scoped local" case that's important for me in any case -- "using" is a step in the right direction, as it enforces consistent naming of destructors.
D manages to have deterministic destruction in a GC context - so it can be done
@Earwicker: In what way does C# have deterministic destruction?
@JohnMcFarlane: I'll edit, although it's tricky to do totally accurately given that there's a difference between what's guaranteed by the language and a specific VM implementation.
|
10

There is a pattern that helps here. It's not as nice as destructor based RAII but it does mean that the resource clean-up can be moved to the library (so you can't forget to call it).

It's called Execute Around, and has been discussed here before.

Interestingly I see Jon Skeet chimed in on that thread, but he didn't mention it here - shame on you Jon - missed an opportunity for some rep points there!

BTW, while I'm glad Brian Harry (see Jon's comment, again) went to the lengths of writing the email that he did - and it obviously did reflect a lot of thought that went into the process - and I'm glad we did get "using" out of it in C# - I don't agree with all his conclusions. In particular, I don't see why, if we can have using, we can't have a way to mark a type as behaving that way without "using". Of course it constrains the usage - but so does "using" - and most of the time it's exactly what you want. The trouble with "using" is that the client code still has to remember to use it. With C++ style RAII it's a property of the type. An arguably bigger problem with "using", or more accurately with the Dispose idiom, is that it's a lot more complicated and error prone than most people realise to get right - mostly because of the potential for objects to be brought back from the dead.

3 Comments

Thanks Phil, this is a very nice way to solve the problem in the absence of deterministic destructors.
Not sure if "nice" is the term, especially in the absence of closure syntax - but it gets the job done :-) See my blog posting on the subject here, too: metatechnology.blogspot.com/2007/02/…
@Phil: Sure, the syntax is not exactly "nice," but as you say it gets the job done. Interesting article BTW. As far as I can see this is actually a more general solution than ctors/dtors, for a language having good closure support.
2

Nope. There is no facility for allocating Objects on the stack. Every Object is allocated on the heap and can outlive whatever block it was initialized in. Or it may be collected mid-block, depending on the vagaries of the all mighty Garbage Collector.

If you're working on the server, you might want to check out Java EE. It has nothing to do with RAII, but it does have a decent system for managing the life cycles of expensive objects like DB connections. Java EE 5 is actually pretty nice to work with for a lot of problem spaces.

2 Comments

Thanks Jay. JEE sounds maybe a bit heavyweight for what we need to do, but I'll mention it to the others and see what they think.
Very good answer. C++ does not have any garbage collection, but you can avoid having to remember to destruct by the simple expedient of allocating on the stack. The compiler guarantees to call the destructor no matter how the scope is exited (by normal return or an exception).
1

Try-with-resources is not applicable when resource escapes the lexical scope. However, there are library based techniques to mitigate the issue.

C++ non-lexical lifetime example

Consider following C++ example, where members of a container escape the lexical scope they were created in:

#include <iostream> int open_file() { static int count = 1; int result = count++; std::cout << "Opened " << result << "\n"; return result; } void close_file(int fd) { std::cout << "Closed " << fd << "\n"; } struct Resource { int file; Resource(): file(open_file()) {} ~Resource() { if (file != 0) { close_file(file); } } }; struct Container { Resource resourcelet1, resourcelet2; }; Container allocateResources() { std::cout << "Allocating resources\n"; Container result {{}, {}}; std::cout << "Resources are allocated\n"; return result; } int main() { { Container container = allocateResources(); std::cout << "Working with resources in an external scope: " << container.resourcelet1.file << ", " << container.resourcelet2.file << "\n"; } std::cout << "External scope is disposed\n"; return 0; } 

Output:

Allocating resources Opened 1 Opened 2 Resources are allocated Working with resources in an external scope: 1, 2 Closed 2 Closed 1 External scope is disposed 

Fiddle Here, the lifetime of Resource is bound to the Container and does not terminate with lexical scope of allocation. If Container is itself a member of a complicated object tree or is passed across threads, the lifetime analysis would be further complicated, but C++ makes lifetimes easy to manage.

Closer

Guava provides a trivial container to handle resources to be disposed later. The example above can be ported as:

import java.util.*; import com.google.common.io.Closer; import java.io.Closeable; import java.io.IOException; public class Demo { private int count = 1; public static void println(String message) { System.out.println(message); } int open_file() { int result = count++; println("Opened " + result); return result; } void close_file(int fd) { println("Closed " + fd); } public class Resource implements Closeable { private int file = open_file(); @Override public void close() throws IOException { close_file(file); } }; public class Container implements Closeable { private final Closer closer = Closer.create(); private final Resource resourcelet1, resourcelet2; public Container(Resource a, Resource b) { this.resourcelet1 = closer.register(a); this.resourcelet2 = closer.register(b); } @Override public void close() throws IOException { closer.close(); } }; public Container allocateResources() { println("Allocating resources"); Container result = new Container(new Resource(), new Resource()); println("Resources are allocated"); return result; } public static void main(String[] args) throws IOException { Demo demo = new Demo(); try (Container container = demo.allocateResources()) { println("Working with resources in an external scope: " + container.resourcelet1.file + ", " + container.resourcelet2.file); } println("External scope is disposed\n"); } } 

Fiddle

The main idea is to use an instance of Closer to manage resource that share a lifetime and would be automatically destructed in C++.

Closer is trivial to implement, so no external dependency is required. Pay attention to exception safety, and order of disposal though.

Disposer

Intellij IDEA plugins build a hierarchy of disposable objects with a static method:

Disposer.register(parentDisposable, childDisposable); 

And then the whole hierarchy can be disposed with:

Disposer.dispose(parentDisposable); 

Any object of the hierarchy implementing an interface IDisposable is invoked.

Porting the C++ example above:

 public class Resource implements Disposable { ... @Override public void dispose() { close_file(file); } } public class Container implements Disposable { private final Resource resourcelet1, resourcelet2; public Container(Resource a, Resource b) { Disposer.register(this, a); Disposer.register(this, b); this.resourcelet1 = a; this.resourcelet2 = b; } @Override public void dispose() { Disposer.dispose(this); } } 

The original design intent behind the Disposer is more flexible:

/// Copyright © 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. public class Foo<T> extends JBFoo implements Disposable { public Foo(@NotNull Project project, @NotNull String name, @Nullable FileEditor fileEditor, @NotNull Disposable parentDisposable) { this(project, name, fileEditor, InitParams.createParams(project), DetachedToolWindowManager.getInstance(project)); Disposer.register(parentDisposable, this); } @Override public void dispose() { myFooManager.unregister(this); myDetachedToolWindowManager.unregister(myFileEditor); KeyboardFocusManager.getCurrentKeyboardFocusManager() .removePropertyChangeListener("focusOwner", myMyPropertyChangeListener); setToolContext(null); } } 
  • In this case, the parent disposable is passed into the constructor,
  • The Foo disposable is registered as a child of parentDisposable in the constructor.
  • The dispose() method consolidates the necessary release actions and will be called by the Disposer.

The advantages over Closer are:

  • no additional property is needed for each intermediary lifetime
  • dependencies can be registered externally

However:

  • Disposer is very hard to implement correctly by yourself

3 Comments

Guava's Closer is useful, thanks -- it still requires remembering to pass everything through Closer::register() and call Closer::close() in your own close() implementation, but that's the best we can do I think. Disposer ensures children are closed first, while Closer gives control over this by allowing the caller to add code either before or after the call to Closer::close() (useful if the parent needs the children to be valid initially), and plain try-with-resources ensures children are closed last (assuming that children are constructed before their parents).
In your C++ example though, the output you happened to get depends on the compiler implementing NRVO for the return result; in allocateResources(), which it's not obliged to do until C++17. "C++ makes lifetimes easy to manage" -- you must be joking.
@j_random_hacker To avoid the extra copies in С++, everything could be made movable or emplaced, which would make the example robust, but verbose. Ease of lifetime management can be seen in comparison with languages without RAII. The lifetimes are all ill-defined there.
-1

The approach I take is to use a layered product (sometime a simple static method) which takes care of the resource allocation. You don't want this sort of resource allocation littering your program.

There are plenty of libraries which do this. It not something you should have to worry about in most cases.

1 Comment

Thanks Peter, but my concern is with reliably DEallocating the resource. I don't see how this responsibility could be moved to a library -- you still have to remember to call that library code at the right time.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.