Does Java support RAII/deterministic destruction?
Asked Answered
P

4

48

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!

Penance answered 25/1, 2009 at 8:44 Comment(2)
possible duplicate of Is there a destructor for Java?Tagliatelle
@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 ;-)Penance
M
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.

Meteor answered 25/1, 2009 at 8:54 Comment(10)
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.Penance
D manages to have deterministic destruction in a GC context - so it can be doneCyclostyle
@Phil: Interesting, when I have time I'll have a look at D.Penance
@Phil - so do C# and C++/CLR.Compete
@Earwicker: In what way does C# have deterministic destruction?Meteor
Apparently it has been added to 1.7.Ipswich
What you call deterministic destruction might be better described as reference-counting GC to avoid confusion with RAII which typically doesn't involve reference counting.Contrive
@JohnMcFarlane: I'd say that deterministic destruction is the intended effect. Reference-counting GC is one potential implementation; RAII is another. I was also trying to use the OP's terminology. (It becomes slightly tricky when discussing Java/C# where asking the object to clean up after itself isn't the same as the object actually being destroyed.)Meteor
@JonSkeet: Agreed, but it is specifically reference-counting GC which "impacts performance" and "fails with circular references". In contrast, RAII normally doesn't employ reference counting, is comparable to sweeping GC in performance and has no circular reference problems. The OP uses the term deterministic destruction WRT RAII and doesn't mention reference counting. So I'd maintain that reference-counted GC is a more appropriate term than deterministic destruction.Contrive
@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.Meteor
C
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.

Cyclostyle answered 14/2, 2009 at 13:18 Comment(3)
Thanks Phil, this is a very nice way to solve the problem in the absence of deterministic destructors.Penance
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/…Cyclostyle
@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.Penance
N
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.

Nadbus answered 25/1, 2009 at 9:17 Comment(2)
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.Penance
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).Guyon
H
-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.

Hexahedron answered 25/1, 2009 at 10:30 Comment(1)
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.Penance

© 2022 - 2024 — McMap. All rights reserved.