PHP에서 메모리 누출을 고정하는 방법
-
06-07-2019 - |
문제
내 PHP 앱에는 레코드를 가져올 수있는 가져 오기 스크립트가 있습니다.
현재 CSV 파일에서 가져오고 있습니다. 그것은 CSV 파일의 각 줄, fgetcsv를 사용하여 한 번에 한 줄 씩 읽고 있으며, 각 줄에 대해 많이 데이터베이스 쿼리를 포함하여 해당 레코드에서 처리 한 다음 다음 줄로 이동합니다. 더 많은 메모리를 계속 누적 할 필요는 없습니다.
약 2500 개의 기록을 수입 한 후, PHP는 메모리 제한 (132MB 정도)을 넘어 섰다고 밝혔다.
CSV 파일 자체는 단지 몇 메그 일뿐입니다. 다른 처리는 많은 문자열 비교, 차이 등을 수행합니다. 나는 엄청난 양의 코드가 작동하며 '가장 작은 재생산을 내기가 어려울 것입니다. 견본'.
그러한 문제를 찾고 고치는 데 좋은 방법은 무엇입니까?
발견 된 문제의 원인
런타임 동안 모든 데이터베이스 쿼리를 기록하는 디버그 클래스가 있습니다. 그래서 약 30kb 길이의 SQL 문자열은 기억에 남아있었습니다. 나는 이것이 오랫동안 실행되도록 설계된 스크립트에 적합하지 않다는 것을 알고 있습니다.
다른 메모리 누출 소스가있을 수 있지만 이것이 내 문제의 원인이라고 확신합니다.
다른 팁
실제로 스크립트에 중단되는 메모리 누출이 한두 개의 메모리 누출이 있다고 의심한다면 다음 단계를 수행해야합니다.
- 변화
memory_limit
500KB와 같은 작은 것 - 각 행에 적용되는 처리 단계 중 하나를 제외한 모든 것을 설명하십시오.
- 전체 CSV 파일을 통해 제한된 처리를 실행하고 완료 할 수 있는지 확인하십시오.
- 점차적으로 더 많은 단계를 추가하고 메모리 사용이 급증하는지 확인하십시오.
예시:
ini_set('memory_limit', 1024 * 500);
$fp = fopen("test.csv", 'r');
while($row = fgetcsv($fp)) {
validate_row($row); // step 1: validate
// add these back in one by one and keep an eye on memory usage
//calculate_fizz($row); // step 2: fizz
//calculate_buzz($row); // step 3: buzz
//triangulate($row); // step 4: triangulate
}
echo "Memory used: ", memory_get_peak_usage(), "\n";
최악의 시나리오는이 시나리오입니다 모두 처리 단계는 약간 비효율적이며 모든 처리 단계는 모든 것을 최적화해야합니다.
변수를 수행 한 후에 변수를 지우는 방법에 따라 다릅니다.
레코드가 완료된 것 같지만 여전히 정보를 어딘가에 저장하고 있습니다. 사용 unset () 의심스러운 경우 변수를 정리합니다.
도움이되지 않으면 최소한의 메모리가 어디로 가고 있는지 확인하려면 최소한의 재생 코드 샘플을 제공하십시오.
BTW, 문제를 재현 할 가장 작은 코드 샘플을 생성하는 것은 훌륭한 디버깅 기술입니다.
PHP5.3의 로컬 설치를 시도하고 http://www.php.net/manual/en/function.gc-collect-cycles.php로 전화 할 수 있습니다.
gc_collect_cycles
- 기존 쓰레기주기의 힘 수집
상황이 향상되면 최소한 문제를 확인했습니다.
파일을 어떻게 읽고 있습니까? Fread/FileGetContents 또는 기타 기능을 사용하는 경우 전체 파일이 호출 시간에로드되므로 전체 파일 크기 (또는 Fread가 많이로드)를 소비하게됩니다. 그러나 사용하는 경우 fgetcsv 줄의 길이에 따라 한 번에 한 줄만 읽으면 메모리에서 훨씬 쉬울 수 있습니다.
또한 각 루프에서 가능한 많은 변수를 재사용하고 있는지 확인하십시오. 많은 양의 데이터가있는 배열이 없는지 확인하십시오.
마지막 참고로 루프 전에 파일을 열고 후에 닫는지 확인하십시오.
$fh = fopen(...);
while(true)
{
//...
}
fclose($fh);
당신은 정말로 이것을하고 싶지 않습니다.
while(true)
{
$fh = fopen(...);
//...
fclose($fh);
}
그리고 다른 사람들이 말했듯이 코드를 보지 않고는 말하기가 어렵습니다.
코드를 보지 않고 원인을 말하기는 어렵습니다. 그러나 일반적인 문제는 재귀 참조입니다. 객체 A는 객체 B와 다른 방법으로 포인트를 사용하여 GC가 망칠 수 있습니다.
현재 파일을 처리하는 방법을 모르겠지만 한 번에 한 행 씩 파일 만 읽으려고 시도 할 수 있습니다. 전체 파일을 한 번에 읽으면 더 많은 메모리를 소비 할 수 있습니다.
이것은 실제로 배치 처리 작업을 위해 종종 파이썬을 선호하는 이유 중 하나입니다.
php.ini에서 memory_limit을 변경할 수 있습니까?
또한 변수에 대해 UNSET ($ var)를 수행 할 수 있습니다. $ var = null도 도움이 될 수 있습니까?
이 질문도 참조하십시오. php : unset () 또는 $ var = null을 사용하여 메모리를 해제하는 데 더 나은 것은 무엇입니까?
나는 같은 문제가 있었고 데이터베이스 프로파일 링 (Zend_db_profiler_firebug) 때문이었습니다. 제 경우에는 분당 1MB가 누출되었습니다. 이 스크립트는 며칠 동안 실행되어야하므로 몇 시간 안에 충돌합니다.