Introduction to Memory-Mapped IO in Java<
Getting a FileChannel object
You can get a FileChannel object that is connected to an underlying file by invoking the getChannel method on an existing FileInputStream, FileOutputStream, or RandomAccessFile object.
A FileChannel object obtained from a RandomAccessFile object will be open for reading if the RandomAccessFile object was created in the "r" mode, and will be open for reading and writing if the object was created in the "rw" mode.
(Note that creating a RandomAccessFile object in rw mode for a particular file name does not delete an existing file having that name if such a file already exists.)
Open channel for reading and writing
The code in Listing 3 begins by deleting a file named junk.txt if such a file already exists.
Then the code in Listing 3 invokes the getChannel method on an anonymous RandomAccessFile object's reference to get a FileChannel object that can read from and write to a new physical file named junk.txt.
new File("junk.txt").delete(); FileChannel rwCh = ( new RandomAccessFile( "junk.txt","rw")). getChannel(); Listing 3
The FileChannel object's reference is stored in the reference variable named rwCh.
(At this point, the file is empty, with a size of zero.)
Populate the physical disk file
The code in Listing 4 writes the contents of the ByteBuffer object to the physical file.
System.out.println( "Bytes written = " + rwCh.write(buf)); Listing 4
As you learned in a previous lesson, the write method of the FileChannel class returns the number of bytes written to the file. The code in Listing 4 displays this value as shown in Figure 2.
Bytes written = 6 Figure 2
Disconnect from the file
At this point, for purposes of illustration, I elected to close the FileChannel object so that it can no longer be used to access the file.
I also elected to make the original ByteBuffer object and the original array object eligible for garbage collection so that their data is no longer accessible.
Following the execution of the code in Listing 5, the data is available only via the file named junk.txt.
rwCh.close(); buf = null; array = null; Listing 5
Get a new FileChannel object
The code in Listing 6 gets a new FileChannel object for reading and writing the existing file named junk.txt.
rwCh = new RandomAccessFile( "junk.txt","rw"). getChannel(); Listing 6
Map the file into memory
Now we've gotten to the interesting part. The code in Listing 7 invokes the map method of the FileChannel class to map the entire file named junk.txt into a memory area as a ByteBuffer object. Then the code in Listing 7 closes the FileChannel object.
long fileSize = rwCh.size(); ByteBuffer mapFile = rwCh.map( FileChannel.MapMode.READ_WRITE, 0, fileSize); rwCh.close(); Listing 7
(Despite the fact that the channel has been closed, the data in the file continues to be available via the memory map, as will be demonstrated later.)
A more-detailed discussion
The code in Listing 7 is very important, and I don't want to pass over it lightly.
Listing 7 begins by invoking the size method on the FileChannel object's reference. The size method returns the current size of the channel's file (junk.txt), measured in bytes and returned as type long.
(At this point, the size of the file is six bytes.)
Invoking the map method
Then the code in Listing 7 uses this size value to invoke the map method on the FileChannel object's reference.
The map method deserves some additional discussion.
The MappedByteBuffer class
To begin with, the map method returns a reference to an object of the MappedByteBuffer class, which I haven't discussed prior to this point in this series of tutorials. Therefore, I will begin by explaining the characteristics of a MappedByteBuffer object.
A MappedByteBuffer object extends ByteBuffer, and is a direct byte buffer whose content is a memory-mapped region of a file.
Created via the map factory method
As you saw in Listing 7, mapped byte buffer objects are created via the map method of the FileChannel class.
The MappedByteBuffer class extends the ByteBuffer class, providing operations that are specific to memory-mapped file regions. Those operations are:
- force - Forces any changes made to this buffer's content to be written to the storage device containing the mapped file.
- isLoaded - Tells whether or not this buffer's content is resident in physical memory.
- load - Loads this buffer's content into physical memory.
Valid until garbage collected
A MappedByteBuffer object and the file mapping that it represents remains valid until the buffer itself is garbage-collected.
Contents can change or become inaccessible
The contents of a MappedByteBuffer object can change at any time. This can happen, for example, if the content of the corresponding region of the mapped file is changed.
(According to sun, whether or not such changes occur, and when they occur, is operating-system dependent and therefore unspecified.)
All or part of a mapped byte buffer may become inaccessible at any time. This can happen, for example, if the mapped file is truncated.
An attempt to access an inaccessible region of a mapped byte buffer will not change the buffer's contents and will cause an unspecified exception to be thrown either at the time of the access or at some later time.
(According to Sun, it is strongly recommended that appropriate precautions be taken to avoid the manipulation of a mapped file by this program, or by a concurrently running program, except to read or write the file's content.)
Page 2 of 4