मेमोरी लीक्स का निदान करना - # बाइट्स की मेमोरी साइज़ समाप्त हो जाना


98

मुझे खतरनाक त्रुटि-संदेश का सामना करना पड़ा, संभवतः श्रमसाध्य प्रयास के माध्यम से, PHP स्मृति से बाहर चला गया है:

#### बाइट्स की अनुमत स्मृति आकार फ़ाइल (#### बाइट्स को आवंटित करने की कोशिश की) 123 पर फ़ाइल में।

सीमा बढ़ाना

यदि आप जानते हैं कि आप क्या कर रहे हैं और सीमा बढ़ाना चाहते हैं तो memory_limit देखें :

ini_set('memory_limit', '16M');
ini_set('memory_limit', -1); // no limit

सावधान रहें! आप केवल लक्षण को हल कर सकते हैं न कि समस्या को!

रिसाव का निदान:

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

foreach ($users as $user) {
    $task = new Task;
    $task->run($user);
    unset($task); // Free the variable in an attempt to recover memory
    print memory_get_usage(true); // increases over time
}

इस सवाल के प्रयोजनों के लिए मान लेते हैं सबसे खराब स्पेगेटी कोड कल्पनीय में वैश्विक गुंजाइश कहीं में छिपा है जाने $userया Task

क्या उपकरण, PHP ट्रिक्स या डिबगिंग वूडू मुझे समस्या को खोजने और ठीक करने में मदद कर सकते हैं?


PS - मैंने हाल ही में इस प्रकार की सटीक समस्या के साथ भाग लिया है। दुर्भाग्य से, मैंने यह भी पाया कि php में एक बच्चे को ऑब्जेक्ट विनाश की समस्या है। यदि आप एक मूल वस्तु को परेशान करते हैं, तो उसके बच्चे की वस्तुओं को मुक्त नहीं किया जाता है। यह सुनिश्चित करने के बाद कि मैं एक संशोधित अनसेट का उपयोग करता हूं जिसमें सभी बाल वस्तुओं के लिए एक पुनरावर्ती कॉल शामिल है __destruct और इसी तरह। यहाँ विवरण: paul-m-jones.com/archives/262 :: मैं कुछ ऐसा कर रहा हूं: फ़ंक्शन सुपर_सेट ($ आइटम) {if (is_object ($ आइटम) && method_exists ($ आइटम, "__dructruct")) {$ आइटम -> __ विनाश (); } अनसेट ($ आइटम); }
जोश

जवाबों:


48

PHP में कूड़ा उठाने वाला नहीं है। यह मेमोरी को प्रबंधित करने के लिए संदर्भ गिनती का उपयोग करता है। इस प्रकार, मेमोरी लीक का सबसे आम स्रोत चक्रीय संदर्भ और वैश्विक चर हैं। यदि आप एक रूपरेखा का उपयोग करते हैं, तो आपके पास इसे ढूंढने के लिए बहुत से कोड होंगे, मुझे डर लग रहा है। सबसे सरल साधन चुनिंदा स्थानों पर कॉल करना है memory_get_usageऔर इसे नीचे रखना है जहां कोड लीक होता है। कोड का पता लगाने के लिए आप xdebug का भी उपयोग कर सकते हैं । निष्पादन निशान के साथ कोड चलाएं और show_mem_delta


3
लेकिन बाहर देखो ... उत्पन्न ट्रेस फ़ाइलें ENORMOUS होगी। पहली बार जब मैंने एक ज़ेंड फ्रेमवर्क ऐप पर xdebug ट्रेस चलाया तो इसे चलाने के लिए एक बहु जीबी (kb या MB ... GB) आकार की फ़ाइल को चलाने और उत्पन्न करने में एक लूंग टाइम लगा। बस इसके बारे में पता होना चाहिए।
rg88

1
हाँ, यह बहुत भारी है .. GB की आवाज़ थोड़ी बहुत है - जब तक कि आपके पास एक बड़ी स्क्रिप्ट नहीं थी। शायद कुछ पंक्तियों को संसाधित करने की कोशिश करें (रिसाव की पहचान करने के लिए पर्याप्त होना चाहिए)। इसके अलावा, उत्पादन सर्वर पर xdebug एक्सटेंशन स्थापित न करें।
troelskn

31
5.3 PHP के बाद से वास्तव में एक कचरा कलेक्टर है। दूसरी ओर, मेमोरी प्रोफाइलिंग फंक्शन को दूर कर दिया गया है xdebug :(
wdev

3
+1 मिला लीक! एक वर्ग जिसमें चक्रीय संदर्भ थे! एक बार इन संदर्भों () से परेशान होने के बाद, वस्तुओं को उम्मीद के अनुसार कचरा एकत्र किया गया था! धन्यवाद! :)
रिनोगो

@rinogo तो आपको लीक के बारे में कैसे पता चला? क्या आप साझा कर सकते हैं कि आपने क्या कदम उठाए?
जॉनीक्यू

11

यहां हमने एक चाल बताई है कि कौन सी स्क्रिप्ट हमारे सर्वर पर सबसे अधिक मेमोरी का उपयोग कर रही है।

निम्न स्निपेट को किसी फ़ाइल में सहेजें, जैसे /usr/local/lib/php/strangecode_log_memory_usage.inc.php:

<?php
function strangecode_log_memory_usage()
{
    $site = '' == getenv('SERVER_NAME') ? getenv('SCRIPT_FILENAME') : getenv('SERVER_NAME');
    $url = $_SERVER['PHP_SELF'];
    $current = memory_get_usage();
    $peak = memory_get_peak_usage();
    error_log("$site current: $current peak: $peak $url\n", 3, '/var/log/httpd/php_memory_log');
}
register_shutdown_function('strangecode_log_memory_usage');

Httpd.conf में निम्नलिखित जोड़कर इसे नियोजित करें:

php_admin_value auto_prepend_file /usr/local/lib/php/strangecode_log_memory_usage.inc.php

इसके बाद लॉग फाइल का विश्लेषण करें /var/log/httpd/php_memory_log

touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_logअपने वेब उपयोगकर्ता को लॉग फ़ाइल में लिखने से पहले आपको इसकी आवश्यकता हो सकती है ।


8

मैंने एक बार एक पुरानी स्क्रिप्ट में देखा कि PHP "फोर" लूप के बाद भी दायरे में "वैरिएबल" को बनाए रखेगा। उदाहरण के लिए,

foreach($users as $user){
  $user->doSomething();
}
var_dump($user); // would output the data from the last $user 

मुझे यकीन नहीं है कि भविष्य के PHP संस्करणों ने इसे ठीक किया है या नहीं जब से मैंने इसे देखा है। यदि यह मामला है, तो आप इसे मेमोरी से हटाने के unset($user)लिए doSomething()लाइन के बाद कर सकते हैं । YMMV।


13
PHP सी / जावा / आदि जैसे लूप / कंडीशन को स्कोप नहीं देती है। लूप / सशर्त के अंदर घोषित की गई कोई भी चीज लूप / सशर्त (डिजाइन द्वारा [?]) से बाहर निकलने के बाद भी दायरे में है। दूसरी ओर, तरीके / कार्य, वैसे ही बंद कर दिए जाते हैं जैसे आप अपेक्षा करते हैं - फ़ंक्शन निष्पादन समाप्त होने के बाद सब कुछ जारी हो जाता है।
फ्रैंक किसान

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

आप unset()इसे कर सकते हैं, लेकिन ध्यान रखें कि वस्तुओं के लिए, आप जो कर रहे हैं वह बदल रहा है जहां आपका चर इंगित कर रहा है - आपने वास्तव में इसे मेमोरी से नहीं हटाया है। पीएचपी स्वचालित रूप से मेमोरी को एक बार फ्री कर देगा क्योंकि यह किसी भी तरह से दायरे से बाहर है, इसलिए बेहतर समाधान (इस उत्तर के संदर्भ में, ओपी का सवाल नहीं है) छोटे कार्यों का उपयोग करना है, इसलिए वे लूप से उस चर पर भी नहीं लटक रहे हैं। लंबा।
रिच कोर्ट

@patcoll इसका मेमोरी लीक से कोई लेना-देना नहीं है। यह केवल सरणी सूचक बदल रहा है। यहां देखें: संस्करण 3a पर prismnet.com/~mcmahon/Notes/arrays_and_pointers.html
हरम स्मट्स

7

Php में मेमोरी लीक होने के कई संभावित बिंदु हैं:

  • खुद php
  • php एक्सटेंशन
  • php पुस्तकालय जो आप उपयोग करते हैं
  • आपका php कोड

डीप रिवर्स इंजीनियरिंग या php सोर्स कोड नॉलेज के बिना पहले 3 को खोजना और ठीक करना काफी कठिन है। आखिरी के लिए आप मेमोरी_ कोड_ मेमोरी के साथ मेमोरी लीकिंग कोड के लिए बाइनरी सर्च का उपयोग कर सकते हैं


91
आपका उत्तर सामान्य है क्योंकि यह मिल सकता है
ट्रैविसो

2
इसकी शर्म की बात है कि 7.2 php भी वे कोर php मेमोरी लीक को ठीक करने में सक्षम नहीं हैं। आप इसमें लंबे समय तक चलने वाली प्रक्रिया नहीं चला सकते।
आफताब नवीद

6

मैं हाल ही में एक आवेदन पर इस समस्या में भाग गया, मैं क्या समान परिस्थितियों में इकट्ठा करने के लिए। एक स्क्रिप्ट जो PHP के क्ली में चलती है जो कई पुनरावृत्तियों पर लूप करती है। मेरी स्क्रिप्ट कई अंतर्निहित पुस्तकालयों पर निर्भर करती है। मुझे संदेह है कि एक विशेष पुस्तकालय का कारण है और मैंने कई घंटे व्यर्थ किए हैं ताकि यह बिना किसी लाभ के कक्षाओं के लिए उपयुक्त विनाशकारी तरीकों को जोड़ने की कोशिश कर सके। एक अलग लाइब्रेरी (जो एक ही समस्या हो सकती है) के लिए एक लंबी रूपांतरण प्रक्रिया का सामना करते हुए मैं अपने मामले में समस्या के लिए एक क्रूड काम के साथ आया था।

मेरी स्थिति में, एक linux cli पर, मैं उपयोगकर्ता रिकॉर्ड्स के एक समूह पर लूप कर रहा था और उनमें से प्रत्येक के लिए मैंने कई कक्षाओं का एक नया उदाहरण बनाया था। मैंने PHP की निष्पादन विधि का उपयोग करके कक्षाओं के नए उदाहरण बनाने की कोशिश करने का फैसला किया ताकि वे प्रक्रिया "नए धागे" में चल सकें। यहाँ एक बहुत ही बुनियादी नमूना है जिसका मैं उल्लेख कर रहा हूँ:

foreach ($ids as $id) {
   $lines=array();
   exec("php ./path/to/my/classes.php $id", $lines);
   foreach ($lines as $line) { echo $line."\n"; } //display some output
}

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


6

मैं एक ही समस्या के साथ आया था, और मेरे समाधान के लिए एक नियमित रूप से foreach की जगह थी। मैं बारीकियों के बारे में निश्चित नहीं हूं, लेकिन ऐसा लगता है कि फॉर्च्यूनर ऑब्जेक्ट को कॉपी (या किसी तरह एक नया संदर्भ) बनाता है। लूप के लिए एक नियमित का उपयोग करना, आप सीधे आइटम तक पहुंचते हैं।


5

मेरा सुझाव है कि आप php मैन्युअल की जाँच करें या gc_enable()कचरा इकट्ठा करने के लिए फ़ंक्शन को जोड़ें ... यह स्मृति लीक है न कि आपके कोड को कैसे प्रभावित करता है।

पुनश्च: php में एक कचरा संग्राहक होता है gc_enable()जो कोई तर्क नहीं लेता है।


3

मैंने हाल ही में देखा कि PHP 5.3 लंबो फंक्शंस का उपयोग होने पर अतिरिक्त मेमोरी छोड़ दी जाती है।

for ($i = 0; $i < 1000; $i++)
{
    //$log = new Log;
    $log = function() { return new Log; };
    //unset($log);
}

मुझे यकीन नहीं है कि क्यों, लेकिन यह फ़ंक्शन हटाए जाने के बाद भी प्रत्येक मेमने को एक अतिरिक्त 250 बाइट्स लेने लगता है।


2
मैं भी यही कहने जा रहा था। यह 5.3.10 ( # 60139 ) के रूप में तय किया गया है
क्रिस्टोफर Ives

@KristopherIves, अद्यतन के लिए धन्यवाद! आप सही हैं, यह अब कोई समस्या नहीं है इसलिए मुझे अब उन्हें पागलों की तरह इस्तेमाल करने से डरना नहीं चाहिए।
Xeoncross

2

यदि आप किसी फ़ंक्शन के सही होने के बाद केवल GC के लिए कर रहे PHP के बारे में कहते हैं, तो आप किसी फ़ंक्शन के अंदर लूप की सामग्री को वर्कअराउंड / प्रयोग के रूप में लपेट सकते हैं।


1
@DavidKullmann वास्तव में मुझे लगता है कि मेरा उत्तर गलत है। आखिरकार, run()यह कहा जाता है भी एक समारोह है, जिसके अंत में जीसी होना चाहिए।
बार्ट वैन ह्युकेलोम

2

एक बड़ी समस्या मुझे create_function का उपयोग करके हुई थी । मेमने के कार्यों की तरह, यह स्मृति में उत्पन्न अस्थायी नाम को छोड़ देता है।

मेमोरी लीक का एक और कारण (Zend फ्रेमवर्क के मामले में) Zend_Db_Profiler है। सुनिश्चित करें कि यदि आप Zend फ्रेमवर्क के तहत स्क्रिप्ट चलाते हैं तो अक्षम है। उदाहरण के लिए, मैं अपने आवेदन में था।

resources.db.profiler.enabled    = true
resources.db.profiler.class      = Zend_Db_Profiler_Firebug

लगभग 25.000 क्वेरीज़ + प्रोसेसिंग का भार चलाने से पहले, मेमोरी को एक अच्छी 128Mb (मेरी अधिकतम मेमोरी सीमा) पर लाया।

बस सेटिंग करके:

resources.db.profiler.enabled    = false

यह 20 एमबी से कम रखने के लिए पर्याप्त था

और यह स्क्रिप्ट सीएलआई में चल रही थी, लेकिन यह Zend_Application को तत्काल शुरू कर रहा था और बूटस्ट्रैप को चला रहा था, इसलिए इसने "विकास" कॉन्फिगर का इस्तेमाल किया।

यह वास्तव में xDebug प्रोफाइलिंग के साथ स्क्रिप्ट को चलाने में मदद करता है


2

मैंने इसे स्पष्ट रूप से उल्लेखित नहीं देखा था, लेकिन xdebug समय और मेमोरी ( 2.6 ) के रूप में एक शानदार काम करता है । आप यह जानकारी ले सकते हैं कि यह उत्पन्न करता है और इसे अपनी पसंद के एक gui सामने के छोर से पास करता है: webgrind (केवल समय), kcachegrind , qcachegrind या अन्य ।

उदाहरण (qcachegrind का): यहां छवि विवरण दर्ज करें


1

मुझे इस बातचीत में थोड़ी देर हो गई है लेकिन मैं Zend फ्रेमवर्क के लिए उचित कुछ साझा करूँगा।

मुझे php 5.3.8 (phpfarm का उपयोग करके) एक ZF ऐप के साथ काम करने के बाद मेमोरी लीक की समस्या थी, जिसे php 5.2.9 के साथ विकसित किया गया था। मुझे पता चला कि अपाचे की httpd.conf फ़ाइल में मेमोरी लीक शुरू हो रही थी, मेरी वर्चुअल होस्ट परिभाषा में, जहाँ यह कहती है SetEnv APPLICATION_ENV "development"। इस लाइन के बाहर टिप्पणी करने के बाद, मेमोरी लीक बंद हो गया। मैं अपनी php स्क्रिप्ट में इनलाइन वर्कअराउंड के साथ आने की कोशिश कर रहा हूं (मुख्य रूप से इसे मुख्य index.php फ़ाइल में मैन्युअल रूप से परिभाषित करके)।


1
सवाल कहता है कि वह सीएलआई में चल रहा है। इसका मतलब है कि अपाचे प्रक्रिया में बिल्कुल भी शामिल नहीं है।
मैक्सिम

1
@ मैक्सटाइम अच्छा बिंदु, मैं इसे पकड़ने में असफल रहा, धन्यवाद। ओह ठीक है, उम्मीद है कि कुछ यादृच्छिक Googler को नोट से लाभ होगा जो मैंने यहाँ छोड़ दिया है, क्योंकि यह पृष्ठ मेरी समस्या को हल करने का प्रयास करते समय मेरे लिए आया था।
fronzee

इस सवाल पर मेरे जवाब की जाँच करें, शायद यही आपका मामला था।
एंडी

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

0

मैंने इसे यहां उल्लेख नहीं किया था, लेकिन एक चीज जो सहायक हो सकती है वह है रिफंड को देखने के लिए xdebug और xdebug_debug_zval ('variableName') का उपयोग करना।

मैं इस तरह से प्राप्त होने वाले php एक्सटेंशन का एक उदाहरण प्रदान कर सकता हूं: Zend Server का Z-Ray। यदि डेटा संग्रह सक्षम है, तो यह स्मृति उपयोग प्रत्येक पुनरावृत्ति पर गुब्बारा होगा जैसे कि कचरा संग्रह बंद था।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.