संपादित/चेतावनी: इस समाधान के साथ संभावित गठिया हैं, क्योंकि यह MappedByteBuffer
का भारी उपयोग करता है, और यह स्पष्ट नहीं है कि संबंधित संसाधन कैसे जारी किए जाते हैं। this Q&A & JDK-4724038 : (fs) Add unmap method to MappedByteBuffer देखें। एक वर्ग है जो "ब्लॉक" और में नक्शे में
रैप इस:
कहा जा रहा है, यह भी इस पोस्ट के अंत
मैं वास्तव में क्या Nim suggested करना होगा देखने के लिए कृपया फिर जब आप लिख रहे हों तो ब्लॉक को ले जाया जाएगा .. इसके लिए एल्गोरिदम काफी सरल है .. बस एक ब्लॉक आकार चुनें जो आपके द्वारा लिखे गए डेटा के लिए समझ में आता है ..
वास्तव में, मैं वास्तव में किया था कि साल पहले और बस कोड को खोदा, यह (एक डेमो के लिए न्यूनतम करने के लिए छीन लिया, एक भी विधि के साथ डेटा लिखने के लिए) इस प्रकार है:
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
public class SlidingFileWriterThingy {
private static final long WINDOW_SIZE = 8*1024*1024L;
private final RandomAccessFile file;
private final FileChannel channel;
private MappedByteBuffer buffer;
private long ioOffset;
private long mapOffset;
public SlidingFileWriterThingy(Path path) throws IOException {
file = new RandomAccessFile(path.toFile(), "rw");
channel = file.getChannel();
remap(0);
}
public void close() throws IOException {
file.close();
}
public void seek(long offset) {
ioOffset = offset;
}
public void writeBytes(byte[] data) throws IOException {
if (data.length > WINDOW_SIZE) {
throw new IOException("Data chunk too big, length=" + data.length + ", max=" + WINDOW_SIZE);
}
boolean dataChunkWontFit = ioOffset < mapOffset || ioOffset + data.length > mapOffset + WINDOW_SIZE;
if (dataChunkWontFit) {
remap(ioOffset);
}
int offsetWithinBuffer = (int)(ioOffset - mapOffset);
buffer.position(offsetWithinBuffer);
buffer.put(data, 0, data.length);
}
private void remap(long offset) throws IOException {
mapOffset = offset;
buffer = channel.map(FileChannel.MapMode.READ_WRITE, mapOffset, WINDOW_SIZE);
}
}
यहाँ एक परीक्षण टुकड़ा है:
SlidingFileWriterThingy t = new SlidingFileWriterThingy(Paths.get("/tmp/hey.txt"));
t.writeBytes("Hello world\n".getBytes(StandardCharsets.UTF_8));
t.seek(1000);
t.writeBytes("Are we there yet?\n".getBytes(StandardCharsets.UTF_8));
t.seek(50_000_000);
t.writeBytes("No but seriously?\n".getBytes(StandardCharsets.UTF_8));
और क्या आउटपुट फ़ाइल की तरह दिखता है:
$ hexdump -C /tmp/hey.txt
00000000 48 65 6c 6c 6f 20 77 6f 72 6c 64 0a 00 00 00 00 |Hello world.....|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000003e0 00 00 00 00 00 00 00 00 41 72 65 20 77 65 20 74 |........Are we t|
000003f0 68 65 72 65 20 79 65 74 3f 0a 00 00 00 00 00 00 |here yet?.......|
00000400 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
02faf080 4e 6f 20 62 75 74 20 73 65 72 69 6f 75 73 6c 79 |No but seriously|
02faf090 3f 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |?...............|
02faf0a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
037af080
मुझे आशा है कि मैं रुई नहीं था अनावश्यक बिट्स और नामकरण को हटाकर सब कुछ ... कम से कम ऑफसेट गणना सही दिखती है (0x3e0 + 8 = 1000, और 0x02faf080 = 50000000)।
ब्लॉक (बाएँ स्तंभ) फ़ाइल के कब्जे की संख्या, और एक ही आकार के एक और गैर स्पार्स फ़ाइल:
$ head -c 58388608 /dev/zero > /tmp/not_sparse.txt
$ ls -ls /tmp/*.txt
8 -rw-r--r-- 1 nug nug 58388608 Jul 19 00:50 /tmp/hey.txt
57024 -rw-r--r-- 1 nug nug 58388608 Jul 19 00:58 /tmp/not_sparse.txt
ब्लॉक (और वास्तविक "विरल") की संख्या ओएस & पर निर्भर करेगा फाइल सिस्टम, उपर्युक्त डेबियन बस्टर पर था, ext4 - मैकोज़ के लिए स्पैस फाइलें एचएफएस + पर समर्थित नहीं हैं, और विंडोज़ पर उन्हें प्रोग्राम को कुछ विशिष्ट करने की आवश्यकता होती है जिसे मैं पर्याप्त नहीं जानता, लेकिन यह आसान या यहां तक कि करने योग्य नहीं लगता है जावा से, सुनिश्चित नहीं है।
मेरे पास ताजा संख्या नहीं है लेकिन उस समय "स्लाइडिंग -MappedByteBuffer
तकनीक" बहुत तेज थी, और जैसा कि आप ऊपर देख सकते हैं, यह फ़ाइल में छेद छोड़ देता है।
आपको WINDOW_SIZE
को आपके लिए समझ में आने वाली किसी चीज़ को अनुकूलित करने की आवश्यकता होगी, जो आपको विधियों को जोड़कर, writeBytes
को लपेटकर, जो कुछ भी आपको उपयुक्त बनाता है, जोड़ दें। साथ ही, इस स्थिति में यह आवश्यकतानुसार फ़ाइल को बढ़ाएगा, लेकिन WINDOW_SIZE
के हिस्सों से, जिसे आपको अनुकूलित करने की भी आवश्यकता हो सकती है।
जब तक कोई बहुत अच्छा कारण नहीं है, तो जटिल जटिल दोहरी-मोड प्रणाली को बनाए रखने के बजाय, यह एकल तंत्र के साथ इसे सरल रखना सबसे अच्छा है।
कमजोरी और स्मृति की खपत के बारे में, मैं राम की 800GB के साथ एक घंटे के लिए किसी भी मुद्दे के बिना लिनक्स पर नीचे तनाव परीक्षण भाग गया गया है, एक मशीन पर, और रैम 1G के साथ एक और बहुत मामूली वी एम पर । सिस्टम पूरी तरह स्वस्थ दिखता है, जावा प्रक्रिया किसी भी महत्वपूर्ण मात्रा में हीप मेमोरी का उपयोग नहीं करती है।
String path = "/tmp/data.txt";
SlidingFileWriterThingy w = new SlidingFileWriterThingy(Paths.get(path));
final long MAX = 5_000_000_000L;
while (true) {
long offset = 0;
while (offset < MAX) {
offset += Math.pow(Math.random(), 4) * 100_000_000;
if (offset > MAX/5 && offset < 2*MAX/5 || offset > 3*MAX/5 && offset < 4*MAX/5) {
// Keep 2 big "empty" bands in the sparse file
continue;
}
w.seek(offset);
w.writeBytes(("---" + new Date() + "---").getBytes(StandardCharsets.UTF_8));
}
w.seek(0);
System.out.println("---");
Scanner output = new Scanner(new ProcessBuilder("sh", "-c", "ls -ls " + path + "; free")
.redirectErrorStream(true).start().getInputStream());
while (output.hasNextLine()) {
System.out.println(output.nextLine());
}
Runtime r = Runtime.getRuntime();
long memoryUsage = (100 * (r.totalMemory() - r.freeMemory()))/r.totalMemory();
System.out.println("Mem usage: " + memoryUsage + "%");
Thread.sleep(1000);
}
तो हाँ प्रयोग पर आधारित है, हो सकता है यह केवल हाल ही में Linux सिस्टम पर ठीक से काम करता है, शायद यह सिर्फ उस विशेष कार्यभार के साथ भाग्य है ... लेकिन मैं इसे कुछ प्रणालियों और पर एक मान्य समाधान है सोचने के लिए शुरू कर वर्कलोड, यह उपयोगी हो सकता है।
क्या फ़ाइल का आकार तय किया गया है? या क्या कुंजी के आधार पर इसे विकसित करने की आवश्यकता है? मैं लिखने के संचालन के लिए बस 'मैप्डबेट बफर' का उपयोग करूंगा .. अगर फ़ाइल बहुत बड़ी है या बढ़ने की जरूरत है, तो मैं इसे एक वर्ग में लपेटूंगा जो "ब्लॉक" में मैप करता है और फिर जब आप लिख रहे हों तो ब्लॉक को ले जाया जाएगा .. इसके लिए एल्गोरिदम काफी सरल है .. बस एक ब्लॉक आकार चुनें जो आपके द्वारा लिखे गए डेटा के लिए समझ में आता है .. – Nim
फ़ाइल का आकार समय से पहले ज्ञात नहीं है। फ़ाइल नेटवर्क ड्राइव पर हो सकती है - मुझे यकीन नहीं है कि यह आपके समाधान को प्रभावित करता है – rghome
'java.nio.channels' पर एक नज़र डालें। आप 'FileChannel' के साथ यादृच्छिक पहुंच कर सकते हैं, और buffered डेटा लिख सकते हैं। – teppic