为什么在这个示例中Java Cleaner不起作用?

huangapple go评论63阅读模式
英文:

Why Java Cleaner is not working in this example?

问题

以下是您要翻译的代码部分:

I'm using Java 11. I have a class that uses the deprecated finalize method, and I want to start using 'Cleaner' instead.

This is the class I use:

    public class TempFile2 extends File
    {
        private static final Cleaner cleaner = Cleaner.create();
    
        public TempFile2(String pathname)
        {
            super(pathname);
            cleaner.register(this, cleanAction());
        }
    
        private Runnable cleanAction() {
            return () -> {
                System.out.println("inside cleanAction");
                if (this.exists())
                {
                    System.println("deleting " + this.getName());
                    this.delete();
                }
            };
        }
    }

And using it like so:

    public static void main(String[] args) throws IOException, InterruptedException {
        String filePath = "<some_path>";
        TempFile2 tempFile2 = new TempFile2(filePath);
        tempFile2.createNewFile();
        tempFile2 = null;
        System.gc();
        Thread.sleep(10);
    }

Why is the cleanAction() method inside TempFile2 not called? (by the way, I know that System.gc() doesn't guarantee GC to run, but I know that in my example it runs since I'm also monitoring with VisualVM and trigger GC from there, and verify it runs).   

When I run the same except TempFile2 is using a finalize() method then the finalize() is called.
英文:

I'm using Java 11. I have a class that uses the deprecated finalize method, and I want to start using 'Cleaner' instead.

This is the class I use:

public class TempFile2 extends File
{
    private static final Cleaner cleaner = Cleaner.create();

    public TempFile2(String pathname)
    {
        super(pathname);
        cleaner.register(this, cleanAction());
    }

    private Runnable cleanAction() {
        return () -&gt; {
            System.out.println(&quot;inside cleanAction&quot;);
            if (this.exists())
            {
                System.out.println(&quot;deleting &quot; + this.getName());
                this.delete();
            }
        };
    }
}

And using it like so:

public static void main(String[] args) throws IOException, InterruptedException {
    String filePath = &quot;&lt;some_path&gt;&quot;;
    TempFile2 tempFile2 = new TempFile2(filePath);
    tempFile2.createNewFile();
    tempFile2 = null;
    System.gc();
    Thread.sleep(10);
}

Why is the cleanAction() method inside TempFile2 not called? (by the way, I know that System.gc() doesn't guarantee GC to run, but I know that in my example it runs since I'm also monitoring with VisualVM and trigger GC from there, and verify it runs).

When I run the same except TempFile2 is using a finalize() method then the finalize() is called.

答案1

得分: 4

你正好做了Cleaner的javadoc告诉你不要做的事情:

请注意,清理操作不得引用已注册的对象。如果引用了,对象将无法变为虚引用,并且清理操作将不会自动调用。

不建议使用诸如Lambda表达式或(匿名)内部类等方式,因为这将保留一个强引用。这正是在这里发生的,因为你的Lambda表达式引用了this,它是TempFile2的实例。

你不能直接编写等效于旧终结器的代码。可以考虑在构造函数中调用File#deleteOnExit()(这对于长时间运行的程序可能会有太长的延迟),或者在清理操作中注册路径作为字符串或Path,然后使用该路径执行清理操作。

例如,像这样:

public class TempFile2 extends File {

    private static final Cleaner cleaner = Cleaner.create();

    public TempFile2(String pathname) {
        super(pathname);
        cleaner.register(this, new CleanupFileAction(this.toPath()));
    }

    private static class CleanupFileAction implements Runnable {

        private final Path filePath;

        private CleanupFileAction(Path filePath) {
            this.filePath = filePath;
        }

        @Override
        public void run() {
            try {
                Files.deleteIfExists(filePath);
            } catch (IOException ignored) {
            }
        }
    }
}

请注意,如果File.toPath创建的Path对象以某种方式保留对原始TempFile2实例的引用,这也不起作用,但幸运的是Path和(File#toPath())不是这样实现的。

顺便提一下,关于Cleaner实例的一般建议是,除非你认为你的代码将触发太多的清理操作而单个线程无法跟上,否则只为你的应用程序或库创建一个,而不是为每个类创建一个。

英文:

You're doing exactly what the javadoc of Cleaner tells you not to do:

> Note that the cleaning action must not refer to the object being
> registered. If so, the object will not become phantom reachable and
> the cleaning action will not be invoked automatically.

It is not recommended to use things like lambdas or (anonymous) inner classes, because that will (or in the case of lambdas, could) retain a strong reference. That is exactly what happens here because your lambda references this, which is the instance of TempFile2.

You cannot write a direct equivalent of your old finalizer. Either consider calling File#deleteOnExit() in the constructor (which might be too long a delay for long running programs), or register the path as a string or Path in your cleanup action, and do the cleanup action with that.

For example, something like this:

public class TempFile2 extends File {

    private static final Cleaner cleaner = Cleaner.create();

    public TempFile2(String pathname) {
        super(pathname);
        cleaner.register(this, new CleanupFileAction(this.toPath()));
    }

    private static class CleanupFileAction implements Runnable {

        private final Path filePath;

        private CleanupFileAction(Path filePath) {
            this.filePath = filePath;
        }

        @Override
        public void run() {
            try {
                Files.deleteIfExists(filePath);
            } catch (IOException ignored) {
            }
        }
    }
}

Note that if the Path object created by File.toPath would somehow retain a reference to the original TempFile2 instance, this wouldn't work either, but luckily that is not how Path and (File#toPath()) is implemented.

As an aside, the general advice when it comes to Cleaner instances, is to create only one for your application or library, and not one for each class, unless you think so many cleanup actions will get triggered by your code that a single thread is not sufficient to keep up.

huangapple
  • 本文由 发表于 2023年2月24日 00:12:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75547428.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定