Сила Java MemoryMapped File
В JDK 1.4 была добавлена интересная особенность файла отображения памяти в Java, которая позволяет отображать любой файл в память ОС для эффективного чтения. Файл с отображенной памятью может использоваться для разработки решения типа IPC . Эта статья представляет собой эксперимент с отображенным в память файлом для создания IPC .
Некоторые подробности о Memory Mapped File, определение из WIKI
Файл с отображением в памяти — это сегмент виртуальной памяти, которому была назначена прямая побайтная корреляция с некоторой частью файла или файлового ресурса. Этот ресурс обычно является файлом, который физически присутствует на диске, но также может быть устройством, объектом общей памяти или другим ресурсом, на который операционная система может ссылаться через дескриптор файла. Когда-то эта корреляция между файлом и пространством памяти позволяет приложениям обрабатывать отображенную часть, как если бы она была первичной памятью.
Пример программы
Ниже у нас есть две программы на Java, одна из которых является писателем, а другая — читателем. Автор является производителем и пытается записать в файл сопоставления памяти, читатель является потребителем, и он читает сообщения из файла сопоставления памяти. Это просто пример программы, чтобы показать вам идею, она не обрабатывает много крайних случаев, но она достаточно хороша, чтобы построить что-то поверх файла с отображенной памятью.
MemoryMapWriter
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class MemoryMapWriter { public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException { File f = new File( "c:/tmp/mapped.txt" ); f.delete(); FileChannel fc = new RandomAccessFile(f, "rw" ).getChannel(); long bufferSize= 8 * 1000 ; MappedByteBuffer mem =fc.map(FileChannel.MapMode.READ_WRITE, 0 , bufferSize); int start = 0 ; long counter= 1 ; long HUNDREDK= 100000 ; long startT = System.currentTimeMillis(); long noOfMessage = HUNDREDK * 10 * 10 ; for (;;) { if (!mem.hasRemaining()) { start+=mem.position(); mem =fc.map(FileChannel.MapMode.READ_WRITE, start, bufferSize); } mem.putLong(counter); counter++; if (counter > noOfMessage ) break ; } long endT = System.currentTimeMillis(); long tot = endT - startT; System.out.println(String.format( "No Of Message %s , Time(ms) %s " ,noOfMessage, tot)) ; } } |
MemoryMapReader
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class MemoryMapReader { /** * @param args * @throws IOException * @throws FileNotFoundException * @throws InterruptedException */ public static void main(String[] args) throws FileNotFoundException, IOException, InterruptedException { FileChannel fc = new RandomAccessFile( new File( "c:/tmp/mapped.txt" ), "rw" ).getChannel(); long bufferSize= 8 * 1000 ; MappedByteBuffer mem = fc.map(FileChannel.MapMode.READ_ONLY, 0 , bufferSize); long oldSize=fc.size(); long currentPos = 0 ; long xx=currentPos; long startTime = System.currentTimeMillis(); long lastValue=- 1 ; for (;;) { while (mem.hasRemaining()) { lastValue=mem.getLong(); currentPos += 8 ; } if (currentPos < oldSize) { xx = xx + mem.position(); mem = fc.map(FileChannel.MapMode.READ_ONLY,xx, bufferSize); continue ; } else { long end = System.currentTimeMillis(); long tot = end-startTime; System.out.println(String.format( "Last Value Read %s , Time(ms) %s " ,lastValue, tot)); System.out.println( "Waiting for message" ); while ( true ) { long newSize=fc.size(); if (newSize>oldSize) { oldSize = newSize; xx = xx + mem.position(); mem = fc.map(FileChannel.MapMode.READ_ONLY,xx , oldSize-xx); System.out.println( "Got some data" ); break ; } } } } } } |
наблюдение
Использование отображенного в память файла может быть очень хорошим вариантом для развития межпроцессного взаимодействия, пропускная способность которого также достаточно высока как для производителя, так и для потребителя. Статистика производительности по производителю и потребителю вместе:
Каждое сообщение представляет собой один длинный номер
Продукт — 10 миллионов сообщений — 16 (s)
Потребитель — 10 миллионов сообщений 0,6 (с)
Очень простое сообщение используется, чтобы показать вам идею, но это может быть любой тип сложного сообщения, но при наличии сложной структуры данных сериализация может добавить к накладным расходам. Есть много методов, чтобы преодолеть эти накладные расходы. Подробнее в следующем блоге.