现在的位置: 首页 > 综合 > 正文

学习JavaNIO-文件内存映射

2013年09月02日 ⁄ 综合 ⁄ 共 5226字 ⁄ 字号 评论关闭
学习JavaNIO-文件内存映射
最近看了文件内存映射。在NIO中,使用起来很方便。
文件通过内存映射以后,访问速度自然是提高了。
当然也有很多问题,现在我们来看看NIO中的内存映射文件。

Java代码 复制代码 收藏代码
  1. public abstract class FileChannel extends  
  2. AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel { 
  3. // This is a partial API listing 
  4. public abstract MappedByteBuffer map (MapMode mode, long position,long size)  
  5. public static class MapMode {  
  6.     public static final MapMode READ_ONLY  
  7.     public static final MapMode READ_WRITE  
  8.     public static final MapMode PRIVATE  
  9.     }  

内存映射文件,是通过一个FileChannel来完成的,返回的MappedByteBuffer,也是一种ByteBuffer。这里有三种映射模式。主要针对这三种映射模式的差异说说。

根据map函数,我们得知,我们可以映射部分文件,也可以全部映射。
如果请求的大小,size超出文件,那么文件会相应的增长以对应映射。如果是Integer.MAX_VALUE,那么文件就会达到2.1GB
当然,如果你请求的只是一个只读文件,而你的size超出文件大小,那么就会抛出IOException。
对于 MapMode的前两种,READ_ONLY和READ_WRITE来说,都很好理解。要注意的而是,如果在一个只读文件上使用READ_WRITE,是要抛出NonWritableChannelException异常的。
MapMode.PRIVATE模式很有意思,称为“写时拷贝”(copy-on-write)的映射。这是操作系统中常用的技术。比如创建一个子进程时,子进程共享父进程的地址空间,当子进程修改空间的时候,才会拷贝要修改的部分。对于PRIVATE模式来说,只有使用put函数的时候,才会去拷贝。可能拷贝某一页或者几页。假设此时有其他的映射,是看不到本次put后的改变的。也就是说,私有的。

注意,没有unmap函数,也就是说,一旦map成功,那么即使FileChannel被关闭,映射依然存在,只有映射对应的MappedByteBuffer内存被回收,映射才算取消。

MappedByteBuffer直接反应磁盘文件,如果磁盘文件结构或大小改变,那么可能就无法访问文件。
MappedByteBuffer 直接内存访问,并不占用虚拟机内存。
MappedByteBuffer 也是ByteBuffer,所以可以用于SocketChannel之类的通道读写。
对于MappedByteBuffer还有几个函数可以介绍下。

Java代码 复制代码 收藏代码
  1. public abstract class MappedByteBuffer extends ByteBuffer {  
  2. // This is a partial API listing  
  3. public final MappedByteBuffer load( )  
  4. public final boolean isLoaded( )  
  5. public final MappedByteBuffer force( )  

load函数用于从磁盘文件加载到内存。这个操作将会引起大量系统调用。慎用。一旦加载完成,再次访问文件,就和访问内存一样快。但是,这些都起决于操作系统底层调用。

isLoaded函数是用来判断文件是否完全加载到内存。

force则是强制同步内存文件到磁盘。也就是把MappedByteBuffer中的更改应用到磁盘中。这里包括了文件的元数据(最近一次访问,作者,创建时间等)。当然,如果是READ_ONLY或者PRIVATE模式,则不起作用。

下面这个例子,使用内存映射文件,生成一个HTTP应答格式的文件。我们来看看这个例子:

Java代码 复制代码 收藏代码
  1. package shaoxin.nio; 
  2.  
  3. import java.io.FileInputStream; 
  4. import java.io.FileOutputStream; 
  5. import java.io.IOException; 
  6. import java.net.URLConnection; 
  7. import java.nio.ByteBuffer; 
  8. import java.nio.MappedByteBuffer; 
  9. import java.nio.channels.FileChannel; 
  10. import java.nio.channels.FileChannel.MapMode; 
  11.  
  12. /***
  13. * Dummy HTTP server using MappedByteBuffer.
  14. * Given a filename on the command line, pretend to be
  15. * a web server and generate an HTTP response containing
  16. * the file content preceded by appropriate headers The
  17. * data is send with a gathering write.
  18. *
  19. * @author Ron Hitchens (ron@ronsoft.com)
  20. *
  21. */ 
  22. public class MappedHttp { 
  23.     private static final String OUTPUT_FILE="MappedHttp.out"
  24.     private static final String LINE_SEP = "\r\n"
  25.     private static final String SERVER_ID = "Server: Ronsoft Dummy Server"
  26.     private static final String HTTP_HDR =  
  27.         "HTTP/1.0 200 OK" + LINE_SEP + SERVER_ID + LINE_SEP; 
  28.     private static final String HTTP_404_HDR =  
  29.         "HTTP/1.0 404 Not Found" + LINE_SEP + SERVER_ID + LINE_SEP; 
  30.     private static final String MSG_404 = "Cound not open file: "
  31.     public static void main(String[] args) throws Exception{ 
  32.         if(args.length < 1){ 
  33.             System.err.println("Usage:filename"); 
  34.             return
  35.         } 
  36.          
  37.         String file = args[0]; 
  38.         ByteBuffer header = ByteBuffer.wrap(bytes(HTTP_HDR)); 
  39.         ByteBuffer dynhdrs = ByteBuffer.allocate(128); 
  40.         ByteBuffer[] gather = {header,dynhdrs,null}; 
  41.         String contentType = "unknown/unknown"
  42.         long contentLength = -1
  43.         try
  44.             FileInputStream fis = new FileInputStream(file); 
  45.             FileChannel fc = fis.getChannel(); 
  46.             MappedByteBuffer filedata =  
  47.                 fc.map(MapMode.READ_ONLY, 0, fc.size()); 
  48.             gather[2] = filedata; 
  49.             contentLength = fc.size(); 
  50.             contentType = URLConnection.guessContentTypeFromName(file); 
  51.         }catch(IOException e){ 
  52.             //file could not be opend; report problem 
  53.             ByteBuffer buf = ByteBuffer.allocate(128); 
  54.             String msg = MSG_404 + LINE_SEP; 
  55.             buf.put(bytes(msg)); 
  56.             buf.flip(); 
  57.             //Use the HTTP error response 
  58.             gather[0] = ByteBuffer.wrap(bytes(HTTP_404_HDR)); 
  59.             gather[2] = buf; 
  60.             contentLength = msg.length(); 
  61.             contentType = "text/plain"
  62.         } 
  63.          
  64.         StringBuffer sb = new StringBuffer(); 
  65.         sb.append("Content-Length: " + contentLength); 
  66.         sb.append(LINE_SEP); 
  67.         sb.append("Content-Type: ").append(contentType); 
  68.         sb.append(LINE_SEP).append(LINE_SEP); 
  69.         dynhdrs.put(bytes(sb.toString())); 
  70.         dynhdrs.flip(); 
  71.          
  72.         FileOutputStream fos = new FileOutputStream(OUTPUT_FILE); 
  73.         FileChannel out = fos.getChannel(); 
  74.         // All the buffers have been prepared , write them out 
  75.         while(out.write(gather) > 0){ 
  76.             //Empty body; loop until all buffers are empty           
  77.         } 
  78.         out.close(); 
  79.         System.out.println("output written to "+ OUTPUT_FILE); 
  80.     } 
  81.      
  82.     //Convert a String to its constituent bytes 
  83.     //from the ASCII character set 
  84.     private static byte[] bytes(String string)throws Exception { 
  85.         return (string.getBytes("US-ASCII")); 
  86.     } 

抱歉!评论已关闭.