导致崩溃转储的Java错误的解决方法

Yrl*_*lec 8 java windows multithreading nio crash-dumps

我开发的一个程序偶尔会因为这个错误而崩溃JVM:http://bugs.java.com/bugdatabase/view_bug.do?video_id = 8029516.遗憾的是,Oracle尚未解决该漏洞,并且错误报告称没有已知的解决方法.

我试图通过在KeyWatcher线程中调用.register(sWatchService,eventKinds)来修改bug报告中的示例代码,方法是将所有挂起的注册请求添加到我在KeyWatcher线程中循环但仍在崩溃的列表中.我猜这与sWatchService上的同步效果相同(就像尝试过的bug报告的提交者一样).

你能想出办法解决这个问题吗?

apa*_*gin 3

我已经设法创建了一个解决方法,尽管它有点难看。

该错误存在于 JDK 方法中WindowsWatchKey.invalidate(),该方法释放本机缓冲区,而后续调用仍可能访问它。这一单行代码通过将缓冲区清理延迟到 GC 来解决这一问题。

这是JDK 的已编译补丁。为了应用它,添加以下 Java 命令行标志:
-Xbootclasspath/p:jdk-8029516-patch.jar

如果在您的情况下无法选择修补 JDK,那么在应用程序级别上仍然有解决方法。它依赖于 Windows WatchService 内部实现的知识。

public class JDK_8029516 {
    private static final Field bufferField = getField("sun.nio.fs.WindowsWatchService$WindowsWatchKey", "buffer");
    private static final Field cleanerField = getField("sun.nio.fs.NativeBuffer", "cleaner");
    private static final Cleaner dummyCleaner = Cleaner.create(Thread.class, new Thread());

    private static Field getField(String className, String fieldName) {
        try {
            Field f = Class.forName(className).getDeclaredField(fieldName);
            f.setAccessible(true);
            return f;
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public static void patch(WatchKey key) {
        try {
            cleanerField.set(bufferField.get(key), dummyCleaner);
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注册密钥后立即调用JDK_8029516.patch(watchKey),可以防止watchKey.cancel()过早释放本机缓冲区。