programing tip

메모리 누수 진단-허용 된 메모리 크기 # 바이트 소진

itbloger 2020. 8. 29. 09:35
반응형

메모리 누수 진단-허용 된 메모리 크기 # 바이트 소진


나는 끔찍한 오류 메시지, 아마도 고통스러운 노력을 통해 PHP에 메모리가 부족합니다.

123 행의 file.php에서 #### 바이트의 허용 된 메모리 크기가 소진되었습니다 (#### 바이트 할당 시도).

한계 늘리기

수행중인 작업을 알고 제한을 늘리려면 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 트릭 또는 부두 디버깅이 문제를 찾고 해결하는 데 도움이 될 수 있습니까?


PHP에는 가비지 수집기가 없습니다. 참조 카운팅을 사용하여 메모리를 관리합니다. 따라서 메모리 누수의 가장 일반적인 원인은 순환 참조와 전역 변수입니다. 프레임 워크를 사용하는 경우이를 찾기 위해 탐색 할 코드가 많이있을 것입니다. 가장 간단한 도구는 선택적으로 호출을 수행 memory_get_usage하고 코드가 누출되는 위치로 범위를 좁히는 것입니다. xdebug 를 사용하여 코드 추적을 만들 수도 있습니다 . 실행 추적show_mem_delta.


다음은 서버에서 가장 많은 메모리를 사용하는 스크립트를 식별하는 데 사용한 트릭입니다.

예를 들어 다음 코드 조각을 파일에 저장하십시오 /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웹 사용자가 로그 파일에 쓰기 전에 해야 할 수도 있습니다 .


PHP에서 메모리 누수가 발생할 수있는 몇 가지 지점이 있습니다.

  • PHP 자체
  • PHP 확장
  • 사용하는 PHP 라이브러리
  • 당신의 PHP 코드

깊은 리버스 엔지니어링이나 PHP 소스 코드 지식 없이는 처음 3 개를 찾아 수정하는 것은 매우 어렵습니다. 마지막으로 memory_get_usage로 메모리 누수 코드에 대한 이진 검색을 사용할 수 있습니다.


이전 스크립트에서 PHP가 foreach 루프 후에도 범위 내에서 "as"변수를 유지한다는 것을 한 번 발견했습니다. 예를 들면

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

I'm not sure if future PHP versions fixed this or not since I've seen it. If this is the case, you could unset($user) after the doSomething() line to clear it from memory. YMMV.


I recently ran into this problem on an application, under what I gather to be similar circumstances. A script that runs in PHP's cli that loops over many iterations. My script depends on several underlying libraries. I suspect a particular library is the cause and I spent several hours in vain trying to add appropriate destruct methods to it's classes to no avail. Faced with a lengthy conversion process to a different library (which could turn out to have the same problems) I came up with a crude work around for the problem in my case.

In my situation, on a linux cli, I was looping over a bunch of user records and for each one of them creating a new instance of several classes I created. I decided to try creating the new instances of the classes using PHP's exec method so that those process would run in a "new thread". Here is a really basic sample of what I am referring to:

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
}

Obviously this approach has limitations, and one needs to be aware of the dangers of this, as it would be easy to create a rabbit job, however in some rare cases it might help get over a tough spot, until a better fix could be found, as in my case.


I came across the same problem, and my solution was to replace foreach with a regular for. I'm not sure about the specifics, but it seems like foreach creates a copy (or somehow a new reference) to the object. Using a regular for loop, you access the item directly.


I would suggest you check the php manual or add the gc_enable() function to collect the garbage... That is the memory leaks dont affect how your code runs.

PS: php has a garbage collector gc_enable() that takes no arguments.


I recently noticed that PHP 5.3 lambda functions leave extra memory used when they are removed.

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

I'm not sure why, but it seems to take an extra 250 bytes each lambda even after the function is removed.


If what you say about PHP only doing GC after a function is true, you could wrap the loop's contents inside a function as a workaround/experiment.


One huge problem I had was by using create_function. Like in lambda functions, it leaves the generated temporary name in memory.

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

Running approximately 25.000 queries + loads of processing before that, brought the memory to a nice 128Mb (My max memory limit).

By just setting:

resources.db.profiler.enabled    = false

it was enough to keep it under 20 Mb

And this script was running in CLI, but it was instantiating the Zend_Application and running the Bootstrap, so it used the "development" config.

It really helped running the script with xDebug profiling


I didn't see it explicitly mentioned, but xdebug does a great job profiling time and memory (as of 2.6). You can take the information it generates and pass it off to a gui front end of your choice: webgrind (time only), kcachegrind, qcachegrind or others and it generates very useful call trees and graphs to let you find the sources of your various woes.

Example (of qcachegrind): enter image description here


I'm a little late to this conversation but I'll share something pertinent to Zend Framework.

I had a memory leak problem after installing php 5.3.8 (using phpfarm) to work with a ZF app that was developed with php 5.2.9. I discovered that the memory leak was being triggered in Apache's httpd.conf file, in my virtual host definition, where it says SetEnv APPLICATION_ENV "development". After commenting this line out, the memory leaks stopped. I'm trying to come up with an inline workaround in my php script (mainly by defining it manually in the main index.php file).


I didn't see it mentioned here but one thing that might be helpful is using xdebug and xdebug_debug_zval('variableName') to see the refcount.

I can also provide an example of a php extension getting in the way: Zend Server's Z-Ray. If data collection is enabled it memory use will balloon on each iteration just as if garbage collection was off.

참고URL : https://stackoverflow.com/questions/849549/diagnosing-memory-leaks-allowed-memory-size-of-bytes-exhausted

반응형