1 एमबी या उससे अधिक का जावा बाइट दो बार रैम लेता है


14

Windows 10 पर नीचे कोड रनिंग / OpenJDK 11.0.4_x64 आउटपुट के रूप में पैदा करता है used: 197और expected usage: 200। इसका मतलब यह है कि एक मिलियन तत्वों के 200 बाइट सरणियों का लगभग अनुमान है। 200 एमबी रैम। सब कुछ ठीक है।

जब मैं बाइट सरणी आवंटन कोड में से बदलने new byte[1000000]के लिए new byte[1048576](जो 1024 * 1024 तत्वों के लिए है,), यह आउटपुट के रूप में पैदा करता है used: 417और expected usage: 200। क्या बिल्ली है?

import java.io.IOException;
import java.util.ArrayList;

public class Mem {
    private static Runtime rt = Runtime.getRuntime();
    private static long free() { return rt.maxMemory() - rt.totalMemory() + rt.freeMemory(); }
    public static void main(String[] args) throws InterruptedException, IOException {
        int blocks = 200;
        long initiallyFree = free();
        System.out.println("initially free: " + initiallyFree / 1000000);
        ArrayList<byte[]> data = new ArrayList<>();
        for (int n = 0; n < blocks; n++) { data.add(new byte[1000000]); }
        System.gc();
        Thread.sleep(2000);
        long remainingFree = free();
        System.out.println("remaining free: " + remainingFree / 1000000);
        System.out.println("used: " + (initiallyFree - remainingFree) / 1000000);
        System.out.println("expected usage: " + blocks);
        System.in.read();
    }
}

विजुअल्म के साथ थोड़ा गहरा खोज, मैं पहले मामले में उम्मीद के अनुसार सब कुछ देखता हूं:

बाइट सरणियों 200mb तक ले

दूसरे मामले में, बाइट सरणियों के अलावा, मैं बाइट सरणियों के रूप में रैम की समान मात्रा को लेने वाले int सरणियों की समान संख्या देखता हूं:

int सरणियाँ अतिरिक्त 200mb तक ले जाती हैं

ये int सरणियाँ, वैसे, यह नहीं दिखाती हैं कि उन्हें संदर्भित किया गया है, लेकिन मैं उन्हें इकट्ठा नहीं कर सकता ... (बाइट सरणियाँ ठीक वही दिखाती हैं जहाँ उन्हें संदर्भित किया जाता है।)

किसी भी विचार यहाँ क्या हो रहा है?


ArrayList से डेटा बदलने की कोशिश करें <byte []> को byte [ब्लॉक] [], और अपने लूप में: डेटा [i] = नया बाइट [1000000] ArrayList के आंतरिक पर निर्भरता को खत्म करने के लिए
jynynn2

यह बेहतर स्थानिक इलाके के लिए int[]एक बड़े का अनुकरण करने के लिए आंतरिक रूप से JVM के साथ कुछ करने के लिए हो सकता byte[]है?
जैकब जी।

@JacobG। यह निश्चित रूप से कुछ आंतरिक दिखता है, लेकिन गाइड में कोई संकेत नहीं दिखता है ।
कायमान

सिर्फ दो अवलोकन: 1. यदि आप 1024 * 1024 से 16 घटाते हैं तो ऐसा लगता है कि यह अपेक्षित है। 2. एक jdk8 के साथ व्यवहार अलग-अलग प्रतीत होता है फिर यहां क्या देखा जा सकता है।
दूसरा

@ सेकेंड हां, जादुई सीमा स्पष्ट रूप से है कि सरणी 1 एमबी रैम लेती है या नहीं। मुझे लगता है कि यदि आप सिर्फ 1 को हटाते हैं, तो मेमोरी रनटाइम दक्षता और / या मैनेजमेंट ओवरहेड के लिए 1MB तक मायने रखता है ... मजेदार है कि JDK8 अलग तरह से व्यवहार करता है!
जॉर्ज

जवाबों:


9

यह जो वर्णन करता है वह G1 कचरा कलेक्टर का आउट-ऑफ-द-बॉक्स व्यवहार है जो आमतौर पर 1MB "क्षेत्रों" में चूक जाता है और जावा 9 में एक JVM डिफ़ॉल्ट बन गया है। अन्य GCs सक्षम के साथ चल रहा है अलग-अलग संख्या देता है।

कोई भी वस्तु जो आधे से अधिक क्षेत्र के आकार को "विनम्र" माना जाता है ... उन वस्तुओं के लिए जो ढेर क्षेत्र के आकार के कई गुना से थोड़े बड़े होते हैं, यह अप्रयुक्त स्थान ढेर के टुकड़े हो सकते हैं।

मैं भागा java -Xmx300M -XX:+PrintGCDetailsऔर यह दर्शाता है कि ढेर नम्र क्षेत्रों से समाप्त हो गया है:

[0.202s][info   ][gc,heap        ] GC(51) Old regions: 1->1
[0.202s][info   ][gc,heap        ] GC(51) Archive regions: 2->2
[0.202s][info   ][gc,heap        ] GC(51) Humongous regions: 296->296
[0.202s][info   ][gc             ] GC(51) Pause Full (G1 Humongous Allocation) 297M->297M(300M) 1.935ms
[0.202s][info   ][gc,cpu         ] GC(51) User=0.01s Sys=0.00s Real=0.00s
...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

हम चाहते हैं कि हमारी 1MiB byte[]"आधे से कम G1 क्षेत्र के आकार" की हो, इसलिए इसे जोड़ने -XX:G1HeapRegionSize=4Mसे एक कार्यात्मक अनुप्रयोग मिलता है:

[0.161s][info   ][gc,heap        ] GC(19) Humongous regions: 0->0
[0.161s][info   ][gc,metaspace   ] GC(19) Metaspace: 320K->320K(1056768K)
[0.161s][info   ][gc             ] GC(19) Pause Full (System.gc()) 274M->204M(300M) 9.702ms
remaining free: 100
used: 209
expected usage: 200

G1 के गहन अवलोकन में: https://www.oracle.com/technical-resources/articles/java/g1gc.html

G1 का क्रशिंग विवरण: https://docs.oracle.com/en/java/javase/13/gctuning/garbage-first-garbage-collector-tuning.html#GUID-2428DA90-B93D-48E6-B336-A849ADFF1C552


मैं धारावाहिक GC के साथ और लंबे सरणी के साथ 8MB (और 1024-1024-2 आकार के साथ ठीक था) के साथ और G1HeapRegionSize बदलने से मेरे मामले में कुछ भी नहीं किया
गोटोफाइनल

मैं इस पर अस्पष्ट हूं। क्या आप उपयोग किए गए जावा इनवोकेशन को स्पष्ट कर सकते हैं और उपरोक्त कोड का उत्पादन एक लंबे [] के साथ
drekbour

@GotoFinal, मैं ऊपर बताई गई किसी भी समस्या का पालन नहीं करता। मैंने उस कोड का परीक्षण किया long[1024*1024]जिसके साथ G1 के साथ 1600M का अपेक्षित उपयोग मिलता है, -XX:G1HeapRegionSize[1M प्रयुक्त: 1887, 2M का उपयोग करके भिन्न : 2097, 4M उपयोग किया गया: 3358, 8M उपयोग किया: 3358, 16M उपयोग किया: 3363, 32M उपयोग किया: 1682]। साथ -XX:+UseConcMarkSweepGCप्रयोग किया है: 1687 के साथ -XX:+UseZGCप्रयोग किया है: 2105. के साथ -XX:+UseSerialGCइस्तेमाल किया: 1698
drekbour

gist.github.com/c0a4d0c7cfb335ea9401848a6470e816 जैसे कोई भी जीसी विकल्प बदले बिना, यह प्रिंट कर देगा, used: 417 expected usage: 400लेकिन अगर मैं हटा दूंगा कि -2यह एमबीएमबी में बदल जाएगा used: 470, और 50 * 2
लोंग

1
वही चीज। अंतर ~ 50 एमबी है, और आपके पास 50 "विनम्र" ब्लॉक हैं। यहाँ जीसी विस्तार है: 1024 * 1024 -> [0.297s][info ][gc,heap ] GC(18) Humongous regions: 450->4501024 * 1024-2 -> [0.292s][info ][gc,heap ] GC(20) Humongous regions: 400->400यह साबित करता है कि पिछले दो लॉन्ग फोर्स जी 1 को एक और 1 एमबी क्षेत्र आवंटित करने के लिए सिर्फ 16 बाइट्स में स्टोर करना है।
drekbour
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.