我已经为特定的二进制格式(如果有人感兴趣,可以使用nfdump )编写了一个解析器类,它使用 java.nio 的MappedByteBuffer来读取每个数 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* 方法来读取 shorts、ints、longs 和其他字节序列,然后再点击文件末尾并关闭地图。
我从来没有见过我的开发主机上抛出的异常。但我的生产主机和开发主机之间的显着区别在于,在前者上,我通过 NFS 读取这些文件的序列(最终可能 6-8TB,仍在增长)。在我的开发机器上,我在本地选择的这些文件较少(60GB),但是当它在生产主机上炸毁时,通常早在它达到 60GB 数据之前。
两台机器都运行 java 1.6.0_20-b02,虽然生产主机运行的是 Debian/lenny,但开发主机是 Ubuntu/karmic。我不相信这会有所作为。两台机器都有 16GB 的 RAM,并且使用相同的 java 堆设置运行。
我认为,如果我的代码中存在错误,那么 JVM 中的错误就足够了,不会给我抛出适当的异常!但我认为这只是一个特定的 JVM 实现错误,由于 NFS 和 mmap 之间的交互,可能是官方修复的6244515的重现。
我已经尝试添加“加载”调用以强制 MappedByteBuffer 将其内容加载到 RAM 中 - 这似乎延迟了我完成的一次测试运行中的错误,但不能阻止它。或者它可能是巧合,这是它在崩溃之前最长的一次!
如果你已经读到这里,并且以前用 java.nio 做过这种事情,你的直觉是什么?现在我的是在没有 nio 的情况下重写它:)