2009-10-28 16 views
7

को गहन रूप से अंतिम रूप देने के लिए मैं एक खोज लाइब्रेरी का उपयोग कर रहा हूं जो खोज संभाल ऑब्जेक्ट को खोलने की सलाह देता है जिससे क्वेरी कैश का लाभ हो सकता है। उस समय मैंने देखा है कि कैश फूला हुआ है (कुछ सौ मेग्स और बढ़ता रहता है) और ओओएम ने किक शुरू कर दिया। इस कैश की सीमाओं को लागू करने का कोई तरीका नहीं है और न ही यह योजना है कि यह कितनी मेमोरी का उपयोग कर सकती है। इसलिए मैंने एक्सएमएक्स सीमा बढ़ा दी है, लेकिन यह समस्या का केवल एक अस्थायी समाधान है।सॉफ़्ट रेफरेंस रेफरेंस

आखिरकार मैं इस ऑब्जेक्ट को java.lang.ref.SoftReference बनाने के लिए सोच रहा हूं। तो अगर सिस्टम मुफ्त मेमोरी पर कम चलाता है, तो यह वस्तु को जाने देगा और मांग पर एक नया बनाया जाएगा। इससे ताजा शुरुआत के बाद कुछ गति कम हो जाएगी, लेकिन ओओएम को मारने से यह एक बेहतर विकल्प है।

सॉफ़्ट रेफरेंस के बारे में मुझे एकमात्र समस्या यह है कि उनके संदर्भों को अंतिम रूप देने का कोई साफ तरीका नहीं है। मेरे मामले में, खोज संभाल को नष्ट करने से पहले मुझे इसे बंद करने की आवश्यकता है, अन्यथा सिस्टम फ़ाइल डिस्क्रिप्टर से बाहर हो सकता है। जाहिर है, मैं इस हैंडल को किसी अन्य ऑब्जेक्ट में लपेट सकता हूं, उस पर एक फाइनलाइज़र लिख सकता हूं (या एक संदर्भक्यू/फैंटॉम रिफरेंस पर हुक) और जाने दो। लेकिन हे, इस ग्रह में हर एक लेख फाइनलाइजर्स का उपयोग करने के खिलाफ सलाह देता है, और विशेष रूप से - फ़ाइल हैंडल को मुक्त करने के लिए अंतिमकर्ताओं के खिलाफ (उदा। प्रभावी जावा संस्करण II, पृष्ठ 27.)।

तो मैं कुछ हद तक परेशान हूं। क्या मुझे इन सभी सलाहयों को ध्यान से अनदेखा करना चाहिए और आगे बढ़ना चाहिए। अन्यथा, क्या कोई अन्य व्यावहारिक विकल्प हैं? अग्रिम में धन्यवाद।

संपादित करें # 1: नीचे दिए गए पाठ को टॉम Hawtin द्वारा सुझाए गए कुछ कोड का परीक्षण करने के बाद जोड़ा गया था। मेरे लिए, ऐसा प्रतीत होता है कि कोई सुझाव काम नहीं कर रहा है या मुझे कुछ याद आ रहा है। कोड यह रहा: अगर मैं (ऊपर कोड के रूप में) -Xmx10m और SoftReferences साथ ऊपर टुकड़ा चलाने

class Bloat { // just a heap filler really 
    private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z; 

    private final int ii; 

    public Bloat(final int ii) { 
     this.ii = ii; 
    } 
} 

// as recommended by Tom Hawtin 
class MyReference<T> extends SoftReference<T> { 
    private final T hardRef; 

    MyReference(T referent, ReferenceQueue<? super T> q) { 
     super(referent, q); 
     this.hardRef = referent; 
    } 
} 

//...meanwhile, somewhere in the neighbouring galaxy... 
{ 
    ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>(); 
    Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>(); 
    int i=0; 

    while(i<50000) { 
//  set.add(new MyReference<Bloat>(new Bloat(i), rq)); 
     set.add(new SoftReference<Bloat>(new Bloat(i), rq)); 

//  MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll(); 
     SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll(); 

     if (polled != null) { 
     Bloat polledBloat = polled.get(); 
     if (polledBloat == null) { 
      System.out.println("is null :("); 
     } else { 
      System.out.println("is not null!"); 
     } 
     } 
     i++; 
    } 
} 

, मैं मुद्रित is null :( की टन हो रही है। लेकिन अगर मैं MyReference के साथ कोड को प्रतिस्थापित करता हूं (MyReference के साथ दो पंक्तियों को अपूर्ण करता हूं और सॉफ़्ट रेफरेंस वाले लोगों को टिप्पणी करता हूं) तो मुझे हमेशा ओओएम मिलता है।

जैसा कि मैंने सलाह से समझा, MyReference के अंदर हार्ड संदर्भ होने से ReferenceQueue पर ऑब्जेक्ट को रोकना नहीं चाहिए, है ना?

उत्तर

5

Toms जवाब है, सही है, लेकिन कोड है कि प्रश्न में जोड़ा गया है क्या टॉम द्वारा प्रस्तावित किया गया था के रूप में ही नहीं है।क्या टॉम अधिक इस तरह दिखता का प्रस्ताव किया गया था:

class Bloat { // just a heap filler really 
    public Reader res; 
    private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z; 

    private final int ii; 

    public Bloat(final int ii, Reader res) { 
     this.ii = ii; 
     this.res = res; 
    } 
} 

// as recommended by Tom Hawtin 
class MySoftBloatReference extends SoftReference<Bloat> { 
    public final Reader hardRef; 

    MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) { 
     super(referent, q); 
     this.hardRef = referent.res; 
    } 
} 

//...meanwhile, somewhere in the neighbouring galaxy... 
{ 
    ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>(); 
    Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>(); 
    int i=0; 

    while(i<50000) { 
     set.add(new MySoftBloatReference(new Bloat(i, new StringReader("test")), rq)); 

     MySoftBloatReference polled = (MySoftBloatReference) rq.poll(); 

     if (polled != null) { 
      // close the reference that we are holding on to 
      try { 
       polled.hardRef.close(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
     i++; 
    } 
} 

ध्यान दें कि बड़ा अंतर यह है कि कठिन संदर्भ वस्तु को बंद कर दिया जाना चाहिए करने के लिए है। आस-पास की वस्तु कचरा हो सकती है, और कर सकती है, इसलिए आप ओओएम नहीं मारेंगे, हालांकि आपको अभी भी संदर्भ बंद करने का मौका मिलता है। एक बार जब आप लूप छोड़ देते हैं, तो वह भी कचरा इकट्ठा किया जाएगा। बेशक, वास्तविक दुनिया में, आप शायद res सार्वजनिक उदाहरण सदस्य नहीं बनायेंगे।

उस ने कहा, यदि आप खुले फ़ाइल संदर्भ धारण कर रहे हैं, तो आप स्मृति से बाहर होने से पहले उन लोगों से बाहर निकलने का एक बहुत ही वास्तविक जोखिम चलाते हैं। आप शायद यह सुनिश्चित करने के लिए एक एलआरयू कैश भी रखना चाहते हैं कि आप हवा 500 खुली फ़ाइलों में उंगली चिपकते रहें। ये MyReference टाइप भी हो सकते हैं ताकि आवश्यकता होने पर वे कचरा भी एकत्रित हो सकें।

MySoftBloatReference कैसे काम करता है, इस बारे में थोड़ा सा स्पष्टीकरण देने के लिए, बेस क्लास, जो सॉफ़्ट रेफरेंस है, अभी भी उस ऑब्जेक्ट का संदर्भ रखती है जो सभी मेमोरी को हॉगिंग कर रही है। ओओएम को होने से रोकने के लिए आपको वह वस्तु है जिसे आपको मुक्त करने की आवश्यकता है। हालांकि, यदि ऑब्जेक्ट मुक्त हो गया है, तो आपको अभी भी उन संसाधनों को मुक्त करने की आवश्यकता है जो ब्लोट का उपयोग कर रहे हैं, यानी, ब्लोट दो प्रकार के संसाधन, मेमोरी और फ़ाइल हैंडल का उपयोग कर रहा है, इन दोनों संसाधनों को मुक्त करने की आवश्यकता है, या आप दौड़ते हैं संसाधनों में से एक या अन्य में से। सॉफ़्ट रेफरेंस उस ऑब्जेक्ट को मुक्त करके मेमोरी संसाधन पर दबाव को संभालता है, हालांकि आपको अन्य संसाधन, फ़ाइल हैंडल को भी रिलीज़ करने की आवश्यकता है। चूंकि ब्लोट को पहले से ही मुक्त कर दिया गया है, इसलिए हम इसका उपयोग संबंधित संसाधन को मुक्त करने के लिए नहीं कर सकते हैं, इसलिए MySoftBloatReference आंतरिक संसाधन के लिए एक कठिन संदर्भ रखता है जिसे बंद करने की आवश्यकता है। एक बार यह सूचित किया गया है कि ब्लोट को मुक्त कर दिया गया है, यानी जब संदर्भ संदर्भक्यू में संदर्भ बदल जाता है, तो MySoftBloatReference इसके संबंधित संदर्भ के माध्यम से संबंधित संसाधन को भी बंद कर सकता है।

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

+0

क्या आपके कोड को ठीक करना संभव होगा ताकि यह संकलित हो सके? जैसे MyReference कन्स्ट्रक्टर ब्लोट रेफरेंस तर्क लेता है और उसे हार्डरफ को असाइन करना होता है, लेकिन हार्डरफ एक पूरी तरह से अलग प्रकार का है (ResourceThatMustBeClosed)। साथ ही, क्या आप विस्तार कर सकते हैं कि संसाधनों को प्राप्त करने के बाद ब्लोट अभी भी जरूरी क्यों है? पीएस मुझे इतना जरूरतमंद नहीं होगा अगर इस प्रश्न में कोई बोनस प्वाइंट संलग्न नहीं था: पी – mindas

+0

मैंने कोड अपडेट किया है, और (उम्मीद है) एक स्पष्ट जोड़ा यह कैसे काम करता है इसका स्पष्टीकरण? यदि नहीं, तो मुझे बताएं ... –

+0

कोड तय किया गया है ताकि यह संकलित हो। बस इसे खाली कक्षा में फेंक दें, और उपयुक्त जोड़ें आयात। –

7

संसाधनों की एक सीमित संख्या के लिए: सबक्लास SoftReference। मुलायम संदर्भ संलग्न वस्तु को इंगित करना चाहिए। उप-वर्ग में एक मजबूत संदर्भ संसाधन का संदर्भ लेना चाहिए, इसलिए यह हमेशा दृढ़ता से पहुंच योग्य होता है। ReferenceQueuepoll के माध्यम से पढ़ते समय संसाधन को बंद कर दिया जा सकता है और कैश से निकाल दिया जा सकता है। कैश को सही ढंग से जारी किया जाना चाहिए (यदि SoftReference स्वयं कचरा इकट्ठा किया गया है, तो इसे ReferenceQueue पर लगाया नहीं जा सकता है)।

सावधान रहें कि आपके पास केवल कैश में निरस्त संसाधनों की सीमित संख्या है - पुरानी प्रविष्टियों को बेदखल करें (वास्तव में, यदि आप परिस्थिति के अनुरूप हैं तो सीमित संदर्भ कैश के साथ नरम संदर्भों को त्याग सकते हैं)। आमतौर पर यह मामला है कि यह गैर-स्मृति संसाधन है जो अधिक महत्वपूर्ण है, इस मामले में कोई विदेशी संदर्भ वस्तु वाले एलआरयू-बेदखल कैश पर्याप्त होना चाहिए।

(मेरा जवाब संख्या 1000। लंदन DevDay से प्रकाशित किया गया था।)

+1

Slick। 15 15 15 15 15. –

+1

मुझे आश्चर्य है कि यह एक घंटे या नींद के बाद दूरस्थ रूप से सुसंगत था (है ना?), एक अंधेरे कमरे में एक दिन (खराब कॉफी सेवा के साथ काम करने वाले वाईफ़ाई के साथ) और एक स्पीकर सुनने की कोशिश कर रहा है । लेकिन उसे किया जाना ज़रूरी है। –

+0

टॉम, क्या आप कृपया एक अधिक विस्तृत उत्तर पोस्ट कर सकते हैं (या इसे संपादित कर सकते हैं), आखिरकार कुछ (छद्म) कोड के साथ? मेरे पास भी एक कठिन दिन था, शायद कल मैं इसे बेहतर समझूंगा, लेकिन अभी, दुर्भाग्य से मैं सक्षम नहीं हूं। –

2

अहम।
(जहां तक ​​मुझे पता है) आप दोनों सिरों से छड़ी नहीं पकड़ सकते हैं। या तो आप अपनी जानकारी पकड़ते हैं, या आप इसे जाने देते हैं।
हालांकि ... आप कुछ महत्वपूर्ण जानकारी धारण कर सकते हैं जो आपको अंतिम रूप देने में सक्षम बनाती हैं। बेशक, मुख्य जानकारी "वास्तविक जानकारी" के बाद काफी छोटी होनी चाहिए और इसके पहुंच योग्य ऑब्जेक्ट ग्राफ़ में वास्तविक जानकारी नहीं होनी चाहिए (कमजोर संदर्भ आपको वहां मदद कर सकते हैं)। मौजूदा उदाहरण (महत्वपूर्ण जानकारी क्षेत्र पर ध्यान देना) पर
भवन:

public class Test1 { 
    static class Bloat { // just a heap filler really 
     private double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z; 

     private final int ii; 

     public Bloat(final int ii) { 
      this.ii = ii; 
     } 
    } 

    // as recommended by Tom Hawtin 
    static class MyReference<T, K> extends SoftReference<T> { 
     private final K keyInformation; 

     MyReference(T referent, K keyInformation, ReferenceQueue<? super T> q) { 
      super(referent, q); 
      this.keyInformation = keyInformation; 
     } 

     public K getKeyInformation() { 
      return keyInformation; 
     } 
    } 

    //...meanwhile, somewhere in the neighbouring galaxy... 
    public static void main(String[] args) throws InterruptedException { 
     ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>(); 
     Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>(); 
     int i = 0; 

     while (i < 50000) { 
      set.add(new MyReference<Bloat, Integer>(new Bloat(i), i, rq)); 

      final Reference<? extends Bloat> polled = rq.poll(); 

      if (polled != null) { 
       if (polled instanceof MyReference) { 
        final Object keyInfo = ((MyReference) polled).getKeyInformation(); 
        System.out.println("not null, got key info: " + keyInfo + ", finalizing..."); 
       } else { 
        System.out.println("null, can't finalize."); 
       } 
       rq.remove(); 
       System.out.println("removed reference"); 
      } 

संपादित करें:
मैं "या तो आपकी जानकारी पकड़ या उसे जाने दिया" पर विस्तृत करना चाहते हैं। मान लीजिए कि आपके पास अपनी जानकारी रखने का कोई तरीका है। इससे जीसी को आपके डेटा को अनमार्क करने के लिए मजबूर होना पड़ा, जिससे दूसरे जीसी चक्र में डेटा के साथ डेटा को वास्तव में साफ किया जा सकता है। यह संभव है - और यह वास्तव में क्या अंतिम() के लिए है। चूंकि आपने कहा है कि आप दूसरे चक्र को नहीं चाहते हैं, तो आप अपनी जानकारी नहीं रख सकते हैं (यदि ए -> बी तो! बी ->! ए)। जिसका मतलब है कि आपको इसे जाने देना चाहिए।

संपादित 2:
वास्तव में, दूसरा चक्र होगा - लेकिन आपके "प्रमुख डेटा" के लिए, आपका "प्रमुख ब्लोट डेटा" नहीं। वास्तविक चक्र को पहले चक्र पर साफ़ कर दिया जाएगा।

Edit3:
जाहिर है, वास्तविक समाधान संदर्भ कतार से दूर करने के लिए एक अलग थ्रेड का उपयोग() पोल नहीं है(), निकाल सकते हैं (, समर्पित धागे पर अवरुद्ध) होगा।

+0

उल्लेख करना भूल गया - -Xmx 10mb के साथ इस उदाहरण को चलाने से ओओएम उत्पन्न नहीं होता है और सभी प्रकार की संख्या सूचीबद्ध करता है (माना जाता है "महत्वपूर्ण जानकारी")। –

0

@ पॉल - उत्तर और स्पष्टीकरण के लिए बहुत बहुत धन्यवाद।

@Ran - मुझे लगता है कि आपके वर्तमान कोड में ++ लूप के अंत में गायब है। इसके अलावा, आपको लूप में rq.remove() करने की आवश्यकता नहीं है क्योंकि rq.poll() पहले से ही शीर्ष संदर्भ को हटा देता है, है ना?

कुछ अंक:

1) मैं Thread.Sleep (1) बयान जोड़ने के लिए बाद मैं पाश (पॉल और दौड़ा) के दोनों समाधान के लिए ++ में OOM से बचने के लिए था, लेकिन है कि बड़ी तस्वीर के लिए अप्रासंगिक है और है मंच भी निर्भर है। मेरी मशीन में क्वाड-कोर सीपीयू है और सन लिनक्स 1.6.0_16 जेडीके चला रहा है।

2) इन समाधानों को देखने के बाद मुझे लगता है कि मैं फाइनलाइज़र का उपयोग कर चिपकूँगा। बलोच की पुस्तक निम्नलिखित कारणों प्रदान करता है:

  • कोई गारंटी finalizers तुरंत निष्पादित किया जाएगा, इसलिए कुछ भी समय एक finalizer में महत्वपूर्ण कभी नहीं - और न ही वहाँ SoftRererences के लिए किसी भी गारंटी देता है कर रहे हैं!
  • कभी नहीं एक finalizer पर निर्भर महत्वपूर्ण लगातार स्थिति को अपडेट - मैं
  • वहाँ finalizers प्रयोग करने के लिए एक गंभीर प्रदर्शन दंड है नहीं कर रहा हूँ - मेरी सबसे खराब स्थिति में मैं प्रति मिनट या तो एक भी वस्तु के बारे में अंतिम रूप देने से होगी। मुझे लगता है कि मैं इसके साथ रह सकता हूं।
  • कोशिश/अंत में उपयोग करें - ओह हाँ, मैं निश्चित रूप से करूँगा!

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

3) अफसोस की बात है कि पॉल, टॉम और रण के बीच अंक विभाजित करने का कोई तरीका नहीं है :( मुझे उम्मीद है कि टॉम को कोई फर्क नहीं पड़ता क्योंकि उन्हें पहले से ही बहुत कुछ मिला है :) पॉल और रण के बीच निर्णय करना बहुत कठिन था - I दोनों जवाब काम सोचें और सही हैं। मैं केवल पॉल के जवाब में झंडा स्वीकार कर रहा हूं क्योंकि इसे उच्च रेटिंग मिली है (और इसमें अधिक विस्तृत स्पष्टीकरण है), लेकिन रैन का समाधान बिल्कुल बुरा नहीं है और अगर मैं सॉफ़्ट रेफरेंस का उपयोग करके इसे लागू करना चाहता हूं तो शायद मेरी पसंद होगी। धन्यवाद दोस्तों!

+0

i ++ - हाँ, शायद इसे प्रतिलिपि/पेस्ट के माध्यम से नहीं बनाया गया है। हटाने के लिए कोई ज़रूरत नहीं है() - सही। मुझे संदर्भों में से आधा गुम है। –

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