Skip to main content

Destructor (Don't laugh!!)

41 replies [Last post]
samlalani
Offline
Joined: 2004-10-13
Points: 0

I know that this topic has been beat up many times before, but just hang in there and read the rest of this post before deciding.

I would like the following added to Java: when the last reference to an object goes out-of-scope, a function gets called (usually called a destructor). However, the memory for this object doesn't have to be cleaned up at this time; that's the garbage collector's job. This function is where you can, for example, put code to close files if you opened files in the constructor. Take a look at the following piece of code:

for (int i = 0; i < 10; i++)
{
SomeClass x = new SomeClass ();
x.addLine ("line " + i);
}

If you didn't write SomeClass, you won't know whether it opens a file, or outputs to a socket, or handles data in a different way, and you SHOULDN'T have to know that, that's what Object Oriented Programming is all about. However, the writer of the class would like to know when an object of that class goes out-of-scope so that if a file was opened, it can be closed in the "destructor".

I've read posts that say that a destructor is difficult in a language that is based on garbage-collection. However, this would just be a function call. The JVM must know when the last reference to an object is removed so it can queue up the object for garbage collection. At that moment when the last reference is removed, the destructor function can get called.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
brucechapman
Offline
Joined: 2004-03-18
Points: 0

> So what would be the solution for the problem other
> than telling the programmers they must not forget to
> close the connection manually?
>
> Monika.

In the cases [b]where it is important enough[/b], the idiom to use in this case is that rather than passing a resource to the user, have the user pass the actions to a resource manager. The resource manager performs the actions and does whatever it needs to do, in order to maintain the integrity of the resource.

[code]
/* The resource API */
interface ResourceUser {
void perform(Resource r);
}
class ResourceManager {
public performWithResource(ResourceUser performer) {
Resource r = allocateResource();
try {
performer.perform(r);
} finally {
releaseResource(r);
}
}
}
[/code]
[code]
/* resource user */
ResourceManager mgr = ???;
mgr.performWithResource(
new ResourceUser() {
public void perform(Resource r) {
r.doSomething();
}
}
}
[/code]
Yes, the coding for the user is no less than a finally block, BUT, they are forced to do it this way, there can be no mistake.

Delegates / closures or some other mechanism to reduce the clutter of anonymous inner classes would make the user code cleaner.

java.security.AccessController's doPrivileged() methods work this way, and that is where I learnt the technique from.

monika_krug
Offline
Joined: 2004-10-14
Points: 0

Interesting, thank you.

Monika.

swpalmer
Offline
Joined: 2003-06-10
Points: 0

> So what would be the solution for the problem other
> than telling the programmers they must not forget to
> close the connection manually?

That isn't much different from forgetting to clean up some resource in a finalizer or 'destructor'.
The point is the the finally block will always run. You avoid the case of some code path exiting your method without your cleanup running - this is in many cases equivalent to the C/C++ practice of using a destructor for stack-based objects to do the resource cleanup. For longer-living heap-based objects that are deallocated in some other method a different solution is needed, but try/finally works for the case where C/C++ would use a local stack-based object.

zander
Offline
Joined: 2003-06-13
Points: 0

> Oh, I think it's needed and wanted very much. It's
> just not possible.
>
> One example, where it would be useful: A database
> connection has to be released at the end of every
> method of a certain class. It can be forgotten easily
> and it's not failfast, so hard to find. Just like
> memory leaks used to be in C++.

I wrote a object-persistence manager a couple of years ago and at the time I never heard of try {}finally {}, causing lots of problems like the one you describe.
But since I found out about this the dozens of connections are always closed correctly due to a simple design pattern being fail-save and simple to understand.

So, do you think [b]another[/b] solution is needed for your stated problem, and why is finally{} not enough?
Do remember that things happening by themselves does not make for the most predictable of circumstances..

monika_krug
Offline
Joined: 2004-10-14
Points: 0

So what would be the solution for the problem other than telling the programmers they must not forget to close the connection manually?

Monika.

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> So what would be the solution for the problem other
> than telling the programmers they must not forget to
> close the connection manually?
>

I belive that is the solution because any other solution that you come up with will carry semantics that may not be applicable in my situation.

Maybe we can also offer a set of classes that register their JNI resouce and GC will trigger their release. We sort of have this with finalization but that said, finialization overhead is not insignificant. Consider the case of Gavin King's experience of removing finalization from Hibrinate.

In general, we need to be doing things to speed up GC, not slow it down. Adding any overhead to GC while it is in a stop the world phase is currently unacceptable IMHO. GC overheads have been a major cause of outages in many of the applications that I've delt with. So although it's an option, it's one that must be followed with care.

kcpeppe
Offline
Joined: 2003-06-15
Points: 0

> What I had originally asked for is just a function
> call when an object goes out of scope, not something
> that cleans up or closes files.

I think that question has long been answered; when an object becomes collectable and if it implements/over-rides finalize(), then GC will set it aside until that method has been called (there is a seperate thread running in the VM that is responsible for running finalization). Once finalization has been run, GC can free the Java resources associated with that object.

I do stress the term Java resources as file handles are allocated using native methods and are allocated in C heap (not Java heap). GC will not free memory that has been allocated by a user action in Java. For example, if you open a file, C heap is malloc'ed to maintain the structures and it is only close() that will trigger the freeing of that memory. So there is a slight disconnect between Java memory management and C memory management.

Consider the code fragments

// fragment 1
static Object o;

public static void main( String[] args)

for ( i = 0; i < 1000; i++) {
o = new Object();
}
}

// fragment 2

public static void main( String[] args)

Object o;
for ( i = 0; i < 1000; i++) {
o = new Object();
}
}

// fragment 3
public static void main( String[] args)

for ( i = 0; i < 1000; i++) {
Object o = new Object();
}
}

In fragement 1 we see that Object o is assigned a new Object. This in turn released the object that was being held which makes it avaliable for GC. In fragment 2, Object o is assigned a new Object which in turn releases the object being held thus making it avaliable for GC. In fragment 3, the object o is released at the end of each loop as the variable falls out of scope. So although fragment 1 and 2 would appear to be different than fragment 3, in fact it is 2 and 3 that have the same behavior. This is because the underlying compiler and JIT optimizations are applied to the code. If you don't understand how these optimizations work, then it becomes difficult to know how to interact with memory management in some meaningful way.

From this we can see that GC is the only process that understands when an object is truly out of scope and as such is the only one that can free memory. I would suggest that if you create another method called destroy then you are interfering with Java's memory management scheme and Java's memory management usually works best when you understand it and let it do it's job. If you are looking to close a file (or free another natively held resource), you could use finalize().

The point is, Java is not C++. There are some really sophisticated things happening in the VM and trying to apply C++ controls to Java is simply not a great idea. I would suggest Bruce Eckels Thinking in Java to help you over come your need to write C++ like code in Java (it's a problem that we all suffer from ;) ). This link might provide you with a better understanding of GC http://java.sun.com/docs/hotspot/gc1.4.2/

Cheers
Kirk Pepperdine

samlalani
Offline
Joined: 2004-10-13
Points: 0

What I had originally asked for is just a function call when an object goes out of scope, not something that cleans up or closes files. That was just an example. If you have the option of having a function called when an object goes out of scope, you can determine whether or not you want to close out your items. This would only be used if you know your object won't have other references. This would just give you an option, that's it.

Maybe the function would only be called if your class implements a desctructible interface (or something like that). I would just like the option.

zander
Offline
Joined: 2003-06-13
Points: 0

> What I had originally asked for is just a function
> call when an object goes out of scope, not something
> that cleans up or closes files. That was just an
> example. If you have the option of having a function
> called when an object goes out of scope, you can
> determine whether or not you want to close out your
> items. This would only be used if you know your
> object won't have other references. This would just
> give you an option, that's it.
>
> Maybe the function would only be called if your class
> implements a desctructible interface (or something
> like that). I would just like the option.

Unfortunately re-requesting does not change the point that this is impossible.
I really think you should take a long-hard-look on WHY you want this so we can follow that reason and try to find another solution to the problem. As of now; I simply disagree this is needed or even wanted.

monika_krug
Offline
Joined: 2004-10-14
Points: 0

Oh, I think it's needed and wanted very much. It's just not possible.

One example, where it would be useful: A database connection has to be released at the end of every method of a certain class. It can be forgotten easily and it's not failfast, so hard to find. Just like memory leaks used to be in C++.

Monika.

brucechapman
Offline
Joined: 2004-03-18
Points: 0

> Oh, I think it's needed and wanted very much. It's
> just not possible.

A solution is needed, but that does not justify this proposed solution, all that means is that there is a real need. This solution is not possible. If posters can identify the real needs, then others might be able to help them find workable solutions.

I still haven't seen a need mentioned here, that can't be solved with existing technology.

zander
Offline
Joined: 2003-06-13
Points: 0

> But you can make this same exact argument with
> memory.

Thats an interresting comment.
The GC runs BECAUSE it is out of memory, expecting the GC to free up memory.

What this thread actually asks for is when the JVM is almost out of file-connections it should do a GC.
IIRC all OSes simply fail the low-level API to return a new file/socket/etc handle. So at that level of the native code the JVM knows there is a shortage and can start a GC.
In fact, I read about the free JVMs doing such a thing already.

Will that solve the problem?

mthornton
Offline
Joined: 2003-06-10
Points: 0

Ultimately (almost) all of these resource reduce to memory, unfortunately memory which is outside the scope of the JVM. Thus JVM running on an OS which had pervasive GC would not have this problem at all.
With current OS, perhaps one alternative would be to recognise errors which mean 'short of resource' and in these cases invoke the GC and retry the operation. This isn't entirely satisfactory because the resource hog may be another application working in the same way.

zander
Offline
Joined: 2003-06-13
Points: 0

> Ultimately (almost) all of these resource reduce to
> memory, unfortunately memory which is outside the
> scope of the JVM. Thus JVM running on an OS which had
> pervasive GC would not have this problem at all.
> With current OS, perhaps one alternative would be to
> recognise errors which mean 'short of resource' and
> in these cases invoke the GC and retry the operation.
> This isn't entirely satisfactory because the resource
> hog may be another application working in the same
> way.

I think you completely missed the point of my post; this is JVM native code, not operating system code that triggers a GC.

Or else you are saying something but failed to explain to silly me what it is you mean.

mthornton
Offline
Joined: 2003-06-10
Points: 0

If the entire OS was written in Java as well, and all resources allocated from the same pool of memory?

gipak
Offline
Joined: 2004-10-11
Points: 0

I don't consider destructors really necessary for java, as far as let's say DB connections or file streams go, it is always required to close them explicitly. if it could be done by a destructor when object gets garbage collected it would make these bugs (yes! bugs) hard to find. An application would crash with to many open file descriptors or database connections "rarely", not "most of the time". I personally prefer bugs that pop up often, not say, after calling a release "stable" :). This is exactly the reason i like Oracle's jdbc drivers, they don't have finalizers, or anything that act's as a "failsafe", they require you to close everything explicitly, they are "failfast" what paradoxically is a very good feature.

regexguy
Offline
Joined: 2003-06-20
Points: 0

But you can make this same exact argument with memory. So you should prefer malloc and free to garbage collection because it forces you to be careful with this resource just like the other ones.

I will grant you that using finalize() as a "failsafe" for closing connections is not good in the current garbage collection universe. It would be different if you could have a true destructor.

Actually, in the current scheme I think a finalizer might be good to use to log error messages -- "Hey, you forgot to clean this up and the garbage collector was forced to do it."

Also, even with the current scheme I find it's not too hard to get a rare database connection problem in a "stable" release.

abies
Offline
Joined: 2003-06-10
Points: 0

> But you can make this same exact argument with
> memory. So you should prefer malloc and free to
> garbage collection because it forces you to be
> careful with this resource just like the other ones.

No, memory is very different thing for one reason. If there is no free memory, gc will kick in and try to free it. If there is no enough file descriptors, window handles or any other native resources, there is no mechanism which automatically tries to free all possible resources of given type.

I'm +1 for some kind of mechanism to show that finalize method of object should be automatically called when it goes out of scope - possibly even setting internal finalized flag for jvm, so object won't get enqueued on to-finalize list inside gc.

gipak
Offline
Joined: 2004-10-11
Points: 0

Hehe, to be honest, logging an error IS the only reason I use finalize() [rotfl]. And to be honest I moved to Java from C (with just a bit of C++). And there is one problem with destructors - threads. I'm not sure what kind of creapy damage would cause a "destructing" thread if it gets blocked . Or worse what would a deadlock cause. Just being... sceptic.

rreyelts
Offline
Joined: 2003-06-10
Points: 0

[i]Isn't there something in the JVM that decrements how many references there are to any particular object AT THE TIME that the reference variable goes out-of-scope? That's when I would like the destructor to get called.[/i]

Sun's garbage collector works by traversing the graph of live objects from root points. So, no, you don't instantaneously know when an object no longer has references to it.

vikstar
Offline
Joined: 2004-04-23
Points: 0

So, a major set-back of destructors in java is that there are no reference counters? How hard is it to implement a reference counter in the JVM? You could then add a "destruct" keyword which would be called whenever the reference count goes to 0: [code]public class MyClass {
/* the destructor */
destruct {
}
/* a constructor */
public MyClass() {
}
}[/code] Perhaps the overhead of keeping a reference count for every single object isn't worth the very few classes that actually need it? Or maybe it would be possible to only have a reference count for objects that have a destruct block?

dtbullock
Offline
Joined: 2004-03-04
Points: 0

Please see the JavaDoc for the java.lang.ref package, which will be useful for the scenario you describe.

jcrosbie
Offline
Joined: 2003-06-21
Points: 0

Having spent a fair amount of time away from Java and back to C++ I am reminded of how powerful constructor/destructor symmetry is. As many have pointed out this isn't possible with garbage collection unless you can guarantee that it will be called once the object goes out of scope.

What I haven't seen anyone mention (apologies if I missed it) is what if there were a way in Java to allocate objects on the stack? What if I could specify an object to exist on the stack and once it exits scope guarantee that finalize() is called, as a destructor is in C++? Maybe something like this:

public class TestClass
{
public void doQuery()
{
local MyConnection conn = new MyConnection("dbparam");
// do something
}
}

Where MyConnection is a class whose finalizer() ensures the connection is closed when it is called.

Of course, complications arise when you try to return one of these objects, but the compiler should be able to handle not returning an object tagged with "local." And rules could also exist preventing non-local objects from obtaining references to local ones.

Thoughts?

bfandreas
Offline
Joined: 2004-10-26
Points: 0

Well, the ResourceManager/Delegate pattern is a solution that at least solves the issue with external allocated resources.
As for Destructors, you simply don't get them in "managed" environments.
In fact, it is explicitly DISCOURAGED to acually do anything in the finalize method. Remember, the GC is about to permanently kill your object. If you do something like

protected void finalize() {
someListIHoldAReferenceTo.add(this);
}

you can confuse the heck out of the GC.

Just find out why you need to know when an object gets out of scope and find another solution to that problem.

zander
Offline
Joined: 2003-06-13
Points: 0

This exact example is the reason my GCing objects with finalizers takes so much more resources.

If the garbage collector finds an object that has no references what it does depends on a finalize() being present

if(finalize present)
add to queue of to-finalize objects. This adds a reference to the object again, effectively postponing the GC-ing.

if(no finalize present or object already finalized)
garbage collect

so; adding a finalize() to your object means it will always have to be detected by the GC _twice_ before it will be removed.

jtr
Offline
Joined: 2003-06-10
Points: 0

Well, I posted this in the javadoc thread, but IMHO, just by adding:
- consistent method names for freeing a resource (dispose(), close(), whatever, you pick) in ALL APIs, or
- a section of javadoc which explicitly calls out (maybe with a blink tag!) what method should be called to free resources

Either or both of these would go a long way to helping newbees who forget to close sockets, dispose Graphics objects, Windows, etc...

swpalmer
Offline
Joined: 2003-06-10
Points: 0

Would it be useful to add a per-method "finally" block to the language?

This would be similar to declaring a method as "synchronized" instead of putting [code]synchronize(this) { blah();}[/code] within the body of the method.

e.g.
[code]
public void doStuff()
{
blah();
}
finally
{
cleanUpAfterDoStuff();
}[/code]
Which would be equivalent to:
[code]public void doStuff()
{
try
{
blah();
}
finally
{
cleanUpAfterDoStuff();
}
}[/code]

Hmm... on second thought, there isn't much to be gained is there :)

monika_krug
Offline
Joined: 2004-10-14
Points: 0

Though destructors would be really useful in many cases where cleanup is necessary, I think it is impossible to include destructors in Java. We would get the same problems as C++ has: An object has been destructed, but there are still references to it. As objects are not created on the stack, there is no such thing as going out of scope for an object. The local variable that points to the object goes out of scope, but it might not be the only variable holding this reference.[code]void method()
{
Type t = new Type();
new SomeClass().someMethod(t);
} [/code]Destruct the object now, i.e. call its finalize method or destructor? t is going out of scope. But the compiler does not know what happened to t in SomeClass.someMethod. Maybe this class looks like this:[code]class SomeClass
{
Type tt;
void someMethod(t)
{
tt = t;
}
}[/code]The JVM does not use the pointer-counting model for garbage collection, which is a good thing, as it allows circularly referenced objects to be collected and it makes the garbage collector more efficient (though I could not explain why the latter is the case). But it also means that it is not possible to determine the point in time exactly when the last reference is destroyed and the object is ready for garbage collection.

Possible Solutions:

- Create your own reference/pointer class which manages references to the real objects. The constructor of the actual class has package or protected access and the reference class is in the same package. The reference object gives out all the references to the actual object and counts them. Then you can determine when the last reference disappears and call a .dispose() or .close() method of the object. However, you would have to make sure that before a variable is assigned to a different object or goes out of scope it reduces the count in the reference object, so maybe this does not help much. It's not less work than calling the clean-up methods yourself, it just makes sure you don't accidentally clean up an object that is still used somewhere else.

- Use AspectJ to enforce the execution of some code (e.g. call the clean-up method) after the execution of each method of the object (or each method whose signature fulfilles a certain pattern). The advice in the aspects does not have access to local variables, though, except for parameters.

Monika.

abies
Offline
Joined: 2003-06-10
Points: 0

> The
> local variable that points to the object goes out of
> scope, but it might not be the only variable holding
> this reference.[code]void method()
> {
> Type t = new Type();
> new SomeClass().someMethod(t);
> } [/code]Destruct the object now, i.e. call its
> finalize method or destructor? t is going out of
> scope. But the compiler does not know what happened
> to t in SomeClass.someMethod.

Of course. But we have same situation at the moment.
[code]void method()
{
Type t = null;
try {
t = new Type();
new SomeClass().someMethod(t);
} finally {
if ( t != null ) {
t.finalize();
}
}
}
[/code]

This is equivalent code with current java. Here someMethod could save pointer to t as well, and it will get invalid resource when it tries to access it again. Adding automatic destuctors won't change a thing here, except we won't have to put variables into try/finally blocks, with null/check-for-null code. Best example here is JDBC ResultSet - getting Connection,PreparedStatement and ResultSet and then closing all 3 of them is way too much code which could be as well done by compiler. Of course, contract would have to be changed a bit, to require closing connection at finalize - current Oracle doesn't do it for example.

monika_krug
Offline
Joined: 2004-10-14
Points: 0

You can't call t.finalize(), it's not public.

Monika.

regexguy
Offline
Joined: 2003-06-20
Points: 0

I realize that it is almost impossible to do this, and neither finalize() and try {} finally {} really do the job.

However, the inability to reliably use the garbage collector to free up non-memory resources (file descriptors, db connections, gdi objects, etc.) means that the programmer must effectively keep track of all references to the object and destruct it just as if we had no garbage collection. Back to the dark ages.

I've tried to imagine ways around it -- I actually think having a -Xrefcount flag in the JVM to use a simple reference counting garbage collector might be nice. I realize you'd never be able to reclaim circular references then, but as a tradeoff you'd get reliable execution of finalize() and no pauses for garbage collection.

This is, IMHO, a really serious problem -- but I suspect really solving it is maybe a Nobel prize.

vhi
Offline
Joined: 2004-10-11
Points: 0

The problem with -Xrefcount kind of flag is that they change the behaviour of your code, which IMHO is dangerous. For example, if I write reclamation code in 'finalize' and depend on -Xrefcount for it to work, then it greatly reduces the portability of my code. Users will not be able to share JVM, etc. And if the -Xrefcount is by-chance switched off, my code will fail. It is akin to having side-effects in 'assert' statements.

regexguy
Offline
Joined: 2003-06-20
Points: 0

But -Xrefcount is a similar sort of danger as requiring real time, as far as I can see.

But I'll grant it is an ugly solution.

tsinger
Offline
Joined: 2003-06-10
Points: 0

Why don't you use try/finally?

samlalani
Offline
Joined: 2004-10-13
Points: 0

[b]finalize[/b] is NOT guaranteed to be called when the object goes out-of-scope. It is called by the garbage collector whenever it gets around to it. The example for-loop that I gave would not work very well if you closed the file in the finalizer function because finalizer may not have been called yet before the next iteration of the loop.

brucechapman
Offline
Joined: 2004-03-18
Points: 0

But currently, the only mechanism to know when something "goes out of scope" is the garbage collector.

If you require something more explicit, you'll just need to add a close() or similar method. Finalize can then do the close() automatically it it hasn't been done explicitly.

samlalani
Offline
Joined: 2004-10-13
Points: 0

But how does the garbage collector know to collect the object? Isn't there something in the JVM that decrements how many references there are to any particular object AT THE TIME that the reference variable goes out-of-scope? That's when I would like the destructor to get called.

I can't imagine that the garbage collector figures out ALL the objects need to be garbage collected EVERY TIME it runs!

brucechapman
Offline
Joined: 2004-03-18
Points: 0

GC is very sophisticated these days.

It does not use reference counters.

Search the java.co.sun site if you really want all the gory details on how the various GC strategies work.

brucechapman
Offline
Joined: 2004-03-18
Points: 0

I think this is planned for a new language from SUN (provisional name = OAK) :)

See finalize() here
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#method_sum...

samlalani
Offline
Joined: 2004-10-13
Points: 0

Wasn't [b]oak[/b] the original name for Java? Is there a new language also called oak?

cowwoc
Offline
Joined: 2003-08-24
Points: 0

My guess is that this is already handled by Object.finalize()