복붙노트

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

PHP

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

나는 두려운 오류 메시지를 겪어왔다. 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 트릭 또는 디버깅 부두를 사용하면 문제를 찾아 해결할 수 있습니까?

해결법

  1. ==============================

    1.PHP에는 가비지 컬렉터가 없습니다. 참조 카운팅을 사용하여 메모리를 관리합니다. 따라서 메모리 누수의 가장 일반적인 원인은 순환 참조 및 전역 변수입니다. 프레임 워크를 사용한다면 트롤링 할 코드가 많을 것입니다. 가장 간단한 방법은 memory_get_usage에 호출을 선택적으로 배치하고 코드 누설 위치로 범위를 좁히는 것입니다. xdebug를 사용하여 코드 추적을 생성 할 수도 있습니다. 실행 추적 및 show_mem_delta를 사용하여 코드를 실행합니다.

    PHP에는 가비지 컬렉터가 없습니다. 참조 카운팅을 사용하여 메모리를 관리합니다. 따라서 메모리 누수의 가장 일반적인 원인은 순환 참조 및 전역 변수입니다. 프레임 워크를 사용한다면 트롤링 할 코드가 많을 것입니다. 가장 간단한 방법은 memory_get_usage에 호출을 선택적으로 배치하고 코드 누설 위치로 범위를 좁히는 것입니다. xdebug를 사용하여 코드 추적을 생성 할 수도 있습니다. 실행 추적 및 show_mem_delta를 사용하여 코드를 실행합니다.

  2. ==============================

    2.PHP에서 메모리가 누출되는 몇 가지 가능한 점이 있습니다.

    PHP에서 메모리가 누출되는 몇 가지 가능한 점이 있습니다.

    깊은 리버스 엔지니어링이나 PHP 소스 코드 지식없이 첫 번째 3 개를 찾아서 고치는 것은 매우 어렵습니다. 마지막으로 memory_get_usage로 메모리 누출 코드를 검색 할 수 있습니다.

  3. ==============================

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

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

    다음 스 니펫을 파일 (예 : /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에있는 로그 파일을 분석하십시오.

    웹 사용자가 로그 파일에 쓸 수 있으려면 / var / log / httpd / php_memory_log && chmod 666 / var / log / httpd / php_memory_log를 눌러야 할 수도 있습니다.

  4. ==============================

    4.나는 이전 스크립트에서 PHP가 "as"변수를 내 foreach 루프 후에도 범위 내에서 유지한다는 것을 알았습니다. 예를 들어,

    나는 이전 스크립트에서 PHP가 "as"변수를 내 foreach 루프 후에도 범위 내에서 유지한다는 것을 알았습니다. 예를 들어,

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

    나는 미래 PHP 버전이 이것을 보아 왔기 때문에 이것을 고칠 지 모르겠다. 이 경우 doSomething () 행 다음에 메모리에서 값을 지우려면 설정을 해제 ($ user) 할 수 있습니다. YMMV.

  5. ==============================

    5.나는 최근에 비슷한 상황으로 모여 응용 프로그램에서이 문제에 부딪혔다. 많은 반복을 반복하는 PHP의 cli에서 실행되는 스크립트. 내 스크립트는 몇 가지 기본 라이브러리에 따라 다릅니다. 나는 특정 라이브러리가 원인이라고 생각하며 쓸데없는 클래스를 적절한 파괴 메소드에 추가하려고 몇 시간을 허비했다. 다른 라이브러리 (동일한 문제가있는 것으로 판명 될 수 있음)에 대한 오랜 변환 프로세스에 직면했을 때 제 경우의 문제에 대한 근본적인 해결 방법을 생각해 냈습니다.

    나는 최근에 비슷한 상황으로 모여 응용 프로그램에서이 문제에 부딪혔다. 많은 반복을 반복하는 PHP의 cli에서 실행되는 스크립트. 내 스크립트는 몇 가지 기본 라이브러리에 따라 다릅니다. 나는 특정 라이브러리가 원인이라고 생각하며 쓸데없는 클래스를 적절한 파괴 메소드에 추가하려고 몇 시간을 허비했다. 다른 라이브러리 (동일한 문제가있는 것으로 판명 될 수 있음)에 대한 오랜 변환 프로세스에 직면했을 때 제 경우의 문제에 대한 근본적인 해결 방법을 생각해 냈습니다.

    필자의 상황에서는 리눅스 클라이 (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
    }
    

    분명히이 접근법에는 한계가 있으며 토끼의 일을 만들기가 쉽기 때문에이 위험성을 인식 할 필요가 있습니다. 그러나 드문 경우지만 더 나은 해결 방법이 발견 될 때까지 어려운 지점을 극복하는 데 도움이 될 수 있습니다. 내 경우처럼.

  6. ==============================

    6.나는 같은 문제를 겪었고, 나의 해결책은 foreach를 보통의 것으로 대체하는 것이었다. 구체적인 내용은 잘 모르겠지만 foreach가 객체에 사본 (또는 어떻게 든 새로운 참조)을 만드는 것처럼 보입니다. 일반 for 루프를 사용하면 항목에 직접 액세스 할 수 있습니다.

    나는 같은 문제를 겪었고, 나의 해결책은 foreach를 보통의 것으로 대체하는 것이었다. 구체적인 내용은 잘 모르겠지만 foreach가 객체에 사본 (또는 어떻게 든 새로운 참조)을 만드는 것처럼 보입니다. 일반 for 루프를 사용하면 항목에 직접 액세스 할 수 있습니다.

  7. ==============================

    7.나는 최근에 PHP 5.3 람다 함수가 제거 될 때 여분의 메모리를 남겨 두는 것으로 나타났습니다.

    나는 최근에 PHP 5.3 람다 함수가 제거 될 때 여분의 메모리를 남겨 두는 것으로 나타났습니다.

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

    이유는 모르겠지만 함수가 제거 된 후에도 각 람다를 250 바이트 더 추가하는 것으로 보입니다.

  8. ==============================

    8.난 당신이 PHP 매뉴얼을 확인하거나 가비지 수집하는 gc_enable () 함수를 추가하는 것이 좋습니다 ... 그것은 메모리 누수가 코드가 실행되는 방법에 영향을 미치지 않습니다.

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

    추신 : PHP는 인자를 취하지 않는 가비지 컬렉터 gc_enable ()을가집니다.

  9. ==============================

    9.PHP가 함수를 수행 한 후에 GC를 수행한다고 말하면 함수의 루프 내용을 임시 해결책 / 실험으로 감쌀 수 있습니다.

    PHP가 함수를 수행 한 후에 GC를 수행한다고 말하면 함수의 루프 내용을 임시 해결책 / 실험으로 감쌀 수 있습니다.

  10. ==============================

    10.내가 가진 한 가지 큰 문제는 create_function을 사용하는 것이 었습니다. 람다 함수처럼, 생성 된 임시 이름을 메모리에 남겨 둡니다.

    내가 가진 한 가지 큰 문제는 create_function을 사용하는 것이 었습니다. 람다 함수처럼, 생성 된 임시 이름을 메모리에 남겨 둡니다.

    Zend Framework의 경우 메모리 누수의 또 다른 원인은 Zend_Db_Profiler입니다. 젠드 프레임 워크에서 스크립트를 실행하는 경우 비활성화되어 있는지 확인하십시오. 예를 들어 나는 내 application.ini에 folowing을 가지고 있었다.

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

    그 전에 처리량이 약 25.000 개의 쿼리를 실행하면 메모리가 128MB (최대 메모리 한도)로 향상되었습니다.

    그냥 설정함으로써 :

    resources.db.profiler.enabled    = false
    

    20Mb 이하로 유지하는 것으로 충분했다.

    이 스크립트는 CLI에서 실행 중이지만 Zend_Application을 인스턴스화하고 부트 스트랩을 실행 중이므로 "개발"구성을 사용했습니다.

    xDebug 프로파일 링으로 스크립트를 실행하는 데 실제로 도움이되었습니다.

  11. ==============================

    11.명시 적으로 언급 한 것을 보지 못했지만 xdebug는 시간과 메모리를 프로파일 링하는 훌륭한 작업을 수행합니다 (2.6 현재). 생성 된 정보를 가져와 원하는 GUI 프론트 엔드 인 webgrind (시간 만), kcachegrind, qcachegrind 또는 기타로 전달할 수 있으며 다양한 불만의 원인을 찾을 수있는 매우 유용한 콜 트리 및 그래프를 생성합니다 .

    명시 적으로 언급 한 것을 보지 못했지만 xdebug는 시간과 메모리를 프로파일 링하는 훌륭한 작업을 수행합니다 (2.6 현재). 생성 된 정보를 가져와 원하는 GUI 프론트 엔드 인 webgrind (시간 만), kcachegrind, qcachegrind 또는 기타로 전달할 수 있으며 다양한 불만의 원인을 찾을 수있는 매우 유용한 콜 트리 및 그래프를 생성합니다 .

    qcachegrind의 예 :

  12. ==============================

    12.이 대화에 조금 늦었지만 젠드 프레임 워크와 관련된 것을 공유 할 것입니다.

    이 대화에 조금 늦었지만 젠드 프레임 워크와 관련된 것을 공유 할 것입니다.

    PHP 5.3.8 (phpfarm 사용)을 설치 한 후 PHP 5.2.9로 개발 된 ZF 응용 프로그램을 사용하여 메모리 누수 문제가 발생했습니다. 아파치의 httpd.conf 파일에서 가상 호스트 정의에서 SetEnv APPLICATION_ENV "development"라는 메모리 누수가 발생했다는 것을 발견했다. 이 줄을 주석 처리 한 후 메모리 누수가 중지되었습니다. 내 PHP 스크립트 (주로 index.php 주 파일에서 수동으로 정의하여)에서 인라인 해결 방법을 생각해 내려고 해요.

  13. ==============================

    13.여기에 언급 된 것을 보지 못했지만 도움이 될만한 한 가지는 xdebug와 xdebug_debug_zval ( 'variableName')을 사용하여 참조 개수를 보는 것입니다.

    여기에 언급 된 것을 보지 못했지만 도움이 될만한 한 가지는 xdebug와 xdebug_debug_zval ( 'variableName')을 사용하여 참조 개수를 보는 것입니다.

    Zend Server의 Z-Ray 인 PHP 확장 예제를 제공 할 수도 있습니다. 데이터 수집이 활성화 된 경우 가비지 수집이 꺼져있는 것처럼 각 반복에서 메모리 사용이 급증 할 것입니다.

  14. from https://stackoverflow.com/questions/849549/diagnosing-memory-leaks-allowed-memory-size-of-bytes-exhausted by cc-by-sa and MIT license