(编辑:问题-另请参见“如何提供帮助”-将是其他人是否可以重现该错误,如果已知(链接),则建议采用什么解决方法。)
目标:创建一个小型临时文件,然后删除带有最终文件名的文件,然后将temp重命名为final。 (标准的安全保存方法。您确实会使用它,正确。)
问题:多次测试循环迭代后失败。 Files.move()
抛出FileAlreadyExistsException
,使用file.renameTo()
进行测试有时会返回false。该文件的确没有被重命名。我的测试使我感到失败的频率与方法无关。我明确创建了一个测试类-参见下文。问题绝对不是我的代码。
在非常罕见的情况下,Files.move()会抛出FileSystemException
,说“该进程无法访问该文件,因为该文件正在被另一个进程使用。”,就像该文件一样。尚未在写入后关闭,但使用了Files.write,因此情况“不能”。在使用第三方库时,有时会遇到类似的问题,并使用System.gc()
来可靠地解决它们,这就是为什么我也将此功能添加到测试类中,但是 this 在所有测试期间,异常仅发生一次。那是没有System.gc的,所以我不知道gc是否有帮助。
为什么是Java错误: FileAlreadyExistsException
不适用,因为destination.exists()在调用之前以及引发异常之后立即返回false。即使原因不在Java之外,当Java也声称文件不存在时,断言文件不存在是一个Java错误。
发生不时:
如果每次迭代中重命名目标都不同。
,如果文件只是来回无休无止地重命名。
如果出现问题,请等待1 ms,然后重试。但是在极少数情况下,此重试循环的计数器达到24。
要重现::循环地执行[创建文件X,将其重命名为Y,然后将其删除。]
猜想::Java的重命名因缓存(至少在此平台上)而混乱,并且Java的file.exists()可能使用了不同的方法。
您将如何提供帮助: :请尝试在以下所述的平台和其他平台上重现该错误并进行报告。另外,请通知Java开发者社区。我已经找到https://bugs.openjdk.java.net/browse/JDK-8150700了,但这简直是白痴。我也没有得到有关该过程的信息,没有帐户,并且本人已经投入了很多时间,必须重新编码。请帮助,以便我们消除此错误。
到目前为止转载:
测试环境:标准Oracle Java版本下载JDK8144。Java-版本:
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
Windows 10 Pro版本1803(操作系统内部版本17134.228)
英特尔®酷睿TM i3-2120 CPU @ 3.30 GHz
测试类结果没有问题(因为目标名称每次都不同):
Doing test withOUT forced Garbage Collection.
Completed create-rename-delete attempt 0x0
Completed create-rename-delete attempt 0x100
...
Completed create-rename-delete attempt 0xfe00
Completed create-rename-delete attempt 0xff00
No fatal problems.
具有所描述问题的测试类结果,可靠地使用0x10000测试:
Doing test withOUT forced Garbage Collection.
Completed create-rename-delete attempt 0x0
Completed create-rename-delete attempt 0x100
...
Completed create-rename-delete attempt 0x3900
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0x3a00
...
Completed create-rename-delete attempt 0x4700
Files.move() attempt 1 of 4096 failed: java.nio.file.FileSystemException: renameBugDemoFile.dat -> renameBugDemoFile.ren: The process cannot access the file because it is being used by another process.
Completed create-rename-delete attempt 0x4800
...
Completed create-rename-delete attempt 0x7700
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 2 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0x7800
...
Completed create-rename-delete attempt 0x9900
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 2 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0x9a00
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 2 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0x9b00
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 2 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0x9c00
...
Completed create-rename-delete attempt 0xa300
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 2 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 3 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 4 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 5 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 6 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 7 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 8 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 9 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 10 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 11 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 12 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 13 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 14 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 15 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 16 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 17 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 18 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 19 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 20 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Files.move() attempt 21 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0xa400
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0xa500
Completed create-rename-delete attempt 0xa600
Completed create-rename-delete attempt 0xa700
Completed create-rename-delete attempt 0xa800
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0xa900
Completed create-rename-delete attempt 0xaa00
Completed create-rename-delete attempt 0xab00
Completed create-rename-delete attempt 0xac00
Files.move() attempt 1 of 4096 failed: java.nio.file.FileAlreadyExistsException: renameBugDemoFile.dat -> renameBugDemoFile.ren
Completed create-rename-delete attempt 0xad00
...
Completed create-rename-delete attempt 0xfe00
Completed create-rename-delete attempt 0xff00
No fatal problems.
如果您不必检查大量代码,则在计算机上进行测试的可能性会更高,所以这是一个最低版本:
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
final public class FileRenameProblemDemoMinimal {
public static void main(final String[] args) {
final File demofile = new File("renameBugDemoFile.dat");
final File demofileAfterRenaming = new File("renameBugDemoFile.ren");
demofile.delete();
demofileAfterRenaming.delete();
for (int i = 0; i < 0x10000; i++) {
createDemoFile(demofile);
// System.gc();
if (!renameUsingFilesMoveAndBruteForce(demofile, demofileAfterRenaming)) {
System.err.println("q.e.d.: FAILURE! (Attempt was 0x" + Integer.toHexString(i) + ")");
System.exit(-1);
} else {
if (!demofileAfterRenaming.delete()) {
throw new Error();
} else {
if (demofileAfterRenaming.exists()) {
throw new Error();
}
}
if (i % 0x100 == 0) {
System.err.println("Completed create-rename-delete attempt 0x" + Integer.toHexString(i));
}
}
}
System.err.println("No fatal problems.");
System.exit(0);
}
private static boolean renameUsingFilesMoveAndBruteForce(final File source, final File destination) {
if (destination.exists()) {
throw new Error("destination already exists: " + destination);
}
final int amountOfRetriesWith1MsPause = 0x1000;
int i = 0;
while (i <= amountOfRetriesWith1MsPause) {
i++;
try {
Files.move(source.toPath(), destination.toPath()); // , StandardCopyOption.REPLACE_EXISTING); // WOULD NOT MAKE A DIFFERENCE!
return true;
} catch (IOException e) {
if (destination.exists()) {
throw new Error();
}
System.err.println("Files.move() attempt " + i + " of " + amountOfRetriesWith1MsPause + " failed: " + e.toString());
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return false;
}
private static void createDemoFile(final File file) {
final byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
try {
Files.write(file.toPath(), bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试类具有适用于所有情况的代码。您必须通过(取消)注释进行选择。照原样,该代码将演示正在发生的问题,但不会失败,因为它仅重试重命名。 System.gc的使用(这会降低测试速度)目前处于关闭状态。
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
final public class FileRenameProblemDemo {
final private static File DEMOFILE = new File("renameBugDemoFile.dat");
final private static File DEMOFILE_AFTER_RENAMING = new File("renameBugDemoFile.ren");
final private static int AMOUNT_OF_MAIN_ITERATIONS = 0x10000;
final private static int AMOUNT_OF_RETRIES_WITH_1MS_PAUSE = 0x1000;
public static void main(final String[] args) {
deleteFileOptionallyButWithExtremeParanoia(DEMOFILE);
deleteFileOptionallyButWithExtremeParanoia(DEMOFILE_AFTER_RENAMING);
// runTestOnlyWithRenames(); // <-- No problems at all.
runTestWithCreateAndRenames(false); // <-- Reliable problems. Sometimes it only takes a few iterations, often a few thousand.
System.err.println("No fatal problems.");
System.exit(0);
}
private static File createDifferentFileName(final int i) {
return DEMOFILE_AFTER_RENAMING;
// return new File(String.valueOf(i) + "_" + DEMOFILE_AFTER_RENAMING.getName());
}
private static void runTestOnlyWithRenames() {
createDemoFile(DEMOFILE);
for (int i = 0; i < AMOUNT_OF_MAIN_ITERATIONS; i++) {
// if (!renameUsingFilesMove(DEMOFILE, DEMOFILE_AFTER_RENAMING)) {
if (!renameUsingRename(DEMOFILE, DEMOFILE_AFTER_RENAMING)) {
// if (!renameUsingFilesMoveAndBruteForce(DEMOFILE, DEMOFILE_AFTER_RENAMING)) {
// if (!renameUsingRenameAndBruteForce(DEMOFILE, DEMOFILE_AFTER_RENAMING)) {
System.err.println("q.e.d.: FAILURE! (Attempt was 0x" + Integer.toHexString(i) + ")");
System.exit(-1);
} else {
// if (!renameUsingFilesMove(DEMOFILE_AFTER_RENAMING, DEMOFILE)) {
// if (!renameUsingRename(DEMOFILE_AFTER_RENAMING, DEMOFILE)) {
if (!renameUsingFilesMoveAndBruteForce(DEMOFILE_AFTER_RENAMING, DEMOFILE)) {
// if (!renameUsingRenameAndBruteForce(DEMOFILE_AFTER_RENAMING, DEMOFILE)) {
throw new Error(); // We're confident that this approach never fails and want to keep the code short.
}
if (i % 0x100 == 0) {
// System.err.println("Completed back-and-forth renaming attempt " + Integer.toHexString(i));
System.err.println("Completed rename-back&forth attempt 0x" + Integer.toHexString(i));
}
}
}
}
private static void runTestWithCreateAndRenames(final boolean forceGarbageCollection) {
if (forceGarbageCollection) {
System.err.println("Doing test WITH forced Garbage Collection.\n");
} else {
System.err.println("Doing test withOUT forced Garbage Collection.\n");
}
for (int i = 0; i < AMOUNT_OF_MAIN_ITERATIONS; i++) {
createDemoFile(DEMOFILE);
if (forceGarbageCollection) {
System.gc(); // Because in a different project, I had experienced consistent move-problems (Consistent = different than here.) which were consistently solved by running GC after closing after read access.
}
final File demoFileAfterRenaming = createDifferentFileName(i);
// if (!renameUsingFilesMove(DEMOFILE, demoFileAfterRenaming)) {
// if (!renameUsingRename(DEMOFILE, demoFileAfterRenaming)) {
if (!renameUsingFilesMoveAndBruteForce(DEMOFILE, demoFileAfterRenaming)) {
// if (!renameUsingRenameAndBruteForce(DEMOFILE, demoFileAfterRenaming)) {
System.err.println("q.e.d.: FAILURE! (Attempt was 0x" + Integer.toHexString(i) + ")");
System.exit(-1);
} else {
if (!demoFileAfterRenaming.delete()) {
throw new Error();
} else {
// Deletion supposedly successful.
if (demoFileAfterRenaming.exists()) {
throw new Error();
}
}
if (i % 0x100 == 0) {
System.err.println("Completed create-rename-delete attempt 0x" + Integer.toHexString(i));
}
}
}
}
private static boolean renameUsingFilesMove(final File source, final File destination) {
if (destination.exists()) {
throw new Error();
}
try {
Files.move(source.toPath(), destination.toPath());
// Files.move(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); // DOESN'T MAKE A DIFFERENCE!
} catch (IOException e) {
if (destination.exists()) {
throw new Error();
}
System.err.println("Files.move() attempt one of one failed: " + toStringIntlForThrowables(e));
return false;
}
return true;
}
private static boolean renameUsingRename(final File source, final File destination) {
if (destination.exists()) {
throw new Error();
}
if (source.renameTo(destination)) {
if (source.exists()) {
throw new Error();
}
if (!destination.exists()) {
throw new Error();
}
return true;
} else {
if (!source.exists()) {
throw new Error();
}
if (destination.exists()) {
throw new Error();
}
System.err.println("renameTo() attempt one of one failed.");
return false;
}
}
private static boolean renameUsingFilesMoveAndBruteForce(final File source, final File destination) {
if (destination.exists()) {
throw new Error("destination already exists: " + destination);
}
int i = 0;
while (i <= AMOUNT_OF_RETRIES_WITH_1MS_PAUSE) {
i++;
try {
Files.move(source.toPath(), destination.toPath());
return true;
// Files.move(source.toPath(), destination.toPath(), StandardCopyOption.REPLACE_EXISTING); // DOESN'T MAKE A DIFFERENCE!
} catch (IOException e) {
if (destination.exists()) {
throw new Error();
}
System.err.println("Files.move() attempt " + i + " of " + AMOUNT_OF_RETRIES_WITH_1MS_PAUSE + " failed: " + toStringIntlForThrowables(e));
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return false;
}
private static boolean renameUsingRenameAndBruteForce(final File source, final File destination) {
if (destination.exists()) {
throw new Error();
}
int i = 0;
while (i <= AMOUNT_OF_RETRIES_WITH_1MS_PAUSE) {
i++;
if (source.renameTo(destination)) {
if (source.exists()) {
throw new Error();
}
if (!destination.exists()) {
throw new Error();
}
return true;
} else {
if (!source.exists()) {
throw new Error();
}
if (destination.exists()) {
throw new Error();
}
System.err.println("renameTo() attempt " + i + " of " + AMOUNT_OF_RETRIES_WITH_1MS_PAUSE + " failed.");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return false;
}
private static void deleteFileOptionallyButWithExtremeParanoia(final File file) {
if (file.exists()) {
if (file.isFile()) {
if (!file.delete()) {
throw new Error();
}
if (file.exists()) {
throw new Error();
}
} else {
throw new Error();
}
}
}
private static void createDemoFile(final File file) {
final byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
try {
Files.write(file.toPath(), bytes);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param t the exception (Null would cause the return of null.)
* @return the same as exception.toString except with getMessage() instead of getLocalizedMessage(), because a log
* file with e.g. Danish error text (CUSTOMER SYSTEM SETTINGS.) doesn't help the developer.
*/
public static String toStringIntlForThrowables(final Throwable t) {
if (t == null) {
return null;
}
final String className = t.getClass().getName();
final String message = t.getMessage();
return (message != null) ? (className + ": " + message) : className;
}
}