2009-07-17 18 views
15

PHP कार्यक्रमों (ड्रूपल मॉड्यूल इत्यादि) में भावी मेमोरी लीक से बचने की कोशिश में, मैं सरल PHP स्क्रिप्ट्स के साथ गड़बड़ कर रहा हूं जो स्मृति को रिसाव करता है।यह सरल PHP स्क्रिप्ट रिसाव स्मृति क्यों करता है?

क्या एक PHP विशेषज्ञ मुझे यह जानने में मदद कर सकता है कि इस स्क्रिप्ट के बारे में स्मृति उपयोग लगातार चढ़ने का कारण बनता है?

विभिन्न पैरामीटर बदलने, इसे स्वयं चलाने का प्रयास करें। परिणाम दिलचस्प हैं। संदेश यह है:

<?php 

function memstat() { 
    print "current memory usage: ". memory_get_usage() . "\n"; 
} 

function waste_lots_of_memory($iters) { 
    $i = 0; 
    $object = new StdClass; 
    for (;$i < $iters; $i++) { 
    $object->{"member_" . $i} = array("blah blah blha" => 12345); 
    $object->{"membersonly_" . $i} = new StdClass; 
    $object->{"onlymember"} = array("blah blah blha" => 12345); 
    } 
    unset($object); 
} 

function waste_a_little_less_memory($iters) { 
    $i = 0; 
    $object = new StdClass; 
    for (;$i < $iters; $i++) { 

    $object->{"member_" . $i} = array("blah blah blha" => 12345); 
    $object->{"membersonly_" . $i} = new StdClass; 
    $object->{"onlymember"} = array("blah blah blha" => 12345); 

    unset($object->{"membersonly_". $i}); 
    unset($object->{"member_" . $i}); 
    unset($object->{"onlymember"}); 

    } 
    unset($object); 
} 

memstat(); 

waste_a_little_less_memory(1000000); 

memstat(); 

waste_lots_of_memory(10000); 

memstat(); 

मेरे लिए, उत्पादन होता है:

current memory usage: 73308 
current memory usage: 74996 
current memory usage: 506676 

[सेट किए बिना अधिक वस्तु सदस्यों को संपादित]

+0

मैं समस्या को अलग करने के लिए, एक समय में लूप के लिए लाइनों को हटाने का प्रयास करूंगा। –

उत्तर

34

unset() एक चर द्वारा उपयोग की गई स्मृति को मुक्त नहीं करता है। मेमोरी को मुक्त किया जाता है जब "कचरा कलेक्टर" (उद्धरण में PHP के बाद संस्करण 5.3.0 से पहले वास्तविक कचरा कलेक्टर नहीं था, केवल एक स्मृति मुक्त दिनचर्या जो ज्यादातर प्राइमेटिव पर काम करती है) फिट बैठती है।

इसके अलावा, तकनीकी रूप से, आपको unset() पर कॉल करने की आवश्यकता नहीं है क्योंकि $object चर आपके फ़ंक्शन के दायरे तक सीमित है।

अंतर दिखाने के लिए यहां एक स्क्रिप्ट है। मैंने अंतिम कॉल के बाद मेमोरी अंतर दिखाने के लिए आपके memstat() फ़ंक्शन को संशोधित किया।

<?php 
function memdiff() { 
    static $int = null; 

    $current = memory_get_usage(); 

    if ($int === null) { 
     $int = $current; 
    } else { 
     print ($current - $int) . "\n"; 
     $int = $current; 
    } 
} 

function object_no_unset($iters) { 
    $i = 0; 
    $object = new StdClass; 

    for (;$i < $iters; $i++) { 
     $object->{"member_" . $i}= array("blah blah blha" => 12345); 
     $object->{"membersonly_" . $i}= new StdClass; 
     $object->{"onlymember"}= array("blah blah blha" => 12345); 
    } 
} 

function object_parent_unset($iters) { 
    $i = 0; 
    $object = new StdClass; 

    for (;$i < $iters; $i++) { 
     $object->{"member_" . $i}= array("blah blah blha" => 12345); 
     $object->{"membersonly_" . $i}= new StdClass; 
     $object->{"onlymember"}= array("blah blah blha" => 12345); 
    } 

    unset ($object); 
} 

function object_item_unset($iters) { 
    $i = 0; 
    $object = new StdClass; 

    for (;$i < $iters; $i++) { 

     $object->{"member_" . $i}= array("blah blah blha" => 12345); 
     $object->{"membersonly_" . $i}= new StdClass; 
     $object->{"onlymember"}= array("blah blah blha" => 12345); 

     unset ($object->{"membersonly_" . $i}); 
     unset ($object->{"member_" . $i}); 
     unset ($object->{"onlymember"}); 
    } 
    unset ($object); 
} 

function array_no_unset($iters) { 
    $i = 0; 
    $object = array(); 

    for (;$i < $iters; $i++) { 
     $object["member_" . $i] = array("blah blah blha" => 12345); 
     $object["membersonly_" . $i] = new StdClass; 
     $object["onlymember"] = array("blah blah blha" => 12345); 
    } 
} 

function array_parent_unset($iters) { 
    $i = 0; 
    $object = array(); 

    for (;$i < $iters; $i++) { 
     $object["member_" . $i] = array("blah blah blha" => 12345); 
     $object["membersonly_" . $i] = new StdClass; 
     $object["onlymember"] = array("blah blah blha" => 12345); 
    } 
    unset ($object); 
} 

function array_item_unset($iters) { 
    $i = 0; 
    $object = array(); 

    for (;$i < $iters; $i++) { 
     $object["member_" . $i] = array("blah blah blha" => 12345); 
     $object["membersonly_" . $i] = new StdClass; 
     $object["onlymember"] = array("blah blah blha" => 12345); 

     unset ($object["membersonly_" . $i]); 
     unset ($object["member_" . $i]); 
     unset ($object["onlymember"]); 
    } 
    unset ($object); 
} 

$iterations = 100000; 

memdiff(); // Get initial memory usage 

object_item_unset ($iterations); 
memdiff(); 

object_parent_unset ($iterations); 
memdiff(); 

object_no_unset ($iterations); 
memdiff(); 

array_item_unset ($iterations); 
memdiff(); 

array_parent_unset ($iterations); 
memdiff(); 

array_no_unset ($iterations); 
memdiff(); 
?> 

आप वस्तुओं का उपयोग कर रहे हैं, सुनिश्चित करें वर्गों क्रम में __unset() लागू करता unset() को ठीक से साफ संसाधनों अनुमति देने के लिए करते हैं। stdClass जैसे वेरिएबल स्ट्रक्चर क्लासेस का उपयोग जितना संभव हो उतना बचने की कोशिश करें या उन सदस्यों को मान असाइन करें जो आपके क्लास टेम्पलेट में स्थित नहीं हैं क्योंकि उनको सौंपा गया स्मृति आमतौर पर ठीक से साफ़ नहीं किया जाता है।

PHP 5.3.0 और ऊपर एक बेहतर कचरा कलेक्टर है लेकिन यह डिफ़ॉल्ट रूप से अक्षम है। इसे सक्षम करने के लिए, आपको एक बार gc_enable() पर कॉल करना होगा।

+0

@mjgoins: मैंने अपनी पोस्ट को अधिक जानकारी के साथ संपादित किया। डिफ़ॉल्ट मेमोरी कलेक्टर आदिम प्रकारों पर सबसे अच्छा काम करता है। जैसे ही आप मिश्रण में संसाधनों और वस्तुओं को शुरू करना शुरू करते हैं, यह असफल हो जाता है। –

+0

तो उत्तर PHP 5.3 तक है, php * निश्चित मेमोरी स्पेस में मनमाने ढंग से लंबी नौकरी नहीं चला सकता है। यहां तक ​​कि मेरा काम जो कम स्मृति को कम करता है धीरे-धीरे बाहर चला जाएगा, हालांकि उच्च स्मृति आवंटन के साथ इसमें कई दिन लगेंगे। – mjgoins

+0

@mjgoins: मैं कई कार्यों को चलाता हूं जो कहीं भी एक मिनट से कई घंटों तक ले सकते हैं। चाल वस्तुओं से बचने के लिए है (या यदि आपको जरूरी है तो उन्हें पुन: उपयोग करने का प्रयास करें) और प्राइमेटिव्स के साथ चिपके रहें (आपके उदाहरण में, आप आसानी से सरणी का उपयोग कर सकते हैं)। –

2

memory_get_usage() की मेरी समझ यह उत्पादन पर निर्भर कर सकता है कि है ऑपरेटिंग सिस्टम और संस्करण कारकों की एक विस्तृत श्रृंखला।

अधिक महत्वपूर्ण बात यह है कि एक चर को अनसेट करने से इसकी याददाश्त मुक्त नहीं होती है, इसे प्रक्रिया से हटा दिया जाता है, और इसे ऑपरेटिंग सिस्टम पर वापस भेज दिया जाता है (फिर, इस ऑपरेशन की विशेषताएं ऑपरेटिंग सिस्टम निर्भर होती हैं)।

संक्षेप में, आपको शायद स्मृति लीक को देखने के लिए एक और जटिल सेटअप की आवश्यकता है।

0

मुझे PHP में सटीक कार्यप्रणालियों के बारे में निश्चित नहीं है, लेकिन कुछ अन्य भाषाओं में एक ऑब्जेक्ट जिसमें अन्य ऑब्जेक्ट होते हैं, जब शून्य पर सेट किया जाता है, तो मूल रूप से अन्य ऑब्जेक्ट्स को शून्य में सेट नहीं करता है। यह उन वस्तुओं के संदर्भ को समाप्त करता है, लेकिन चूंकि PHP में जावा अर्थ में "कचरा संग्रह" नहीं होता है, इसलिए उप-ऑब्जेक्ट स्मृति में तब तक मौजूद होते हैं जब तक वे अलग-अलग हटा दिए जाते हैं।

+0

यदि कोई इस पर आता है, तो किसी ऑब्जेक्ट (या अन्य मूल्य) के संदर्भ में स्मृति को मुक्त करने की प्रक्रिया को PHP में "कचरा कलेक्टर" नहीं माना जाता है क्योंकि यह एक नहीं है कार्यक्षमता का अलग टुकड़ा, लेकिन इसका मतलब यह नहीं है कि ऐसा नहीं होता है। किसी भी चर के संदर्भों की गणना की जाती है, और 'अनसेट() 'आदि उस गणना को कम कर देता है; यदि गिनती शून्य हो गई है, तो स्मृति को तुरंत PHP मेमोरी मैनेजर के भीतर मुक्त कर दिया जाता है, जिसे एक नए चर द्वारा उपयोग करने के लिए तैयार किया जाता है। – IMSoP

3

memory_get_usage रिपोर्ट करता है कि ओएस से कितनी मेमोरी PHP आवंटित की गई है। यह आवश्यक रूप से उपयोग में सभी चर के आकार से मेल नहीं खाता है। यदि PHP के पास स्मृति का सर्वोच्च उपयोग होता है, तो यह निर्णय ले सकता है कि अप्रयुक्त राशि को तुरंत वापस न करें। आपके उदाहरण में, फ़ंक्शन waste_a_little_less_memory समय के साथ अप्रयुक्त चर सेट करता है। तो चोटी का उपयोग अपेक्षाकृत छोटा है। waste_lots_of_memory इसे हटाने से पहले बहुत सारे चर (= उपयोग की गई बहुत सारी मेमोरी) बनाता है। तो चोटी का उपयोग बहुत बड़ा है।

22

memory_get_usage() "स्मृति की मात्रा देता है, बाइट में, जो वर्तमान में आपके PHP स्क्रिप्ट के लिए आवंटित किया जा रहा है।"

स्मृति की मात्रा ओएस द्वारा प्रक्रिया के लिए आवंटित है यही कारण है, नहीं की राशि आवंटित चर द्वारा उपयोग की जाने वाली स्मृति। PHP हमेशा ओएस को स्मृति जारी नहीं करता है - लेकिन जब भी नए चर आवंटित किए जाते हैं तब भी उस स्मृति को फिर से उपयोग किया जा सकता है।

यह प्रदर्शित करना सरल है। करने के लिए अपनी स्क्रिप्ट के अंत बदलें: पीएचपी वास्तव में स्मृति लीक कर रहा है, अब

memstat(); 
waste_lots_of_memory(10000); 
memstat(); 
waste_lots_of_memory(10000); 
memstat(); 

अगर आप सही कर रहे हैं, और, आप स्मृति के उपयोग दो बार बढ़ने देखना चाहिए।

current memory usage: 88272 
current memory usage: 955792 
current memory usage: 955808 

इसका कारण यह है स्मृति waste_lots_of_memory की प्रारंभिक मंगलाचरण के बाद "मुक्त कर दिया"() दूसरे मंगलाचरण से फिर से प्रयोग किया जाता है: हालांकि, यहां वास्तविक परिणाम है।

PHP के साथ अपने 5 वर्षों में, मैंने स्क्रिप्ट लिखी हैं जिन्होंने कई घंटों में डेटा की लाखों ऑब्जेक्ट्स और गीगाबाइट संसाधित की हैं, और स्क्रिप्ट जो एक समय में महीनों तक चलती हैं। PHP का मेमोरी मैनेजमेंट अच्छा नहीं है, लेकिन यह उतना ही बुरा नहीं है जितना आप इसे बना रहे हैं।

+0

यह प्रश्न का सही उत्तर है, और इसके मुकाबले कई और अधिक उत्साहजनक है। – IMSoP

0

memory_get_usage() तत्काल स्मृति उपयोग नहीं देता है, लेकिन प्रक्रिया को चलाने के लिए संग्रहीत स्मृति। एक विशाल सरणी को सेट किए बिना ($ array_a) के मामले में स्मृति जारी करेंगे नहीं लेकिन उपभोग अधिक memory_get_usage() अपने सिस्टम में के अनुसार ...

$array_a="(SOME big array)"; 
$array_b=""; 
//copy array_a to array_b 
for($line=0; $line<100;$line++){ 
$array_b[$line]=$array_a[$line]; 
} 

unset($array_a); //having this shows actually a bigger consume 
print_r($array_b); 

गूंज memory_get_usage();

संबंधित मुद्दे