Java 8中的java.util.logging.FileHandler是否已损坏?

首先,一个简单的测试代码:

package javaapplication23; import java.io.IOException; import java.util.logging.FileHandler; public class JavaApplication23 { public static void main(String[] args) throws IOException { new FileHandler("./test_%u_%g.log", 10000, 100, true); } } 

此测试代码仅使用Java 7创建一个文件“test_0_0.log”,无论我多久运行一次该程序。 这是预期的行为,因为构造函数中的append参数设置为true。

但是如果我在Java 8中运行此示例,则每次运行都会创建一个新文件(test_0_0.log,test_0_1.log,test_0_2.log,…)。 我认为这是一个错误。

Imho,Java的相关变化是这样的:

 @@ -413,18 +428,18 @@ // object. Try again. continue; } - FileChannel fc; + try { - lockStream = new FileOutputStream(lockFileName); - fc = lockStream.getChannel(); - } catch (IOException ix) { - // We got an IOException while trying to open the file. - // Try the next file. + lockFileChannel = FileChannel.open(Paths.get(lockFileName), + CREATE_NEW, WRITE); + } catch (FileAlreadyExistsException ix) { + // try the next lock file name in the sequence continue; } + boolean available; try { - available = fc.tryLock() != null; + available = lockFileChannel.tryLock() != null; // We got the lock OK. } catch (IOException ix) { // We got an IOException while trying to get the lock. @@ -440,7 +455,7 @@ } // We failed to get the lock. Try next file. - fc.close(); + lockFileChannel.close(); } } 

(完整: OpenJDK变更集6123:ac22a52a732c )

我知道通常FileHandler会被Logmanager关闭,但如果系统或应用程序崩溃或进程被终止,情况就不是这样了。 这就是我在上面的示例代码中没有“close”语句的原因。

现在我有两个问题:

1)你有什么看法? 这是一个错误吗? (几乎在以下评论和答案中回答)

2)您是否知道在Java 8中获取旧Java 7行为的解决方法? (更重要的问题……)

谢谢你的回答。

关闭FileHandler会删除’lck’文件。 如果在低于update 40(java.util.logging)的JDK8版本下存在锁文件,则FileHandler将轮换。 从OpenJDK讨论中,如果当前进程无法锁定lck文件,则决定始终旋转。 给出的原因是当锁文件存在时旋转总是更安全。 因此,如果您使用混合JDK版本的旋转模式,这会变得非常讨厌,因为JDK7版本将重用锁,但JDK8版本将保留并旋转。 这与您的测试用例有关。

如果我从工作目录中清除所有日志和lck文件然后运行,则使用JDK8:

 public static void main(String[] args) throws IOException { System.out.println(System.getProperty("java.runtime.version")); new FileHandler("./test_%u.log", 10000, 100, true).close(); } 

我总是看到一个名为’test_0.log.0’的文件。 我使用JDK7获得相同的结果。

最重要的是,您必须确保关闭FileHandler。 如果从未从记录器树中收集或删除它,则LogManager将关闭您的FileHandler。 否则你必须关闭它。 修复此问题后,在运行新的修补代码之前清除所有锁定文件。 然后请注意,如果JVM进程崩溃或被杀死,则不会删除锁定文件。 如果关闭时出现I / O错误,则不会删除锁定文件。 当下一个进程启动时,FileHandler将旋转。

正如您所指出的,如果上述条件超过100次运行,则可以使用JDK8上的所有锁定文件。 对此的简单测试是在不删除日志和lck文件的情况下运行以下代码两次:

 public static void main(String[] args) throws Exception { System.out.println(System.getProperty("java.runtime.version")); ReferenceQueue q = new ReferenceQueue<>(); for (int i=0; i<100; i++) { WeakReference h = new WeakReference<>( new FileHandler("./test_%u.log", 10000, 2, true), q); while (q.poll() != h) { System.runFinalization(); System.gc(); System.runFinalization(); Thread.yield(); } } } 

但是,如果正确修复了JDK-6774110,则上述测试用例将不起作用。 可以在RFR上的OpenJDK站点上跟踪此问题:8048020 – 对java.util.logging.FileHandler和FileHandler webrev的 回归 。