변수의 메모리 사용량 (크기)을 결정하는 방법은 무엇입니까?


102

주어진 변수가 얼마나 많은 메모리를 사용하는지 알아내는 함수가 PHP (또는 PHP 확장)에 있습니까? sizeof요소 / 속성 수만 알려줍니다.

memory_get_usage전체 스크립트에서 사용하는 메모리 크기를 제공한다는 점에서 도움이됩니다 . 단일 변수에 대해 이것을 수행하는 방법이 있습니까?

이것은 개발 시스템에 있으므로 확장 또는 디버그 도구를로드 할 수 있습니다.


편집 - 그 5 년 후, 그리고 몇 가지 문제가 :( 여전히 약간 미해결이다
Piskvor 건물 왼쪽

답변:


46

아마도 메모리 프로파일 러가 필요할 것입니다. 나는 정보를 수집했지만 도움이 될만한 몇 가지 중요한 것을 복사했습니다.

아시다시피 Xdebug는 2. * 버전부터 메모리 프로파일 링 지원을 중단했습니다. 여기에서 "제거 된 기능"문자열을 검색하십시오 : http://www.xdebug.org/updates.php

제거 된 기능

제대로 작동하지 않았기 때문에 메모리 프로파일 링에 대한 지원을 제거했습니다.

기타 프로파일 러 옵션

PHP 메모리 프로파일 러

https://github.com/arnaud-lb/php-memory-profiler . 이 기능을 활성화하기 위해 Ubuntu 서버에서 수행 한 작업입니다.

sudo apt-get install libjudy-dev libjudydebian1
sudo pecl install memprof
echo "extension=memprof.so" > /etc/php5/mods-available/memprof.ini
sudo php5enmod memprof
service apache2 restart

그리고 내 코드에서 :

<?php
memprof_enable();
// do your stuff
memprof_dump_callgrind(fopen("/tmp/callgrind.out", "w"));

마지막으로 KCachegrind로callgrind.out 파일을 엽니 다.

Google gperftools 사용 (권장!)

먼저 https://code.google.com/p/gperftools/ 에서 최신 패키지를 다운로드하여 Google gperftools 를 설치합니다 .

그리고 언제나처럼 :

sudo apt-get update
sudo apt-get install libunwind-dev -y
./configure
make
make install

이제 코드에서 :

memprof_enable();

// do your magic

memprof_dump_pprof(fopen("/tmp/profile.heap", "w"));

그런 다음 터미널을 열고 실행하십시오.

pprof --web /tmp/profile.heap

pprof 는 아래와 같이 기존 브라우저 세션에 새 창을 만듭니다.

memprof 및 gperftools를 사용한 PHP 메모리 프로파일 링

Xhprof + Xhgui (내 의견으로는 CPU와 메모리를 모두 프로파일 링하는 것이 최고)

XhprofXhgui 그 문제 순간에 있다면 당신은뿐만 아니라하거나 메모리 사용량 CPU 사용량을 프로파일 링 할 수 있습니다. 매우 완벽한 솔루션이며 완전한 제어권을 제공하며 로그는 mongo 또는 파일 시스템 모두에서 작성할 수 있습니다.

자세한 내용 은 여기를 참조하십시오 .

검은 화재

Blackfire는 Symfony2 사용자 인 SensioLabs의 PHP 프로파일 러입니다. https://blackfire.io/

puphpet 을 사용 하여 가상 머신을 설정하면 지원된다는 사실에 기뻐할 것입니다 ;-)

Xdebug 및 추적 메모리 사용량

XDEBUG2 는 PHP 용 확장입니다. Xdebug를 사용하면 매개 변수 및 반환 값을 포함한 모든 함수 호출을 다른 형식의 파일에 기록 할 수 있습니다. 세 가지 출력 형식이 있습니다. 하나는 사람이 읽을 수있는 추적을 의미하고 다른 하나는 구문 분석하기가 더 쉽기 때문에 컴퓨터 프로그램에 더 적합하며 마지막 하나는 추적 형식을 지정하기 위해 HTML을 사용합니다. 설정을 통해 두 가지 형식 사이를 전환 할 수 있습니다. 여기에 예제가 있습니다 .

forp

forp 단순하고, 비침 입적 이며, 프로덕션 지향적 인 PHP 프로파일 러입니다. 일부 기능은 다음과 같습니다.

  • 시간 측정 및 각 기능에 할당 된 메모리

  • CPU 사용량

  • 함수 호출의 파일 및 줄 번호

  • Google의 추적 이벤트 형식으로 출력

  • 기능 설명

  • 기능 그룹화

  • 함수 별칭 (익명 함수에 유용)

DBG

DBG 는 완전한 기능을 갖춘 PHP 디버거로, PHP 스크립트를 디버깅하는 데 도움이되는 대화 형 도구입니다. 프로덕션 및 / 또는 개발 웹 서버에서 작동하며 IDE 또는 콘솔에서 로컬 또는 원격으로 스크립트를 디버그 할 수 있으며 그 기능은 다음과 같습니다.

  • 원격 및 로컬 디버깅

  • 명시 적 및 암시 적 활성화

  • 함수 호출, 동적 및 정적 메서드 호출을 포함한 호출 스택 (파라미터 포함)

  • 해당 (중첩 된) 위치에서 변수를 평가할 수있는 기능으로 호출 스택 탐색

  • Step in / Step out / Step over / Run to cursor 기능

  • 조건부 중단 점

  • 글로벌 중단 점

  • 오류 및 경고 로깅

  • 병렬 디버깅을위한 다중 동시 세션

  • GUI 및 CLI 프런트 엔드 지원

  • IPv6 및 IPv4 네트워크 지원

  • 디버거에 의해 전송 된 모든 데이터는 선택적으로 SSL로 보호 될 수 있습니다.


2
사용자들은 바로 내가 찾던 정보, 감사합니다.
Piskvor는

93

단일 변수의 메모리 사용량을 얻는 직접적인 방법은 없지만 Gordon이 제안했듯이 memory_get_usage. 할당 된 총 메모리 양을 반환하므로 해결 방법을 사용하여 단일 변수의 사용량을 얻기 위해 전후 사용량을 측정 할 수 있습니다. 이것은 약간 해키하지만 작동합니다.

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;

이것은 신뢰할 수있는 방법이 아니며 변수를 할당하는 동안 다른 어떤 것도 메모리에 영향을주지 않는다는 것을 확신 할 수 없으므로 근사값으로 만 사용해야합니다.

함수 내부에 변수의 복사본을 만들고 사용 된 메모리를 측정하여 실제로이를 함수로 바꿀 수 있습니다. 이것을 테스트하지는 않았지만 원칙적으로 나는 그것에 대해 잘못된 것을 보지 못했습니다.

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}

14
$tmp = $var얕은 복사본을 만듭니다. $ tmp가 수정 될 때까지 더 많은 메모리를 할당하지 않습니다.
Gordon

@Gordon, 당신이 맞아요, 나는 그 점을 약간 간과했습니다. 변수의 유형이나 크기를 변경하지 않고는 변수를 수정하는 적절한 방법을 찾을 수 없으므로 그대로 두겠습니다. 아마도 누군가가 적절한 아이디어를 떠 올릴 수 있습니다. :)
Tatu Ulmanen

7
어때 $tmp = unserialize(serialize($var))? 이것은 위의 Aistina의 접근 방식을 결합합니다.
Gordon

3
또한 $var이미 함수에 전달 된 내용의 얕은 복사본 또는 참조 $tmp이므로은 필요하지 않지만에 다시 할당 할 수 있습니다 $var. 내부 참조를에서 $tmp로 저장합니다 $var.
Gordon

역 참조하는 좀 더 우아한 방법이 아닌가 $tmp로부터는 $var?
Tomáš Zato-Monica 복원

24

아니 없어. 그러나 근사치에 대한 결과를 serialize($var)확인할 수 있습니다 strlen.


이것은 전체 GC 일을 피하기 때문에 훨씬 더 나은 접근 방식입니다.
Gleno 2013

12
끔찍한 근사치입니다. PHP에서 배열의 모든 항목은 ~ 80 바이트이지만 strlen(serialize(array(1,2,3)))30 바이트 입니다.
gsnedders 2013

2
@Aistina, -1. 당신은 잘못된 것을 측정하고 있습니다. 변수와 직렬화 된 변수는 완전히 다른 두 가지이며 완전히 다른 결과를 제공합니다.
Pacerier

1
뿐만 아니라 순환 참조와 같은 직렬화 할 수없는 특정 데이터 구조에서 완전히 실패 합니다.
duskwuff -inactive- dec

20

Tatu Ulmanens에 대한 답변 :

$start_memory자체가 메모리 ( PHP_INT_SIZE * 8)를 차지 한다는 점에 유의해야합니다 .

따라서 전체 기능은 다음과 같아야합니다.

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $var = unserialize(serialize($var));
    return memory_get_usage() - $start_memory - PHP_INT_SIZE * 8;
}

추가 답변으로 추가해서 죄송합니다. 아직 답변에 대해 댓글을 달 수 없습니다.

업데이트 : * 8은 명확하지 않습니다. 그것은 분명히 php 버전과 아마도 64/32 비트에 의존 할 수 있습니다.


4
이유를 설명해 주 * 8시겠습니까? 감사!
sierrasdetandil 2013

@sierrasdetandil $ start_memory가 PHP_INT_SIZE바이트 만 차지하는 것이 아니라 PHP_INT_SIZE*8. 이 함수를 호출하여 시도 할 수 있습니다. 0을 반환해야합니다.function sizeofvar() { $start_memory = memory_get_usage(); return memory_get_usage() - $start_memory - PHP_INT_SIZE*8; }
para

8일정하지 않습니다. 내 개발 시스템 (PHP 5.6.19)의 주석 기능에 따라 -16. 또한 흥미롭게도에서 php -a함수의 두 줄을 호출하면 다양한 값이 제공됩니다.
Paul DelRe

@PaulDelRe 예, 아마도 이러한 종류의 버전 / 64 비트에 따라 다릅니다.
para

이제 unserialize () 호출에서 치명적인 오류가 발생합니다. 도움이되지 않습니다! 변수가 너무 커서 메모리가 부족하면 해당 변수에 대한 함수를 호출하면 더 많은 메모리를 사용하게됩니다. :(
john ktejik

4

보다:

이것은 특정 변수의 메모리 사용량을 제공하지 않습니다. 그러나 변수를 할당하기 전후에이 함수를 호출 한 다음 값을 비교할 수 있습니다. 사용 된 메모리에 대한 아이디어를 얻을 수 있습니다.

PECL 확장 Memtrack을 볼 수도 있지만 문서는 말할 필요도없이 거의 존재하지 않지만 약간 부족합니다.


예. 질문에 대한 답변을 간접적으로 사용할 수 있습니다.
Notinlist 2010

3

콜백 반환 값에 대한 메모리 차이를 계산하도록 선택할 수 있습니다. PHP 5.3 이상에서 사용할 수있는보다 우아한 솔루션입니다.

function calculateFootprint($callback) {
    $startMemory = memory_get_usage();
    $result = call_user_func($callback);
    return memory_get_usage() - $startMemory;
}

$memoryFootprint = calculateFootprint(
    function() {
        return range(1, 1000000);
    }
);

echo ($memoryFootprint / (1024 * 1024)) . ' MB' . PHP_EOL;

3

두 변수가 메모리에서 동일한 할당 공간을 공유 할 수 있으므로 변수의 정확한 풋 프린트를 소급하여 계산할 수 없습니다.

두 배열간에 메모리를 공유해 보겠습니다. 두 번째 배열을 할당하면 첫 번째 배열의 메모리의 절반이 소모됩니다. 첫 번째 설정을 해제해도 거의 모든 메모리가 두 번째 메모리에서 사용됩니다.

echo memory_get_usage()."\n"; // <-- 433200
$c=range(1,100);
echo memory_get_usage()."\n"; // <-- 444348 (+11148)
$d=array_slice($c, 1);
echo memory_get_usage()."\n"; // <-- 451040 (+6692)
unset($c);
echo memory_get_usage()."\n"; // <-- 444232 (-6808)
unset($d);
echo memory_get_usage()."\n"; // <-- 433200 (-11032)

그래서 우리는 두 번째 배열이 메모리의 절반을 사용한다는 결론을 내릴 수 없습니다. 첫 번째 배열을 해제하면 거짓이되기 때문입니다.

PHP에서 메모리가 할당되는 방식과 용도에 대한 전체보기를 보려면 다음 기사를 읽어 보시기 바랍니다. PHP 배열 (및 값)이 실제로 얼마나 큰가? (힌트 : BIG!)

PHP 문서 의 Reference Counting Basics 에는 메모리 사용에 대한 많은 정보와 공유 데이터 세그먼트에 대한 참조 수가 있습니다.

여기에 공개 된 다른 솔루션은 근사치에는 좋지만 PHP 메모리의 미묘한 관리를 처리 할 수있는 솔루션은 없습니다.

  1. 새로 할당 된 공간 계산

할당 후 새로 할당 된 공간을 원하면 할당 전후에 사용해야 memory_get_usage()합니다. 사본과 함께 사용하면 현실을 잘못 볼 수 있기 때문입니다.

// open output buffer
echo "Result: ";
// call every function once
range(1,1); memory_get_usage();

echo memory_get_usage()."\n";
$c=range(1,100);
echo memory_get_usage()."\n";

첫 번째의 결과를 저장하려면 memory_get_usage()변수가 이전에 이미 존재해야하고 memory_get_usage()이전에 또 다른 함수를 호출해야하며 다른 모든 함수도 호출해야합니다.

위의 예와 같이 에코하려면 출력 버퍼를 여는 데 필요한 계산 메모리를 피하기 위해 출력 버퍼가 이미 열려 있어야합니다.

  1. 필요한 공간 계산

함수를 사용하여 변수 사본을 저장하는 데 필요한 공간을 계산하려면 다음 코드가 다양한 최적화를 처리합니다.

<?php
function getMemorySize($value) {
    // existing variable with integer value so that the next line
    // does not add memory consumption when initiating $start variable
    $start=1;
    $start=memory_get_usage();
    // json functions return less bytes consumptions than serialize
    $tmp=json_decode(json_encode($value));
    return memory_get_usage() - $start;
}

// open the output buffer, and calls the function one first time
echo ".\n";
getMemorySize(NULL);

// test inside a function in order to not care about memory used
// by the addition of the variable name to the $_GLOBAL array
function test() {
    // call the function name once 
    range(1,1);

    // we will compare the two values (see comment above about initialization of $start)
    $start=1;
    $start=memory_get_usage();
    $c=range(1,100);
    echo memory_get_usage()-$start."\n";
    echo getMemorySize($c)."\n";
}
test();

// same result, this works fine.
// 11044
// 11044

변수 이름의 크기는 할당 된 메모리에서 중요합니다.

  1. 코드를 확인하세요 !!

변수는 PHP 소스 코드에서 사용되는 내부 C 구조에 의해 정의 된 기본 크기를 갖습니다. 이 크기는 숫자의 경우 변동하지 않습니다. 문자열의 경우 문자열 길이를 추가합니다.

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;

변수 이름의 초기화를 고려하지 않으면 변수가 사용하는 양을 이미 알고 있습니다 (숫자와 문자열의 경우).

숫자의 경우 44 바이트

+ 문자열의 경우 24 바이트

+ 문자열의 길이 (최종 NUL 문자 포함)

(이 숫자는 PHP 버전에 따라 변경 될 수 있습니다)

메모리 정렬로 인해 4 바이트의 배수로 반올림해야합니다. 변수가 전역 공간 (함수 내부가 아님)에 있으면 64 바이트를 더 할당합니다.

따라서이 페이지에있는 코드 중 하나를 사용하려면 간단한 테스트 케이스 (문자열 또는 숫자)를 사용한 결과가이 게시물의 모든 표시 ($ _GLOBAL 배열, 첫 번째 함수 호출, 출력 버퍼, ...)


1
... 그리고 그것은 우리가 zvalue, is_ref그리고 copy-on-write 의 내부에 들어가기 전 입니다. 감사합니다.
Piskvor는

1
덕분에 PHP 매뉴얼에서 그 페이지를 놓쳤습니다. 내 답변을 완성하기 위해 링크를 추가했습니다 (하지만 이미 읽은 것 같습니다).
아담

2

비슷한 문제가 있었고 내가 사용한 해결책은 변수를 파일에 쓴 다음 파일에 파일 크기 ()를 실행하는 것이 었습니다. 대략 다음과 같습니다 (테스트되지 않은 코드).

function getVariableSize ( $foo ) 
{
    $tmpfile = "temp-" . microtime(true) . ".txt";
    file_put_contents($tmpfile, $foo);
    $size = filesize($tmpfile);
    unlink($tmpfile);
    return $size;
}

이 솔루션은 디스크 IO를 포함하기 때문에 그렇게 빠르지는 않지만 memory_get_usage 트릭보다 훨씬 더 정확한 것을 제공해야합니다. 필요한 정밀도에 따라 다릅니다.


이 솔루션은 문자열 및 문자열의 단일 차원 배열에서만 작동하며 사용 strlen이 더 쉬울 것입니다.
Adam


1
function mesure($var){
    $start = memory_get_usage();
    if(is_string($var)){
        $newValue = $var . '';
    }elseif(is_numeric($var)){
        $newValue = $var + 0;
    }elseif(is_object($var)){
        $newValue = clone $var;
    }elseif(is_array($var)){
        $newValue = array_flip($var, []);
    }
    return memory_get_usage() - $start;
}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.