2012-06-22 22 views
26

मेरे पास एक सरल ActiveRecord मॉडल है जिसे तालिका में 100 रिकॉर्ड के साथ Student कहा जाता है। मैं एक रेल कंसोल सत्र में निम्नलिखित है:हैश में ActiveRecord ऑब्जेक्ट्स कचरा नहीं एकत्रित हैं - एक बग या कैशिंग सुविधा का एक प्रकार?

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0 

x = Student.all 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100 

x = nil 
GC.start 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0  # Good! 

अब मैं निम्नलिखित है:

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0 

x = Student.all.group_by(&:last_name) 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100 

x = nil 
GC.start 

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100  # Bad! 

किसी को भी व्याख्या कर सकते हैं कि ऐसा क्यों होता है और वहाँ अंतर्निहित जानने के बिना इस समस्या के समाधान के लिए एक स्मार्ट तरीका है कि क्या हैश संरचना? मैं जानता हूँ कि मैं यह कर सकता:

x.keys.each{|k| x[k]=nil} 
x = nil 
GC.start 

और इसे सही ढंग स्मृति से सभी छात्र वस्तुओं को हटाने जाएगा, लेकिन अगर वहाँ एक सामान्य समाधान है मैं सोच रहा हूँ (मेरे वास्तविक जीवन समस्या व्यापक प्रसार है और अधिक जटिल डेटा है ऊपर दिखाए गए हैश की तुलना में संरचनाएं)।

मैं रूबी 1.9.3-पी 0 और रेल 3.1.0 का उपयोग कर रहा हूं।

अद्यतन (हल)

नीचे

प्रति ऑस्कर डेल बेन विवरण, कुछ ActiveRecord :: संबंध वस्तुओं (वे वास्तव में दोनों कोड के टुकड़े में बनाए गए हैं, लेकिन समस्या पैदा करने वाले कोड स्निपेट में बनाए जाते हैं किसी कारण से वे केवल दूसरे में "दुर्व्यवहार"। क्या कोई प्रकाश पर प्रकाश डाल सकता है?)। ये ActiveRecord ऑब्जेक्ट्स के संदर्भ को एक आवृत्ति चर के माध्यम से @records कहते हैं। यह आवृत्ति चर ActiveRecord :: संबंध पर "रीसेट" विधि के माध्यम से शून्य पर सेट किया जा सकता है। आप सभी संबंध वस्तुओं पर इस प्रदर्शन करने के लिए सुनिश्चित करने के लिए है:

ObjectSpace.each_object(ActiveRecord::Base).count 
# => 100 

ObjectSpace.each_object(ActiveRecord::Relation).each(&:reset) 

GC.start 
ObjectSpace.each_object(ActiveRecord::Base).count 
# => 0 

ध्यान दें: आपके, (ruby-mass मणि ऑस्कर डेल बेन संदर्भित का प्रयोग करके) Mass.detach उपयोग कर सकते हैं, हालांकि यह कोड तुलना में बहुत धीमी हो जाएगा ऊपर। ध्यान दें कि उपरोक्त कोड स्मृति से कुछ ActiveRecord :: संबंध ऑब्जेक्ट्स को नहीं हटाता है। हालांकि ये बहुत महत्वहीन प्रतीत होता है। आप कर कोशिश कर सकते हैं:

Mass.index(ActiveRecord::Relation)["ActiveRecord::Relation"].each{|x| Mass.detach Mass[x]} 
GC.start 

और यह ActiveRecord :: संबंध वस्तुओं में से कुछ को दूर होता है, लेकिन उन सभी को नहीं (यकीन नहीं क्यों, और उन है कि छोड़ दिया जाता है कोई Mass.references अजीब है।)।

+0

1.9 या 3.1 के लिए अद्वितीय हो सकता है - मैं इस व्यवहार को रेल 3.0.7 और रूबी एंटरप्राइज़ (री 1.8.7) के साथ नहीं देख रहा हूं। – klochner

+0

धन्यवाद Klochner! मैंने बस रुबी 1.8.7-पी 174 के तहत कोड चलाया। ऐसा लगता है कि रूबी 1.8.7 रेल 3.0.7 और रेल 3.1.0 दोनों पर ऑब्जेक्ट विनाश को सही तरीके से संभालती है। अर्थात। दूसरे उदाहरण में मुझे 0 ऑब्जेक्ट्स मिलते हैं। मैंने रुबी 1.9.2 की भी कोशिश की, और एक ही समस्या 1.9.3 के साथ होती है। क्या आपको लगता है कि YARV में एक बग है? – AmitA

+0

मैंने रूबी 1.8.7 और रेल 2.3.12 के साथ एक परीक्षण चलाया। मैंने केवल कंसोल में परीक्षण किया और एक ही समस्या थी। ** ** को छोड़कर, जब मैंने 'नेमसर्सा' को कंसोल में कचरा लिखा था, तो 'नेमएरर'' शुरू करने के लिए। इस 'जीसी.स्टार्ट' के बाद सबकुछ साफ हो गया। निश्चित नहीं है कि सिर्फ एक उत्सुक दुष्प्रभाव या कुछ और महत्वपूर्ण है। – Casper

उत्तर

10

मुझे लगता है कि मुझे पता है कि क्या हो रहा है, तो ऑब्जेक्ट को GC'ed नहीं मिलेगा। रुबी का जीसी मुक्त अपरिवर्तनीय वस्तुएं नहीं (जैसे प्रतीकों!)। Group_by द्वारा लौटाई गई कुंजियां अपरिवर्तनीय तार हैं, और इसलिए वे एकत्रित कचरा नहीं होंगे।

अद्यतन:

ऐसा लगता है कि समस्या रेल के साथ ही नहीं है। मैं group_by उपयोग करने की कोशिश अकेले, और कभी कभी वस्तुओं मिल कचरा एकत्र नहीं होगा:

oscardelben~/% irb 
irb(main):001:0> class Foo 
irb(main):002:1> end 
=> nil 
irb(main):003:0> {"1" => Foo.new, "2" => Foo.new} 
=> {"1"=>#<Foo:0x007f9efd8072a0>, "2"=>#<Foo:0x007f9efd807250>} 
irb(main):004:0> ObjectSpace.each_object(Foo).count 
=> 2 
irb(main):005:0> GC.start 
=> nil 
irb(main):006:0> ObjectSpace.each_object(Foo).count 
=> 0 
irb(main):007:0> {"1" => Foo.new, "2" => Foo.new}.group_by 
=> #<Enumerator: {"1"=>#<Foo:0x007f9efb83d0c8>, "2"=>#<Foo:0x007f9efb83d078>}:group_by> 
irb(main):008:0> GC.start 
=> nil 
irb(main):009:0> ObjectSpace.each_object(Foo).count 
=> 2 # Not garbage collected 
irb(main):010:0> GC.start 
=> nil 
irb(main):011:0> ObjectSpace.each_object(Foo).count 
=> 0 # Garbage collected 

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

इस मामले में मुझे लगता है कि हैश अभी भी चिह्नित है, भले ही यह दायरे से बाहर हो। ऐसा क्यों हो सकता है इसके कई कारण हैं। मैं जांच जारी रखूंगा।

अद्यतन 2:

मैंने पाया क्या वस्तुओं के संदर्भ रख रहा है। ऐसा करने के लिए मैंने ruby mass मणि का उपयोग किया है। यह पता चला है कि सक्रिय रिकॉर्ड संबंध वापस आने वाली वस्तुओं का ट्रैक रखता है।

User.limit(1).group_by(&:name) 
GC.start 
ObjectSpace.each_object(ActiveRecord::Base).each do |obj| 
    p Mass.references obj # {"ActiveRecord::Relation#70247565268860"=>["@records"]} 
end 

दुर्भाग्य से, संबंध पर reset बुला मदद करने के लिए नहीं लगता था, लेकिन उम्मीद है कि यह अब के लिए पर्याप्त जानकारी है।

+0

भयानक। धन्यवाद – deepak

+1

हम्म..तुम्हें लगता है कि 'group_by' द्वारा वापस किया गया हैश अभी भी जारी किया जाना चाहिए। और उसके साथ हैश से जुड़े सभी तत्व (कुंजी को छोड़कर)। तो यह मुझे समझ में नहीं आता है। अन्यथा आपके द्वारा कभी भी प्रतीकों के साथ लिखा गया हर हैश हमेशा के लिए स्मृति में रहेगा। – Casper

+0

कैस्पर, मुझे यकीन नहीं है, लेकिन मुझे लगता है कि यह हो रहा है। –

2

मैं जवाब

पता नहीं है लेकिन मैं ढेर निरीक्षण http://blog.headius.com/2010/07/browsing-memory-jruby-way.html

पर दिए गए पर एक स्क्रीनशॉट संलग्न कर दिए हैं की कोशिश की, https://skitch.com/deepak_kannan/en3dg/java-visualvm यह एक साधारण कार्यक्रम

class Foo; end 
f1 = Foo.new 
f2 = Foo.new 
GC.start 

तब इस्तेमाल किया था जैसा कि ऊपर दिया गया है jvisualvm। यह irb में चल रहा था।
ऐसा लगता है जैसे jruby ऑब्जेक्ट के दायरे को ट्रैक कर रहा है। यदि ऑब्जेक्ट

+0

आईआरबी की तरह दौड़ गया: jruby -J-Djruby.reify.classes = true -X + O 'जो irb' – deepak

+0

दीपक का जवाब देने के लिए धन्यवाद। हालांकि मैं JRuby का उपयोग नहीं कर रहा हूँ। क्या आपने जेआरबी में पोस्ट किए गए कोड को आजमाया था? क्या जेआरबी का जीसी मेरे कोड के संबंध में एमआरआई से अलग व्यवहार करता है? – AmitA

+0

@AmitA मैंने आपके द्वारा पोस्ट किए गए कोड को आजमाया नहीं है। एक सरल संस्करण का प्रयास किया क्योंकि डीबग करना और विज़ुअलाइज़ करना आसान है। विशेष रूप से मैं ढेर को डंप करना चाहता था, एमआरआई पर कुछ एक्सटेंशन की कोशिश की लेकिन यह संकलित नहीं हुआ। जेआरबीआई जेवीएम पर चलता है ताकि जीसी एमआरआई से अलग हो। – deepak