Bellek sızıntısı Teşhis - # bayt İzin bellek boyutu tükendi

11 Cevap php

Ben belki yoluyla-özenli bir çaba, PHP bellek tükendi, korkunç hata iletisiyle karşılaştı ettik:

# # # # Bayt tükenmiş İzin bellek boyutu hattında 123 file.php içinde (# # # # bayt ayırmaya çalıştı)

Increasing the limit

Eğer ne yaptığınızı biliyor ve sınır görmüyeli artırmak istiyorsanız memory_limit:

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

Dikkat! Sadece belirti değil problem çözme olabilir!

Diagnosing the leak:

Hata mesajı ben, bellek sızması, ya da gereksiz yere biriken olduğuna inandığımız bir döngü withing bir çizgiye işaret. Ben her yinelemenin sonunda memory_get_usage() ifadeleri basılı ettik ve o sınıra ulaşıncaya kadar sayısı yavaş yavaş büyüyebilir görebilirsiniz:

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
}

Bu sorunun amaçları için en kötü spagetti kod akla $user yere küresel kapsamda gizleme veya Task olduğunu varsayalım.

What tools, PHP tricks, or debugging voodoo can help me find and fix the problem?

11 Cevap

PHP bir çöp toplayıcı yoktur. Bu bellek yönetmek için sayım başvuru kullanır. Böylece, bellek sızıntıları en yaygın kaynağı halkalı referansları ve global değişkenler vardır. Eğer bir çerçeve kullanıyorsanız, korkarım, onu bulmak için trol bir sürü kod olacak. Basit alet seçici memory_get_usage çağrı ve nereye kod sızıntıları bunu daraltmak için. Ayrıca xdebug kodunun bir iz oluşturmak için kullanabilirsiniz. execution traces ve show_mem_delta ile kodu çalıştırın.

Php sızıntı bellek birkaç olası noktalar vardır:

  • kendini php
  • php uzantısı
  • Kullandığınız php kütüphanesi
  • kodu php

Bu bulmak ve derin tersine mühendislik veya php kaynak kod bilgisi olmadan ilk 3 düzeltmek oldukça zordur. Geçen biri için memory_get_usage ile bellek sızdırıyor kodu için ikili arama kullanabilirsiniz

Geçenlerde benzer durumlar olabilir toplamak ne altında, bir uygulama bu sorun koştu. Birçok tekrarlamalar üzerinde döngüler PHP'nin cli çalışan bir komut dosyası. Benim komut dosyası birkaç temel kütüphaneleri bağlıdır. Belirli bir kütüphane nedeni şüpheli ve ben boşuna o sınıfları uygun imha metotları eklemek için çalışıyor boşuna birkaç saat geçirdim. Farklı bir kütüphaneye uzun bir dönüşüm süreci (aynı sorunları ortaya dönüşebilir) benim durumumda sorun için geçici bir ham bir çalışma ile geldi ile karşı karşıya.

Benim durumda, bir linux cli, ben kullanıcı kayıtlarının bir grup üzerinde ve oluşturulan çeşitli sınıfların yeni bir örneğini oluşturma bunların her biri için loop oldu. Ben bu süreci bir "yeni bir iş parçacığı" çalıştırmak olurdu böylece PHP'nin exec metodu kullanılarak sınıfların yeni örneklerini oluşturmak denemeye karar verdi. İşte ben bahsediyorum ne gerçekten temel bir örnek:

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
}

Açıkçası bu yaklaşım sınırlamaları vardır, ve bir o bir tavşan iş yaratmak için kolay olurdu gibi daha iyi bir düzeltme bulunamadı kadar, ancak bazı nadir durumlarda zor bir nokta üzerinde yardımcı olabilir, bu tehlikelerin farkında olması gerekir , benim durumumda olduğu gibi.

PHP korumak olacağını, eski bir komut dosyası bir kez fark bile benim foreach döngüsü sonra da kapsam olarak değişken "olarak". Örneğin,

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

Ben gelecekte PHP sürümleri ben gördüm çünkü bu ya da değil sabit olmadığından emin değilim. Bu durumda ise, unset($user) doSomething() satırından sonra bellekten temizlemek için olabilir. YMMV.

Ben aynı sorun geldi ve benim çözüm için düzenli bir foreach değiştirmek oldu. Ben özellikleri hakkında emin değilim, ama foreach nesnesine bir kopyasını (veya bir şekilde yeni bir başvuru) oluşturur gibi görünüyor. Döngüsü için düzenli kullanarak, doğrudan öğeye erişmek.

Ne PHP sadece bir fonksiyon sonra GC yapıyor hakkında söyledikleri doğruysa, bir çözüm / deney gibi bir işlevin içinde döngünün içeriğini sarın.

Geçenlerde PHP 5.3 lambda fonksiyonları onlar kaldırılır kullanılan ek bellek bırakın fark ettim.

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

Ben neden emin değilim, ama bu ekstra 250 bayt fonksiyonu kaldırılır sonra bile her lambda almak gibi görünüyor.

Ben bu konuşma için biraz geç kaldım ama Zend Framework ile ilgili bir şeye paylaşacağım.

Ben php 5.2.9 ile geliştirilen bir ZF uygulaması ile çalışmak için (phpfarm kullanarak) php 5.3.8 yükledikten sonra bir bellek sızıntısı sorunu vardı. Ben bellek sızıntısı benim sanal konak diyor tanımı, SetEnv APPLICATION_ENV "development" olarak, Apache'nin httpd.conf dosyasında tetiklenir olduğunu keşfetti. Üzerinden bu hattı değerlendirildikten sonra, bellek sızıntıları durdu. Ben (özellikle ana index.php dosyasında elle tanımlayarak) benim php komut bir satır içi çözüm ile gelip çalışıyorum.

Ben Bir büyük sorun create_function ile oldu. Lambda fonksiyonları gibi, bellekte oluşturulan geçici ad bırakır.

Another cause of memory leaks (in case of Zend Framework) is the Zend_Db_Profiler. Make sure that is disabled if you run scripts under Zend Framework. For example I had in my application.ini the folowing:

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

Bundan önce, yaklaşık 25.000 sorguları + işleme yükler Koşu, güzel 128Mb (My max hafıza sınırı) için bellek getirdi.

Just ayarlayarak:

resources.db.profiler.enabled    = false

Bu 20 Mb altında tutmak için yeterli oldu

Ve bu komut CLI koşuyordu, ama Zend_Application başlatmasını ve Bootsrap'i koşuyordu, bu nedenle "kalkınma" yapılandırması kullanılır.

Gerçekten xDebug profiling ile komut dosyası çalıştıran yardımcı

Ben bu bellek sızıntıları kod çalışır nasıl etkiler dont ... php manuel kontrol veya çöp toplamak için gc_enable() işlevi eklemek öneririm.

PS: php argümanları yok götüren bir çöp toplayıcı gc_enable() vardır.

Burada komut bizim sunucu üzerinde en çok bellek kullanıyor hangi tanımlamak için kullanmış olduğunuz bir numara.

, Bir dosyaya aşağıdaki satırları kaydedin, örneğin, /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 aşağıdaki ekleyerek istihdam:

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

Sonra /var/log/httpd/php_memory_log de log dosyası analiz

Web kullanıcı günlük dosyasına yazmak için önce touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_log gerekebilir.