Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unmap a file from memory mapped using FileChannel in java?

I am mapping a file("sample.txt") to memory using FileChannel.map() and then closing the channel using fc.close(). After this when I write to the file using FileOutputStream, I am getting the following error:

java.io.FileNotFoundException: sample.txt (The requested operation cannot be per formed on a file with a user-mapped section open)

File f = new File("sample.txt"); RandomAccessFile raf = new RandomAccessFile(f,"rw"); FileChannel fc = raf.getChannel(); MappedByteBuffer mbf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); fc.close(); raf.close();  FileOutputStream fos = new FileOutputStream(f); fos.write(str.getBytes()); fos.close(); 

I presume this may be due to file being still mapped to the memory even after I close the FileChannel. Am I right?. If so, how can I "unmap" the file from memory?(I can't find any methods for this in the API). Thanks.

Edit: Looks like it(adding an unmap method) was submitted as RFE to sun some time back: http://bugs.sun.com/view_bug.do?bug_id=4724038

like image 676
learner135 Avatar asked Jun 04 '10 09:06

learner135


2 Answers

Following static method could be used:

public static void unmap(MappedByteBuffer buffer) {    sun.misc.Cleaner cleaner = ((DirectBuffer) buffer).cleaner();    cleaner.clean(); } 

But this is unsafe solution because of following:
1) Lead to failures if someone use MappedByteBuffer after unmap
2) It relies on MappedByteBuffer implementation details

like image 78
Timur Yusupov Avatar answered Sep 19 '22 04:09

Timur Yusupov


[WinXP,SunJDK1.6] I had a mapped ByteBuffer taken from filechannel. After reading SO posts finally managed to call a cleaner through reflection without any sun.* package imports. No longer file lock is lingering.

edit Added JDK9+ code(Luke Hutchison).

private static void closeDirectBuffer(ByteBuffer cb) {     if (cb==null || !cb.isDirect()) return;     // we could use this type cast and call functions without reflection code,     // but static import from sun.* package is risky for non-SUN virtual machine.     //try { ((sun.nio.ch.DirectBuffer)cb).cleaner().clean(); } catch (Exception ex) { }      // JavaSpecVer: 1.6, 1.7, 1.8, 9, 10     boolean isOldJDK = System.getProperty("java.specification.version","99").startsWith("1.");       try {         if (isOldJDK) {             Method cleaner = cb.getClass().getMethod("cleaner");             cleaner.setAccessible(true);             Method clean = Class.forName("sun.misc.Cleaner").getMethod("clean");             clean.setAccessible(true);             clean.invoke(cleaner.invoke(cb));         } else {             Class unsafeClass;             try {                 unsafeClass = Class.forName("sun.misc.Unsafe");             } catch(Exception ex) {                 // jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,                 // but that method should be added if sun.misc.Unsafe is removed.                 unsafeClass = Class.forName("jdk.internal.misc.Unsafe");             }             Method clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class);             clean.setAccessible(true);             Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");             theUnsafeField.setAccessible(true);             Object theUnsafe = theUnsafeField.get(null);             clean.invoke(theUnsafe, cb);         }     } catch(Exception ex) { }     cb = null; } 

Ideas were taken from these posts.
* How to unmap a file from memory mapped using FileChannel in java?
* Examples of forcing freeing of native memory direct ByteBuffer has allocated, using sun.misc.Unsafe?
* https://github.com/elasticsearch/elasticsearch/blob/master/src/main/java/org/apache/lucene/store/bytebuffer/ByteBufferAllocator.java#L40

like image 29
Whome Avatar answered Sep 19 '22 04:09

Whome