转载自:http://langgufu.iteye.com/blog/2107023

java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用MappedByteBuffer

MappedByteBuffer是java nio引入的文件内存映射方案,读写性能极高。NIO最主要的就是实现了对异步操作的支持。其中一种通过把一个套接字通道(SocketChannel)注册到一个选择器(Selector)中,不时调用后者的选择(select)方法就能返回满足的选择键(SelectionKey),键中包含了SOCKET事件信息。这就是select模型。

SocketChannel的读写是通过一个类叫ByteBuffer(java.nio.ByteBuffer)来操作的.这个类本身的设计是不错的,比直接操作byte[]方便多了.
ByteBuffer有两种模式:直接/间接.间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存
(byte[]).但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即
MappedByteBuffer,文件映射.
    
先中断一下,谈谈操作系统的内存管理.一般操作系统的内存分两部分:物理内存;虚拟内存.虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换".
MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer.MappedByteBuffer
只是一种特殊的 ByteBuffer ,即是ByteBuffer的子类。 MappedByteBuffer
将文件直接映射到内存(这里的内存指的是虚拟内存,并不是物理内存)。通常,可以映射整个文件,如果文件比较大的话可以分段进行映射,只要指定文件的那个部分就可以。

三种方式:
             
FileChannel提供了map方法来把文件影射为内存映像文件: MappedByteBuffer map(int mode,long
position,long size); 可以把文件的从position开始的size大小的区域映射为内存映像文件,mode指出了
可访问该内存映像文件的方式:READ_ONLY,READ_WRITE,PRIVATE.                    
a. READ_ONLY,(只读): 试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException.(MapMode.READ_ONLY)
 b. READ_WRITE(读/写): 对得到的缓冲区的更改最终将传播到文件;该更改对映射到同一文件的其他程序不一定是可见的。 (MapMode.READ_WRITE)
c. PRIVATE(专用): 对得到的缓冲区的更改不会传播到文件,并且该更改对映射到同一文件的其他程序也不是可见的;相反,会创建缓冲区已修改部分的专用副本。 (MapMode.PRIVATE)

三个方法:

a. fore();缓冲区是READ_WRITE模式下,此方法对缓冲区内容的修改强行写入文件
b. load()将缓冲区的内容载入内存,并返回该缓冲区的引用
c. isLoaded()如果缓冲区的内容在物理内存中,则返回真,否则返回假

三个特性:

调用信道的map()方法后,即可将文件的某一部分或全部映射到内存中,映射内存缓冲区是个直接缓冲区,继承自ByteBuffer,但相对于ByteBuffer,它有更多的优点:

a. 读取快
b. 写入快
c. 随时随地写入

下面来看代码:

 1 package study;
2 import java.io.FileInputStream;
3 import java.io.FileOutputStream;
4 import java.nio.ByteBuffer;
5 import java.nio.MappedByteBuffer;
6 import java.nio.channels.FileChannel;
7
8 public class MapMemeryBuffer {
9
10 public static void main(String[] args) throws Exception {
11 ByteBuffer byteBuf = ByteBuffer.allocate(1024 * 14 * 1024);
12 byte[] bbb = new byte[14 * 1024 * 1024];
13 FileInputStream fis = new FileInputStream("e://data/other/UltraEdit_17.00.0.1035_SC.exe");
14 FileOutputStream fos = new FileOutputStream("e://data/other/outFile.txt");
15 FileChannel fc = fis.getChannel();
16 long timeStar = System.currentTimeMillis();// 得到当前的时间
17 fc.read(byteBuf);// 1 读取
18 //MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
19 System.out.println(fc.size()/1024);
20 long timeEnd = System.currentTimeMillis();// 得到当前的时间
21 System.out.println("Read time :" + (timeEnd - timeStar) + "ms");
22 timeStar = System.currentTimeMillis();
23 fos.write(bbb);//2.写入
24 //mbb.flip();
25 timeEnd = System.currentTimeMillis();
26 System.out.println("Write time :" + (timeEnd - timeStar) + "ms");
27 fos.flush();
28 fc.close();
29 fis.close();
30 }
31
32 }
33 运行结果:
34 14235
35 Read time :24ms
36 Write time :21ms
37 我们把标注1和2语句注释掉,换成它们下面的被注释的那条语句,再来看运行效果。14235
38 Read time :2ms
39 Write time :0ms

可以看出速度有了很大的提升。MappedByteBuffer的确快,但也存在一些问题,主要就是内存占用和文件关闭等不确定问题。被MappedByteBuffer打开的文件只有在垃圾收集时才会被关闭,而这个点是不确定的。在javadoc里是这么说的:A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself  is garbage-collected.
这里提供一种解决方案:

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;
}
});

关于MappedByteBuffer资源释放问题

JDK1.4中加入了一个新的包:NIO(java.nio.*)。这个库最大的功能(我认为)就是增加了对异步套接字的支持。其实在 其他语言中,包括在最原始的SOCKET实现(BSD SOCKET),这是一个早有的功能:异步回调读/写事件,通过选择器动态选择感兴趣的事件,等等。
先谈谈操作系统的内存管理。一般操作系统的内存分两部分:物理内存;虚拟内存。虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换"。
MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer。这是一个很好的设计,除了令人头疼的一点在后面会讲到。
java.lang.Object
   java.nio.Buffer
      java.nio.ByteBuffer
          java.nio.MappedByteBuffer
MappedByteBuffer是一个比较方便使用的类。其内容是文件的内存映射区域。映射的字节缓冲区是通过FileChannel.map 方法创建的。映射的字节缓冲区和它所表示的文件映射关系在该缓冲区本身成为垃圾回收缓冲区之前一直保持有效。此类用特定于内存映射文件区域的操作扩展 ByteBuffer 类。 这个类本身的设计是不错的,比直接操作byte[]方便多了。
ByteBuffer有两种模式:直接/间接。间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存(byte
[])。但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即
MappedByteBuffer,文件映射。
在JDK API文档中这样描述的:
全部或部分映射的字节缓冲区可能随时成为不可访问的,例如,如果我们截取映射的文件。试图访问映射的字节缓冲区的不可访问区域将不会更改缓冲区

的内容,并导致在访问时或访问后的某个时刻抛出未指定的异常。因此强烈推荐采取适当的预防措施,以避免此程序或另一个同时运行的程序对映射的文件执行操作
(读写文件内容除外)。

MappedByteBuffer只能通过调用FileChannel的map()取得,再没有其他方式.但是令人奇怪的是,SUN提供了map()却没有提供unmap().这样会导致什么后果呢?
这样,问题就出现了。通过MappedByteBuffer实现文件复制功能非常容易,可以用以下方法来实现。
 1 //文件复制
2 public void copyFile(String filename,String srcpath,String destpath)throws IOException {
3 File source = new File(srcpath+"/"+filename);
4 File dest = new File(destpath+"/"+filename);
5 FileChannel in = null, out = null;
6 try {
7 in = new FileInputStream(source).getChannel();
8 out = new FileOutputStream(dest).getChannel();
9 long size = in.size();
10 MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);
11 out.write(buf);
12 in.close();
13 out.close();
14 source.delete();//文件复制完成后,删除源文件
15 }catch(Exception e){
16 e.printStackTrace();
17 } finally {
18 in.close();
19 out.close();
20 }
21 }
但是如果要实现文件文件复制完成后,删除源文件,以上方法就有问题。因为在source.delete()时,会返回false,删除失败,主 要原因是变量buf仍然有源文件的句柄,文件处于不可删除状态。既然MappedByteBuffer是从FileChannel中map()出来的,为 什么它又不提供unmap()呢?SUN自己也没有讲清楚为什么。O'Reilly的<<Java NIO>>中说是因为"安全"的原因,但是到底unmap()会怎么不安全,作者也没有讲清楚。
在sun网站也有相应的BUG报告:bug id:4724038链接为http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038,但是sun自己不认为是BUG,而只是一个RFE(Request For Enhancement),有待改进。
好在有个叫bellomi的网友提出了一个解决方法,我也测试过,可以实现期望的功能。具体实现代码如下:
 1 public static void clean(final Object buffer) throws Exception {
2 AccessController.doPrivileged(new PrivilegedAction() {
3 public Object run() {
4 try {
5 Method getCleanerMethod = buffer.getClass().getMethod("cleaner",new Class[0]);
6 getCleanerMethod.setAccessible(true);
7 sun.misc.Cleaner cleaner =(sun.misc.Cleaner)getCleanerMethod.invoke(buffer,new Object[0]);
8 cleaner.clean();
9 } catch(Exception e) {
10 e.printStackTrace();
11 }
12 return null;}});
13
14 }

不知道为什么SUN不提供ByteBuffer的派生。毕竟这是一个很实用的类,如果允许派生,那么我就可以操作的就不仅仅限于堆内存和文件了,我可以扩展到任何存储设备。

java处理大文本方案的更多相关文章

  1. Java查询大文本

    但JAVA本身缺少相应的类库,需要硬编码才能实现结构化文件计算,代码复杂且可读性差,难以实现高效的并行处理. 使用免费的集算器可以弥补这一不足.集算器封装了丰富的结构化文件读写和游标计算函数,书写简单 ...

  2. 【Java】大文本字符串滤重的简单方案~

    本文章也同步至本人的CSDN博客中: http://blog.csdn.net/u012881584/article/details/70477832 今天来说一个Java中处理大文本字符串虑重的两个 ...

  3. java处理大文本2G以上

    面试中经常碰到类似问题,问题的关键我觉得是用设置一个缓冲区 还有一个思路 是通过Linux split 命令将文件直接切割成小文件,再进行处理再汇总. 或者jdk7提供的 forkjoin 框架,利用 ...

  4. Java调用SqlLoader将大文本导入数据库

    Java调用SqlLoader将大文本导入数据库 业务场景:将一千万条数据,大约500M的文本文档的数据导入到数据库 分析:通过Java的IO流解析txt文本文档,拼接动态sql实现insert入库, ...

  5. ElasticStack系列之十七 & 大文本搜索性能提升方案

    1. 什么是大文本?具体是什么? 首先需要理解,ElasticSearch 建立索引完成全文检索的前提是将待检索的信息导入到 ElasticSearch 中.而有的信息对应的正文内容会非常的打,可能达 ...

  6. Android OCR文字识别 实时扫描手机号(极速扫描单行文本方案)

    身份证识别:https://github.com/wenchaosong/OCR_identify 遇到一个需求,要用手机扫描纸质面单,获取面单上的手机号,最后决定用tesseract这个开源OCR库 ...

  7. java filechannel大文件的读写

    java读取大文件 超大文件的几种方法 转自:http://wgslucky.blog.163.com/blog/static/97562532201332324639689/   java 读取一个 ...

  8. Android自定义ScrollView分段加载大文本数据到TextView

    以下内容为原创,转载时请注明链接地址:http://www.cnblogs.com/tiantianbyconan/p/3311658.html 这是我现在碰到的一个问题,如果需要在TextView中 ...

  9. jdbc基础 (三) 大文本、二进制数据处理

    LOB (Large Objects)   分为:CLOB和BLOB,即大文本和大二进制数据 CLOB:用于存储大文本 BLOB:用于存储二进制数据,例如图像.声音.二进制文件 在mysql中,只有B ...

随机推荐

  1. Docker深入浅出系列教程——Docker简介

    我是架构师张飞洪,钻进浩瀚代码,十年有余,人不堪其累,吾不改其乐.如果你和我的看法不一样,请关注我的头条号,我们一起奇闻共赏,疑义相析. 本节属于入门简介,从三个小方面进行简单介绍Docker. Do ...

  2. Mysql的内连接,外链接,交叉链接

    内连接:只连接匹配的行  inner join select A.*,B.* from A,B where A.id = B.parent_id 外链接包括左外链接,右外链接,全外链接 左外链接:包含 ...

  3. 201621123044 《Java程序设计》第六周实验总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰 ...

  4. Linux下Java通用安装方法

    1.到oracle官网下下载对应jdk包,一般为%x64%.tar.gz格式. 2.建立目录: $ mkdir /usr/local/java 3.将压缩包解压至/usr/local/java 4.修 ...

  5. L2 约束的最小二乘学习法

    \[ \begin{align*} &J_{LS}{(\theta)} = \frac { 1 }{ 2 } { \left\| \Phi \theta - y \right\| }^{ 2 ...

  6. 原始的Ajax方法 (异步的 JavaScript 和 XML -- (Extensible Markup Language 可扩展标记语言))

    <script language="javascript" type="text/javascript"> var request = false; ...

  7. DevExpress控件的一些快捷操作

    用的DevExpress控件时,有一些操作并不太方便,根据我自己需要的封装了一些控件的事件,调用的时候直接绑定控件的事件就可以了 例如: this.ComboBoxEdit.KeyDown += Ct ...

  8. maven的使用之一简单的安装

    首先,我们知道,在传统的项目中,我们会导入一堆的jar包,那样的话,我们会发现我们的jar包的大小已经占了整个项目大小的90%以上,甚至更多,而且,我们的jar包只能自己使用,如果 其他人想用的话,还 ...

  9. PHP 7.2 新功能介绍

    PHP 7.2 已經在 2017 年 11 月 30 日 正式發布 .這次發布包含新特性.功能,及優化,以讓我們寫出更好的代碼.在這篇文章裡,我將會介紹一些 PHP 7.2 最有趣的語言特性. 你可以 ...

  10. flask 操作mysql的两种方式-sql操作

    flask 操作mysql的两种方式-sql操作 一.用常规的sql语句操作 # coding=utf-8 # model.py import MySQLdb def get_conn(): conn ...