2013-04-09 5 views
10

हमारी जावास्क्रिप्ट लाइब्रेरी के परीक्षण के दौरान मुझे लगता है कि हमें आईई 10 (v10.0.9200.16519 - विंडोज 8 64 बिट) में गंभीर स्मृति रिसाव मिली है, setInterval का जावास्क्रिप्ट कार्यान्वयन।आईई 10 सेट के लिए वर्कअराउंड इंटरवल मेमोरी लीक

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

हमारा टेस्टकेस केवल एक बार setInterval फ़ंक्शन निष्पादित करता है और फिर अंतराल टाइमर को साफ़ करता है, यानी थोड़ी देर के बाद कोई कोड नहीं चल रहा है और अब कोई चर उपलब्ध नहीं है (जहां तक ​​मैं देख सकता हूं कि इस कोड में कोई ग्लोबल्स नहीं पेश किया गया है, सिवाय इसके कि onload में चलाने की विधि के लिए), फिर भी प्रक्रिया मेमोरी के आधे गीगाबाइट (पुनरावृत्तियों की संख्या के आधार पर) लेती है।

दिलचस्प बात यह है कि अगर हम इस setTimeout विधि के बजाय का उपयोग करें (और यह भी समस्या IE9 में मौजूद नहीं लगता है, और क्रोम के वर्तमान संस्करण, एफएफ करता है) नहीं होता है।

समस्या this fiddle के साथ देखी जा सकती है।

इसे विंडोज 8 पर आईई 10 के एक नए उदाहरण में चलाएं और मेमोरी उपयोग देखने के लिए टास्क मैनेजर खोलें। यह 350 मेगाबाइट तक तेजी से बढ़ेगा और स्क्रिप्ट निष्पादित होने के बाद वहां रहेगा।

यह समस्याग्रस्त कोड टुकड़ा का महत्वपूर्ण हिस्सा है:

// the function that when called multiple times will cause the leak in IE10 
var eatMemory = function() { 
    var a = null; // the captured closure variable 
    var intervalId = setInterval(function() { 
     a = createBigArray(); // call a method that allocates a lot of memory 
     clearInterval(intervalId); // stop the interval timer 
    }, 100); 
} 

(मुझे पता है कि यह ठीक करने के लिए कोड के इस विशिष्ट टुकड़ा आसान है लेकिन उस समय नहीं है - यह सिर्फ सबसे नन्हा है। कोड का टुकड़ा हम आया है कि समस्या reproduces साथ। वास्तविक कोड वास्तव में बंद में this कब्जा और उस वस्तु कचरा एकत्र नहीं है।)

वहाँ हमारे कोड में एक बग है या setInterval जहां का उपयोग करने के लिए एक रास्ता है एक क्लोजर वैरिएबल अल का संदर्भ रखता है मेमोरी रिसाव को ट्रिगर किए बिना और "रिकर्सिव" setTimeout कॉल पर वापस किए बिना ऑब्जेक्ट ऑब्जेक्ट?

(मैं भी posted the question on MSDN)

अद्यतन: यह समस्या भी विंडोज 7 पर IE10 में मौजूद है, लेकिन मौजूद नहीं है यदि आप IE9-मानकों मोड में स्विच। मैंने इसे एमएस कनेक्ट में सबमिट किया और प्रगति की रिपोर्ट करेगा।

अद्यतन: माइक्रोसॉफ्ट accepted the issue और सूचना यह IE11 (पूर्वावलोकन संस्करण) में लगाई जानी - मैं इस अपने आप को इस बात की पुष्टि नहीं की है, अभी तक

अद्यतन: IE 11 आधिकारिक तौर पर जारी की गई है (किसी को?) और मैं अपने सिस्टम (विन 8.1 प्रो 64 बिट) के साथ उस संस्करण पर समस्या को पुन: उत्पन्न नहीं कर सकता। setTimeout पर वापस गिरने से

मैं पहले से ही लिखा है के रूप में (और टिप्पणीकर्ताओं सुझाव दिया), इस (तय नहीं) के आसपास काम किया जा सकता है:

+0

आप एक अंतराल क्यों पैदा कर रहे हैं और फिर इसे तुरंत समाशोधन ? क्या वह उद्देश्य को हराने में नहीं है? –

+0

@ ब्रैडएम मैंने प्रश्न को एक स्पष्टीकरण के साथ अद्यतन किया - वास्तविक कोड उस से अधिक जटिल है और हमेशा अंतराल को अंतहीन रूप से साफ़ नहीं करता है। – Sebastian

+0

क्या आपने अज्ञात फ़ंक्शन का उपयोग न करने का प्रयास किया है, लेकिन इसके बजाय फ़ंक्शन संदर्भ - और बाद में इस संदर्भ को स्पष्ट रूप से ओवरराइट कर रहा है? – CBroe

उत्तर

6

पूर्णता खातिर मैं यहाँ एक संभावित समाधान, जोड़ने कर रहा हूँ। यह मामूली नहीं है, क्योंकि कुछ आईडी बुक-रखरखाव करने की जरूरत है।

var registerSetIntervalFix = function(){ 
    var _setTimeout = window.setTimeout; 
    var _clearTimeout = window.clearTimeout; 
    window.setInterval = function(fn, interval){ 
     var recurse = function(){ 
      var newId = _setTimeout(recurse, interval); 
      window.setInterval.mapping[returnValue] = newId; 
      fn(); 
     } 
     var id = _setTimeout(recurse, interval); 
     var returnValue = id; 
     while (window.setInterval.mapping[returnValue]){ 
      returnValue++; 
     } 
     window.setInterval.mapping[returnValue] = id; 
     return returnValue; 
    } 
    window.setInterval.mapping = {}; 
    window.clearInterval = function(id){ 
     var realId = window.setInterval.mapping[id]; 
     _clearTimeout(realId); 
     delete window.setInterval.mapping[id]; 
    } 
} 

विचार रिकर्सिवली setTimeout कॉल करने के लिए setInterval कॉल आवर्ती अनुकरण करने के लिए है: यहाँ मेरी सुझाव दिया गया समाधान है कि आप कर सकते हैं test and fork from this fiddle, है। इस कार्यान्वयन में थोड़ा ओवरहेड है क्योंकि इसे बदलने के लिए id एस के लिए बहीखाता है, इसलिए जब तक यह आवश्यक न हो, मैं इस फिक्स को लागू करने की अनुशंसा नहीं करता।

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

(नोट: हमारे पुस्तकालय के लिए हम अब से setInterval का उपयोग कर बंद हो जाएगा और इसके बजाय कोड है कि उस पर भरोसा करते हैं सीधे setTimeout का उपयोग करने के कुछ हिस्सों को फिर से लिखने।)