我已经为特定的二进制格式编写了一个解析器类(nfdump,如果有人感兴趣的话),它使用java.nio的MappedByteBuffer来读取每个几GB的文件.二进制格式只是一系列标题和大多数固定大小的二进制记录,它们通过调用nextRecord()输出到被调用者,后者推送状态机,在完成时返回null.它表现很好.它适用于开发机器.
在我的生产主机上,它可以运行几分钟或几小时,但似乎总是抛出"java.lang.InternalError:在编译的Java代码中最近的一个不安全的内存访问操作中发生了一个错误",指法其中一个Map.getInt ,getShort方法,即地图中的读取操作.
设置地图的无争议(?)代码是这样的:
/** 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*方法来读取short,int,long和其他字节序列,然后点击文件末尾并关闭地图.
我从来没有见过我的开发主机上抛出的异常.但是我的生产主机和开发之间的重要区别在于前者,我正在通过NFS读取这些文件的序列(最终可能是6-8TB,仍然在增长).在我的开发机器上,我在本地选择了较少的这些文件(60GB),但是当它在生产主机上爆炸时,它通常会在它达到60GB的数据之前.
两台机器都运行java 1.6.0_20-b02,虽然生产主机运行的是Debian/lenny,开发主机是Ubuntu/karmic.我不相信会有任何不同.两台机器都有16GB RAM,并且使用相同的Java堆设置运行.
我认为,如果我的代码中存在错误,JVM中有足够的错误不会给我一个正确的异常!但我认为由于NFS和mmap之间的交互,这只是一个特定的JVM实现错误,可能是6244515的重复,这是正式修复的.
我已经尝试添加一个"加载"调用来强制MappedByteBuffer将其内容加载到RAM中 - 这似乎延迟了我已完成的一次测试运行中的错误,但并未阻止它.或者它可能是巧合,它是崩溃前最长的!
如果你已经读过这篇文章并且以前用java.nio做过这种事情,你的直觉是什么?现在我的是没有nio重写它:)