System.gc() to close file descriptors (was RE: status of gcj's bo ehm collector?)
Boehm, Hans
hans_boehm@hp.com
Fri Dec 7 13:37:00 GMT 2001
> From: Andrew Haley [mailto:aph@cambridge.redhat.com]
> Boehm, Hans writes:
> > Interesting. Unfortunately, I think the standard Java
> interface isn't quite
> > sufficient to do that right. I think the logic to do this
> properly is:
> >
> > if (I'm out of file descriptors and need one, but not before) {
> > for (;;) {
> > System.gc();
> > System.runFinalization();
> > if (the preceding call ran no finalizers || file descriptor is
> > available) break;
> > }
> > }
> >
> > The loop is needed because running finalizers may result in
> > dropping more objects. If finalization dependencies are correctly
> > enforced (in Java the client code needs to do that), that's even
> > reasonably likely. I think this mean you really need
> > runFinalization to return an indication of whether or not it did
> > anything so that you can avoid a potentially infinite loop here.
>> What I'm wondering is whether this is a real or a theoretical
> requirement for gcj. So, for our current implementation, is there any
> chance that calling System.gc() might not call finalizers for files
> that are garmabge before returing?
>In the long run, I think it's a real requirement, though that depends on the
client coding style. At the moment, my impression is that all of this is
used fairly rarely, much of this is still broken in many VMs, and most users
don't know how to use it correctly anyway. Thus it's probably not a
terribly high priority.
I looked at the gcj code a little more closely. System.runFinalization()
also seems to have two other problems:
1. The spec is not clear whether you are allowed to run finalizers in the
calling thread. (I think this is safe only if trusted code doesn't call
untrusted code while holding an important lock, or if you can restrict
untrusted code from invoking runFinalization. I don't understand Java
security well enough to know whether this is reasonable.) We currently run
it in the finalizer thread. (Good.)
2. The spec implies that the method doesn't return until you've made some
effort to run all finalizers. You need that property for the file
descriptor usage. We currently only notify the finalizer thread, and return
immediately. I think this notication is essentially a no-op. If there is
anything waiting to be finalized, the collector will have done the
notification. (Probably bad. runFinalization() should probably wait for
the finalization thread to be idle again. I haven't though enough about
whether that's easily possible.)
Getting back to ordering issues, I looked at what we currently do in
java.io. There seems to be no attempt to order finalization (good for file
descriptor management), but the resulting semantics seem to me to be
problematic:
1. BufferedOutputStreams and the like are not flushed if the file descriptor
is dropped and finalized (or if it persists until process exit). It's
unclear from the spec whether this is right. Anyone know what other JVMs
do? I'd personally consider this to be unfriendly behavior. "Fixing" it
would introduce ordering.
2. There seems to be a minor problem with finalization of FileDescriptors.
They are closed only if they are still open, but the test and the close are
done without synchronization. Thus there seems to be a race, and a File may
be closed more than once. Note that (1) a FileDescriptor may be finalized
twice, since FileInputSream explicitly calls finalize() on the underlying
descriptor, and (2) this is a bug even if there is a single finalizer
thread: When FileDescriptor::close calls ::close, the FileDescriptor object
may no longer be accessible. Thus the finalizer may run concurrently and
try to close the descriptor a second time. Thus the explicit one can
spuriously fail, though this is probably very unlikely. Thus I think at
least finalize() and close() need to synchronize. (I would always view an
unsynchronized finalizer with great suspicion.)
Hans
More information about the Java
mailing list