2010-07-09 18 views
8

मैं नक्शा बनाने के लिए मैपमेकर का उपयोग करना चाहता हूं जो बड़ी वस्तुओं को कैश करता है, यदि पर्याप्त मेमोरी नहीं है तो कैश से हटाया जाना चाहिए। इस छोटे डेमो कार्यक्रम ठीक से काम करने लगता है:कैश बनाने के लिए MapMaker का उपयोग

public class TestValue { 
    private final int id; 
    private final int[] data = new int[100000]; 

    public TestValue(int id) { 
     this.id = id; 
    } 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 
     System.out.println("finalized"); 
    } 
} 


public class Main { 

    private ConcurrentMap<Integer, TestValue> cache; 
    MemoryMXBean memoryBean; 

    public Main() { 
     cache = new MapMaker() 
       .weakKeys() 
       .softValues() 
       .makeMap(); 
     memoryBean = ManagementFactory.getMemoryMXBean(); 
    } 

    public void test() { 
     int i = 0; 
     while (true) { 
      System.out.println("Etntries: " + cache.size() + " heap: " 
       + memoryBean.getHeapMemoryUsage() + " non-heap: " 
       + memoryBean.getNonHeapMemoryUsage()); 
      for (int j = 0; j < 10; j++) { 
       i++; 
       TestValue t = new TestValue(i); 
       cache.put(i, t); 
      } 
      try { 
       Thread.sleep(100); 
      } catch (InterruptedException ex) { 
      } 
     } 
    } 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     Main m = new Main(); 
     m.test(); 
    } 

} 

हालांकि, जब मैं अपने वास्तविक आवेदन में एक ही बात करते हैं, प्रविष्टियों मूल रूप से जैसे ही वे जुड़ जाते हैं कैश से निकाल रहे हैं। मेरे वास्तविक एप्लिकेशन में, मैं पूर्णांक को कुंजी के रूप में भी उपयोग करता हूं, और कैश किए गए मान डिस्क से पढ़ने वाले संग्रह ब्लॉक होते हैं जिनमें कुछ डेटा होता है। जहां तक ​​मैं समझता हूं, कमजोर संदर्भ कूड़े-संग्रहित होते हैं जैसे ही वे अब उपयोग नहीं किए जाते हैं, इसलिए ऐसा लगता है क्योंकि कुंजी कमजोर संदर्भ हैं। अगर मैं इस तरह नक्शा बनाने:

data = new MapMaker() 
      .softValues() 
      .makeMap(); 

प्रविष्टियों कभी नहीं कचरा-एकत्र कर रहे हैं और मैं अपने परीक्षण कार्यक्रम में एक आउट-ऑफ-स्मृति त्रुटि मिलती है। TestValue प्रविष्टियों पर अंतिमकरण विधि कभी नहीं कहा जाता है। मैं निम्नलिखित करने के लिए परीक्षण प्रक्रिया बदल देते हैं:

public void test() { 
    int i = 0; 
    while (true) { 
     for (final Entry<Integer, TestValue> entry : 
      data.entrySet()) { 
      if (entry.getValue() == null) { 
       data.remove(entry.getKey()); 
      } 
     } 
     System.out.println("Etntries: " + data.size() + " heap: " 
      + memoryBean.getHeapMemoryUsage() + " non-heap: " 
      + memoryBean.getNonHeapMemoryUsage()); 
     for (int j = 0; j < 10; j++) { 
      i++; 
      TestValue t = new TestValue(i); 
      data.put(i, t); 
     } 
     try { 
      Thread.sleep(100); 
     } catch (InterruptedException ex) { 
     } 
    } 
} 

प्रविष्टियों कैश और TestValue वस्तुओं पर finalizer कहा जाता है से हटा दिया जाता है, लेकिन कुछ समय बाद मैं भी एक-से-बाहर स्मृति त्रुटि मिलती है।

तो मेरा सवाल है: मानचित्र बनाने के लिए MapMaker का उपयोग करने का सही तरीका क्या है जिसे कैश के रूप में उपयोग किया जा सकता है? यदि मैं कमजोरकेज़ का उपयोग करता हूं तो जितनी जल्दी हो सके मेरे परीक्षण प्रोग्राम प्रविष्टियों को हटा नहीं देता है? क्या यह कैश मानचित्र में संदर्भ कतार जोड़ना संभव है?

+0

किसी को पढ़ने में आसान बनाने के लिए कोड को संपादित कर सकते हैं? – nanda

+0

मैं इससे थोड़ा आश्चर्यचकित हूं। मैंने 'softValues' का बिल्कुल वही तरीका उपयोग किया है और यह ठीक काम करता है, 'स्मृति कम होने पर' सॉफ्ट रेफरेंस को साफ़ किया जा रहा है। – finnw

उत्तर

3

कमजोर चाबियाँ एक गलती की तरह लगती हैं। मजबूत कुंजी का उपयोग करने का प्रयास करें क्योंकि वे पूर्णांक हैं।

+0

मैंने कोशिश की और यह काम करता है अगर मैं एक नई वस्तु बनाने से पहले System.gc() को कॉल करता हूं और इसे कैश में जोड़ता हूं। अगर मैं ऐसा नहीं करता, तो मुझे जल्दी या बाद में आउट-ऑफ-मेमोरी अपवाद मिलता है। क्या यह सही दृष्टिकोण है या आप किसी और चीज की सिफारिश करते हैं? – Michael

+0

आपके पास टेस्टवैल्यू के दो संस्करण हैं, एक में एक बड़ी सरणी है और एक में सिर्फ एक int है। क्या आप बड़ी सरणी के साथ परीक्षण कर रहे हैं? यदि नहीं, तो यह संभव है कि जीसी पर्याप्त स्मृति मुक्त नहीं कर सके। –

8

ऐसी कई चीजें हैं जो हो सकती हैं, लेकिन मुलायम मूल्यों का उपयोग करके आपके परीक्षण कार्यक्रम के संबंध में: आप आउटऑफमेमरी एरर प्राप्त कर सकते हैं भले ही आपके पास सॉफ़्ट रेफरेंस हैं जो अभी तक कचरा एकत्र नहीं किया गया है। वह दोहराना भालू: आप आउटऑफमेमरी त्रुटि प्राप्त कर सकते हैं भले ही आपके पास सॉफ़्ट रेफरेंस हों, जिन्हें अभी तक साफ़ नहीं किया गया है।

सॉफ़्ट रेफरेंस थोड़ा अजीब हैं, वर्तमान मैकेनिक्स के विवरण के लिए http://jeremymanson.blogspot.com/2009/07/how-hotspot-decides-to-clear_07.html देखें। आपके टेस्ट केस में संभावना है कि जीसी के पास दो पूर्ण जीसी करने का समय नहीं था।

जब आप कमजोरकेज़ का उपयोग कर रहे थे, तो सीजी ने उन्हें तुरंत साफ़ कर दिया, और उन्हें पूर्ण जीसी विराम की प्रतीक्षा नहीं करनी पड़ी। (ख/ग WeakReferences आक्रामक तरीके से एकत्र कर रहे हैं।)

मेरी राय में, यदि आप पूर्णांक कुंजी के साथ एक स्मृति के प्रति संवेदनशील कैश चाहता हूँ, मुझे लगता है कि निम्नलिखित उचित है:

data = new MapMaker().softValues().makeMap(); 

आप आसानी से कर सकते हैं एक परीक्षण कार्यक्रम जो OutOfMemoryError फेंकता है, लेकिन यदि आपका असली एप्लिकेशन कुछ हद तक अच्छा व्यवहार करता है, और बहुत अधिक दबाव में नहीं आता है, तो आप ठीक हो सकते हैं। सॉफ़्ट रेफरेंस सही होने के लिए बहुत कठिन हैं।

यदि आपको System.gc() से बाहर स्मृति से बचने की आवश्यकता है, तो मैं आपको एक निश्चित अधिकतम आकार के साथ एक एलआरयू मानचित्र पर स्विच करने की अनुशंसा करता हूं (उदाहरण के लिए java.util.LinkedHashMap का javadoc देखें।) यह समवर्ती नहीं है, लेकिन मुझे आशा है कि यह आपको अंत में बेहतर थ्रूपुट देने जा रहा है ताकि सिस्टम को पूर्ण-रोक कचरा संग्रह अतिरिक्त समय का गुच्छा करने के लिए कहा जा सके।

ओह, और पूर्णांक कुंजी और weakKeys के बारे में अंतिम नोट(): MapMaker कुंजी जब कमजोर या नरम कुंजी का उपयोग कर के लिए पहचान तुलना का उपयोग करता है, और है कि सही ढंग से करने के लिए बहुत मुश्किल है। निम्नलिखित का साक्षी लें:

Map<Integer,String> map = new MapMaker().weakKeys().makeMap(); 
Integer a = new Integer(1); 
Integer b = new Integer(1); 
Integer c = 1; //auto box 
Integer d = 1; //auto box 
map.put(a, "A"); 
map.put(b, "B"); 
map.put(c,"C"); 
map.put(d,"D"); 
map.size() // size is 3; 

शुभकामनाएँ।

+0

+1 क्योंकि मुझे एहसास नहीं हुआ कि आप सभी सॉफ़्ट रेफरेंस जीसीएड होने से पहले आउटऑफमेमरी एरर प्राप्त कर सकते हैं। –

0

मैं आपका ध्यान Suppliers.memoizeWithExpirationm, तत्काल-कैश पर लेना चाहता हूं।

http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/base/Suppliers.html#memoizeWithExpiration(com.google.common.base.Supplier, लंबे, java.util.concurrent.TimeUnit)

+0

कूल! लेकिन RefereceTypes का कोई उल्लेख नहीं है == कोई गारंटी नहीं है संदर्भ को स्मृति त्रुटि से पहले हटा दिया जाएगा – bjornl

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