我正在做一些用Java编写的应用程序的基准测试。对于实验而言,非常重要的一点是结果不受页面缓存的影响(我使用的是Linux)
因此,避免打开页面缓存的最佳方法是在每次打开文件时使用O_DIRECT。因此,我在jre的源代码中更改了相应的代码。
我的方法对于经历FileOutputStream(例如写作)的所有事物都非常适用,但是对FileInputStream(例如阅读)却不起作用。
FileOutputStream
FileInputStream
将O_DIRECT添加到的open-call中时FileInputStream,JVM无法加载任何类:
Error: Could not find or load main class perf.TestDirectIO
该错误不是类路径问题,因为我可以通过使用“未破解”的JVM来解决它。
因此,打开文件似乎存在问题。
对于如何解决此问题的任何建议,我感到非常高兴。
如果有人想做类似的事情,我已经在我的博客中记录了整个黑客事件。
作为参考,这些是我在JVM代码上所做的更改:
jdk/src/share/native/java/io/FileInputStream.c:
jdk/src/share/native/java/io/FileInputStream.c
@@ -58,7 +60,8 @@ JNIEXPORT void JNICALL Java_java_io_FileInputStream_open(JNIEnv *env, jobject this, jstring path) { - fileOpen(env, this, path, fis_fd, O_RDONLY); + fileOpen(env, this, path, fis_fd, O_RDONLY | O_DIRECT); // this is the change that causes all the problems }
此更改有效 jdk/src/solaris/native/java/io/FileOutputStream_md.c:
jdk/src/solaris/native/java/io/FileOutputStream_md.c
@@ -55,8 +55,10 @@ JNIEXPORT void JNICALL Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path, jboolean append) { fileOpen(env, this, path, fos_fd, - O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC)); + O_WRONLY | O_DIRECT | O_CREAT | (append ? O_APPEND : O_TRUNC)); }
我还更改了热点jre以确保对齐内存(这是O_DIRECT的要求) hotspot/src/share/vm/runtime/os.cpp:
hotspot/src/share/vm/runtime/os.cpp
+# include <mm_malloc.h> ... - u_char* ptr = (u_char*)::malloc(size + space_before + space_after); + u_char* ptr = (u_char*)::_mm_malloc(size + space_before + space_after,512);
"The thing that has always disturbed me about O_DIRECT is that the whole interface is just stupid, and was probably designed by a deranged monkey on some serious mind-controlling substances [*].” [*]换句话说,这是一种Oracle主义。
"The thing that has always disturbed me about O_DIRECT is that the whole interface is just stupid, and was probably designed by a
deranged monkey on some serious mind-controlling substances [*].”
[*]换句话说,这是一种Oracle主义。
- 来自Transmeta的Linus Torvalds,2002年5月11日
检查以下内容的“注意”部分man 2 open:
man 2 open
O_DIRECT O_DIRECT标志可能对 用户空间缓冲区 的长度和地址 以及I / O 的 文件偏移量 施加对齐 限制 。在Linux中,对齐限制因文件系统和内核版本而异。 在Linux 2.4下,传输大小以及用户缓冲区和文件偏移的对齐方式都必须是文件系统逻辑块大小的倍数。在Linux 2.6下,对齐到512字节边界就足够了。.... 总之,O_DIRECT是潜在强大的工具,应谨慎使用。建议应用程序将O_DIRECT的使用作为性能选项,默认情况下禁用。
O_DIRECT
O_DIRECT标志可能对 用户空间缓冲区 的长度和地址 以及I / O 的 文件偏移量 施加对齐 限制 。在Linux中,对齐限制因文件系统和内核版本而异。
在Linux 2.4下,传输大小以及用户缓冲区和文件偏移的对齐方式都必须是文件系统逻辑块大小的倍数。在Linux 2.6下,对齐到512字节边界就足够了。....
总之,O_DIRECT是潜在强大的工具,应谨慎使用。建议应用程序将O_DIRECT的使用作为性能选项,默认情况下禁用。
我认为,JRE(类加载器)在FileInputStream中有一些用法,其读取的偏移量或大小未对齐512字节。(对于高级格式,最小对齐方式可能更大,甚至是4096字节或一页4K。)
内核未对齐偏移的行为是灰色区域,一些信息在这里:RFC:澄清直接I / O语义,Theodore Ts’o,tytso @ mit,LWN,2009年
其他有趣的讨论在这里:Linux:使用O_DIRECT访问文件(内核陷阱,2007年)
嗯,当DIRECT发生故障时,似乎应该回退到缓冲的I / O。使用DIRECT的所有IO操作都是同步的。可能是某些DMA效果?或O_DIRECT和的组合mmap?
mmap
更新:
感谢strace输出。这是错误(grep O_DIRECT,然后检查文件描述符操作):
grep O_DIRECT
28290 open("...pact/perf/TestDirectIO.class", O_RDONLY|O_DIRECT) = 11 28290 fstat(11, {st_mode=S_IFREG|0644, st_size=2340, ...}) = 0 28290 fcntl(11, F_GETFD) = 0 28290 fcntl(11, F_SETFD, FD_CLOEXEC) = 0 ...skip 28290 stat("...pact/perf/TestDirectIO.class", {st_mode=S_IFREG|0644, st_size=2340, ...}) = 0 ...skip 28290 read(11, "\312\376\272\276\0\0\0003\0\215\n\0-\0D\t\0E\0F\7\0G\n\0\3\0D\10\0H\n"..., 1024) = 1024 28290 read(11, 0x7f1d76d23a00, 1316) = -1 EINVAL (Invalid argument)
未对齐的读取大小会导致EINVAL错误。您的类文件长2340字节,它是1024 + 1316字节,未对齐。
EINVAL