(原创)用MappedByteBuffer解析大…

最近在写一个txt文本小说阅读器,在解析几M以上的txt文件时,总是由于内存溢出报错,要不然就是运行太慢,在网上找了集中方法后,最后觉得MappedByteBuffer最合适,不仅解决了以上的问题,还方便了阅读时的分章。

-- --------------- MappedByteBuffer在java帮助文档的解析-------------------
public abstract class MappedByteBuffer
extends ByteBuffer

直接字节缓冲区,其内容是文件的内存映射区域。

映射的字节缓冲区是通过 FileChannel.map 方法创建的。此类用特定于内存映射文件区域的操作扩展 ByteBuffer 类。

映射的字节缓冲区和它所表示的文件映射关系在该缓冲区本身成为垃圾回收缓冲区之前一直保持有效。

映射的字节缓冲区的内容可以随时更改,例如,在此程序或另一个程序更改了对应的映射文件区域的内容的情况下。这些更改是否发生(以及何时发生)与操作系统无关,因此是未指定的。

全部或部分映射的字节缓冲区可能随时成为不可访问的,例如,如果我们截取映射的文件。试图访问映射的字节缓冲区的不可访问区域将不会更改缓冲区的内容,并导致在访问时或访问后的某个时刻抛出未指定的异常。因此强烈推荐采取适当的预防措施,以避免此程序或另一个同时运行的程序对映射的文件执行操作(读写文件内容除外)。

除此之外,映射的字节缓冲区的功能与普通的直接字节缓冲区完全相同。

-------------------------------------------------------------------------------------

创建MappedByteBuffer 对象

MappedByteBuffer inputBuffer = new RandomAccessFile(f, "r")
    .getChannel().map(FileChannel.MapMode.READ_ONLY,
      0, 1000);

--------------------------------------------------------------------------------
* map(FileChannel.MapMode mode,long position, long size)
 
* mode - 根据是按只读、读取/写入或专用(写入时拷贝)来映射文件,分别为 FileChannel.MapMode 类中所定义的READ_ONLY、READ_WRITE 或 PRIVATE 之一

* position - 文件中的位置,映射区域从此位置开始;必须为非负数

* size - 要映射的区域大小;必须为非负数且不大于 Integer.MAX_VALUE

-------------- 解析过程代码-------------------------------
  final int BUFFER_SIZE = 0x300000;// 缓冲区大小为3M
  byte[] dst = new byte[BUFFER_SIZE];// 每次读出3M的内容

  long start = System.currentTimeMillis(); //获取解析开始的时间

  for (int offset = 0; offset < inputBuffer.capacity(); offset += BUFFER_SIZE) {

    if (inputBuffer.capacity() - offset >= BUFFER_SIZE) {

    for (int i = 0; i < BUFFER_SIZE; i++)

      dst[i] = inputBuffer.get(offset + i);

    } else {

    for (int i = 0; i < inputBuffer.capacity() - offset; i++)

      dst[i] = inputBuffer.get(offset + i);

    }

    int length = (inputBuffer.capacity() % BUFFER_SIZE == 0) ? BUFFER_SIZE     //获取当次读取的长度
      : inputBuffer.capacity() % BUFFER_SIZE;



MappedByteBuffer在解析大文本数据时速度很快,但也存在一些问题,主要就是内存占用和文件关闭等不确定问题。被MappedByteBuffer打开的文件只有在垃圾收集时才会被关闭,而这个点是不确定的。
 
这里提供一种解决方案(来自网络):
AccessController.doPrivileged(new PrivilegedAction() {
  public Object run() {
    try {
      Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
      getCleanerMethod.setAccessible(true);
      sun.misc.Cleaner cleaner = (sun.misc.Cleaner)
      getCleanerMethod.invoke(byteBuffer, new Object[0]);
      cleaner.clean();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }
});








  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值