0

I have a library that provide a Resource class, which should be cleaned by Java 9 Cleaner.

import java.lang.ref.Cleaner;
public class Resource {
    static Cleaner CLEANER = Cleaner.create();

    static class CleanerHandler implements Runnable {
        private final String name;

        CleanerHandler(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("Running Cleaner for " + name);
        }
    }

    public Resource(String name) {
        CLEANER.register(this, new CleanerHandler(name));
    }

}

Suppose I have an application CleanerMain that uses Resource. The application will create two Resources, one for local usage, and another one will be stored on a static field.

public class CleanerMain {

    private static Resource SINGLETON = new Resource("global_resource");

    public static void main(String[] arguments) {
        Resource localResource = new Resource("local_resource");
    }
}

The application will be loaded in a ClassLoader (e.g. as an OSGi module or a WAR file) . I expect the ClassLoader will be unloaded after the application terminated.

However, the ClassLoader never unload. I never saw the log Running Cleaner for global_resource, though Running Cleaner for local_resource will be displayed.

The problem can be reproduced by starting a sbt shell and execute runMain CleanerMain

// build.sbt
TaskKey[Unit]("gc", "Perform System.gc()") := {
  System.gc()
}

Note I provide a sbt task to perform System.gc(). But it does not help JVM to collect global_resource or unload the ClassLoader.

$ sbt -J-Xlog:class+unload=info
[4.268s][info][class,unload] unloading class $1dd3210e9cda6e6faf3e$ 0x00000007c0704c28
[info] Loading settings from idea.sbt ...
[info] Loading global plugins from /Users/twer/.sbt/1.0/plugins
[6.938s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2317/1862022854 0x00000007c096e828
[6.938s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2314/1263423349 0x00000007c096dc28
[6.938s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2313/1416148598 0x00000007c096d828
[6.938s][info][class,unload] unloading class jdk.internal.reflect.GeneratedConstructorAccessor5 0x00000007c092d028
[6.938s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2204/2088963974 0x00000007c092cc28
[6.938s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2205/832626395 0x00000007c092c828
[6.938s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2203/247832656 0x00000007c092c028
[6.938s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2202/1272449757 0x00000007c092c428
[6.938s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2200/573210156 0x00000007c092bc28
[6.938s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2201/1054035589 0x00000007c092b828
[6.938s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2199/864020513 0x00000007c092b428
[6.938s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2197/1730739373 0x00000007c092a828
[6.938s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2195/1889391840 0x00000007c092ac28
[6.938s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2193/1883646233 0x00000007c0929828
[6.938s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2196/834352185 0x00000007c0929428
[6.938s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2191/1271342924 0x00000007c0929c28
[6.938s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2194/964794066 0x00000007c092a028
[6.938s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2190/2077351835 0x00000007c092a428
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2189/1898558699 0x00000007c0928c28
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2188/1200655829 0x00000007c0928828
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2187/2122434745 0x00000007c0928428
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2186/2119841115 0x00000007c0928028
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2184/1842386909 0x00000007c0927428
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2182/1696943117 0x00000007c0927028
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2183/1035863485 0x00000007c0927828
[6.938s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2181/863632802 0x00000007c0926c28
[6.938s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2154/10566077 0x00000007c08eec28
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2152/1127241241 0x00000007c08ee428
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2150/1510063672 0x00000007c08edc28
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2148/1860233843 0x00000007c08ed428
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2146/1286502317 0x00000007c08ecc28
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2142/829754298 0x00000007c08ebc28
[6.938s][info][class,unload] unloading class sbt.librarymanagement.DependencyFilterExtra$$anon$2$$Lambda$2140/1782115063 0x00000007c08eb428
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2138/1768513715 0x00000007c08eac28
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2136/27449893 0x00000007c08ea428
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2134/204419489 0x00000007c08e9c28
[6.938s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2132/538764795 0x00000007c08e9028
[6.938s][info][class,unload] unloading class sbt.librarymanagement.DependencyFilter$$Lambda$2130/514769339 0x00000007c08e7828
[6.938s][info][class,unload] unloading class sbt.io.IO$$$Lambda$1997/1978868777 0x00000007c0875c28
[6.938s][info][class,unload] unloading class sbt.io.IO$$$Lambda$1995/1414642509 0x00000007c0875428
[6.938s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1812/827059833 0x00000007c0828c28
[6.938s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1810/1274501898 0x00000007c0828428
[6.938s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1808/427763295 0x00000007c0827c28
[6.938s][info][class,unload] unloading class java.lang.invoke.LambdaForm$MH/2046367300 0x00000007c0827428
[6.938s][info][class,unload] unloading class java.lang.invoke.LambdaForm$DMH/241446552 0x00000007c0826c28
[6.938s][info][class,unload] unloading class java.lang.invoke.LambdaForm$DMH/1629192413 0x00000007c0826428
[6.938s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1806/1846159181 0x00000007c0825c28
[6.938s][info][class,unload] unloading class java.lang.invoke.LambdaForm$DMH/1370803139 0x00000007c0825028
[6.938s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1803/555261988 0x00000007c0824828
[6.938s][info][class,unload] unloading class sbt.Info$$Lambda$1799/2128443263 0x00000007c0823828
[6.938s][info][class,unload] unloading class sbt.Info$$Lambda$1798/1604599396 0x00000007c0823428
[6.938s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1796/242303008 0x00000007c0822c28
[6.938s][info][class,unload] unloading class sbt.InputTask$$$Lambda$1794/495384449 0x00000007c0822428
[6.938s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1792/1919993452 0x00000007c0821428
[6.938s][info][class,unload] unloading class sbt.std.FullInstance$$$Lambda$1789/1632362915 0x00000007c081f828
[6.939s][info][class,unload] unloading class sbt.Project$RichTaskSessionVar$$Lambda$1787/818768380 0x00000007c0820c28
[6.939s][info][class,unload] unloading class sbt.Project$RichTaskSessionVar$$Lambda$1786/1707177614 0x00000007c081fc28
[6.939s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1785/1406446596 0x00000007c081e428
[6.939s][info][class,unload] unloading class sbt.InputTask$$Lambda$1779/1575782527 0x00000007c081b828
[6.939s][info][class,unload] unloading class sbt.internal.Load$$$Lambda$1776/911414257 0x00000007c081ac28
[6.939s][info][class,unload] unloading class sbt.Scoped$DefinableTask$$Lambda$1742/718898651 0x00000007c0809028
[6.939s][info][class,unload] unloading class sbt.Scoped$DefinableTask$$Lambda$1741/2141277569 0x00000007c0808c28
[6.939s][info][class,unload] unloading class sbt.librarymanagement.CrossVersionFunctions$$Lambda$1727/1915497383 0x00000007c0804c28
[6.939s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1722/1222769759 0x00000007c0803828
[6.939s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1721/839493008 0x00000007c0803428
[6.939s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1719/1185812006 0x00000007c0802c28
[6.939s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1718/2007085583 0x00000007c0802828
[6.939s][info][class,unload] unloading class sbt.internal.util.BasicAttributeMap$$Lambda$1712/388524031 0x00000007c0800c28
[6.939s][info][class,unload] unloading class sbt.std.TaskExtra$$anon$4$$Lambda$1710/584011560 0x00000007c07ff828
[6.939s][info][class,unload] unloading class sbt.std.TaskExtra$$anon$4$$Lambda$1708/688753695 0x00000007c07ff428
[6.939s][info][class,unload] unloading class sbt.std.TaskExtra$$Lambda$1707/1300772899 0x00000007c07fec28
[6.939s][info][class,unload] unloading class sbt.internal.util.EvaluateSettings$INode$$Lambda$1701/1287105801 0x00000007c07fd428
[6.939s][info][class,unload] unloading class sbt.internal.util.EvaluateSettings$INode$$Lambda$1699/1216884928 0x00000007c07fcc28
[info] Loading project definition from /Users/twer/workspace/static-cleaner-test/project
[info] Loading settings from build.sbt ...
[info] Set current project to static-cleaner-test (in build file:/Users/twer/workspace/static-cleaner-test/)
[info] sbt server started at local:///Users/twer/.sbt/1.0/server/ac09f3126a948ca68d3f/sock
sbt:static-cleaner-test> runMain CleanerMain
[warn] Multiple main classes detected.  Run 'show discoveredMainClasses' to see the list
[info] Running CleanerMain 
[success] Total time: 0 s, completed Jan 24, 2018, 3:11:41 PM
sbt:static-cleaner-test> gc
[11.835s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2778/1611088935 0x00000007c0a88428
[11.835s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2776/1333727936 0x00000007c0a87c28
[11.835s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2580/1413550615 0x00000007c0a14828
[11.835s][info][class,unload] unloading class sbt.librarymanagement.CrossVersionFunctions$$Lambda$2436/153217649 0x00000007c08eac28
Running Cleaner for local_resource
[success] Total time: 0 s, completed Jan 24, 2018, 3:11:43 PM
sbt:static-cleaner-test> gc
[success] Total time: 0 s, completed Jan 24, 2018, 3:11:44 PM
sbt:static-cleaner-test> gc
[success] Total time: 0 s, completed Jan 24, 2018, 3:11:45 PM

If I remove Resource SINGLETON.

public class CleanerMain2 {
    
    public static void main(String[] arguments) {
        Resource localResource = new Resource("local_resource");
    }
}

then JVM is able to unload the ClassLoader.

$ sbt -J-Xlog:class+unload=info
[4.504s][info][class,unload] unloading class $1dd3210e9cda6e6faf3e$ 0x00000007c0704c28
[info] Loading settings from idea.sbt ...
[info] Loading global plugins from /Users/twer/.sbt/1.0/plugins
[7.296s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2312/1094459247 0x00000007c096d428
[7.296s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2310/1320570298 0x00000007c096cc28
[7.296s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2307/2112293268 0x00000007c096c428
[7.296s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2200/640141239 0x00000007c092b828
[7.296s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2198/1336665608 0x00000007c092b028
[7.296s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2199/558583552 0x00000007c092ac28
[7.296s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2196/1021158914 0x00000007c092a828
[7.296s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2194/1215619972 0x00000007c092bc28
[7.296s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2195/327444195 0x00000007c092b428
[7.296s][info][class,unload] unloading class sbt.io.Hash$$$Lambda$2193/244510474 0x00000007c092a028
[7.296s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2192/398188748 0x00000007c0929c28
[7.296s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2186/1663387063 0x00000007c0928828
[7.296s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2191/1336927537 0x00000007c0928428
[7.296s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2190/1368378621 0x00000007c0928c28
[7.296s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2185/1268018082 0x00000007c0929428
[7.296s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2187/1280016085 0x00000007c0928028
[7.296s][info][class,unload] unloading class sbt.internal.inc.Stamper$$$Lambda$2188/2044671837 0x00000007c0929828
[7.296s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2184/1598564708 0x00000007c0927c28
[7.296s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2183/598636885 0x00000007c0927428
[7.296s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2182/1163103608 0x00000007c0927028
[7.296s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2178/1097339756 0x00000007c0926c28
[7.296s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2180/1776103111 0x00000007c0927828
[7.296s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2181/427560339 0x00000007c0926828
[7.296s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2179/1116506356 0x00000007c0926428
[7.297s][info][class,unload] unloading class scala.collection.parallel.Task$$Lambda$2176/1931959341 0x00000007c0925c28
[7.297s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2149/1632104444 0x00000007c08edc28
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2147/951655327 0x00000007c08ed428
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2145/906184298 0x00000007c08ecc28
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2143/353174638 0x00000007c08ec428
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2141/1871103024 0x00000007c08ebc28
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2137/2016625837 0x00000007c08eac28
[7.297s][info][class,unload] unloading class sbt.librarymanagement.DependencyFilterExtra$$anon$2$$Lambda$2135/440876376 0x00000007c08ea028
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2133/1964977506 0x00000007c08e9c28
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2131/1060080941 0x00000007c08e9428
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2129/243387300 0x00000007c08e8c28
[7.297s][info][class,unload] unloading class sbt.librarymanagement.RichUpdateReport$$Lambda$2127/918282376 0x00000007c08e8028
[7.297s][info][class,unload] unloading class sbt.librarymanagement.DependencyFilter$$Lambda$2125/118535508 0x00000007c08de828
[7.297s][info][class,unload] unloading class scala.collection.generic.Shrinkable$$Lambda$2008/1613248130 0x00000007c087a428
[7.297s][info][class,unload] unloading class sbt.io.IO$$$Lambda$1991/485981493 0x00000007c086cc28
[7.297s][info][class,unload] unloading class sbt.io.IO$$$Lambda$1989/238294722 0x00000007c086c428
[7.297s][info][class,unload] unloading class sbt.internal.BuildStreams$$$Lambda$1983/156325408 0x00000007c086a828
[7.297s][info][class,unload] unloading class sbt.internal.BuildStreams$$$Lambda$1982/1681481604 0x00000007c0869428
[7.297s][info][class,unload] unloading class sbt.internal.BuildStreams$$$Lambda$1979/719766168 0x00000007c0868828
[7.297s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1793/2099570980 0x00000007c0823428
[7.297s][info][class,unload] unloading class java.lang.invoke.LambdaForm$MH/1981037155 0x00000007c0822428
[7.297s][info][class,unload] unloading class java.lang.invoke.LambdaForm$DMH/84883684 0x00000007c0821c28
[7.297s][info][class,unload] unloading class java.lang.invoke.LambdaForm$DMH/1034705834 0x00000007c0821028
[7.297s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1785/1570117727 0x00000007c081fc28
[7.297s][info][class,unload] unloading class sbt.InputTask$$Lambda$1780/548480316 0x00000007c081e428
[7.297s][info][class,unload] unloading class sbt.Info$$Lambda$1768/2099487642 0x00000007c081a428
[7.297s][info][class,unload] unloading class sbt.SessionVar$$$Lambda$1766/1466250819 0x00000007c0819828
[7.297s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1763/73374128 0x00000007c0817828
[7.297s][info][class,unload] unloading class sbt.librarymanagement.CrossVersionFunctions$$Lambda$1732/1513402381 0x00000007c0807028
[7.297s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1728/90739503 0x00000007c0805828
[7.297s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1726/1351789811 0x00000007c0805028
[7.297s][info][class,unload] unloading class sbt.Defaults$$$Lambda$1724/1188000171 0x00000007c0804828
[7.297s][info][class,unload] unloading class sbt.std.TaskExtra$$$Lambda$1717/666540636 0x00000007c0802c28
[7.297s][info][class,unload] unloading class sbt.internal.util.BasicAttributeMap$$Lambda$1715/585322260 0x00000007c0802028
[7.297s][info][class,unload] unloading class sbt.internal.util.BasicAttributeMap$$Lambda$1714/965150718 0x00000007c0800c28
[7.297s][info][class,unload] unloading class sbt.std.TaskExtra$$Lambda$1712/1301296031 0x00000007c0800028
[7.297s][info][class,unload] unloading class sbt.std.TaskExtra$$Lambda$1711/778734195 0x00000007c07ffc28
[7.297s][info][class,unload] unloading class sbt.std.TaskExtra$$Lambda$1710/1045788057 0x00000007c07ff828
[7.297s][info][class,unload] unloading class sbt.std.TaskExtra$$anon$4$$Lambda$1706/2089129482 0x00000007c07ff028
[7.297s][info][class,unload] unloading class sbt.std.TaskExtra$$anon$4$$Lambda$1707/545992152 0x00000007c07fe828
[7.297s][info][class,unload] unloading class sbt.internal.util.EvaluateSettings$INode$$Lambda$1701/2137554850 0x00000007c07fd428
[7.297s][info][class,unload] unloading class sbt.internal.util.EvaluateSettings$INode$$Lambda$1699/1907331047 0x00000007c07fcc28
[info] Loading project definition from /Users/twer/workspace/static-cleaner-test/project
[info] Loading settings from build.sbt ...
[info] Set current project to static-cleaner-test (in build file:/Users/twer/workspace/static-cleaner-test/)
[info] sbt server started at local:///Users/twer/.sbt/1.0/server/ac09f3126a948ca68d3f/sock
sbt:static-cleaner-test> runMain CleanerMain2
[info] Running CleanerMain2 
[success] Total time: 1 s, completed Jan 24, 2018, 3:19:11 PM
Running Cleaner for local_resource
sbt:static-cleaner-test> gc
[12.734s][info][class,unload] unloading class Resource$CleanerHandler 0x00000007c0a92c08
[12.734s][info][class,unload] unloading class Resource 0x00000007c0a92a18
[12.734s][info][class,unload] unloading class CleanerMain2 0x00000007c0a92828
[12.734s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2770/75632023 0x00000007c0a7f828
[12.734s][info][class,unload] unloading class sbt.Classpaths$$$Lambda$2768/1059168902 0x00000007c0a7f028
[12.734s][info][class,unload] unloading class sbt.librarymanagement.CrossVersionFunctions$$Lambda$2432/1035741625 0x00000007c08e8c28
[success] Total time: 0 s, completed Jan 24, 2018, 3:19:13 PM
sbt:static-cleaner-test> gc
[success] Total time: 0 s, completed Jan 24, 2018, 3:19:15 PM

I also tried finalize. Unlike Cleaner, finalize does not prevent classes from unloading. Unfortunately it is deprecated in Java 9.

So, what is the proper approach to ensure a static native resource being cleaned up in Java 9?

  • As a library author, should I create static Cleaners?
  • As an application developer, should I create static resources?
  • As an application server developer, what should I do to unload a module?
Yang Bo
  • 3,586
  • 3
  • 22
  • 35
  • Your cleaner references code loaded by the class loader, so it’s obvious why this prevents unloading the class loader and its classes. This also perfectly demonstrates the reasons why `finalize()` has been deprecated. It “works”, in other words, there will be an execution of code of an *unloaded class loader*… – Holger Jan 24 '18 at 13:13
  • I understand why it does not unload. I just don't know how to resolve it. – Yang Bo Jan 24 '18 at 13:16
  • Use different class loaders. – Holger Jan 24 '18 at 13:17
  • Who should create different class loaders? The author of the native binding library, the author of the application, or the author of the application server? – Yang Bo Jan 24 '18 at 13:20
  • No, finalize is not executed in the unloaded class loader. Instead, finalize is invoked when the class loader is ready to unload. – Yang Bo Jan 24 '18 at 13:29
  • It is saided that `finalize` is evil because a terrible implemented `finalize` **may** resurrect a ready-to-collected object back to live. As opposite to `finalize`, some `Cleaner`s make the object **never** ready-to-collected. And those usage of `Cleaner` was even not considered as a terrible implementation to me. – Yang Bo Jan 24 '18 at 13:34
  • A `Cleaner` may not resurrect a collected object, unless, of course, a reference to it has been stored in a `static` field that is accessible to the `Cleaner`, like in your example. That’s exactly why this scenario is not supported, otherwise, it wouldn’t be better than `finalize()`. Since ordinary code does not assume that objects referenced by `static` fields can get garbage collected, it’s the responsibility of whoever creates class loaders with the intend to make that work, to create them in a way that it works, like having `Resource` and its cleaner in one loader and the field in another. – Holger Jan 24 '18 at 14:29
  • Alice created a class of random number generator by reading `/dev/random`, whose file handle should be `close` by a static `Cleaner` in Alice's library. Bob wants to create a WAR of some `Servlet`s that shares an static instance of Alice's random number generator. Were you suggesting that Bob should create Alice's class in another `ClassLoader`, instead of simply use Alice's library? – Yang Bo Jan 24 '18 at 14:44
  • No, I’m suggesting that 1) being closed by a `Cleaner` should not be the primary resource cleanup method, 2) the resource should not be shared by a `static` field. Both has been discouraged for more than twenty years now. External resources should be closed by an explicit action rather than hoping that the garbage collector will free them eventually. If you do not follow the recommended practice, then, well, yes, you have to put both into different class loaders. Which isn’t a tough job with an application environment that already supports dependency management through different class loaders. – Holger Jan 24 '18 at 15:23
  • Fair enough! Thank you @Holger ! – Yang Bo Jan 24 '18 at 15:35

0 Answers0