1 এমবি বা আরও বেশি জাভা বাইট অ্যারেটি রামের দ্বিগুণ গ্রহণ করে


14

উইন্ডোজ 10 / ওপেনজেডকে 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();
    }
}

ভিজ্যুভ্যালের সাথে কিছুটা গভীরভাবে তাকালে, আমি প্রত্যাশা অনুযায়ী প্রথম ক্ষেত্রে সবকিছু দেখতে পাচ্ছি:

বাইট অ্যারেগুলি 200 এমবি গ্রহণ করে

দ্বিতীয় ক্ষেত্রে, বাইট অ্যারেগুলি ছাড়াও, আমি দেখতে পাই যে বাইট অ্যারেগুলির সমান পরিমাণ র‌্যাম গ্রহণ করছে একই সংখ্যক আন্ত অ্যারেগুলি:

ইনট অ্যারেগুলি অতিরিক্ত 200 এমবি গ্রহণ করে

এই অভ্যন্তরীণ অ্যারেগুলি, যাইহোক, তারা রেফারেন্সযুক্ত তা দেখায় না তবে আমি তাদের আবর্জনা সংগ্রহ করতে পারি না ... (বাইট অ্যারেগুলি যেখানে তারা রেফারেন্স করা হয়েছে ঠিক ঠিক তা দেখায়))

কোন ধারণা এখানে কি ঘটছে?


অ্যারেলিস্টের <বাইট []> বাইট [ব্লক] [] করতে এবং আপনার লুপের জন্য ডেটা পরিবর্তন করার চেষ্টা করুন: ডেটা [i] = নতুন বাইট [1000000]
অ্যারেলিস্টের

অভ্যন্তরীণভাবে জেভিএমের সাথে আরও উন্নত স্থানের জন্য int[]একটি বৃহতকে অনুকরণ করতে ব্যবহার করে এটির সাথে কি কিছু থাকতে পারে byte[]?
জ্যাকব জি।

@JacobG। এটি অবশ্যই অভ্যন্তরীন কিছু দেখায়, তবে গাইডের মধ্যে কোনও ইঙ্গিত পাওয়া যাচ্ছে না ।
কেয়ামান

মাত্র দুটি পর্যবেক্ষণ: 1. আপনি যদি 1024 * 1024 থেকে 16 বিয়োগ করেন তবে মনে হয় এটি প্রত্যাশার মতো কাজ করে। ২. jdk8 এর সাথে আচরণটি আলাদা বলে মনে হচ্ছে তারপরে এখানে কী পর্যবেক্ষণ করা যায়।
দ্বিতীয়

@ সেকেন্ড হ্যাঁ, theন্দ্রজালিক সীমাটি স্পষ্টতই অ্যারেটি 1MB র‌্যাম নেয় কিনা। আমি ধরে নিয়েছি যে আপনি যদি মাত্র 1 টি বিয়োগ করেন তবে রানটাইম দক্ষতার জন্য মেমরিটি প্যাড করা হয় এবং / অথবা অ্যারেগুলির জন্য পরিচালন ওভারহেড 1 এমবিতে গণনা করা হয় ... মজার বিষয় হচ্ছে জেডিকে 8 অন্যরকম আচরণ করে!
জর্জি

উত্তর:


9

এটি যা বর্ণনা করে তা হ'ল G1 আবর্জনা সংগ্রাহকের বাইরে থাকা আচরণ যা সাধারণত 1MB "অঞ্চলগুলিতে" ডিফল্ট হয়ে যায় এবং জাভা 9-তে একটি জেভিএম ডিফল্ট হয়ে যায়, সক্ষম অন্যান্য GC- এর সাথে চালানো বিভিন্ন নম্বর দেয়।

অর্ধেক অঞ্চলের আকারের যে কোনও বস্তুটিকে "হুমংগস" হিসাবে বিবেচনা করা হয় ... হিপ অঞ্চল আকারের একাধিকের চেয়ে সামান্য বড় অবজেক্টগুলির জন্য, এই অব্যবহৃত স্থানটি apੇਰকে খণ্ডিত করে দিতে পারে।

আমি দৌড়ে এসেছি 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

আমরা চাই যে আমাদের 1 এমআইবি byte[]"জি 1 অঞ্চল আকারের অর্ধেকেরও কম" হোক যাতে যুক্তটি -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

জি 1 এর গভীরতর সংক্ষিপ্তসার: https://www.oracle.com/technical-resources/articles/java/g1gc.html

জি 1 এর ক্রাশিং বিশদ: https://docs.oracle.com/en/java/javase/13/gctuning/garbage-first-garbage-collector-tuning.html#GUID-2428DA90-B93D-48E6-B336-A849ADF1C552


সিরিয়াল জিসি এবং লম্বা অ্যারের সাথে আমার একই সমস্যা রয়েছে যা 8 এমবি লাগে (এবং আকারটি 1024-1024-2 এর সাথে ভাল ছিল) এবং G1HeapRegionSize পরিবর্তন করা আমার ক্ষেত্রে কিছুই করেনি
GotoFinal

আমি এ সম্পর্কে অস্পষ্ট। একটি দীর্ঘ []
ড্রেকবোর

@ গোটোফাইনাল, আমি উপরে উল্লিখিত কোনও সমস্যা পর্যবেক্ষণ করি না। আমি কোডটি পরীক্ষা করেছি long[1024*1024]যা G1 সহ 1600M এর প্রত্যাশিত ব্যবহার দেয়, -XX:G1HeapRegionSize[1 এম ব্যবহৃত: 1887, 2 এম ব্যবহৃত: 2097, 4 এম ব্যবহৃত: 3358, 8 এম ব্যবহৃত: 3358, 16 এম ব্যবহৃত: 3363, 32 এম ব্যবহৃত: 1682] সঙ্গে -XX:+UseConcMarkSweepGCব্যবহৃত: তা 1687 সালে উন্নত সঙ্গে -XX:+UseZGCব্যবহৃত: 2105. সাথে -XX:+UseSerialGCব্যবহৃত: 1698
drekbour

gist.github.com/c0a4d0c7cfb335ea9401848a6470e816 ঠিক এর মতো কোড, কোনও জিসি অপশন পরিবর্তন না করে এটি মুদ্রণ করবে used: 417 expected usage: 400তবে আমি যদি -2এটি সরিয়ে ফেলব যে এটি used: 470প্রায় 50MB চলে গেছে, এবং 50 * 2
ল্যাংগুলি

1
একই জিনিস. পার্থক্যটি ~ 50MB, এবং আপনার 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এটা ঠিক 16 বাইট সঞ্চয় করতে অন্য 1MB অঞ্চল বরাদ্দ করা ওই গত দুই জন্য longs বল G1 প্রমাণ করে।
drekbour
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.