我正在对一个用Java编写的应用程序进行一些基准测试。对于实验来说,结果不受页面缓存的影响是非常重要的(我使用的是Linux)
因此,避免页面缓存的最佳方法是在打开文件时使用O_DIRECT。因此,我在jre的源代码中修改了相应的代码。
我的方法对于通过FileOutputStream
的所有事情都非常有效(例如,写),但是对于FileInputStream
(例如,阅读)就不起作用。
当将O_DIRECT添加到FileInputStream
的开放调用时,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);
4条答案
按热度按时间qcbq4gxm1#
老帖子,但是我最近写了一个叫做Jaydio的小库,希望能解决这个问题。也许你会发现它很有用。
0qx6xfy62#
[*]换句话说,这是一个甲骨文。
--Linus Torvalds from Transmeta, 11 May 2002中的一个
检查
man 2 open
的“注解”部分:O_直接
O_DIRECT标志可能会对用户空间缓冲区的长度和地址 * 以及I/O的 * 文件偏移量 * 施加对齐 * 限制。在Linux中,对齐限制因文件系统和内核版本而异......
在Linux 2.4下,传输大小、用户缓冲区和文件偏移量的对齐都必须是文件系统的逻辑块大小的倍数。在Linux 2.6下,对齐512字节边界就足够了。......
总而言之,O_DIRECT是一个潜在的强大工具,应谨慎使用。建议应用程序将O_DIRECT的使用视为一个性能选项,默认情况下禁用该选项。
我认为,在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(内核陷阱,2007)
嗯,当DIRECT出现故障时,似乎应该回退到缓冲I/O。DIRECT的所有IO操作都是同步的。可能是一些DMA效应?或者是
O_DIRECT
和mmap
的组合?下面是错误代码(
grep O_DIRECT
,然后检查文件描述符操作):未对齐的读取大小会导致
EINVAL
错误。您的类文件长度为2340字节,它是1024+1316字节,这是未对齐的。bbuxkriu3#
您可以在Java下使用O_DIRECT,利用Java Native Access (JNA)。here提供了启用O_DIRECT的InputStream和OutputStream的实现。
az31mfrm4#
只是更新,因为这篇文章是第一个显示,如果你谷歌
O_DIRECT JVM
:使用
O_DIRECT
执行Direct I/O的功能已合并到2017版JDK中:创建FileChannel时,可以传递
ExtendedOpenOption.DIRECT
:在2022年,还可以考虑将新的外部内存API与
MemorySegment
一起用于后备缓冲区。