2008-09-15 19 views
16

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

मुझे वेब में कुछ सलाह मिली, जो संदर्भ काउंटर के वेतन वृद्धि/कमी संचालन के कॉल स्टैक एकत्र करने का प्रस्ताव रखती है। यह मुझे एक अच्छा संकेत देता है, कोड के किस टुकड़े ने संदर्भ काउंटर को बढ़ाने या घटने का कारण बना दिया है।

लेकिन मुझे जो कुछ चाहिए वह कुछ प्रकार का एल्गोरिदम है जो संबंधित "वृद्धि/कमी कॉल स्टैक्स" को जोड़ता है। कॉल स्टैक्स के इन जोड़े को हटाने के बाद, मुझे उम्मीद है कि (कम से कम) एक "कॉल स्टैक बढ़ाएं" छोड़ दिया गया है, जो मुझे "अनावश्यक" संदर्भ के साथ कोड का टुकड़ा दिखाता है, जिससे इसी वस्तु को मुक्त नहीं किया जाता है। अब रिसाव को ठीक करने का कोई बड़ा सौदा नहीं होगा!

लेकिन क्या किसी के पास समूह "एल्गोरिदम" का विचार है?

विकास विंडोज XP के तहत होता है।

(मुझे आशा है कि किसी को समझ में आया, कि मैं क्या समझाने की कोशिश की ...)

संपादित करें: मैं वृत्तीय संदर्भ की वजह से लीक के बारे में बात कर रहा हूँ।

+0

मुझे यकीन नहीं है कि मैं समझता हूं ... क्या आप एक प्रोग्रामिंग भाषा या मंच को विशेष रूप से संदर्भित कर सकते हैं? मेमोरी लीक उनके हैंडलिंग में भिन्न हो सकते हैं। – zxcv

+0

स्मार्ट पॉइंटर्स आमतौर पर सी ++/एसटीएल का मतलब है। –

+0

यहां कुछ भ्रम है। मुझे लगता है कि यह आवंटित स्मृति के बीच अंतर करने योग्य होगा जिसमें पॉइंटर नहीं है (स्मार्ट पॉइंटर्स के साथ नहीं होना चाहिए), सर्कुलर संदर्भों के कारण लीक, और जिन वस्तुओं का जीवन समय बढ़ाया जाता है, वे पॉइंटर को चारों ओर रखा जाता है। –

उत्तर

15

ध्यान दें कि एक स्रोत बनाया गया था। उदाहरण के लिए, ए के पास बी के लिए एक स्मार्ट पॉइंटर है, और बी के पास एक स्मार्ट पॉइंटर है। न तो ए और बी बी नष्ट हो जाएगा।आपको निर्भरताओं को तोड़ना होगा, और फिर तोड़ना होगा।

यदि संभव हो, तो बूस्ट स्मार्ट पॉइंटर्स का उपयोग करें, और पॉइंटर्स के लिए shared_ptr का उपयोग करें जो डेटा के मालिक होने चाहिए, और पॉइंटर्स के लिए weak_ptr को हटाएं जिन्हें कॉल करना नहीं है।

1

अगर मैं तुम्हें थे मैं लॉग लेने के लिए और एक त्वरित स्क्रिप्ट लिखने हैं निम्नलिखित (मेरा रूबी में है) की तरह कुछ करने के लिए:

def allocation?(line) 
    # determine if this line is a log line indicating allocation/deallocation 
end 

def unique_stack(line) 
    # return a string that is equal for pairs of allocation/deallocation 
end 

allocations = [] 
file = File.new "the-log.log" 
file.each_line { |line| 
    # custom function to determine if line is an alloc/dealloc 
    if allocation? line 
    # custom function to get unique stack trace where the return value 
    # is the same for a alloc and dealloc 
    allocations[allocations.length] = unique_stack line 
    end 
} 

allocations.sort! 

# go through and remove pairs of allocations that equal, 
# ideally 1 will be remaining.... 
index = 0 

while index < allocations.size - 1 
    if allocations[index] == allocations[index + 1] 
    allocations.delete_at index 
    else 
    index = index + 1 
    end 
end 

allocations.each { |line| 
    puts line 
} 

यह मूलतः लॉग के माध्यम से चला जाता है और प्रत्येक आवंटन/आवंटन रद्द करने कब्जा और प्रत्येक जोड़ी के लिए एक अद्वितीय मूल्य संग्रहीत करता है, फिर इसे सॉर्ट करें और मिलान करने वाले जोड़े को हटा दें, देखें कि क्या बचा है। , हर AddRef() रिकॉर्ड कॉल-स्टैक पर - - रिलीज़ (मिलान:

अद्यतन: सभी मध्यस्थ संपादन के लिए खेद है (मैं गलती से इससे पहले कि मैं किया गया था पोस्ट)

+0

नोट, मुझे लगता है कि आपके पास पहले से ही आपके प्रश्न में बात किए गए आवंटन/मुक्त निशान के साथ एक लॉग है, लेकिन अन्य उत्तर सामान्य रूप से मेमोरी लीक को हल करने में आपकी मदद कर सकते हैं –

6

तरह से मैं यह कर बस है) इसे हटा देता है। इस तरह के कार्यक्रम के अंत में मुझे मशीनिंग विज्ञप्ति के बिना AddRefs() के साथ छोड़ दिया गया है। जोड़े मिलान करने के लिए कोई ज़रूरत नहीं,

2

क्या मैं हल करने के लिए इस/ओवरराइड करने के लिए malloc/नई & मुक्त हटाना ऑपरेटरों ऐसी है कि वे ऑपरेशन के बारे में जितना संभव हो उतना डेटा संरचना में ट्रैक रखने के है किया है आप प्रदर्शन कर रहे हैं

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

लिखते समय दिनचर्या को मुक्त/हटाएं, मैं भी कॉलर के पते और सूचक जानकारी का ट्रैक रखता हूं।फिर मैं सूची में पीछे की ओर देखता हूं और पॉइंटर का उपयोग करके मेरी कुंजी के रूप में malloc/new समकक्ष से मिलान करने का प्रयास करता हूं। अगर मुझे यह नहीं मिला, तो लाल झंडा उठाएं।

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

फिर आपके पास प्रत्येक लेनदेन का आह्वान करने वाले कार्यों के साथ-साथ आपकी स्मृति आवंटन/डीलोकेशन इतिहास प्रदर्शित करने वाला तीसरा दिनचर्या होगा। (यह आपके लिंकर से प्रतीकात्मक मानचित्र को पार्स करके पूरा किया जा सकता है)। आपको पता चलेगा कि आपने किसी भी समय कितनी मेमोरी आवंटित की होगी और यह किसने किया था।

यदि आपके पास इन लेनदेन (8-बिट माइक्रोकंट्रोलर के लिए मेरा सामान्य मामला) करने के लिए पर्याप्त संसाधन नहीं हैं, तो आप एक ही सीरियल या टीसीपी लिंक के माध्यम से एक ही मशीन को पर्याप्त संसाधनों के साथ अन्य मशीन पर आउटपुट कर सकते हैं।

2

जब से तुम ने कहा कि आप उपयोग कर रहे विंडोज, आप माइक्रोसॉफ्ट के उपयोगकर्ता के मोड डंप ढेर उपयोगिता का लाभ लेने के लिए सक्षम हो सकता है, UMDH है, जो के साथ आता है Debugging Tools for Windows। यूएमडीएच आपके एप्लिकेशन के मेमोरी उपयोग के स्नैपशॉट बनाता है, प्रत्येक आवंटन के लिए इस्तेमाल किए गए स्टैक को रिकॉर्ड करता है, और आपको आवंटन "लीक" मेमोरी को कौन से कॉल देखने के लिए कई स्नैपशॉट्स की तुलना करने देता है। यह dbghelp.dll का उपयोग कर स्टैक निशान का प्रतीक आपके लिए प्रतीकों का भी अनुवाद करता है।

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

2

यह रिसाव खोजने की बात नहीं है। स्मार्ट पॉइंटर्स के मामले में यह शायद कुछ सामान्य जगहों जैसे CreateObject() को निर्देशित करेगा, जिसे हजारों बार कहा जा रहा है। यह निर्धारित करने का मामला है कि कोड में किस स्थान पर रिलीज() को रेफ-गिनती ऑब्जेक्ट पर कॉल नहीं किया गया था।

1

मैं Google's Heapchecker का बड़ा प्रशंसक हूं - यह सभी रिसाव नहीं पकड़ पाएगा, लेकिन उनमें से अधिकतर प्राप्त होते हैं। (टिप:। यह आपके सभी unittests में लिंक)

4

आप एक नियतात्मक तरह से रिसाव पुन: पेश कर सकते हैं, एक सरल तकनीक मैं अक्सर इस्तेमाल किया निर्माण के अपने आदेश में अपने सभी स्मार्ट संकेत दिए गए नंबर पर (एक स्थिर का उपयोग करें निर्माता में काउंटर), और रिसाव के साथ इस आईडी की रिपोर्ट करें। फिर प्रोग्राम को दोबारा चलाएं, और एक डीबगब्रैक() को ट्रिगर करें जब एक ही आईडी वाला स्मार्ट पॉइंटर बनाया जाए। http://www.codeproject.com/KB/applications/visualleakdetector.aspx

+0

आप इसे स्टैक डंप के साथ भी कर सकते हैं। यह आपको रेफर्स/डेफ्स – Ray

4

मैं क्या एक वर्ग है कि फंक्शन और लाइन पैरामीटर लेता है के साथ स्मार्ट सूचक लपेट है:

आप भी इस महान उपकरण विचार करना चाहिए। जब भी निर्माता को बुलाया जाता है, उस समारोह और रेखा के लिए गिनती बढ़ाएं, और जब भी विनाशक को बुलाया जाता है तो गिनती घट जाती है। फिर, एक फ़ंक्शन लिखें जो फ़ंक्शन/लाइन/गिनती जानकारी को डंप करता है। यही कारण है कि आपको बताता है जहां अपने संदर्भ के सभी संदर्भ की गिनती स्मार्ट संकेतपरिपत्र dependancies साथ संकेत दिए गए हैं साथ लीक की

+0

के यूआईडी के माध्यम से आउटपुट को जोड़ देगा, मैंने इस तकनीक का उपयोग किया है। अगर सब कुछ CreateObj से आ रहा है, तो CreateObj के फ़ंक्शन/लाइन के कॉलर को पास करें। (बेशक, मैक्रोज़ के माध्यम से ऐसा करना। अगर आपके पास कोई फंक्शन है "मुझे किसने बुलाया?") –

+1

@ क्रैजी/जो: क्या आप मुझे कुछ उदाहरण बता सकते हैं जो दिखाता है कि "मुझे किसने बुलाया?" – anand

+1

कॉलर की पहचान करना केवल स्टैकट्रैक का पहला हिस्सा है, इसलिए http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes आपको क्या देता है आप की जरूरत है। बैकट्रैक()/execinfo.h। –

4

संदर्भ चक्रों का पता लगाने के लिए आपको सभी संदर्भ-गिनती वस्तुओं का ग्राफ होना चाहिए। ऐसा ग्राफ बनाना आसान नहीं है, लेकिन यह किया जा सकता है।

जीवित संदर्भ-गणना वाली वस्तुओं को पंजीकृत करने के लिए वैश्विक set<CRefCounted*> बनाएं। यदि आपके पास सामान्य AddRef() कार्यान्वयन है तो यह आसान है - बस this सेट पर पॉइंटर जोड़ें जब ऑब्जेक्ट की संदर्भ संख्या 0 से 1 तक जाती है। इसी प्रकार, रिलीज़() में सेट से ऑब्जेक्ट को हटा दें जब संदर्भ गणना 1 से 0 हो जाती है।

अगला, प्रत्येक CRefCounted* से संदर्भित वस्तुओं का सेट प्राप्त करने के लिए कुछ तरीका प्रदान करें। यह virtual set<CRefCounted*> CRefCounted::get_children() हो सकता है या जो कुछ भी आपको उपयुक्त बनाता है। अब आपके पास ग्राफ चलने का एक तरीका है।

अंत में, cycle detection in a directed graph के लिए अपने पसंदीदा एल्गोरिदम लागू करें। कार्यक्रम शुरू करें, कुछ चक्र बनाएं और चक्र डिटेक्टर चलाएं। का आनंद लें! :)

+0

मुझे आपकी सोच का तरीका पसंद है, लेकिन मुझे नहीं लगता कि किसी ऑब्जेक्ट से संदर्भित ऑब्जेक्ट प्राप्त करने का एक आसान तरीका है: get_children –

+0

@lzprgmr, हां, 'get_children() को मैन्युअल रूप से प्रत्येक भाग लेने वाले वर्ग के लिए कोड किया जाना है कक्षा के संदर्भों के ज्ञान का ज्ञान। मेरा मतलब है "निर्माण करना आसान नहीं है"। – Constantin

0

पहला कदम यह जानना कि कक्षा क्या लीक हो रही है। एक बार जब आप इसे जानते हैं, तो आप यह देख सकते हैं कि संदर्भ कौन बढ़ा रहा है: 1. share_ptr द्वारा लिपटे वर्ग के निर्माता पर ब्रेकपॉइंट डालें। 2. इसके बाद संदर्भ गणना बढ़ने पर shared_ptr के अंदर डीबगर के साथ कदम: परिवर्तनीय पीएन-> pi _-> use_count_ अभिव्यक्ति का मूल्यांकन करके उस चर का पता लें (इस तरह कुछ: & यह-> pn-> pi_। use_count_), आपको एक पता मिलेगा 3. विजुअल स्टूडियो डीबगर में, डीबग-> नया ब्रेकपॉइंट-> नया डेटा ब्रेकपॉइंट पर जाएं ... परिवर्तनीय का पता दर्ज करें 4. प्रोग्राम चलाएं। आपका प्रोग्राम हर बार रुक जाएगा जब कोड में कुछ बिंदु बढ़ रहा है और संदर्भ काउंटर को कम कर रहा है। फिर आपको यह जांचने की आवश्यकता है कि वे मेल खाते हैं या नहीं।