锁定老帖子 主题:MappedByteBuffer小结
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2012-07-28
java的内存映射文件有如下特点:
1,使用虚拟内存,因此分配(map)的内存大小不受JVM的-Xmx参数限制,但是也是有大小限制的,首先他理论上不能超过Integer.MAX_VALUE也就是32位操作系统的2G,其次,其实际值在不用操作系统还不一样,在win7 32位操作系统下,他不能超过1.5G,具体多少,没测出来,也不知道什么原因。
2, 对应读大文件,当文件超出1.5G限制是,可以重新MAP下,通过POSITION参数来获取文件后面的内容。
3,它的读取和来回读取要不普通IO快的多,但是单纯的写入还不如普通I/O的一般速度。此结论来自以下测试代码 package com.chat; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; public class FileChannelStudy { static String filename1 = "d:\\work\\code\\filechannelstudy.txt"; static String filename2 = "d:\\work\\code\\file.txt"; static String content = "abcdefghijk\r\n"; static long size = 1024000000l; static long num = size / 10*6; static long startT = 0; static long endT = 0; public static void setStartT() { mbb = null; if(cnt %50 == 0) { System.gc(); System.out.println("call gc"); } startT = System.currentTimeMillis(); } public static long ellipseT() { endT = System.currentTimeMillis(); long consumeT = endT - startT; System.out.println("consume time :"+ consumeT/1000 + " second"); return consumeT / 1000; } /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // readFile1(); createFile(true); preparedFile1(); preparedFile2(); } public static void createFile(boolean bReCreate) throws IOException { if(!bReCreate) { File f = new File(filename1); if(!f.exists()) f.createNewFile(); f = new File(filename2); if(!f.exists()) f.createNewFile(); } else { File f = new File(filename1); if(f.exists()) f.delete(); f.createNewFile(); f = new File(filename2); if(f.exists()) f.delete(); f.createNewFile(); } } public static void preparedFile2() throws IOException { BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(filename2)); try { System.out.println("fill file by io"); setStartT(); for (int i = 0; i < num; i++) { bo.write(content.getBytes()); } ellipseT(); } finally { if(bo != null) bo.close(); } } public static void preparedFile1() throws IOException { long mapsize = content.getBytes().length*1000000*100; long position = 0; FileChannel ch = new RandomAccessFile(filename1,"rw").getChannel(); MappedByteBuffer mbb = ch.map(MapMode.READ_WRITE, position, mapsize); int cnt = 0; try { System.out.println("fill file by nio"); setStartT(); for (int i = 0; i < num; i++) { if(mbb.remaining() < content.getBytes().length) { cnt ++; position += mbb.position(); mbb = null; if(cnt %50 == 0) { System.gc(); System.out.println("call gc"); } mbb = ch.map(MapMode.READ_WRITE, position, mapsize); } mbb.put(content.getBytes()); } ellipseT(); } finally { if(ch != null) ch.close(); } } public static void readFile1() throws IOException { long mapsize = content.getBytes().length*1000000; long position = 0; //long rper = 2000000000; long rper = 1300000000; FileChannel ch = new RandomAccessFile(filename1,"rw").getChannel(); MappedByteBuffer mbb = ch.map(MapMode.READ_WRITE, 0, rper); int rs = 102400; byte dst[] = new byte[rs]; int cnt = 0; while(mbb.hasRemaining()) { ByteBuffer bb = mbb.get(dst); cnt ++; if(cnt %50 == 0) System.out.println(bb.toString()); } } } 4,谁然FileOutputStream也有channel功能,但是如果要用内存映射文件方式写文件,则只能使用RandomAccessFile,视乎是因为写时就有读,所以只能用它。
5, 他跟其他ByteBuffer不一样的地方,其他ByteBuffer需要用channel.write/read来写入/读取目标的数据,而MappedByteBuffer直接就是对于目标,它的修改会自动写入到磁盘中,除非你设定了PRIVATE。
6, 内存溢出问题,除了尺寸限制,在写大文件时,由于要不停的重新map,会导致内存溢出,或者说gc来不及回收内存,如上面程序,如果把prepareFile1中的 mbb = null; if(cnt %50 == 0) { System.gc(); System.out.println("call gc"); } 代码删除,则在3G左右就会报内存溢出;如果只保留mbb=null;则在5G左右报内存溢出,都保留则不报内存溢出。因此需要手工运行System.gc().
7, 对于中文读写,需要转码。 当然原来io也需要转码,不过有InputStreamReader中可以指定字符集因此可以不自己写代码。 如果不转码,则用UE等工具打开文件看到的是乱码,但是用java的MappedByteBuffer读取处理还是中文。 转码代码:
public static ByteBuffer getBytes(String str) {// 将字符转为字节(编码) Charset cs = Charset.forName("GBK"); ByteBuffer bb = ByteBuffer.wrap(str.getBytes(cs)); return bb; } public static String getChars(ByteBuffer bb) {// 将字节转为字符(解码) Charset cs = Charset.forName("GBK"); bb.flip(); CharBuffer cb = cs.decode(bb); return cb.toString(); }
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 15512 次