2012-01-11 25 views
11

मुझे WeakHashMap के साथ कुछ परेशानी का सामना करना पड़ा।क्या वीक हैशैप एक पूर्ण जीसी के दौरान साफ़ हो गया है?

List<byte[]> list = new ArrayList<byte[]>(); 

Map<String, Calendar> map = new WeakHashMap<String, Calendar>(); 
String anObject = new String("string 1"); 
String anOtherObject = new String("string 2"); 

map.put(anObject, Calendar.getInstance()); 
map.put(anOtherObject, Calendar.getInstance()); 
// In order to test if the weakHashMap works, i remove the StrongReference in this object 
anObject = null; 
int i = 0; 
while (map.size() == 2) { 
    byte[] tab = new byte[10000]; 
    System.out.println("iteration " + i++ + "map size :" + map.size()); 
    list.add(tab); 
} 
System.out.println("Map size " + map.size()); 

इस कोड काम करता है:

इस नमूना कोड पर विचार करें। लूप के अंदर, मैं ऑब्जेक्ट बना रहा हूं। जब एक मामूली जीसी होता है, तो नक्शा का आकार 1360 वें पुनरावृत्ति पर 1 के बराबर होता है। सब ठीक है।

अब जब मैं इस लाइन टिप्पणी:

//anObject = null; 

मैं एक OutOfMemoryError की अपेक्षा mapSize हमेशा 2. के बराबर है क्योंकि हालांकि 26XXX वीं यात्रा पर, एक पूर्ण जीसी होता है और नक्शे का आकार बराबर है 0 तक मुझे समझ में नहीं आता क्यों?

मैंने सोचा कि नक्शा साफ़ नहीं होना चाहिए क्योंकि दोनों वस्तुओं के मजबूत संदर्भ भी हैं।

+0

मुझे लगता है कि आपका परीक्षण सही नहीं है। यदि आप 'while (map.size() == 2) {' to 'जबकि (map.size()> 0) {', दो परीक्षण तब तक समाप्त हो जाएंगे जब तक नक्शा खाली न हो, चाहे आप टिप्पणी करें 'anObject = शून्य 'या नहीं। बीटीडब्ल्यू, मैंने पहले ही कोशिश की है। – donnior

+0

अंत में 'anObject' और' anOtherObject' प्रिंट करें। कंपाइलर देखता है कि अब आप उनका उपयोग नहीं कर रहे हैं और उन्हें पहले हटा सकते हैं। –

उत्तर

10

जस्ट-इन-टाइम संकलक कोड का विश्लेषण, देखता है कि anObject और anOtherObject पाश बाद इस्तेमाल नहीं कर रहे हैं, और उन्हें स्थानीय चर तालिका से निकाल देता है या null करने के लिए उन्हें सेट है, जबकि पाश अभी भी चल रहा है। इसे ओएसआर संकलन कहा जाता है।

बाद में जीसी तारों को एकत्र करता है क्योंकि उनके लिए कोई मजबूत संदर्भ नहीं रहता है।

यदि आप लूप के बाद anObject का उपयोग करते हैं तो आपको अभी भी OutOfMemoryError मिल जाएगा।

अद्यतन: आपको मेरे ब्लॉग में OSR compilation के बारे में अधिक विस्तृत चर्चा मिलेगी। खुदाई के

+0

मुझे लगता है कि आप बिल्कुल सही हैं - लेकिन क्या यह संभावित रूप से ब्रेकिंग जेआईटी अनुकूलन नहीं है? यदि 'anObject' का फ़ाइनिलाइज़र है और यह संदर्भ गायब होने से पहले जीसीडी है तो फाइनलर इसका मतलब होने से पहले संभावित रूप से निष्पादित होगा। – berry120

+0

यह क्या टूट सकता है? जब फ़ाइनलाइज़र चलाया जाता है तो मजबूत संदर्भ मौजूद नहीं होता है। – Joni

+0

यह इस अर्थ में तोड़ सकता है कि यह जल्द से जल्द एक फाइनलर चला सकता है; मुश्किल संदर्भ वास्तव में गुंजाइश से बाहर चला गया है। – berry120

7

बिट पता चलता है कि इस स्पष्ट JLS में कवर किया जाता है, खंड 12.6.1: एक कार्यक्रम के

अनुकूलन परिवर्तनों तैयार किया जा सकता है कि वस्तुओं कि तुलना में कम होने के लिए पहुंचा जा सकता है की संख्या को कम जो निष्पक्ष रूप से पहुंचने योग्य माना जाएगा। उदाहरण के लिए, एक कंपाइलर या कोड जेनरेटर एक वेरिएबल या पैरामीटर सेट करने का चयन कर सकता है जिसे अब इस ऑब्जेक्ट के लिए भंडारण को संभावित रूप से पुनः प्राप्त करने के लिए उपयोग करने के लिए उपयोग नहीं किया जाएगा।

(Bolding मेरी है।)

http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.6.1

तो संक्षेप में, JIT जब भी यह चाहता है मजबूत संदर्भ हटाने के लिए अगर यह काम कर सकते हैं कि वे कभी नहीं इस्तेमाल किया जा जाएगा अनुमति दी है दोबारा - जो वास्तव में यहां हो रहा है।

यह एक अच्छा सवाल है और यह एक महान गूढ़ व्यक्ति के लिए बनाता है जो आसानी से दिखा सकता है क्योंकि किसी ऑब्जेक्ट में दायरे में एक मजबूत संदर्भ होता है, इसका मतलब यह नहीं है कि यह कचरा एकत्र नहीं किया गया है। इसके बाद से इसका मतलब है कि जब आप फाइनलर चलाएंगे तो आप स्पष्ट रूप से कुछ भी गारंटी नहीं दे सकते हैं, यह उस मामले में भी हो सकता है जहां ऐसा लगता है कि वस्तु अभी भी गुंजाइश में है!

उदाहरण के लिए:

List<byte[]> list = new ArrayList<byte[]>(); 

Object thing = new Object() { 
    protected void finalize() { 
     System.out.println("here"); 
    } 
}; 
WeakReference<Object> ref = new WeakReference<Object>(thing); 

while(ref.get()!=null) { 
    list.add(new byte[10000]); 
} 
System.out.println("bam"); 

ऊपर एक सरल उदाहरण से पता चलता है कि वस्तु को अंतिम रूप दिया जाता है और GC'd पहले भले ही thing के संदर्भ में अभी भी मौजूद है है

7
(यहाँ छपा है, तो bam।)

जोनी सैलोनन और berry120 से उत्कृष्ट उत्तरों के लिए बस एक छोटी सी चीज़ जोड़ने के लिए। यह दिखाया जा सकता है कि जेआईटी वास्तव में "परिवर्तनीय हटाने" के लिए जिम्मेदार है, इसे आसानी से -Djava.compiler=NONE के साथ बंद कर दिया गया है। एक बार जब आप इसे बंद कर देते हैं, तो आप ओओएमई प्राप्त करते हैं।

अगर हम जानना चाहते हैं कि हुड के नीचे क्या हो रहा है, तो विकल्प XX:+PrintCompilation जेआईटी गतिविधि दिखाता है।

1  java.lang.String::hashCode (64 bytes) 
2  java.lang.String::charAt (33 bytes) 
3  java.lang.String::indexOf (151 bytes) 
4  java.util.ArrayList::add (29 bytes) 
5  java.util.ArrayList::ensureCapacity (58 bytes) 
6 ! java.lang.ref.ReferenceQueue::poll (28 bytes) 
7  java.util.WeakHashMap::expungeStaleEntries (125 bytes) 
8  java.util.WeakHashMap::size (18 bytes) 
1%  WeakHM::main @ 63 (126 bytes) 
Map size 0 

पिछले संकलन (@ ध्वज के साथ) एक OSR (पर ढेर रिप्लेसमेंट) संकलन (जाँच अधिक जानकारी के लिए https://gist.github.com/1165804#file_notes.md) है: सवाल उत्पादन पर हम पाते हैं से कोड के साथ इसका इस्तेमाल करते हुए पीछा कर रहा है। सरल शब्दों में, यह VM को चलने के दौरान एक विधि को प्रतिस्थापित करने में सक्षम बनाता है और इसका प्रयोग लूप में फंसे जावा विधियों के प्रदर्शन को बेहतर बनाने के लिए किया जाता है। मुझे लगता है कि इस संकलन के बाद ट्रिगर किया गया है, जेआईटी उन चरों को हटा देता है जिनका अब उपयोग नहीं किया जाता है।