Сила 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 (с)
Очень простое сообщение используется, чтобы показать вам идею, но это может быть любой тип сложного сообщения, но при наличии сложной структуры данных сериализация может добавить к накладным расходам. Есть много методов, чтобы преодолеть эти накладные расходы. Подробнее в следующем блоге.