我已经为特定的二进制格式编写了一个解析器类(nfdump http://nfdump.sf.net/如果有人感兴趣)它使用 java.nio映射字节缓冲区 http://java.sun.com/j2se/1.4.2/docs/api/java/nio/MappedByteBuffer.html读取每个几 GB 的文件。二进制格式只是一系列标头和大部分固定大小的二进制记录,通过调用 nextRecord() 将其提供给被调用者,该状态机会推送状态机,完成后返回 null。它表现良好。它可以在开发机器上运行。
在我的生产主机上,它可以运行几分钟或几个小时,但似乎总是抛出“java.lang.InternalError:编译的 Java 代码中最近不安全的内存访问操作中发生故障”,指着 Map.getInt 之一、getShort方法,即map中的读操作。
设置地图的无争议(?)代码是这样的:
/** Set up the map from the given filename and position */
protected void open() throws IOException {
// Set up buffer, is this all the flexibility we'll need?
channel = new FileInputStream(file).getChannel();
MappedByteBuffer map1 = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
map1.load(); // we want the whole thing, plus seems to reduce frequency of crashes?
map = map1;
// assumes the host writing the files is little-endian (x86), ought to be configurable
map.order(java.nio.ByteOrder.LITTLE_ENDIAN);
map.position(position);
}
然后我使用各种 map.get* 方法来读取短整型、整数、长整型和其他字节序列,然后到达文件末尾并关闭映射。
我从未见过我的开发主机上抛出异常。但我的生产主机和开发主机之间的显着区别在于,在前者上,我通过 NFS 读取这些文件的序列(最终可能为 6-8TB,仍在增长)。在我的开发计算机上,我在本地选择了较小的这些文件 (60GB),但当它在生产主机上崩溃时,通常会在它达到 60GB 数据之前就发生。
两台机器都运行 java 1.6.0_20-b02,尽管生产主机运行 Debian/lenny,但开发主机运行 Ubuntu/karmic。我不相信这会产生任何影响。两台机器都有 16GB RAM,并且使用相同的 java 堆设置运行。
我认为,如果我的代码中存在错误,那么 JVM 中的错误就足够多,不会引发适当的异常!但我认为这只是由于 NFS 和 mmap 之间的交互而导致的一个特定的 JVM 实现错误,可能是重复出现的6244515 https://bugs.java.com/bugdatabase/view_bug?bug_id=6244515这是官方固定的。
我已经尝试添加“加载”调用来强制 MappedByteBuffer 将其内容加载到 RAM 中 - 这似乎延迟了我所做的一次测试运行中的错误,但并没有阻止它。也可能是巧合,这是它坠毁前所经历的最长时间!
如果您已经读到这里并且以前使用 java.nio 做过这种事情,您的直觉会是什么?现在我的目标是在没有 nio 的情况下重写它:)