MemoryMappedFilepublic class MemoryMappedFile extends Object This class implements a virtual disk file backed by a pool of direct
memory MappedByteBuffers, designed for high-speed random reads/writes.
NOTE: Abandoning this code for now, as JRE 1.4 series does not provide an
unmap() method for MappedByteBuffers, so we have to wait for the VM
to lazily free the underlying direct memory regions, which means we
eventually run out of direct memory when accessing files larger than the
allowed direct memory space, since the unused buffers are not freed
quickly enough. Forcing the memory unmap via sun.misc.Cleaner does not work
properly under Windows, throwing the error described in the clean(x) method
below. |
Fields Summary |
---|
public static final int | MODE_READ_ONLY | public static final int | MODE_READ_WRITE | public static long | cache_hits | public static long | cache_misses | private static final int | BLOCK_SIZE | private final File | file | private int | access_mode | private FileChannel | channel | private Object[] | mapKeys | private int[] | counts |
Constructors Summary |
---|
public MemoryMappedFile(File file)
this.file = file;
mapKeys = new Object[ new Long(file.length()/BLOCK_SIZE).intValue() + 1 ];
Arrays.fill( counts, 0 );
|
Methods Summary |
---|
private java.nio.MappedByteBuffer | createMappedBuffer(long file_offset, int length)
if ( channel == null ) {
FileChannel fc = new RandomAccessFile( file, "rw" ).getChannel();
MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE, file_offset, length );
if ( access_mode == MODE_READ_ONLY ) fc.close();
else channel = fc;
return mbb;
}
return channel.map( FileChannel.MapMode.READ_WRITE, file_offset, length );
| public int | getAccessMode() return access_mode;
| public void | setAccessMode(int mode)
if ( mode == MODE_READ_ONLY && access_mode == MODE_READ_WRITE && channel != null ) {
try { channel.close(); } catch( Exception e ) { Debug.printStackTrace( e ); }
channel = null;
}
access_mode = mode;
| public void | write(org.gudy.azureus2.core3.util.DirectByteBuffer buffer, int buffer_offset, long file_offset, int length)
if ( access_mode == MODE_READ_ONLY ) {
throw new IOException( "cannot write to a read-only file" );
}
if ( buffer.limit(DirectByteBuffer.SS_OTHER) - buffer_offset < length ) {
throw new IOException( "not enough buffer remaining to write given length" );
}
file.createNewFile();
int key_pos = new Long( file_offset / BLOCK_SIZE ).intValue();
int map_offset = new Long( file_offset % BLOCK_SIZE ).intValue();
int written = 0;
while( written < length ) {
MappedByteBuffer mbb = null;
long f_offset = file_offset + written;
int length_to_write = BLOCK_SIZE - map_offset;
if ( length - written < length_to_write ) length_to_write = length - written;
//try grab the buffer from the pool
if ( mapKeys.length > key_pos ) {
Object key = mapKeys[ key_pos ];
if ( key != null ) {
mbb = MemoryMapPool.getBuffer( key );
//check if the current buff is too small
if ( mbb != null && mbb.capacity() < (map_offset + length_to_write ) ) {
MemoryMapPool.clean( mbb );
mbb = null;
}
}
}
else { //need to extend the key array
mapKeys = new Object[ key_pos * 2 ];
}
//create new buffer, fully-sized, unless it's extending the file
if ( mbb == null ) {
int size = BLOCK_SIZE;
if ( f_offset + length_to_write > file.length() ) {
size = map_offset + length_to_write;
}
mbb = createMappedBuffer( f_offset - map_offset, size );
cache_misses++;
}
else cache_hits++;
//do the write
buffer.position( DirectByteBuffer.SS_OTHER,buffer_offset + written );
buffer.limit( DirectByteBuffer.SS_OTHER,buffer.position(DirectByteBuffer.SS_OTHER) + length_to_write );
mbb.position( map_offset );
mbb.put( buffer.getBuffer(DirectByteBuffer.SS_OTHER) );
written += length_to_write;
//add the buffer (back) to the pool
mapKeys[ key_pos ] = MemoryMapPool.addBuffer( mbb );
key_pos++;
map_offset = 0;
}
|
|