2012-12-05 20 views
5

पर एसटीएल का उपयोग करने वाली बहु-थ्रेडेड Win32 सेवा के साथ मेमोरी समस्याएं मेरे पास सी ++ (वीएस -2010) में लिखी गई एक बहु-थ्रेडेड Win32 सेवा है जो मानक टेम्पलेट लाइब्रेरी का व्यापक उपयोग करती है। कार्यक्रम का व्यवसाय तर्क ठीक से काम करता है, लेकिन जब कार्य प्रबंधक (या संसाधन प्रबंधक) को देखते हैं तो प्रोग्राम एक चलनी की तरह स्मृति को रिसाव करता है।वीएस -2010

मेरे पास एक परीक्षण सेट है जो लगभग 16 एक साथ अनुरोध/सेकंड औसत है। जब कार्यक्रम पहली बार शुरू होता है तो यह 1.5 एमबी रैम के पड़ोस में कहीं भी खपत करता है। पूर्ण परीक्षण चलाने के बाद (जिसमें 12-15 मिनट लगते हैं) मेमोरी खपत 12 एमबी के करीब कहीं खत्म होती है। आम तौर पर, यह एक प्रोग्राम के लिए एक समस्या नहीं होगी जो एक बार चलता है और फिर समाप्त हो जाता है, लेकिन इस कार्यक्रम का उद्देश्य लगातार चलना है। वास्तव में बहुत बुरा है।

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

मैंने वीएलडी को लीक की तलाश करने की कोशिश की है और मुझे एक जोड़ा मिला है जिसे मैंने उपचार किया है, लेकिन फिर भी समस्या बनी हुई है। मैंने होर्ड को एकीकृत करने की कोशिश की है, लेकिन इसने वास्तव में समस्या को और भी खराब कर दिया है (मैं शायद इसे ठीक से एकीकृत नहीं कर रहा हूं, लेकिन मैं नहीं देख सकता)।

तो मैं निम्नलिखित प्रश्न पूछना चाहता हूं: क्या एक ऐसा प्रोग्राम बनाना संभव है जो एक बहु-थ्रेडेड वातावरण में एसटीएल का उपयोग करता है जो स्मृति को रिसाव नहीं करेगा? पिछले हफ्ते के दौरान मैंने इस कार्यक्रम में 200 से भी कम बदलाव नहीं किए हैं। मैंने परिवर्तनों के नतीजों की योजना बनाई है और उनके सभी में एक ही मूल प्रोफ़ाइल है। मैं उन सभी एसटीएल भलाई को हटाना नहीं चाहता हूं जिन्होंने इस एप्लिकेशन को इतना आसान बना दिया है। मैं किसी भी सुझाव की सराहना करता हूं कि मैं इस ऐप को स्टाइल से बाहर जाने की तरह स्मृति को लीक किए बिना कैसे काम कर सकता हूं।

किसी भी मदद के लिए धन्यवाद!

पीएस मैं निरीक्षण/व्यक्तिगत संपादन के लिए मेमोरी टेस्ट की एक प्रति पोस्ट कर रहा हूं।

#include <string> 
#include <iostream> 
#include <Windows.h> 
#include <map> 

using namespace std; 

#define MAX_THD_COUNT 1000 

DWORD WINAPI ClientThread(LPVOID param) 
{ 
    unsigned int thdCount = (unsigned int)param; 

    map<int, string> m; 

    for (unsigned int x = 0; x < 1000; ++x) 
    { 
     string s; 

     for (unsigned int y = 0; y < (x % (thdCount + 1)); ++y) 
     { 
      string z = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 
      unsigned int zs = z.size(); 

      s += z[(y % zs)]; 
     } 

     m[x] = s; 
    } 

    m.erase(m.begin(), m.end()); 

    ExitThread(0); 

    return 0; 
} 

int main(int argc, char ** argv) 
{ 
    // wait for start 
    string inputWait; 
    cout << "type g and press enter to go: "; 
    cin >> inputWait; 

    // spawn many memory-consuming threads 
    for (unsigned int thdCount = 0; thdCount < MAX_THD_COUNT; ++thdCount) 
    { 
     CreateThread(NULL, 0, ClientThread, (LPVOID)thdCount, NULL, NULL); 

     cout 
      << (int)(MAX_THD_COUNT - thdCount) 
      << endl; 

     Sleep(250); 
    } 

    // wait for end 
    cout << "type e and press enter to end: "; 
    cin >> inputWait; 

    return 0; 
} 
+2

'm.erase (m.begin(), m.end()) अधिकांश पुस्तकों में' m.clear() 'होगा, लेकिन चूंकि यह प्राप्ति के बाद समाप्त हो जाती है, फिर भी सुनिश्चित नहीं है यह भी क्यों है। इसके अलावा, आप विखंडन और/या अशुद्ध सीआरटी-शटडाउन का अनुभव कर रहे हैं। इसके लिए, आपको अपने धागे के लिए '_beginthreadex()' का उपयोग करना चाहिए, और * बाहर * बाहर निकलने के लिए बाहर निकलना नहीं है। थ्रेड प्रो-रिटर्न के माध्यम से समाप्त होने दें। इसके अलावा, आप प्रत्येक निष्पादन के साथ एक थ्रेड हैंडल लीक कर रहे हैं। इसे 'CreateThread()' के तुरंत बाद बंद किया जाना चाहिए, जो वर्तमान में हैंडल को भी सहेज नहीं रहा है। – WhozCraig

+0

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

+0

यहां देखें http://support.microsoft.com/kb/104641/en-gb यह कुछ भी नहीं हो सकता – James

उत्तर

1

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

एमएस रनटाइम लाइब्रेरी में कुछ फ़ंक्शन हैं जो आपको स्मृति अनुरोधों को डीबग करने और यह निर्धारित करने की अनुमति देते हैं कि एक ध्वनि एल्गोरिदम होने के बाद ठोस रिसाव हो और आपको विश्वास है कि आप कुछ भी स्पष्ट रूप से स्पष्ट नहीं देखते हैं। अधिक जानकारी के लिए the debug routines देखें।

  1. सेटअप बंद के बाद किसी भी मेमोरी लीक के साथ डिबग खिड़की स्पैमिंग के लिए उचित _Crt रिपोर्ट मोड:

    अंत में, मैं परीक्षण जिग आप ने लिखा करने के लिए निम्न सुधार किए हैं।

  2. संशोधित धागे की स्टार्टअप पाश MAXIMUM_WAIT_OBJECTS पर लगातार चल धागे की अधिकतम संख्या रखने के लिए (64 हैंडल के रूप में वर्तमान में WIN32 परिभाषित)
  3. सीआरटी को दिखाने के लिए एक उद्देश्यपूर्ण लीक चार सरणी आवंटन में फेंक दिया जाएगा, वास्तव में, पकड़ कार्यक्रम समाप्ति पर डंप करते समय।
  4. एलिमिनेटेड कंसोल कीबोर्ड इंटरैक्शन। बस इसे चलाओ।

उम्मीद है कि जब आप आउटपुट लॉग देखते हैं तो यह समझ में आता है। नोट: इसके लिए आपको किसी भी उचित डंप करने के लिए डीबग मोड में संकलित करना होगा।

#include <windows.h> 
#include <dbghelp.h> 
#include <process.h> 
#include <string> 
#include <iostream> 
#include <map> 
#include <vector> 

using namespace std; 

#define MAX_THD_COUNT 250 
#define MAX_THD_LOOPS 250 

unsigned int _stdcall ClientThread(void *param) 
{ 
    unsigned int thdCount = (unsigned int)param; 
    map<int, string> m; 

    for (unsigned int x = 0; x < MAX_THD_LOOPS; ++x) 
    { 
     string s; 
     for (unsigned int y = 0; y < (x % (thdCount + 1)); ++y) 
     { 
      string z = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 
      size_t zs = z.size(); 
      s += z[(y % zs)]; 
     } 
     m[x].assign(s); 
    } 
    return 0; 
} 

int main(int argc, char ** argv) 
{ 
    // setup reporting mode for the debug heap. when the program 
    // finishes watch the debug output window for any potential 
    // leaked objects. We're leaking one on purpose to show this 
    // will catch the leaks. 
    int flg = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); 
    flg |= _CRTDBG_LEAK_CHECK_DF; 
    _CrtSetDbgFlag(flg); 

    static char msg[] = "Leaked memory."; 
    new std::string(msg); 

    // will hold our vector of thread handles. we keep this fully populated 
    // with running threads until we finish the startup list, then wait for 
    // the last set of threads to expire. 
    std::vector<HANDLE> thrds; 
    for (unsigned int thdCount = 0; thdCount < MAX_THD_COUNT; ++thdCount) 
    { 
     cout << (int)(MAX_THD_COUNT - thdCount) << endl; 
     thrds.push_back((HANDLE)_beginthreadex(NULL, 0, ClientThread, (void*)thdCount, 0, NULL)); 
     if (thrds.size() == MAXIMUM_WAIT_OBJECTS) 
     { 
      // wait for any single thread to terminate. we'll start another one after, 
      // cleaning up as we detected terminated threads 
      DWORD dwRes = WaitForMultipleObjects(thrds.size(), &thrds[0], FALSE, INFINITE); 
      if (dwRes >= WAIT_OBJECT_0 && dwRes < (WAIT_OBJECT_0 + thrds.size())) 
      { 
       DWORD idx = (dwRes - WAIT_OBJECT_0); 
       CloseHandle(thrds[idx]); 
       thrds.erase(thrds.begin()+idx, thrds.begin()+idx+1); 
      } 
     } 
    } 

    // there will be threads left over. need to wait on those too. 
    if (thrds.size() > 0) 
    { 
     WaitForMultipleObjects(thrds.size(), &thrds[0], TRUE, INFINITE); 
     for (std::vector<HANDLE>::iterator it=thrds.begin(); it != thrds.end(); ++it) 
      CloseHandle(*it); 
    } 

    return 0; 
} 

आउटपुट डिबग खिड़की

नोट: वहाँ दो सूचना लीक कर रहे हैं। एक std :: स्ट्रिंग आवंटन है, दूसरा std :: स्ट्रिंग के भीतर बफर है जो हमारी संदेश प्रतिलिपि रखता है।

Detected memory leaks! 
Dumping objects -> 
{80} normal block at 0x008B1CE8, 8 bytes long. 
Data: <09  > 30 39 8B 00 00 00 00 00 
{79} normal block at 0x008B3930, 32 bytes long. 
Data: < Leaked memor> E8 1C 8B 00 4C 65 61 6B 65 64 20 6D 65 6D 6F 72 
Object dump complete. 
0

यह एक आसान काम नहीं है बड़े अनुप्रयोगों को डीबग करना।
आपका नमूना यह दिखाने के लिए सबसे अच्छा विकल्प नहीं है कि क्या हो रहा है।
आपके वास्तविक कोड का एक टुकड़ा बेहतर अनुमान लगाता है।
बेशक यह संभव नहीं है, इसलिए मेरा सुझाव है: अधिकतम संरचनाओं का उपयोग करें, जिसमें सभी संरचनाओं में सम्मिलन और हटाना नियंत्रण शामिल है। इस जानकारी के लिए काउंटर का प्रयोग करें। जब उन्हें संदेह होता है कि क्या हो रहा है यह समझने के लिए कुछ डेटा का डंप करें। जानकारी को सहेजने के लिए असीमित रूप से काम करने का प्रयास करें ताकि आपके आवेदन पर कम प्रभाव हो। यह एक आसान काम नहीं है, लेकिन किसी भी व्यक्ति के लिए जो चुनौती का आनंद लेता है और सी/सी ++ में प्रोग्राम के लिए और भी ज्यादा प्यार करता है, वह सवारी होगी।
दृढ़ता और सादगी लक्ष्य होना चाहिए।
शुभकामनाएं