2013-08-03 2 views
5

Есть ли способ Java для предварительного распределения пространства на диске для исключительного использования в приложении?Предварительное распределение места на диске для хранения файлов

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

+0

Wnat Вы имеете в виду с предварительным размещением? Чтобы убедиться, что имеется определенное пространство пространства или доступны определенные адреса? – Jocke

+0

Я хочу зарезервировать N GB для моего приложения, чтобы там не было другого приложения или ОС.Я думаю, что предварительное выделение достаточно большого файла будет работать, но я надеюсь, что есть более эффективные подходы, такие как DB/VFS. – Tosha

ответ

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

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

Подробнее о RandomAccessFile here.

Подробнее о FileLock (объект java) here.

Подробнее о ByteBuffer here.

+0

'setLength' фактически не будет размещать хранилище для файла в большинстве файловых систем, таких как ext4 и xfs, которые по умолчанию используют разреженные файлы. Ответ «fallocate» является наиболее правильным. Тем не менее, его основными преимуществами являются долговечность (нет необходимости в метаданных выделения блоков синхронизации/флеша) и надежности (без SIGBUS в файле с отображением памяти из-за полного диска); производительность может не сильно отличаться. –

+0

@TrevorRobinson Спасибо, я не знал. Глядя на источники OpenJDK, кажется, что linux-варианты управляются с помощью 'lseek()', поэтому вы, вероятно, правы в этом аспекте. Хотя использование 'fallocate()', вероятно, будет работать JNI, если оно используется из java. – initramfs

+0

'setLength' вызывает' ftruncate' (через примитив 'JVM_SetLength') для фактической установки длины в POSIX-системах (' lseek' используется только для перемещения указателя файла). 'ftruncate' не превалирует, поэтому' fallocate' необходим (но нигде не используется в JDK). Здесь я отвечу на JNA-ответ. –

-4

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

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

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

+0

Java не намного медленнее, чем C++, поэтому я не стал бы выбирать между языками по производительности – Jocke

+0

Да, Java работает аналогично C++. Но, как правило, он используется для работы на высоком уровне и в бизнесе - выше ОС, а не в виде голых мелочей. Поскольку JVM является абстракцией, вы обычно не имеете близкого и быстрого доступа к API-интерфейсам большого объема, таким как необработанный диск, захват мультимедиа и т. Д. –

+2

-1 _Не беспокоить_ на самом деле не является ответом. Производительность R/W может быть неважной, но зарезервированное место для файла, который будет заселен позже, будет. Плюс, если мы говорим о дисковых приводах, наличие всех кусков рядом друг с другом, а не по всему месту, может значительно улучшить производительность, особенно. для больших файлов (я знаю, что это старый ответ, но все же ...) – rath

0

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

UPD:

fallocate -l 10G 10Gigfile

+0

Какая команда bash в частности? – JamesThomasMoon1979

+0

@ JamesThomasMoon1979 обновили ответ – odiszapc

7

Вот урезанная версия моего ЮНА на основе 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); 
     } 
    } 
}