Предварительное выделение дискового пространства для хранения файлов


Существует ли способ Java предварительно выделить дисковое пространство для исключительного использования в приложении?

Нет необходимости в том, чтобы это пространство было отдельной файловой системой или частью существующей файловой системы (таким образом, это может быть база данных), но оно должно позволять резервировать указанный объем пространства и допускать случайные операции чтения/записи с достаточно высокой пропускной способностью.

4 5

4 ответа:

Можно попробовать использовать объект 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 затем можно использовать для чтения / записи в файл. Убедитесь, что в целевой файловой системе достаточно свободного места. Пространство не может быть "эксклюзивным", но вы всегда можете использовать блокировки файлов для этого.

P. S: Если вы в конечном итоге осознаете, что жесткие диски медленные и бесполезные (или предназначенный для использования оперативной памяти с самого начала) вы можете использовать объект ByteBuffer из java.НИО. Методов allocate() и allocateDirect() должно быть более чем достаточно. Байтовый буфер будет выделен в оперативную память (и возможный SwapFile) и будет исключительным для этой java-программы. Произвольный доступ может быть осуществлен путем изменения положения буфера. Так как эти буферы используют целые числа со знаком для опорной позиции, максимальные размеры ограничены 2^31-1.

Подробнее о RandomAccessFile Здесь .

Подробнее о FileLock (объекте java) читайте здесь.

Подробнее о ByteBuffer читайте здесь.

Вот урезанная версия моего решения на основе JNA fallocate. Основная хитрость заключается в получении собственного файлового дескриптора. До сих пор я тестировал его только на Linux, но он должен работать на всех современных системах POSIX/non-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);
        }
    }
}

В системах Linux можно использовать системный вызов fallocate (). Это очень быстро. Просто запустите команду Bash.

UPD:

fallocate -l 10G 10Gigfile

Вы можете предварительно выделить место, написав большой файл, но, честно говоря, я бы не стал беспокоиться. Производительность будет довольно хорошей/ вероятно, лучше, чем вам нужно.

Если бы вам действительно нужна была производительность, вы бы писали C++/C# и делали RAW I / O.

Но это обычно делается только при написании движка СУБД, захвата больших объемов мультимедиа или аналогичного.