2014-04-25 10 views
8

एक कार्यक्रम है कि मैं विकसित किया है इस बग के कारण कभी-कभी JVM क्रैश हो रहा है का कारण बनता है: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8029516। दुर्भाग्यवश बग को ओरेकल द्वारा हल नहीं किया गया है और बग रिपोर्ट में कहा गया है कि कोई ज्ञात कामकाज नहीं है।वर्कअराउंड जो क्रैश डम्प

मैंने KeyWatcher थ्रेड में लूप को एक सूची में लंबित पंजीकरण अनुरोध जोड़कर, बजाय keyWatcher थ्रेड में .register (sWatchService, eventKinds) को कॉल करके बग रिपोर्ट से उदाहरण कोड को संशोधित करने का प्रयास किया है, लेकिन मैंने यह अभी भी दुर्घटनाग्रस्त है। मुझे लगता है कि यह SWatchService पर सिंक्रनाइज़ करने के समान ही प्रभाव था (जैसे बग रिपोर्ट की सबमिटकर्ता की कोशिश की गई)।

क्या आप इस बारे में सोचने के किसी भी तरीके से सोच सकते हैं?

+0

विषम। इससे मेरा काम बनता है। मैं बस दो बार चेक किया। – Yrlec

+0

लिंक काम करता है और मेरे लिए काम नहीं करता है। शायद ओरेकल समस्या। कोड देखने के बिना जवाब देना मुश्किल है, लेकिन वॉच सर्विस और वॉचकी कक्षाओं के लिए जिम्मेदार ** एक और केवल एक ** देखने के लिए अपने कोड को पुनर्व्यवस्थित करें। अन्य थ्रेड उन कक्षाओं या सेवाओं का उपयोग आपके वॉचर वर्ग के माध्यम से करेंगे। –

+0

यह मूल स्मृति की स्मृति मुक्त करने में एक मुद्दा है। मुझे लगता है कि यह विंडोज लाइब्रेरी है जो मॉलोक/फ्री लागू करता है जो दोषी है। मैं जांचूंगा कि आपके पास नवीनतम डीएलएल हो सकता है। –

उत्तर

3

मैं हालांकि यह कुछ हद तक बदसूरत है एक समाधान बनाने के लिए प्रबंधित किया है।

बग जेडीके विधि WindowsWatchKey.invalidate() में है जो देशी बफर जारी करता है जबकि बाद की कॉल अभी भी इसका उपयोग कर सकती है। This one-liner जीसी तक बफर क्लीन-अप में देरी से समस्या को हल करता है।

यहां एक संकलित patch जेडीके में संकलित है।ताकि इसे निम्नलिखित जावा आदेश-पंक्ति ध्वज जोड़ने के लागू करने के लिए:
-Xbootclasspath/p:jdk-8029516-patch.jar

तो पैचिंग JDK अपने मामले में एक विकल्प नहीं है, वहां अभी भी आवेदन स्तर पर एक समाधान नहीं है। यह विंडोज वॉच सेवा आंतरिक कार्यान्वयन के ज्ञान पर निर्भर करता है।

public class JDK_8029516 { 
    private static final Field bufferField = getField("sun.nio.fs.WindowsWatchService$WindowsWatchKey", "buffer"); 
    private static final Field cleanerField = getField("sun.nio.fs.NativeBuffer", "cleaner"); 
    private static final Cleaner dummyCleaner = Cleaner.create(Thread.class, new Thread()); 

    private static Field getField(String className, String fieldName) { 
     try { 
      Field f = Class.forName(className).getDeclaredField(fieldName); 
      f.setAccessible(true); 
      return f; 
     } catch (Exception e) { 
      throw new IllegalStateException(e); 
     } 
    } 

    public static void patch(WatchKey key) { 
     try { 
      cleanerField.set(bufferField.get(key), dummyCleaner); 
     } catch (IllegalAccessException e) { 
      throw new IllegalStateException(e); 
     } 
    } 
} 

कॉल JDK_8029516.patch(watchKey) सही होने के बाद कुंजी registred है, और यह देशी बफर समय से पहले ही जारी करने से watchKey.cancel() कर पाएगा।

+0

बहुत बढ़िया! यह पूरी तरह से काम किया। मैं बहुत प्रभावित और आभारी हूं! – Yrlec

+0

अच्छा! @apangin ने आपको http://mail.openjdk.java.net/mailman/listinfo/core-libs-dev मेलिंग सूची में अपना प्रस्तावित फ़िक्स पोस्ट करने पर विचार किया है? – anttix

3

आप समस्या को हल ही काम करने में सक्षम नहीं हो सकता है लेकिन आप त्रुटि से निपटने और इसे संभाल कर सकते हैं। मुझे आपकी विशिष्ट स्थिति नहीं पता लेकिन मैं कल्पना कर सकता हूं कि सबसे बड़ा मुद्दा पूरे जेवीएम का दुर्घटना है। try ब्लॉक में सभी को रखना काम नहीं करता है क्योंकि आप JVM क्रैश नहीं पकड़ सकते हैं।

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

अब सबसे बड़ा शेष हिस्सा मुख्य प्रक्रिया और फ़ाइल देखने की प्रक्रिया के बीच कुछ संचार लागू करना है। यह फ़ाइल देखने की प्रक्रिया के मानक input/output या Socket/ServerSocket या किसी अन्य तंत्र का उपयोग करके किया जा सकता है।

4

टिप्पणियों से:

ऐसा लगता है कि हम जब वहाँ एक लंबित ReadDirectoryChangesW बकाया है मैं/हे रद्द करने के साथ समस्या आ रही है।

बयान और उदाहरण कोड से संकेत मिलता है बग शुरू हो रहा है कि जब:

  1. एक लंबित घटना है कि भस्म नहीं किया गया
  2. (यह या WatchService.poll() या WatchService.take() को दिखाई नहीं हो सकता है हो सकता है) नहीं है
  3. WatchKey.cancel() कुंजी

पर कहा जाता है यह कोई सार्वभौमिक वैकल्पिक हल के साथ एक बुरा बग है। दृष्टिकोण आपके आवेदन के विनिर्देशों पर निर्भर करता है। एक ही स्थान पर घड़ियों को पूल करने पर विचार करें ताकि आपको WatchKey.cancel() पर कॉल करने की आवश्यकता न हो। यदि एक बिंदु पर पूल बहुत बड़ा हो जाता है, तो पूरे WatchService को बंद करें और शुरू करें। कुछ समान है।

public class FileWatcerService { 
    static Kind<?>[] allEvents = new Kind<?>[] { 
     StandardWatchEventKinds.ENTRY_CREATE, 
     StandardWatchEventKinds.ENTRY_DELETE, 
     StandardWatchEventKinds.ENTRY_MODIFY 
    }; 

    WatchService ws; 

    // Keep track of paths and registered listeners 
    Map<String, List<FileChangeListener>> listeners = new ConcurrentHashMap<String, List<FileChangeListener>>(); 
    Map<WatchKey, String> keys = new ConcurrentHashMap<WatchKey, String>(); 

    boolean toStop = false; 

    public interface FileChangeListener { 
     void onChange(); 
    } 

    public void addFileChangeListener(String path, FileChangeListener l) { 
     if(!listeners.containsKey(path)) { 
      listeners.put(path, new ArrayList<FileChangeListener>()); 
      keys.put(Paths.get(path).register(ws, allEvents), path); 
     } 
     listeners.get(path).add(l); 
    } 

    public void removeFileChangeListener(String path, FileChangeListener l) { 
     if(listeners.containsKey(path)) 
      listeners.get(path).remove(l); 
    } 

    public void start() { 
     ws = FileSystems.getDefault().newWatchService(); 
     new Thread(new Runnable() { 
      public void run() { 
       while(!toStop) { 
        WatchKey key = ws.take(); 
        for(FileChangeListener l: listeners.get(keys.get(key))) 
         l.onChange(); 
       } 
      } 
     }).start(); 
    } 

    public void stop() { 
     toStop = true; 
     ws.close(); 
    } 
} 
संबंधित मुद्दे