JDK-8028683 : Memory-Mapped files cannot be renamed
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 7u9
  • Priority: P3
  • Status: Closed
  • Resolution: Won't Fix
  • OS: windows_7
  • Submitted: 2013-03-20
  • Updated: 2013-11-20
  • Resolved: 2013-11-20
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_09 " 
Java (TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot (TM) 64-Bit Server VM (build 23.5-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
MappedByteBuffer does not provide a unmap method to unmap a memory-mapped file. Our application uses memory-mapped IO to read data from memory-mapped archive files. If new data is added to one of the archive files, the old file is deleted and a new file is created. On Windows it is not possible to delete memory-mapped files. As a workaround we renamed mapped files we want to delete and deleted them after the GC has unmapped the file.

With Java Plattform Standard Edition 6.0 it was possible to rename memory-mapped files on Windows using File.renameTo(File dst). Unfortunately that is no longer possible with Java 7.

With Java 7 our only option now seems to be to use

sun.misc.Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
cleaner.clean();

and then delete the file. Please either make it possible to rename memory-mapped files on Windows again or preferably add a unmap method to MappedByteBuffer.



REGRESSION.  Last worked in version 6u31

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Open a file, get the filechannel, then create a mapped byte buffer. Try to rename the file using file.renameTo(dst) or Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING).

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
file.renameTo(dst) or Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING) should rename memory-mapped files on Windows.
ACTUAL -
Memory-Mapped files cannot be renamed on Windows. Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING) will throw an exception:

java.nio.file.FileSystemException: c:\Temp\test.bin -> c:\Temp\test2.bin: The process cannot access the file because it is being used by another process.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.nio.file.FileSystemException: c:\Temp\test.bin -> c:\Temp\test2.bin: The process cannot access the file because it is being used by another process.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package mappedbytebuffertest;

import sun.nio.ch.DirectBuffer;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class Main {

    public static void main(String[] args) throws Exception {
        File file =  new File( " c:\\Temp\\test.bin " );
        // create file and add some data to it if it does not already exist
        if (!file.exists()) {
            FileOutputStream fout = new FileOutputStream(file);
            DataOutputStream dout = new DataOutputStream(fout);
            for(int i = 0; i < 1024; i++) dout.write(i);
            dout.close();
            fout.close();
        }

        // now map the file
        FileInputStream is = new FileInputStream(file);
        MappedByteBuffer buffer =  is.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, is.getChannel().size());
        is.close();

        // now try to rename the file
        File dst = new File( " c:\\Temp\\test2.bin " );
        if (file.renameTo(dst)) System.out.println( " Renamed file to  "  + dst); else System.out.println( " Could not rename file to  "  + dst);

        // try the new JDK 7 API
        Path path = file.toPath();
        Path dstPath = dst.toPath();
        try {
            Files.move(path, dstPath, StandardCopyOption.REPLACE_EXISTING);
            System.out.println( " Renamed file to  "  + dstPath);
        } catch (IOException ex) {
            System.out.println( " Could not rename file to  "  + dstPath +  "  reason:  "  + ex);
        }

        // now unmap the file
        sun.misc.Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
        cleaner.clean();

        // try the new JDK 7 API
        try {
            Files.move(path, dstPath, StandardCopyOption.REPLACE_EXISTING);
            System.out.println( " Renamed file to  "  + dstPath);
        } catch (IOException ex) {
            System.out.println( " Could not rename file to  "  + dstPath +  "  reason:  "  + ex);
        }
    }
}

Running this programm will produce the following output:

Could not rename file to c:\Temp\test2.bin
Could not rename file to c:\Temp\test2.bin reason: java.nio.file.FileSystemException: c:\Temp\test.bin -> c:\Temp\test2.bin: The process cannot access the file because it is being used by another process.

Renamed file to c:\Temp\test2.bin
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Unmap the file before renaming it, using:

sun.misc.Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
cleaner.clean();
Comments
As per previous comment, the details in JDK-4724038 are the background as to why there isn't an unmap method. As regards deleting or renaming when there is a file-mapping then this is a Windows operating system issues (the rename issue may be because it is necessary to keep an open handle while the mapping exists - more details on this can be found in JDK-6816049).
20-11-2013

Deferring to dev team for decision.
19-11-2013

See write-up in JDK-4724038
21-03-2013