Files.move / file.rename要经过多次迭代(Windows 10)都失败,肯定是Java错误

时间:2018-09-21 08:29:50

标签: java windows-10

(编辑:问题-另请参见“如何提供帮助”-将是其他人是否可以重现该错误,如果已知(链接),则建议采用什么解决方法。)

目标:创建一个小型临时文件,然后删除带有最终文件名的文件,然后将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了,但这简直是白痴。我也没有得到有关该过程的信息,没有帐户,并且本人已经投入了很多时间,必须重新编码。请帮助,以便我们消除此错误。

到目前为止转载:

  • Luis G.在评论中说,他使用Javac版本“ 1.8.0_121”,java版本“ 1.8.0_171”的最小测试类的可执行文件在Windows 10 Home(内部版本1803)上确认了该问题。 li>

测试环境:标准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;
    }

}

0 个答案:

没有答案