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
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
[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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With