태그 보관물: memory-leaks

memory-leaks

메모리 누수 진단-허용 된 메모리 크기 # 바이트 소진 할 때까지 숫자가 천천히 증가하는 것을 볼

나는 끔찍한 오류 메시지, 아마도 고통스러운 노력을 통해 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가 foreach 루프 후에도 범위 내에서 “as”변수를 유지한다는 것을 한 번 발견했습니다. 예를 들면

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

나는 그것을 본 이후로 미래의 PHP 버전이 이것을 고쳤는지 아닌지 확실하지 않습니다. 이 경우, 당신은 할 수 unset($user)애프터 doSomething()라인 메모리에서 지워집니다. YMMV.


답변

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

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

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


답변

나는 최근에 비슷한 상황으로 모인 응용 프로그램 에서이 문제에 직면했습니다. 많은 반복을 반복하는 PHP의 cli에서 실행되는 스크립트. 내 스크립트는 여러 기본 라이브러리에 의존합니다. 특정 라이브러리가 원인이라고 생각하고 적절한 소멸 메서드를 해당 클래스에 추가하는 데 몇 시간을 허비했습니다. 다른 라이브러리로의 긴 변환 프로세스 (동일한 문제가있을 수 있음)에 직면하여 필자의 경우 문제에 대한 조잡한 해결 방법을 생각해 냈습니다.

내 상황에서는 Linux cli에서 여러 사용자 레코드를 반복하고 각각에 대해 내가 만든 여러 클래스의 새 인스턴스를 만듭니다. PHP의 exec 메서드를 사용하여 클래스의 새 인스턴스를 만들어 해당 프로세스가 “새 스레드”에서 실행되도록하기로 결정했습니다. 다음은 제가 언급 한 내용의 매우 기본적인 샘플입니다.

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
}

분명히이 접근 방식에는 한계가 있으며, 토끼 잡을 만드는 것이 쉬울 것이기 때문에 이것의 위험성을 인식해야합니다. 그러나 드물게 더 나은 해결책을 찾을 수있을 때까지 어려운 지점을 극복하는 데 도움이 될 수 있습니다. , 내 경우와 같이.


답변

나는 같은 문제를 겪었고 내 해결책은 foreach를 일반 for로 바꾸는 것이 었습니다. 세부 사항에 대해서는 잘 모르겠지만 foreach가 개체에 대한 복사본 (또는 새로운 참조)을 만드는 것처럼 보입니다. 일반 for 루프를 사용하여 항목에 직접 액세스합니다.


답변

나는 당신이 PHP 매뉴얼을 확인하거나 gc_enable()쓰레기를 수집하는 기능을 추가하는 것이 좋습니다 . 그것은 메모리 누수가 코드 실행에 영향을 미치지 않는다는 것입니다.

추신 : PHP에는 gc_enable()인수를 사용하지 않는 가비지 수집기 가 있습니다.