强制JVM执行所有没有页面caching的IO(例如O_DIRECT)

我正在做一些用Java编写的应用程序的基准testing。 实验结果不受页面caching影响是非常重要的(我使用的是linux)

因此,每当文件打开时,避免页面caching的最好方法是使用O_DIRECT。 因此,我更改了jre的源代码中的相应代码。

我的方法完全适用于通过FileOutputStream (例如写入)的所有内容,但对于FileInputStream (例如读取)不起作用。

在将O_DIRECT添加到FileInputStream的打开调用中时,JVM无法加载任何类:

 Error: Could not find or load main class perf.TestDirectIO 

这个错误不是类path问题,因为我可以通过使用“unhacked”JVM来修复它。

所以打开文件似乎有一个问题。

我很高兴有关如何解决这个问题的任何build议。

如果有人想要做类似的事情,我已经logging在我的博客整个黑客 。


作为参考,这些是我所做的JVM代码的更改:

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

 @@ -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,以确保内存alignment(即O_DIRECT的要求) 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); 

Solutions Collecting From Web of "强制JVM执行所有没有页面caching的IO(例如O_DIRECT)"

  "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 NOTES部分是否man 2 open

O_DIRECT

O_DIRECT标志可能对用户空间缓冲区的长度和地址以及I / O的文件偏移量施加对齐限制 。 在Linux对齐限制因文件系统和内核版本而异….

在Linux 2.4下,传输大小以及用户缓冲区和文件偏移量的对齐方式都必须是文件系统逻辑块大小的倍数。 在Linux 2.6下,对齐到512字节的边界就足够了。 ….

总之,O_DIRECT是一个潜在的强大工具,应谨慎使用。 建议应用程序使用O_DIRECT作为默认禁用的性能选项。

我想,在JRE(classloader)中有一些FileInputStream的用法,它的读取偏移量或大小不与512字节对齐。 (对于高级格式 ,最小对齐可能更大,甚至是4096字节,或者一个4K页面。)

内核对未对齐偏移的行为是灰色区域,有些信息在这里: RFC:澄清直接I / O语义,Theodore Ts'o,tytso @ mit,LWN,2009

其他有趣的讨论在这里: Linux:使用O_DIRECT访问文件 (kerneltrap,2007)

嗯,似乎应该有回退缓冲I / O时,直接的东西失败。 所有使用DIRECT的IO操作都是同步的。 可能是一些DMA效应? 或者是O_DIRECTmmap组合?

更新:

感谢strace输出。 这里是错误( 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字节,这是不对齐的。

老帖子,但我最近写了一个名为Jaydio的小型图书馆希望解决这个确切的问题。 也许你会觉得它有用。

您可以在Java下使用O_DIRECT,利用Java Native Access(JNA) 。 在这里提供了启用O_DIRECT的InputStream和OutputStream的实现。