在带有Java 8.191的Windows 10上,File.exists()有时会出错

Aar*_*lla 19 java-8 windows-10

我有一堆单元测试,包含如下代码:

File file = new File("src/main/java/com/pany/Foo.java");
assertTrue("Missing file: " + file.getAbsolutePath(), file.exists());
Run Code Online (Sandbox Code Playgroud)

使用Maven Surefire运行它时,此测试突然失败-DforkCount=0.随着-DforkCount=1,它的工作原理.

到目前为止我尝试的事情:

  • 该文件确实存在.Windows资源管理器,命令行(复制和粘贴),文本编辑器,Cygwin都可以找到它并显示内容.这就是为什么我认为这不是一个许可问题.
  • 它不会被单元测试或其他任何东西修改.Git在过去两个月没有显示任何修改.
  • 我检查了文件系统,它很干净.
  • 我尝试过其他版本的Java 8,即8u171和8u181.同样的问题.
  • 我从Cygwin和命令提示符下运行Maven.结果相同.
  • 重启:-)没效果:-(

更多细节:

  • 当我看到这个问题时,我开始看到"分叉的虚拟机在没有正确说再见的情况下终止.虚拟机崩溃或System.exit被调用?" 在其他项目中.这就是为什么我尝试forkCount=0通常有助于在这种情况下找出分叉VM崩溃的原因.
  • 这种情况最近已经开始,可能是在2018年10月更新的Windows 10之前.在此之前,这些版本坚固了大约三年.我认为我的机器在2017年末转为Windows 10.
  • 我正在使用Maven 3.6并且因为一个重要的错误而无法轻松尝试旧版本.我确实看到上面的VM崩溃了Maven 3.5.2.
  • 它始终是失败的相同文件(因此它是稳定的).

ulimit (来自Cygwin)说:

$ ulimit -a
core file size          (blocks, -c) unlimited
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
open files                      (-n) 256
pipe size            (512 bytes, -p) 8
stack size              (kbytes, -s) 2032
cpu time               (seconds, -t) unlimited
max user processes              (-u) 256
virtual memory          (kbytes, -v) unlimited
Run Code Online (Sandbox Code Playgroud)

我想知道256的"打开文件"限制是否仅适用于Cygwin进程,或者是否是Cygwin从Windows读取的内容.

需要帮助请叫我.我的想法已经不多了.

更新1

伯恩哈德让我打印绝对名字.我的回答是我已经使用绝对名字,但我错了.实际代码是:

File file = new File("src/main/java/com/pany/Foo.java");
if (!file.exists()) {
    log.debug("Missing file {}", file.getAbsolutePath());
    ... fail ...
}

... do something with file...
Run Code Online (Sandbox Code Playgroud)

我现在改为:

File file = new File("src/main/java/com/pany/Foo.java").getAbsoluteFile();
if (!file.exists()) {
    log.debug("Missing file {}", file);
}
Run Code Online (Sandbox Code Playgroud)

并解决了这个问题.我只是无法理解为什么.

当Maven创建一个分叉的VM以使用Surefire运行测试时,它可以更改当前目录.因此,在这种情况下,在分叉时测试工作是有意义的,但在同一个VM中运行时会失败(因为VM是在多模块构建的根文件夹中创建的).但是,为什么在exists()修复问题的电话之前使路径绝对?

Aar*_*lla 8

一些背景.每个进程都有一个"当前目录"的概念.从命令行启动时,它是执行命令的目录.从UI启动时,它通常是程序(.exe文件)所在的文件夹.

在命令提示符或BASH中,您可以使用cd运行命令提示符的进程更改此文件夹.

当Maven构建一个多模块项目时,它必须为每个模块更改它(以便相对路径src/main/java/始终指向正确的位置).不幸的是,Java在任何地方都没有"设置当前目录"方法.您只能在创建新流程时指定一个,并且可以修改系统属性user.dir.

这就是原因,new File("a").exists()并以new File("a").getAbsoluteFile().exists()不同的方式工作.

后者将用于new File(System.getProperty("user.dir"), "a")确定路径,前者将使用Windows API函数_wgetdcwd(docs),后者又使用Windows进程的字段来获取当前目录 - 在我们的例子中,这始终是Maven最初启动的文件夹因为当有人更改时,Java不会更新进程中的字段,user.dir而Maven只能更改此属性以"模拟"更改文件夹.

WinNTFileSystem_md.c电话fileToNTPath().这是定义io_util_md.c和调用pathToNTPath().对于相对路径,它将调用currentDirLength()哪些调用currentDir()调用_wgetdcwd().

也可以看看:

这里是其中Surefire插件修改的属性的地方user.dir:https://github.com/apache/maven-surefire/blob/56d41b4c903b6c134c5e1a2891f9f08be7e5039f/maven-surefire-common/src/main/java/org/apache/maven/plugin /surefire/AbstractSurefireMojo.java#L1060

当不分叉,它被复制到当前虚拟机的系统属性:https://github.com/apache/maven-surefire/blob/56d41b4c903b6c134c5e1a2891f9f08be7e5039f/maven-surefire-common/src/main/java/org/apache/maven/plugin /surefire/AbstractSurefireMojo.java#L1133