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等)。我认为这是一个错误。
恕我直言,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”文件。如果锁定文件在低于更新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我得到相同的结果。
最重要的是,您必须确保关闭FileHandlers。如果从未对其进行过垃圾收集或从记录器树中删除,则LogManager将关闭您的FileHandler。否则,您必须关闭它。解决此问题之后,请清除所有锁定文件,然后再运行新的修补代码。然后请注意,如果JVM进程崩溃或被杀死,锁定文件将不会被删除。如果关闭时遇到I
/ O错误,则不会删除锁定文件。当下一个进程开始时,FileHandler将旋转。
如您所指出的,如果上述情况超过100次运行,就有可能用完JDK8上的所有锁定文件。一个简单的测试是两次运行以下代码,而不删除log和lck文件:
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("java.runtime.version"));
ReferenceQueue<FileHandler> q = new ReferenceQueue<>();
for (int i=0; i<100; i++) {
WeakReference<FileHandler> 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,则上述测试用例将无法正常工作。可以在OpenJDK站点上的RFR下追踪此问题:8048020-java.util.logging.FileHandler和FileHandler webrev
上的回归。