2017-06-14 11 views
7

क्यों निम्नलिखित कोडस्मृति त्रुटियों से बाहर जावा

List<Object> list = new ArrayList<>(); 
while (true) { 
    for(int i = 0; i < 1000000; i++){ 
     list.add(new Object()); 
    } 
} 

स्मृति त्रुटि

के बाहर का उत्पादन करता है लेकिन इस कोड

while(true) { 
    List<Object> list = new ArrayList<>(); 
    for(int i = 0; i < 1000000; i++){ 
     list.add(new Object()); 
    } 
} 

मैं देख सकता हूँ कि यह कुछ है कि नहीं है सूची के साथ या तो लूप के अंदर या इसके बाहर स्पष्ट रूप से बनाया जा रहा है, लेकिन मुझे कारण पर अनिश्चितता है।

+4

एक साधारण स्मृति रिसाव ... दूसरे नमूने के लिए आप कचरा कलेक्टर – FieryCat

+0

जावा के लिए काम को सरल बनाते हैं, जहां कचरा कलेक्टर नई वस्तुओं को संग्रहीत करता है। जब ढेर भरा होता है और जीसी नई वस्तुओं को समायोजित नहीं कर सकता है OutOfMemoryError फेंक दिया जाता है। जावा क्लास –

+1

@FieryCat को आसान बनाने के लिए अपर्याप्त देशी मेमोरी होने पर भी यह त्रुटि फेंक दी जा सकती है ... पहले मामले में कचरा इकट्ठा करने के लिए कुछ भी नहीं है। – wvdz

उत्तर

10

पहले मामले में, आपके पास एक ArrayList उदाहरण है और आप इसे स्मृति से बाहर होने तक नए Object उदाहरणों में जोड़ते रहते हैं।

दूसरे मामले में, आप while पाश के प्रत्येक चरण में एक नया ArrayList बना सकते हैं और इसे करने के लिए 1000000Object उदाहरणों जोड़ने के लिए, जो ArrayList पिछले चरण में बनाया है और 1000000Object उदाहरणों इसमें कचरा एकत्र किया जा सकता है इसका मतलब, चूंकि कार्यक्रम में अब उनके संदर्भ नहीं हैं।

ध्यान दें कि दूसरा स्निपेट मेमोरी त्रुटि से भी बाहर निकल सकता है यदि नए Object एस कचरा कलेक्टर की तुलना में तेज़ी से बनाए जाते हैं, पुराने लोगों को छोड़ सकते हैं, लेकिन यह जेवीएम कार्यान्वयन पर निर्भर करता है।

5

पहले स्निपेट में, सूची लूप के बाहर बनाई गई है (और बनाए रखा है!), इसलिए जब तक आप सभी उपलब्ध स्मृति का उपभोग न करें तब तक आप अंतहीन तत्वों को तब तक रखें।

दूसरे स्निपेट में, while लूप का प्रत्येक पुनरावृत्ति एक नया ArrayList ऑब्जेक्ट बनाता है। चूंकि पुनरावृत्ति समाप्त हो जाने के बाद आप उस उदाहरण का संदर्भ नहीं रखते हैं, इसलिए यह सूची कचरा संग्रह के लिए योग्य है, इसलिए पुरानी सूचियां मुक्त हो रही हैं और आप अपनी याददाश्त को समाप्त नहीं करते हैं।

4

आपके दूसरे मामले में सूची बनाई गई (और जहां तत्व जोड़े गए हैं) दायरे से बाहर जा रहे हैं और जीसी के लिए योग्य हैं। नए ArrayList के लिए मेमोरी बनाने के लिए यह जीसीड होगा। इस प्रकार प्रत्येक पुनरावृत्ति के लिए, एक नया ArrayList बनाया गया है और फिर यह जीसी (जब लूप समाप्त होता है) के लिए योग्य हो जाता है। इस प्रकार जब स्मृति कम होती है, तो इन वस्तुओं को जीसीड किया जाएगा।

पहले मामले में आप उसी ArrayList पर तत्व जोड़ रहे हैं। जीसीड नहीं किया जा रहा है।

3

क्योंकि जब आप थोड़ी देर के अंदर सूची बनाते हैं तो आपकी पिछली सूची डंप हो जाती है और आपके पास एक नई खाली सूची होती है। इसके बाद आपकी स्मृति जावा कचरा कलेक्टर द्वारा मुक्त हो जाती है और आप सूची में 1000000 तत्व जोड़ते हैं। फिर एक नई सूची बनाई जाती है और सब कुछ खुद को दोहराता है।

2

पहले उदाहरण में, आप एक सूची बनाते हैं, इसमें आइटम जोड़ते हैं और फिर लूप समाप्त होता है। दूसरे उदाहरण में, आप एक सूची बनाते हैं, इसमें चीजें जोड़ते हैं, फिर नया सूची बनाएं, इसमें चीजों का एक समूह जोड़ें और असीमित दोहराएं। चूंकि पहले उदाहरण में आपका चर लूप के बाहर बनाया गया है, इसलिए भरने के लिए केवल 1 सूची है।

3

पहले परिदृश्य में, सूची ऑब्जेक्ट को लूप के बाहर घोषित किया जाता है, जो फिर से अनिश्चित काल तक चल रहा है (जैसे (सत्य)), इस प्रकार यह स्मृति को समाप्त होने तक जोड़ता रहता है, जबकि दूसरे में आपने सूची को थोड़ी देर के अंदर घोषित कर दिया है, अधिकतम आकार लूप के पुनरावृत्तियों की संख्या तक ही सीमित है।

प्रत्येक बार लूप मौजूद है, तो सूची ऑब्जेक्ट रीसेट किया जाता है, जिसे नया बनाया जाता है जिसे आप जोड़ना शुरू करते हैं, इस प्रकार आपके पास ऊपरी सीमा होती है। पुरानी वस्तु कचरा इकट्ठा होती है जिससे इस प्रकार जेवीएम को साफ़ किया जाता है।

3

इस प्रश्न का उत्तर @Eran, @TheLostMind और सभी द्वारा किया गया है, इसलिए मैं एक ही बिंदु नहीं डाल रहा हूं, मैं सिर्फ SoftReference और WeakReference मेमोरी से "देरी" करने में मदद करता हूं अपवाद।

-Xms64m -Xmx64m के रूप में JVM तर्कों के साथ कोड नीचे चलाएं ताकि आप परिणाम तुरंत देख सकें।

import java.lang.ref.SoftReference; 
import java.lang.ref.WeakReference; 
import java.util.ArrayList; 
import java.util.Date; 
import java.util.List; 

public class OOM { 
    public static void main(String[] args) { 
     System.out.println(new Date()); 
     try { 
      scenario1(false, false); // in my box, OOM occurred with average of 2 seconds. 
      //scenario1(true, false); // in my box, OOM occurred average of 6 seconds. 
      //scenario1(false, true); // in my box, OOM occurred average of 8 seconds. 
     } catch (Exception e) { 
     } catch (Error err){ 

     } 
     System.out.println(new Date()); 
    } 

    private static void scenario1(boolean useSoftReference, boolean useWeakReference) { 
     List<Object> list = new ArrayList<>(); 
     while (true) { 
      for(int i = 0; i < 1000000; i++){ 
       if(useSoftReference){ 
        list.add(new SoftReference<Object>(new Object())); 
       } else if(useWeakReference){ 
        list.add(new WeakReference<Object>(new Object())); 
       } else{ 
        list.add(new Object()); 
       } 
      } 
     } 
    } 
} 
2

दो कोड के बीच फर्क सिर्फ इतना है) सूची सूची का स्थान = नए ArrayList <> (है; लाइन। पहले कोड के लिए, ऐरेलिस्ट ने थोड़ी देर के लूप के बाहर घोषित किया और यह एक ArrayList उदाहरण में वस्तुओं की अनंत संख्या जोड़ता रहता है ताकि स्मृति से बाहर हो। दूसरी ओर, दूसरा लूप के अंदर ArrayList घोषित करता है ताकि यह प्रत्येक लूप चक्र (कई ArrayList उदाहरण) के बाद एक नया ArrayList चालू हो जाएगा। जावा में कचरा कलेक्टर के शासनकाल से, पिछले चक्र के उदाहरण हटा दिए जाएंगे क्योंकि अब इसकी ओर इशारा नहीं किया गया है। नतीजतन, जावा में जीसी दूसरे मामले में स्मृति से बाहर रोकता है।

संबंधित मुद्दे