强制JVM在没有页面缓存的情况下执行所有IO(例如O_DIRECT)

时间:2013-03-05 16:00:40

标签: java linux jvm

我正在用Java编写的应用程序的一些基准测试。对于实验而言,结果不受页面缓存(我正在使用linux)

的影响非常重要

因此,无论何时打开文件,避免页面缓存的最佳方法是使用O_DIRECT。因此,我更改了jre源代码中的相应代码。

我的方法适用于FileOutputStream(例如写作)的所有内容,但它不适用于FileInputStream(例如阅读)。

将O_DIRECT添加到FileInputStream的open-call时,JVM无法加载任何类:

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

此错误不是类路径问题,因为我可以通过使用“未被破解”的JVM来修复它。

因此打开文件似乎存在问题。

我对如何解决问题的任何建议感到非常高兴。

如果有人想做类似的事,我documented the whole hack in my blog


作为参考,这些是对我所做的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以确保内存对齐(这是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);

3 个答案:

答案 0 :(得分:12)

旧帖子,但我最近写了一个名为Jaydio的小型库,希望能解决这个问题。也许你会发现它很有用。

答案 1 :(得分:9)

  "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  [*]."
     

[*]换句话说,它是一种甲骨文。

- Linus Torvalds from Transmeta, 11 May 2002

检查man 2 open

的NOTES部分
  

O_DIRECT

     

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

     

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

     

总之,O_DIRECT是一个应该使用的潜在强大工具     警告。建议应用程序将O_DIRECT用作a     默认情况下禁用的性能选项。

我认为,JRE(类加载器)中有一些FileInputStream用法,其读取偏移量或大小未对齐为512字节。 (对于Advanced Format,最小对齐可能更大,甚至是4096字节,或者是一个4K页面。)

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

其他有趣的讨论在这里:Linux: Accessing Files With O_DIRECT(kerneltrap,2007)

嗯,看起来当DIRECT出现故障时,应该回退到缓冲的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字节,未对齐。

答案 2 :(得分:4)

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