В последнее время в проекте есть требование реализовать копирование файлов.В java чтение и запись потока копирования файлов, легко думать о InputStream и OutputStream в IO, но посмотрев в интернете, есть много способов скопировать файлы, кроме IO, а также классы инструментов, предоставляемые NIO, Apache, и метод копирования файлов, поставляемый с JDK
Копия ввода-вывода
public class IOFileCopy {
private static final int BUFFER_SIZE = 1024;
public static void copyFile(String source, String target) {
long start = System.currentTimeMillis();
try(InputStream in = new FileInputStream(new File(source));
OutputStream out = new FileOutputStream(new File(target))) {
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
System.out.println(String.format("IO file copy cost %d msc", System.currentTimeMillis() - start));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Процесс чтения файла в традиционном IO можно разделить на следующие этапы:
-
Ядро считывает данные с диска в буфер.Этот процесс выполняется оператором диска для чтения данных с диска в буфер ядра с помощью операций прямого доступа к памяти.Этот процессНе зависит от процессора
-
Пользовательский процесс извлекает данные из буфера ядракопироватьв буфер пользовательского пространства
-
Пользовательский процесс считывает данные из буфера пользовательского пространства
Копия NIO
Есть два способа реализовать копирование файлов в NIO: один — через каналы, но через отображение файловой памяти в память.
public class NIOFileCopy {
public static void copyFile(String source, String target) {
long start = System.currentTimeMillis();
try(FileChannel input = new FileInputStream(new File(source)).getChannel();
FileChannel output = new FileOutputStream(new File(target)).getChannel()) {
output.transferFrom(input, 0, input.size());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(String.format("NIO file copy cost %d msc", System.currentTimeMillis() - start));
}
}
Карта памяти файлов:
Сравните адрес пространства ядра с виртуальным адресом пространства пользователя.сопоставить с одним и тем же физическим адресом, оборудование прямого доступа к памяти теперь может заполнять буферы, видимые как для процессов ядра, так и для процессов пользовательского пространства. Пользовательский процесс напрямуючитать по памятиДля файлового содержимого приложению нужно иметь дело только с памятью, и ему не нужно копировать буфер туда и обратно, что значительно повышает эффективность копирования ввода-вывода. загрузить отображаемый файл памятиИспользуемая память находится за пределами области кучи Java
public class NIOFileCopy2 {
public static void copyFile(String source, String target) {
long start = System.currentTimeMillis();
try(FileInputStream fis = new FileInputStream(new File(source));
FileOutputStream fos = new FileOutputStream(new File(target))) {
FileChannel sourceChannel = fis.getChannel();
FileChannel targetChannel = fos.getChannel();
MappedByteBuffer mappedByteBuffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, sourceChannel.size());
targetChannel.write(mappedByteBuffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(String.format("NIO memory reflect file copy cost %d msc", System.currentTimeMillis() - start));
File targetFile = new File(target);
targetFile.delete();
}
}
Копирование файла с отображением в память NIO можно разделить на следующие этапы.
Files#copyFile метод
public class FilesCopy {
public static void copyFile(String source, String target) {
long start = System.currentTimeMillis();
try {
File sourceFile = new File(source);
File targetFile = new File(target);
Files.copy(sourceFile.toPath(), targetFile.toPath());
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(String.format("FileCopy file copy cost %d msc", System.currentTimeMillis() - start));
}
}
FileUtils#copyFile метод
Перед использованием FileUtils необходимо ввести зависимости
-
полагаться
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency>
-
Класс пакета FileUtils#copyFile: FileUtilsCopy.java
public class FileUtilsCopy { public static void copyFile(String source, String target) { long start = System.currentTimeMillis(); try { FileUtils.copyFile(new File(source), new File(target)); } catch (IOException e) { e.printStackTrace(); } System.out.println(String.format("FileUtils file copy cost %d msc", System.currentTimeMillis() - start)); } }
Сравнение производительности
Поскольку методов реализации очень много, мы должны выбрать среди них наилучшую производительность.
тестовая среда:
- windows 10
- ЦП 6 ядер
- JDK1.8
Код теста: PerformTest.java
public class PerformTest {
private static final String source1 = "input/test1.txt";
private static final String source2 = "input/test2.txt";
private static final String source3 = "input/test3.txt";
private static final String source4 = "input/test4.txt";
private static final String target1 = "output/test1.txt";
private static final String target2 = "output/test2.txt";
private static final String target3 = "output/test3.txt";
private static final String target4 = "output/test4.txt";
public static void main(String[] args) {
IOFileCopy.copyFile(source1, target1);
NIOFileCopy.copyFile(source2, target2);
FilesCopy.copyFile(source3, target3);
FileUtilsCopy.copyFile(source4, target4);
}
}
Всего было выполнено пять запусков, а размеры файлов для чтения и записи составляли 9 КБ, 23 КБ, 239 КБ, 1,77 МБ и 12,7 МБ соответственно.
Примечание. Единица измерения — миллисекунды.
Из результата выполнения:
-
Когда файл маленький => IO > NIO [карта памяти] > NIO [конвейер] > Files#copy > FileUtils#copyFile
-
Когда файл маленький => NIO [карта памяти] > IO > NIO [конвейер] > Files#copy > FileUtils#copyFile
-
Когда файл большой => NIO [карта памяти] > > NIO [конвейер] > IO > Files#copy > FileUtils#copyFile
-
Изменение размера буфера ввода-вывода влияет на эффективность копирования, но дело не в том, что чем больше, тем выше производительность, тем больше размер копируемого файла.
Когда размер файла мал, эффективность ввода-вывода выше, чем у NIO, базовая реализация NIO сложнее, а преимущества NIO не очевидны. В то же время инициализация сопоставления памяти NIO занимает много времени, поэтому у нее нет преимуществ перед репликацией IO, когда файл небольшой.
Если вы стремитесь к эффективности, вы можете выбрать отображение памяти NIO для реализации копирования файлов, но при использовании копирования с отображением памяти для больших файлов вы должны уделять особое внимание использованию системной памяти. рекомендовать:большой файлкопировать использованиекарта памяти, исходный текст такой:
For most operating systems, mapping a file into memory is more
expensive than reading or writing a few tens of kilobytes of data via
the usual {@link #read read} and {@link #write write} methods. From the
standpoint of performance it is generally only worth mapping relatively
large files into memory
Накладные расходы на отображение памяти в большинстве операционных систем больше, чем накладные расходы на ввод-вывод.
В то же время, по результатам тестирования, эффект от метода копирования файлов, предоставляемого классом инструмента и JDK, невелик.Если не гнаться за эффективностью, то все равно можно использовать.Ведь если написать один строки кода меньше, вы можете написать на одну строку кода меньше Писать код не радость рыбалки.
Последняя статья годичной давности, боюсь, что в 30-ю ночь слишком много благословений, вы не увидите моего приветствия, здесь я заранее желаю всем удачи в Новом году.
Наконец прикрепил:тестовый код