6

是否有 Java 方法可以预先分配驱动器空间以供应用程序中的独占使用?

该空间不需要是单独的文件系统或现有文件系统的一部分(因此很容易成为数据库),但它应该允许保留指定数量的空间并允许具有足够高吞吐量的随机读/写。

4

4 回答 4

9

这是我基于 JNA 的解决方案的精简版fallocate。主要技巧是获取本机文件描述符。到目前为止,我只在 Linux 上测试过它,但它应该适用于所有现代 POSIX/非 Windows 系统。在 Windows 上没有必要,因为默认情况下 Windows 不会创建稀疏文件(仅使用StandardOpenOption.SPARSE),所以RandomAccessFile.setLength(size)FileChannel.write(ByteBuffer.allocate(1), size - 1)在那里就足够了。

/**
 * Provides access to operating system-specific {@code fallocate} and
 * {@code posix_fallocate} functions.
 */
public final class Fallocate {

    private static final boolean IS_LINUX = Platform.isLinux();
    private static final boolean IS_POSIX = !Platform.isWindows();

    private static final int FALLOC_FL_KEEP_SIZE = 0x01;

    private final int fd;
    private int mode;
    private long offset;
    private final long length;

    private Fallocate(int fd, long length) {
        if (!isSupported()) {
            throwUnsupported("fallocate");
        }
        this.fd = fd;
        this.length = length;
    }

    public static boolean isSupported() {
        return IS_POSIX;
    }

    public static Fallocate forChannel(FileChannel channel, long length) {
        return new Fallocate(getDescriptor(channel), length);
    }

    public static Fallocate forDescriptor(FileDescriptor descriptor, long length) {
        return new Fallocate(getDescriptor(descriptor), length);
    }

    public Fallocate fromOffset(long offset) {
        this.offset = offset;
        return this;
    }

    public Fallocate keepSize() {
        requireLinux("fallocate keep size");
        mode |= FALLOC_FL_KEEP_SIZE;
        return this;
    }

    private void requireLinux(String feature) {
        if (!IS_LINUX) {
            throwUnsupported(feature);
        }
    }

    private void throwUnsupported(String feature) {
        throw new UnsupportedOperationException(feature +
                " is not supported on this operating system");
    }

    public void execute() throws IOException {
        final int errno;
        if (IS_LINUX) {
            final int result = FallocateHolder.fallocate(fd, mode, offset, length);
            errno = result == 0 ? 0 : Native.getLastError();
        } else {
            errno = PosixFallocateHolder.posix_fallocate(fd, offset, length);
        }
        if (errno != 0) {
            throw new IOException("fallocate returned " + errno);
        }
    }

    private static class FallocateHolder {

        static {
            Native.register(Platform.C_LIBRARY_NAME);
        }

        private static native int fallocate(int fd, int mode, long offset, long length);
    }

    private static class PosixFallocateHolder {

        static {
            Native.register(Platform.C_LIBRARY_NAME);
        }

        private static native int posix_fallocate(int fd, long offset, long length);
    }

    private static int getDescriptor(FileChannel channel) {
        try {
            // sun.nio.ch.FileChannelImpl declares private final java.io.FileDescriptor fd
            final Field field = channel.getClass().getDeclaredField("fd");
            field.setAccessible(true);
            return getDescriptor((FileDescriptor) field.get(channel));
        } catch (final Exception e) {
            throw new UnsupportedOperationException("unsupported FileChannel implementation", e);
        }
    }

    private static int getDescriptor(FileDescriptor descriptor) {
        try {
            // Oracle java.io.FileDescriptor declares private int fd
            final Field field = descriptor.getClass().getDeclaredField("fd");
            field.setAccessible(true);
            return (int) field.get(descriptor);
        } catch (final Exception e) {
            throw new UnsupportedOperationException("unsupported FileDescriptor implementation", e);
        }
    }
}
于 2015-05-28T17:11:29.493 回答
8

您可以尝试使用 RandomAccessFile 对象并使用 setLength() 方法。

例子:

File file = ... //Create a temporary file on the filesystem your trying to reserve.
long bytes = ... //number of bytes you want to reserve.

RandomAccessFile rf = null;
try{
    rf = new RandomAccessFile(file, "rw"); //rw stands for open in read/write mode.
    rf.setLength(bytes); //This will cause java to "reserve" memory for your application by inflating/truncating the file to the specific size.

    //Do whatever you want with the space here...
}catch(IOException ex){
    //Handle this...
}finally{
    if(rf != null){
        try{
            rf.close(); //Lets be nice and tidy here.
        }catch(IOException ioex){
            //Handle this if you want...
        }
    }
}

注意:在您创建 RandomAccessFile 对象之前,该文件必须存在。

然后可以使用 RandomAccessFile 对象读取/写入文件。确保目标文件系统有足够的可用空间。该空间可能不是“独占”的,但您始终可以使用文件锁来做到这一点。

PS:如果您最终意识到硬盘驱动器很慢且无用(或从一开始就打算使用 RAM),您可以使用 java.nio 中的 ByteBuffer 对象。allocate() 和 allocateDirect() 方法应该绰绰有余。字节缓冲区将被分配到 RAM(和可能的 SwapFile)中,并且是这个 java 程序独有的。可以通过更改缓冲区的位置来完成随机访问。由于这些缓冲区使用有符号整数来引用位置,因此最大大小限制为 2^31 - 1。

在此处阅读有关 RandomAccessFile的更多信息。

在此处阅读有关 FileLock(java 对象)的更多信息。

在此处阅读有关 ByteBuffer的更多信息。

于 2013-08-03T20:07:58.197 回答
0

在 Linux 系统上,您可以使用 fallocate() 系统调用。它非常快。只需运行 Bash 命令。

升级版:

fallocate -l 10G 10Gigfile

于 2013-12-06T00:41:52.843 回答
-5

您可以通过编写一个大文件来预先分配空间,但老实说我不会打扰。性能会非常好/可能比你需要的更好。

如果您真的需要性能,您将编写 C++/C# 并执行 RAW I/O。

但这通常只在编写 RDBMS 引擎、大容量媒体捕获或类似内容时完成。

于 2013-08-03T10:33:57.280 回答