3

Question

I have a URLClassLoader anonymous class. In it, I've overridden the finalize() method so that the object will automatically close itself when garbage collected:

protected void finalize() throws Throwable {
    close();
}

and I've overridden the close() method to print some information:

public void close() throws IOException {
    System.out.println("Closing...");
    super.close();
    System.out.println("Closed.");
}

Immediately after creating this object, I call System.gc() to destroy it. As expected, the finalize() method gets called, and then the close() method gets called, but I only see Closing... get printed to the console.

Why does execution never reach my second print statement?


Notes

The first thing that I suspected was happening was that super.close() was throwing an exception, and whatever prepares an object for garbage collection was swallowing the exception, so I tried catching any Throwable and printing it:

public void close() throws IOException {
    System.out.println("Closing...");
    try {
        super.close();
        System.out.println("Closed.");
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

But I got the same exact output as before: Closing.... The next thing I did was test what would happen if I closed the class loader manually, so I just called .close() on my class loader, immediately after constructing it, instead of running System.gc(); In this case, both of my print statements were run:

Closing...
Closed.
Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
Kröw
  • 504
  • 2
  • 13
  • 31
  • maybe `super.close` is closing stdout ? – Scary Wombat Jun 20 '19 at 01:52
  • Hmm, I tried printing `"Test"` *after* calling `System.gc()`, and the output was `Test`, and then `Closing...`. All of the code I tested in my question was in a `main` method. Maybe the `stdout` is just closing before the finalization method finishes? Garbage collection does run on a separate thread, right? – Kröw Jun 20 '19 at 01:56
  • try writing to a file rather than stdout? – Scary Wombat Jun 20 '19 at 01:58
  • 2
    It definitely could be a timing issue. The garbage collector does run asynchronously, so if `main` exits too soon, you may not see all the output. Try sleeping on the main thread for a bit before exiting `main` to test this theory. It probably wouldn't hurt to call `flush()` on `System.out` as well after sleeping is over. – Ted Hopp Jun 20 '19 at 05:14
  • 3
    Don't use finalize, it is a failure of the way the Java language was designed. It has been [deprecated in OpenJDK 11](https://cr.openjdk.java.net/~iris/se/11/latestSpec/api/java.base/java/io/FileInputStream.html#finalize()) (see the note) and for good reason. – TT. Jun 20 '19 at 07:21
  • @TT. Even if I was using Java 9+, what would you expect me to do instead? Neither cleanup using `PhantomReference`s nor using `Cleaner`s will allow me to directly refer to my `URLClassLoader` object. `finalize()` seems perfect for this scenario. Could you elaborate on "`it is a failure of the way the Java language was designed`"? I don't really see how that is. – Kröw Jun 20 '19 at 11:18
  • @TedHopp I tried using a 200 millisecond delay after my `gc()` call but before printing `"Test"` and everything got printed in order. – Kröw Jun 20 '19 at 11:20
  • The people that drew up the language made a mistake in thinking that finalize would ever be a good idea to clean up resources. It isn't. The deprecation is an acknowledgement of that. Some research into why should reveal some background on that. Next, consider what you should be doing once finalize is removed (it probably won't real soon, but that's besides the point). That is the way you should be cleaning it up. Hint: URLClassLoader implements AutoCloseable (If you're in Java 7+). – TT. Jun 20 '19 at 13:35
  • @TT. Well, finalization likely won’t ever be removed; that would be a massive backwards compatibility issue. (It’s `Deprecated` annotation declares that there aren’t plans to remove it, I believe). Could you tell me where you were told that it was a “mistake in the language”? I’d looked in to why finalization is “inherently bad” (i.e. in the docs for Java 11) and the only reason relevant to me that I found was that once you give an object a finalizer, you can’t revoke it. Regardless, if `finalize()` were to be removed then I wouldn’t be able to close the loader how I need to. – Kröw Jun 20 '19 at 15:11
  • @TT. `finalize()` is necessary in my case, because I need to close my object exactly when it happens to need to be garbage collected, if ever. I just don’t get how it would be possible for me at all (much less beneficial for me) to switch to any other form of cleanup in this case. (I forgot to mention that I’m in Java 8.) – Kröw Jun 20 '19 at 15:19
  • I don't really need to be told to know something Kröw :-). In any case, the space of a comment section is too small to go in a discussion that is really irrelevant. Notice that the deprecation has `forRemoval=true` in the annotation. As I said before, I don't suspect it will be removed any time soon. To your problem, I do not know how you use URLClassLoader in your code, but for an AutoCloseable you can use try-with-resources to guarantee closing. Also: [Is overriding Object.finalize() bad](https://softwareengineering.stackexchange.com/questions/288715/is-overriding-object-finalize-really-bad) – TT. Jun 20 '19 at 15:19
  • @TT. Thanks for the link to that question. The deprecation annotation on the docs page that you'd referred to earlier doesn't specify a `forRemoval` value, which defaults to `false` for the `Deprecated` annotation. As for my use case, I'm keeping a `WeakReference` to my class loader and regenerating it whenever I need to use it, if it has been garbage collected. Because of this, I've been having to use `finalize()` to perform `close()` so that if my loader has been GCed, the resources it opened will close until the class loader is re-created. – Kröw Jun 20 '19 at 15:33
  • Ah yes, `Object.finalize` doesn't specify for removal, it's in all the classes that ever overrode that method (which is also a hint). In any case, don't count on finalize ever doing what it is supposed to do. There are no guarantees on finalize, and the deprecation note tells you that you should be steering away from using it. Use at your own risk I'd say :-). – TT. Jun 20 '19 at 15:39
  • 1
    @TT., Kröw: See also [this answer](https://stackoverflow.com/a/56454348/1441122). – Stuart Marks Jun 22 '19 at 07:09
  • @StuartMarks Hi Stuart, I had already read (and +'d) your answer and it confirmed my experience with finalizers (in the sense, that they just don't work to clean stuff up). Good read! – TT. Jun 22 '19 at 10:42
  • What does "a `URLClassLoader` anonymous class" mean? Do you mean a subclass of `URLClassLoader` or a class loaded via a `URLClassLoader`? And in what regard is the anonymous nature of the class relevant? – Holger Nov 19 '19 at 16:07
  • @Holger I just meant an anonymous class directly extending `URLClassLoader`. It would be a subclass of `URLClassLoader`. – Kröw Nov 20 '19 at 19:36
  • Is this reliably reproducible? I see a lot of potential for problems here, I could even contrive scenarios with similar behavior, but I’m quiet sure that those do not match your scenario. Besides a standalone program capable of reproducing the issue, some information would help, Java version, number of CPU cores, number and type (jar:, file:, etc) of the URLs added to this class loader… – Holger Nov 21 '19 at 13:41

0 Answers0