2014-05-20 46 views
1

这个问题触发一用户可见的例外是Exception in finalize method和类似的问题相反。在Finalize方法

我创建一个AutoCloseable类构成了严重的风险,如果没有正确关闭。在这种情况下,我正在寻求失败 - 以便用户不会意外地忘记这么做。

我意识到并同意,一般最佳做法是让Closeable优雅地失败并尽其所能缓解来电者的错误,但在这种情况下,来电者不会想错过此操作。如果你在概念上不同意这个想法,我会很感激你的反馈,但在这种情况下,考虑一个关于Java内部的学术练习的问题。

我想如果我调用IllegalStateException并打断用户,如果我的类的finalize()方法被调用,并且实例还没有被清理。但finalize()显式吞下未捕获的异常,这使得这个棘手。导致用户从finalize()方法中看到的RuntimeException的最佳方法是什么?

这里有一个演示类的东西,我这么远:

public class SeriouslyCloseable implements AutoCloseable { 
    // We construct an Exception when the class is initialized, so that the stack 
    // trace informs where the class was created, rather than where it is finalized 
    private final IllegalStateException leftUnclosed = new IllegalStateException(
     "SEVERE: "+getClass().getName()+" was not properly closed after use"); 
    private boolean safelyClosed = false; 

    @Override 
    public void close() { 
    // do work 
    safelyClosed = true; 
    } 

    @Override 
    protected void finalize() throws IllegalStateException { 
    if(!safelyClosed) { 
     // This is suppressed by the GC 
     throw leftUnclosed; 
    } 
    } 
} 

注:我知道也是finalize()不能保证运行,所以任何事情我身边这个方法实施并非绝对不会发生。我仍然喜欢,如果GC给我们机会,可能会发生。

回答

1

你不能强迫的这种方法是通过任意的,实现相关Thread执行,目前还不清楚其中Thread异常应提高从finalize方法抛出异常。

即使您已经知道了哪个线程是针对的,为什么Thread.stop(Throwable)已被弃用(并且自Java 8以来不支持):导致线程在任意代码位置抛出任意throwable可能会导致很多的伤害。例如。错过了线程即将进入的另一个close()操作。此外,在调用finalize方法时,导致错误的线程可能不再活动。


在它不是投掷结束,但报告要实现一个例外。你可以模仿的原始的,未抑制的行为是这样的:

@Override 
protected void finalize() throws Throwable { 
    if(!safelyClosed) { 
     final Thread t = Thread.currentThread(); 
     t.getUncaughtExceptionHandler().uncaughtException(t, leftUnclosed); 
    } 
} 

默认情况下,它会打印出异常堆栈跟踪到控制台。手动呼叫printStackTrace的优势在于它与可能安装的应用程序特定异常处理程序一起工作:

import java.util.logging.Level; 
import java.util.logging.Logger; 

public class ThrowableInFinalize { 
    public static void main(String[] args) throws InterruptedException { 
    Thread.setDefaultUncaughtExceptionHandler(
              new Thread.UncaughtExceptionHandler() { 
     public void uncaughtException(Thread t, Throwable e) { 
     Logger.getLogger("ThrowableInFinalize") 
       .log(Level.SEVERE, "uncaught exception", e); 
     } 
    }); 
    new ThrowableInFinalize(); 
    System.gc(); 
    Thread.sleep(1000); 
    } 

    private final IllegalStateException leftUnclosed = new IllegalStateException(
     "SEVERE: "+getClass().getName()+" was not properly closed after use"); 
    private boolean safelyClosed; 
    @Override 
    protected void finalize() throws Throwable { 
    if(!safelyClosed) { 
     final Thread t = Thread.currentThread(); 
     t.getUncaughtExceptionHandler().uncaughtException(t, leftUnclosed); 
    } 
    } 
} 
1

一种选择是简单地终止JVM彻底:

@Override 
protected void finalize() throws IllegalStateException { 
    if(!safelyClosed) { 
    leftUnclosed.printStackTrace(System.err); 
    System.exit(255); 
    } 
} 

下相当一致地复制所需的行为,包括显示没有结束的Closeable创建其中的跟踪:

private static void resourceLeak() { 
    SeriouslyCloseable sc = new SeriouslyCloseable(); 
    //sc.close(); 
} 

public static void main(String[] args) throws InterruptedException { 
    resourceLeak(); 
    System.gc(); 
    Thread.sleep(1000); 
    System.out.println("Exiting Normally"); 
} 
java.lang.IllegalStateException: SEVERE: SeriouslyCloseable was not properly closed after use 
     at SeriouslyCloseable.<init>(SeriouslyCloseable.java:5) 
     at SeriouslyCloseable.method(SeriouslyCloseable.java:23) 
     at SeriouslyCloseable.main(SeriouslyCloseable.java:28) 
0

不要在敲定时抛出异常。

那么如何才能沟通那个严重的编程错误呢?你可以登录它。但是,只有当有人阅读该日志时才有用。

你可以翻转一些标志,这将使整个应用程序无法使用(或至少你的库) - 存储在静态字段例外(最初为空),并在一些操作扔它,如果它被设置。为了在JVM关闭的情况下生存下来,您可以将它写入文件(但有时您不能),并在启动时加载它并恢复投掷(直到此文件被删除并重新启动应用程序)。

你可以关闭JVM(如dimo414我之前提出的建议),但相同的应用程序服务器上的其他应用程序也不会感谢你,这将防止关闭其他资源。

你可以发送一些信息在其他地方(例如,通过HTTP或JMS),但这需要在其他地方是倾听和记录相比少忽略。

而且你可以实现多种选择如何处理它,并允许用户选择。