[Java] لماذا يتم إجراء ممارسة سيئة لاستدعاء System.gc ()؟


Answers

تم بالفعل شرح أن استدعاء system.gc() قد لا تفعل شيئا ، وأن أي رمز "يحتاج" لجمع القمامة لتشغيل هو كسر.

ومع ذلك ، فإن السبب البراغماتية أنه من الممارسات السيئة استدعاء System.gc() أنه غير فعال. وفي أسوأ الحالات ، هو غير فعال بشكل فظيع ! دعني أشرح.

تقوم خوارزمية GC النموذجية بتعريف القمامة عن طريق اجتياز جميع الكائنات غير الموجودة في الكومة ، واستنتاج أن أي كائن لم تتم زيارته يجب أن يكون هو القمامة. من هذا ، يمكننا أن نمثل العمل الإجمالي لمجموعة القمامة يتكون من جزء واحد يتناسب مع كمية البيانات الحية ، وجزء آخر يتناسب مع كمية القمامة ؛ أي work = (live * W1 + garbage * W2) .

افترض الآن أن تفعل ما يلي في تطبيق واحد مترابطة.

System.gc(); System.gc();

ستقوم المكالمة الأولى (نتوقع) القيام (live * W1 + garbage * W2) العمل ، والتخلص من القمامة البارزة.

ستقوم المكالمة الثانية (live* W1 + 0 * W2) بالعمل واستعادة أي شيء. وبعبارة أخرى ، قمنا بعمل (live * W1) ولم نحقق شيئًا على الإطلاق .

يمكننا أن نمذجة كفاءة المجمِّع ككمية العمل اللازمة لجمع وحدة من القمامة ؛ أي efficiency = (live * W1 + garbage * W2) / garbage . لذا لجعل GC أكثر كفاءة قدر الإمكان ، نحن بحاجة إلى تعظيم قيمة garbage عندما ندير GC. أي الانتظار حتى الكومة ممتلئة. (وأيضا ، اجعل الكومة كبيرة قدر الإمكان. ولكن هذا موضوع منفصل.)

إذا لم يتدخل التطبيق (عن طريق استدعاء System.gc() ) ، سينتظر GC حتى اكتمال كومة الذاكرة المؤقتة قبل التشغيل مما يؤدي إلى مجموعة فعالة من القمامة 1 . ولكن إذا كان التطبيق يفرض تشغيل GC ، فستكون هناك احتمالات بأن الكومة لن تكون ممتلئة ، وستكون النتيجة أن يتم جمع القمامة بشكل غير فعال. وكلما زاد عدد تطبيقات GC ، كلما أصبح GC أكثر كفاءة.

ملاحظة: تفسير أعلاه تفسير أكثر حقيقة أن GC الحديثة النموذجية تقسيم كومة الذاكرة المؤقتة إلى "مسافات" ، قد GC توسيع كومة الذاكرة المؤقتة بشكل ديناميكي ، قد تختلف مجموعة العمل التطبيق من الكائنات غير القمامة وهلم جرا. ومع ذلك ، فإن المبدأ الأساسي نفسه ينطبق على جميع الأجهزة على جميع جامعي القمامة الحقيقيين 2 . من غير الفعال لإجبار GC على التشغيل.

1 - هذه هي الطريقة التي يعمل بها جامع "الإنتاجية". تستخدم جامعات متزامنة مثل CMS و G1 معايير مختلفة لتحديد موعد بدء تشغيل أداة تجميع البيانات المهملة.

2 - أنا أيضًا استبعد مديري الذاكرة الذين يستخدمون العد المرجعي حصريًا ، ولكن لا يستخدم تطبيق جافا الحالي هذا النهج ... لسبب وجيه.

Question

بعد answering على سؤال حول كيفية تحرير كائنات خالية في Java (كان الرجل يقوم بمسح HashMap 1.5 غيغابايت) مع System.gc() ، قيل لي أنه من الممارسات السيئة استدعاء System.gc() يدوياً ، لكن التعليقات لم تكن مقنع تماما. بالإضافة إلى ذلك ، لم يجرؤ أحد على التجوُّل أو إهمال إجابتي.

قيل لي هناك أنه ممارسة سيئة ، ولكن قيل لي أيضا أن جامع القمامة لا توقف بشكل منهجي في العالم بعد الآن ، وأنه يمكن أن تستخدم أيضا بشكل فعال من قبل JVM فقط تلميحا ، لذلك أنا نوع من في الخسارة.

أفهم أن JVM عادةً ما تعرف أفضل منك عندما تحتاج إلى استعادة الذاكرة. أفهم أيضًا أن القلق بشأن بضعة كيلوبايت من البيانات سخيف. كما أنني أفهم أنه حتى وحدات الميغابايت من البيانات ليست كما كانت قبل سنوات قليلة. ولكن لا يزال ، 1.5 غيغابايت؟ وأنت تعرف أن هناك 1.5 جيجابايت من البيانات المعلقة في الذاكرة ؛ انها ليست مثل انها لقطة في الظلام. هل System.gc() سيئ بشكل منهجي ، أم أن هناك نقطة ما تصبح مقبولة؟

لذا فإن السؤال هو في الواقع مزدوج:

  • لماذا هو أو ليس من الممارسات السيئة استدعاء System.gc() ؟ هل هو حقا مجرد تلميح إلى JVM في إطار تطبيقات معينة ، أم أنه دائمًا دورة تجميع كاملة؟ هل هناك حقا تطبيقات لجمع القمامة يمكنها القيام بعملها دون إيقاف العالم؟ يرجى إلقاء بعض الضوء على التأكيدات المختلفة التي أدلى بها الناس في التعليقات على answering .
  • أين العتبة؟ هل من System.gc() استدعاء System.gc() أو هل هناك أوقات مقبولة؟ إذا كان الأمر كذلك ، فما هي تلك الأوقات؟



كان الناس يقومون بعمل جيد لشرح سبب عدم الاستخدام ، لذا سأخبركم عن بعض الحالات التي يجب أن تستخدم فيها:

(تنطبق التعليقات التالية على نقطة التشغيل الفعالة التي تعمل على نظام Linux مع أداة تجميع CMS ، حيث أشعر بالثقة في أن System.gc() تقوم في الواقع باستدعاء مجموعة بيانات كاملة تمامًا).

  1. بعد العمل المبدئي لبدء تطبيقك ، قد تكون حالة سيئة لاستخدام الذاكرة. يمكن أن يكون نصف الجيل الذي أنتجته مليئًا بالقمامة ، مما يعني أنك أقرب إلى نظام إدارة المحتوى الأول. في التطبيقات حيث يهم ذلك ، ليست فكرة سيئة استدعاء System.gc () إلى "إعادة تعيين" كومة الذاكرة المؤقتة إلى حالة بدء تشغيل البيانات المباشرة.

  2. على نفس الأسطر كـ # 1 ، إذا كنت تراقب استخدام كومة الذاكرة المؤقتة عن كثب ، فأنت ترغب في الحصول على قراءة دقيقة لماهية استخدام خط الأساس للذاكرة. إذا كانت أول دقيقتين من وقت تشغيل التطبيق الخاص بك هي كل التهيئة ، فسيتم إفساد بياناتك ما لم تجبر (ahem ... "suggest") على gc الكامل في المقدمة.

  3. قد يكون لديك تطبيق تم تصميمه بحيث لا يقوم أبدًا بترويج أي شيء للجيل المثقوب أثناء تشغيله. ولكن ربما تحتاج إلى تهيئة بعض البيانات الأولية التي ليست كبيرة جدًا حتى يتم نقلها تلقائيًا إلى الجيل المثبت. ما لم يتم استدعاء System.gc () بعد إعداد كل شيء ، يمكن أن تجلس بياناتك في الجيل الجديد حتى يحين الوقت لترويجها. فجأةً ، يتم الوصول إلى تطبيق GC المنخفض ، الذي يتسم بعمولة فائقة ، وتكلفة منخفضة جدًا ، بغرامة زمن الانتقال الضخمة (تحدثًا نسبيًا بالطبع) لترويج هذه الكائنات أثناء العمليات العادية.

  4. من المفيد في بعض الأحيان الحصول على استدعاء System.gc متوفرة في تطبيق إنتاج للتحقق من وجود تسرب للذاكرة. إذا كنت تعلم أن مجموعة البيانات المباشرة في وقت X يجب أن تكون موجودة في نسبة معينة إلى مجموعة البيانات المباشرة في الوقت Y ، فقد يكون من المفيد استدعاء System.gc () وقت X والوقت Y ومقارنة استخدام الذاكرة .




ربما أكون رمزًا كربيًا ، لكنني أدركت أن النقر على أيقونة سلة المهملات على Eclipse و netbeans IDE هي "ممارسة جيدة".




هذا سؤال مزعج للغاية ، وأشعر بأنني أساهم في معارضة جافا على الرغم من مدى فائدتها للغة.

إن حقيقة أنك لا تستطيع الوثوق بـ "System.gc" للقيام بأي شيء أمر شاق للغاية ويمكن أن تستحضر بسهولة "الخوف ، عدم اليقين ، الشك" إلى اللغة.

في كثير من الحالات ، من الجيد التعامل مع ارتفاعات الذاكرة التي تسببها عن قصد قبل حدوث حدث مهم ، مما يجعل المستخدمين يعتقدون أن برنامجك قد تم تصميمه بشكل سيئ / لا يستجيب.

سيكون امتلاك القدرة على التحكم في جمع القمامة أداة تعليمية رائعة ، وبالتالي تحسين فهم الناس لكيفية عمل مجموعة القمامة وكيفية جعل البرامج تستغل سلوكها الافتراضي وكذلك السلوك الخاضع للرقابة.

اسمح لي بمراجعة وسائط سلسلة المحادثات هذه.

  1. انها غير فعالة:

في كثير من الأحيان ، قد لا يقوم البرنامج بأي شيء وأنت تعرف أنه لا يفعل أي شيء بسبب الطريقة التي تم تصميمه بها. على سبيل المثال ، قد يكون هناك نوع من الانتظار الطويل مع مربع رسالة انتظار كبير ، وفي النهاية قد يضيف أيضًا دعوة لجمع القمامة لأن وقت تشغيلها سيستغرق جزءًا صغيرًا جدًا من وقت الانتظار طويلا ولكن تجنب gc من التصرف في منتصف عملية أكثر أهمية.

  1. إنها دائمًا ممارسة سيئة وتشير إلى شفرة مكسورة.

أنا لا أوافق ، لا يهم ما هو جامع القمامة لديك. مهمتها هي تتبع القمامة وتنظيفها.

عن طريق استدعاء gc في الأوقات التي يكون فيها الاستخدام أقل خطورة ، فإنك تخفض احتمالات تشغيله عندما تعتمد حياتك على الكود المحدد الذي يتم تشغيله ولكن بدلاً من ذلك يقرر جمع القمامة.

بالتأكيد ، قد لا تتصرف بالطريقة التي تريدها أو تتوقعها ، ولكن عندما تريد أن تسميها ، فأنت تعرف أن شيئًا ما يحدث ، وأن المستخدم على استعداد لتحمل البطء / التوقف. إذا كان يعمل System.gc ، عظيم! إذا لم يحدث ذلك ، على الأقل حاولت. لا يوجد ببساطة أي جانب لأسفل ما لم يكن جامع البيانات المهملة له آثار جانبية متأصلة تفعل شيئًا غير متوقع بشكل فظيع إلى كيف يفترض أن يتصرف جامع البيانات المهملة إذا تم استدعاؤه يدويًا ، وهذا بحد ذاته يسبب عدم الثقة.

  1. إنها ليست حالة استخدام شائعة:

إنها حالة استخدام لا يمكن تحقيقها بشكل موثوق ، ولكن يمكن أن تكون إذا تم تصميم النظام بهذه الطريقة. يبدو الأمر وكأنه صنع إشارة ضوئية وجعلها بحيث لا تقوم بعض أزرار "إشارات المرور" بأي شيء ، فهي تجعلك تتساءل عن سبب وجود هذا الزر ، لا تحتوي الجافا سكريبت على وظيفة جمع البيانات تدقيقها قدر ما لذلك.

  1. توضح المواصفات أن System.gc () هي تلميح يجب تشغيل GC ويكون VM مجاني لتجاهله.

ما هو "تلميح"؟ ما هو "تجاهل"؟ لا يمكن لجهاز الكمبيوتر ببساطة أن يأخذ تلميحات أو يتجاهل شيئًا ، فهناك مسارات سلوك صارمة قد يحتاجها والتي قد تكون ديناميكية يتم توجيهها من خلال القصد من النظام. قد تتضمن الإجابة المناسبة ما يقوم به جامع البيانات المهملة بالفعل ، على مستوى التنفيذ ، مما يؤدي إلى عدم تنفيذ المجموعة عند طلبها. هل الميزة ببساطة هي nop؟ هل هناك بعض الشروط التي يجب أن ألتقي بها؟ ما هي هذه الشروط؟

كما هو عليه الحال ، غالباً ما تبدو Java في GC مثل الوحش الذي لا تثق به. أنت لا تعرف متى ستأتي أو تذهب ، فأنت لا تعرف ماذا ستفعل ، كيف ستفعلها. يمكنني أن أتخيل بعض الخبراء الذين لديهم فكرة أفضل عن كيفية عمل مجموعة القمامة على أساس كل طلب ، لكن الأغلبية العظمى تأمل ببساطة أن "تعمل فقط" ، وأن تضطر إلى الثقة في وجود خوارزمية مبهمة تبدو وكأنها تعمل من أجلك أمر محبط.

هناك فجوة كبيرة بين القراءة عن شيء ما أو تعلم شيء ما ، ورؤية التنفيذ الفعلي لها ، والاختلافات عبر الأنظمة ، والقدرة على اللعب بها دون الحاجة إلى النظر إلى شفرة المصدر. هذا يخلق الثقة والشعور بالسهولة / الفهم / السيطرة.

لتلخيص ، هناك مشكلة متأصلة في الإجابات "قد لا تفعل هذه الميزة أي شيء ، ولن أخوض في التفاصيل كيف أحدد متى تفعل شيئًا وعندما لا يفعل ذلك ولماذا لن يفعل ذلك أو لا ، وغالبا ما تعني ضمنا أنه ببساطة ضد الفلسفة في محاولة لفعل ذلك ، حتى لو كان القصد من وراء ذلك هو معقول ".

قد يكون من المقبول أن تتصرف Java GC بالطريقة التي تعمل بها ، أو قد لا تفعل ذلك ، ولكن لفهمها ، فمن الصعب اتباعها في أي اتجاه يجب أن تحصل عليه للحصول على نظرة شاملة لما يمكنك الوثوق به في GC للقيام به و ليس من السهل القيام بذلك ، لذلك من السهل جدًا عدم الثقة ببساطة في اللغة ، لأن الغرض من اللغة هو التحكم في السلوك حتى النطاق الفلسفي (من السهل على مبرمج ، وخاصة المبتدئين أن يقعوا في أزمة وجودية من سلوكيات معينة في النظام / اللغة) قادر على تحمل (وإذا كنت لا تستطيع ، فلن تستخدم اللغة حتى تضطر إلى ذلك) ، والمزيد من الأشياء التي لا يمكنك التحكم بها بدون سبب معروف لا يمكنك التحكم بها ضارة بطبيعتها.




أولا ، هناك فرق بين المواصفات والواقع. توضح المواصفات أن System.gc () هي تلميح يجب تشغيل GC ويكون VM مجاني لتجاهله. الواقع هو ، لن تجاهل VM استدعاء System.gc ().

استدعاء GC يأتي مع النفقات العامة غير عادية إلى المكالمة وإذا قمت بذلك في بعض نقطة زمنية عشوائية فمن المحتمل أنك لن ترى أي مكافأة لجهودك. من ناحية أخرى ، من المرجح جدًا أن تسترد المجموعة المحرضة طبيعيا تكاليف المكالمة. إذا كان لديك معلومات تشير إلى أنه يجب تشغيل GC بدلاً من يمكنك إجراء استدعاء System.gc () ويجب أن تشاهد الفوائد. ومع ذلك ، من تجربتي أن هذا يحدث فقط في بضع حالات حافة حيث أنه من غير المحتمل أن يكون لديك معلومات كافية لفهم ما إذا كان يجب استدعاء System.gc ().

أحد الأمثلة المذكورة هنا ، ضرب علبة القمامة في IDE الخاص بك. إذا كنت خارجًا لاجتماعًا ، فلا تضغط عليه. لن تؤثر النفقات العامة عليك وقد يتم تنظيفها عند عودتك. تفعل ذلك في نظام الإنتاج والمكالمات المتكررة لجمع ستجعلها إلى توقف طحن! حتى الدعوات العرضية مثل تلك التي تقدمها RMI يمكن أن تكون مدمرة للأداء.




  1. نظرًا لأن الكائنات يتم تخصيصها ديناميكيًا باستخدام المشغل الجديد ،
    قد تتساءل كيف يتم تدمير هذه الأشياء و
    الذاكرة الصادرة لإعادة تخصيصها في وقت لاحق.

  2. في بعض اللغات ، مثل C ++ ، يجب تحرير الكائنات التي تم تخصيصها ديناميكيًا يدويًا باستخدام عامل حذف.

  3. جافا تأخذ نهجا مختلفا؛ انها يعالج إلغاء تخصيص بالنسبة لك تلقائيا.
  4. يسمى الأسلوب بإنجاز هذا مجموعة garbage. وهو يعمل على النحو التالي: عند عدم وجود أي مراجع لكائن ، يفترض أنه لم يعد هناك حاجة إلى هذا الكائن ، ويمكن استرجاع الذاكرة التي يشغلها الكائن. لا توجد حاجة واضحة لتدمير الأشياء كما في C ++.
  5. يحدث جمع garbage فقط sporadically (إذا الإطلاق) أثناء تنفيذ البرنامج.
  6. لن يحدث ببساطة بسبب وجود كائن واحد أو أكثر لم تعد مستخدمة.
  7. علاوة على ذلك ، ستستخدم تطبيقات وقت التشغيل Java المختلفة طرقًا مختلفة لجمع البيانات المهملة ، ولكن في معظم الأحيان ، لا يجب عليك التفكير بها أثناء كتابة برامجك.



Links