2010-05-11 14 views
7

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

उदाहरण

import java.awt.datatransfer.DataFlavor; 
import java.io.File; 
import java.util.List; 

import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.TransferHandler; 

public class DnDLeakTester extends JFrame { 
    public static void main(String[] args) { 
     new DnDLeakTester(); 

     //Prevent main from returning or the jvm will exit 
     while (true) { 
      try { 
       Thread.sleep(10000); 
      } catch (InterruptedException e) { 

      } 
     } 
    } 
    public DnDLeakTester() { 
     super("I'm leaky"); 

     add(new JLabel("Drop stuff here")); 

     setTransferHandler(new TransferHandler() { 
      @Override 
      public boolean canImport(final TransferSupport support) { 
       return (support.isDrop() && support 
         .isDataFlavorSupported(DataFlavor.javaFileListFlavor)); 
      } 

      @Override 
      public boolean importData(final TransferSupport support) { 
       if (!canImport(support)) { 
        return false; 
       } 

       try { 
        final List<File> files = (List<File>) 
          support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); 

        for (final File f : files) { 
         System.out.println(f.getName()); 
        } 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 

       return true; 
      } 
     }); 

     setDefaultCloseOperation(DISPOSE_ON_CLOSE); 
     pack(); 
     setVisible(true); 
    } 
} 

, पुन: पेश कोड चलाने के लिए और फ्रेम पर कुछ फ़ाइलों को छोड़ दें। फ्रेम बंद करें ताकि इसका निपटारा हो।

रिसाव को सत्यापित करने के लिए मैं जेकोनसोल का उपयोग करके एक ढेर डंप लेता हूं और इसे Eclipse Memory Analysis tool के साथ विश्लेषण करता हूं। यह दिखाता है कि sun.awt.AppContext फ्रेम के संदर्भ में हैशपैप के माध्यम से है। ऐसा लगता है कि ट्रांसफरसपोर्ट गलती पर है।

image of path to GC root http://img402.imageshack.us/img402/4444/dndleak.png

क्या मैं गलत कर रहा हूँ? क्या मुझे किसी भी तरह से खुद को साफ करने के लिए डीएनडी समर्थन कोड पूछना चाहिए?

मैं JDK 1.6 अद्यतन 19.

+0

मुझे लगता है कि यह एक जेवीएम बग है। प्रासंगिक डीएनडी कक्षाओं में अपमानजनक संदर्भों को साफ़ करने के लिए कोड नहीं है, इसलिए जब तक DropHandler को AppContext के मानचित्र से हटाया नहीं जाता है (मैं वास्तव में उस वर्ग को समझ नहीं पाता), रिसाव बनी रहेगी। – tom

+0

यह फ़ोरम पोस्ट एक समान समस्या का वर्णन करता है [http://forums.java.net/jive/thread.jspa?messageID=276311]। इसे कोई जवाब नहीं मिला। – tom

उत्तर

4

हालांकि DropHandler को स्थिर AppContext मानचित्र से हटाया नहीं गया है, यह वास्तव में मूल कारण नहीं है, लेकिन श्रृंखला में केवल कारणों में से एक है। (ड्रॉप हैंडलर को सिंगलटन होने का इरादा है, और ऐपकॉन्टेक्स्ट क्लास को अनलोड किए जाने तक साफ़ नहीं किया जाता है, जो अभ्यास में कभी नहीं होता है।) सिंगलटन ड्रॉपहैंडलर का उपयोग डिज़ाइन द्वारा किया जाता है।

रिसाव का असली कारण यह है कि ड्रॉपहैंडलर ट्रांसफरस्पोर्ट का एक उदाहरण स्थापित करता है, जिसे प्रत्येक डीएनडी ऑपरेशन के लिए पुन: उपयोग किया जाता है, और डीएनडी ऑपरेशन के दौरान, इसे डीएनडी में शामिल घटक के संदर्भ में प्रदान करता है। समस्या यह है कि यह संदर्भ को साफ़ नहीं करता है जब डीएनडी समाप्त करता है। यदि ड्रॉपहैंडलर TransferSupport.setDNDVariables(null,null) कहलाता है जब डीएनडी निकलता है, तो समस्या दूर हो जाएगी। यह सबसे तार्किक समाधान भी है, क्योंकि घटक का संदर्भ केवल तभी आवश्यक है जब डीएनडी प्रगति पर है। अन्य दृष्टिकोण, जैसे कि AppContext मानचित्र को साफ़ करना, एक छोटी निगरानी को ठीक करने के बजाय डिजाइन को बाधित कर रहा है।

लेकिन अगर हम इसे ठीक करते हैं, तो फ्रेम अभी भी एकत्र नहीं किया जाएगा।दुर्भाग्यवश, एक और समस्या प्रतीत होती है: जब मैंने सभी डीएनडी संबंधित कोड पर टिप्पणी की, तो बस एक साधारण जेएफआरएम को कम किया गया, यह भी एकत्र नहीं किया जा रहा था। रीटिंग संदर्भ javax.swing.BufferStrategyPaintManager में था। इसके लिए bug report है, अभी तक unfixed।

तो, अगर हम डीएनडी को ठीक करते हैं, तो हम पुनर्भुगतान के साथ एक और प्रतिधारण समस्या को दबाते हैं। सौभाग्य से, ये सभी बग केवल एक फ्रेम (उम्मीद है कि वही!) पर हैं, इसलिए यह उतना बुरा नहीं है जितना हो सकता है। फ्रेम का निपटारा किया जाता है, इसलिए देशी संसाधन जारी किए जाते हैं, और सभी सामग्री को हटाया जा सकता है, जिससे इसे मुक्त किया जा सकता है, जिससे स्मृति रिसाव की गंभीरता कम हो जाती है।

तो, अंततः अपने प्रश्न का उत्तर देने के लिए, आप कुछ भी गलत नहीं कर रहे हैं, आप बस कुछ ही समय में जेडीके में कुछ बग दे रहे हैं!

अद्यतन: - जोड़ने

-Dswing.bufferPerWindow=false 

JVM स्टार्टअप विकल्प के लिए बग से बचा जाता है रीपेंट प्रबंधक बग त्वरित सुधार है। इस बग को रद्द कर दिया गया है, डीएनडी बग के लिए एक फिक्स पोस्ट करना समझ में आता है:

डीएनडी समस्या को ठीक करने के लिए, आप importData() के अंत में इस विधि में एक कॉल जोड़ सकते हैं।

  private void cancelDnD(TransferSupport support) 
      { 
       /*TransferSupport.setDNDVariables(Component component, DropTargetEvent event) 
       Call setDNDVariables(null, null) to free the component. 
*/ 
       try 
       { 
        Method m = support.getClass().getDeclaredMethod("setDNDVariables", new Class[] { Component.class, DropTargetEvent.class }); 
        m.setAccessible(true); 
        m.invoke(support, null, null); 
        System.out.println("cancelledDnd"); 
       } 
       catch (Exception e) 
       { 
       } 
      } 
+0

जानकारी के लिए धन्यवाद। मैंने कुछ कक्षाओं को बनाए रखने वाले बफरस्ट्रेटी पेंटमैनेजर को देखा था लेकिन डीएनडी सामान से विचलित हो गया था। मैं स्वीकार करता हूं कि प्रतिबिंब चाल काम करेगी - लेकिन मुझे इसका उपयोग करने का इरादा नहीं है – tom

+0

निश्चित रूप से, यदि आप फिक्स का उपयोग करते हैं या नहीं, तो यह आपकी पसंद है, लेकिन अब कम से कम आप समस्या का दायरा जानते हैं और यह तय कर सकते हैं कि आप किस प्रकार से निपटते हैं यह। तुम्हारी तरह। मैं शायद फिक्स जोड़ने में परेशान नहीं हूं, और समस्या को जानना आंशिक रूप से कम हो सकता है, शायद कुछ भी नहीं या अधिकतर, निपटान पर फ्रेम से सामग्री को हटा दें। लेकिन यह अंत में है अब समस्या की सीमा ज्ञात है। मैं जानना बहुत उत्सुक था कि रिसाव क्यों हो रहा था और परिणाम क्या थे, और यह जानना अच्छा लगता है कि अगर हमें अचानक एक फिक्स की आवश्यकता होती है, तो वह उपलब्ध है। मैं आज रात आसान सो सकता हूँ! :-) – mdma

1

चल रहा हूँ यह अपने परिणामों को बदलने करता है अगर आप अपने वर्ग में जोड़ने?

@Override 
public void dispose() 
{ 
    setTransferHandler(null); 
    setDropTarget(null); // New 
    super.dispose(); 
} 

अद्यतन: मैं निपटाने के लिए एक और कॉल जोड़ दिया है। मुझे लगता है कि ड्रॉप लक्ष्य को शून्य पर सेट करना कुछ और संदर्भ जारी करना चाहिए। मुझे उस घटक पर कुछ और दिखाई नहीं देता है जिसका उपयोग आपके घटक को जाने के लिए डीएनडी कोड प्राप्त करने के लिए किया जा सकता है।

+0

आपके सुझाव डेवन के लिए धन्यवाद। नहीं, मुझे डर है कि यह कुछ भी नहीं बदलता है। – tom

+0

क्षमा करें, अभी भी कोई भाग्य नहीं है। मैंने GetDropTarget()। SetComponent (शून्य) की भी कोशिश की, लेकिन यह या तो काम नहीं करता है। समस्या यह है कि DropHandler से DropHandler को हटाने के लिए ट्रांसफरहैंडलर या इसके आंतरिक वर्गों के अंदर कोई कोड नहीं है। पुट() – tom

+0

पूरक के लिए सिर्फ एक निकालना() नहीं है App Apponte द्वारा आयोजित संदर्भ को रिलीज़ करने का कोई तरीका नहीं दिखता है। सौभाग्य से, पुट में उपयोग की जाने वाली कुंजी ड्रॉपटाइटल की कक्षा है, इसलिए केवल 1 उत्कृष्ट उदाहरण हो सकता है। इसे दस्तावेज करें और निपटान करके क्षति को कम करें() बाकी सब कुछ छोड़ दें। GetContentPane() की तरह। RemoveAll()। –

0

प्रासंगिक कक्षाओं के स्रोत को खराब करने के बाद, मुझे विश्वास है कि यह ट्रांसफरहैंडलर में एक अपरिहार्य रिसाव है।

ऐपकॉन्टेक्स्ट के मानचित्र, ड्रॉपहैंडलर में ऑब्जेक्ट को कभी भी हटाया नहीं जाता है। चूंकि कुंजी DropHandler.class ऑब्जेक्ट है, और ड्रॉपहैंडलर एक निजी आंतरिक वर्ग है, ट्रांसफरहैंडलर के बाहर से हटाया जा सकता है, इसका एकमात्र तरीका पूरे मानचित्र को खाली कर रहा है, या प्रतिबिंब चालबाजी के साथ।

ड्रॉपहैंडलर ट्रांसफरसपोर्ट ऑब्जेक्ट का संदर्भ रखता है, जिसे कभी भी साफ़ नहीं किया जाता है। ट्रांसफरस्पोर्ट ऑब्जेक्ट में घटक का संदर्भ होता है (मेरे मामले में एक जेएफआरएएम), जिसे या तो साफ़ नहीं किया जाता है।

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