首页 > 解决方案 > writeObject(this) 停止程序

问题描述

我正在学习垃圾收集和 Java 对象序列化。在我包含序列化代码之前的代码中,有 3 个对象正在完成。现在只有一个 finalize() 被调用,并且在创建FileOutputStream对象后我的程序正在停止

我已经在 Project 类中实现了 java.io.Serializable ,以前当 finalize() 只有一个 println 时,它正在完美执行,3 个对象正在完成,但现在它在该行之后停止

FileOutputStream fout = new FileOutputStream(projectName + ".bin");

类项目:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Project implements Serializable {

    private static final long serialVersionUID = 4721106266710903835L;
    int projectID;
    String projectName;

    public Project() {
        super();
    }

    public Project(int projectID, String projectName) {
        super();
        this.projectID = projectID;
        this.projectName = projectName;
        System.out.println("Object created : " + this.projectID);
    }

    @Override
    protected void finalize() {
        try {
            super.finalize();
        } catch (Throwable e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        System.out.println(this.projectID + " Deallocated");

        try {

            System.out.println("Test 0");
            FileOutputStream fout = new FileOutputStream(projectName + ".bin");
            System.out.println("Test 1");
            ObjectOutputStream oout = new ObjectOutputStream(fout);
            System.out.println("Test 2");

            oout.writeObject(this);
            oout.flush();
            System.out.println("Done");

            fout.close();
            oout.close();
        }
        catch(IOException e) {
            e.printStackTrace();
            System.out.println("Exception");
        }

    }

}

类项目控制:

public class ProjectControl {

    public static void main(String[] args) {
        Project p = new Project(100, "ABC");
        System.out.println(p.projectID);
        System.out.println(p.projectName);

        p = null;

        Project p1 = new Project(200, "DEF");
        Project p2 = p1;

        p1 = null;
        System.out.println(p2.projectID);

        p2 = new Project(300, "GHI");
        System.out.println(new Project(400, "GHI").projectID);
        System.gc();
    }
}

最初finalize()每次打印运行 3 次

<<projectID>> deallocated

现在编程在一次后停止。在遇到该行之前,只有测试 0 被打印一次

FileOutputStream fout = new FileOutputStream(projectName + ".bin");

标签: javafinalizer

解决方案


很难说发生了什么,但我的猜测是当你的对象的finalize方法被调用时 JVM 正在退出。(该finalize()调用将在守护进程终结器线程上运行。当 JVM 检测到所有非守护线程都已终止时,它会启动有序关闭过程。)

为了使您的方案可靠地工作,需要 100% 防弹保证您的finalize方法将在 JVM 退出之前运行完成。JVM 不做这样的保证。相反,javadoc forfinalize()说:

[T]这里不保证最终确定的时间。finalize 方法可能仅在无限延迟之后才在可终结对象上调用,如果有的话。

JLS 12.6说:

Java 编程语言没有指定终止器将在多长时间内被调用,只是说它会在对象的存储被重用之前发生。

如果您依靠最终确定来保存对象的状态,那么您将处于非常不稳定的状态。


曾经有一种叫做的方法java.lang.System.runFinalizersOnExit可能有帮助……理论上。但是它在 Java 1.2 中已被弃用,并在 Java 11 中被删除。根据 javadoc:

这种方法本质上是不安全的。这可能会导致在其他线程同时操作这些对象时对活动对象调用终结器,从而导致行为不稳定或死锁。

因此,如果我们从表面上看这个警告,该方法不会给你 100% 可靠的持久性,甚至可能导致更严重的问题。


finalize方法和机制在 Java 9 中被标记为已弃用,并且可能在未来的 Java 版本中被删除。当这种情况发生时,对于任何依赖于最终确定的应用程序来说,这将是“路的尽头”。你应该把它当作一个重要的暗示,你应该重新考虑你的策略。


如果尽管我上面说了这么多,你仍然想要一种使用终结器“让它工作”的方法,我认为没有一种可靠的方法。


推荐阅读