IT이야기

PHP에서 강제로 메모리 해제

cyworld 2021. 4. 10. 09:43
반응형

PHP에서 강제로 메모리 해제


PHP는 프로그램에서 내가 순차적으로 파일 (과의 무리를 읽기 file_get_contents), gzdecode그들, json_decode그 결과는, 사진의 대부분을 버리고, 내용을 분석하고 배열에 저장 약 1 %.

불행히도 매번 반복 할 때마다 (파일 이름을 포함하는 배열을 순회) 약간의 메모리 손실이있는 것 같습니다 (에 따르면 memory_get_peak_usage매번 약 2-10MB). 내 코드를 이중 및 삼중 확인했습니다. 루프에 불필요한 데이터를 저장하지 않고 (필요한 데이터는 전체적으로 약 10MB를 거의 초과하지 않음) 자주 다시 작성합니다 (실제로는 배열의 문자열). 분명히 PHP는 메모리를 올바르게 해제하지 않으므로 한계에 도달 할 때까지 점점 더 많은 RAM을 사용합니다.

강제 가비지 콜렉션을 수행하는 방법이 있습니까? 아니면 적어도 메모리가 어디에 사용되는지 알아 내려면?


메모리 조각화와 관련이 있습니다.

하나의 문자열에 연결된 두 개의 문자열을 고려하십시오. 각 원본은 출력이 생성 될 때까지 남아 있어야합니다. 출력이 두 입력보다 깁니다.
따라서 이러한 연결의 결과를 저장하려면 새 할당을 만들어야합니다. 원래 문자열 은 해제 되지만 작은 메모리 블록입니다. 당신
의 경우 'str1' . 'str2' . 'str3' . 'str4'에 각. -그리고 그들 중 어느 것도 해방 된 공간에 맞지 않습니다. 문자열은 다른 메모리 사용으로 인해 연속 메모리에 배치되지 않을 가능성이 높습니다 (즉, 각 문자열은 있지만 다양한 문자열이 끝에서 끝까지 배치되지 않음). 따라서 문자열을 해제하면 공간을 효과적으로 재사용 할 수 없기 때문에 문제가 발생합니다. 그래서 당신은 당신이 만드는 각 tmp와 함께 성장합니다. 그리고 당신은 아무것도 재사용하지 않습니다.

배열 기반 implode를 사용하여 정확히 필요한 길이 인 1 개의 출력 만 생성합니다. 1 개의 추가 할당 만 수행합니다. 따라서 훨씬 더 효율적인 메모리와 연결 조각화가 발생하지 않습니다. 파이썬도 마찬가지입니다. 문자열을 연결해야하는 경우 둘 이상의 연결은 항상 배열 기반이어야합니다.

''.join(['str1','str2','str3'])

파이썬에서

implode('', array('str1', 'str2', 'str3'))

PHP에서

sprintf 등가물도 괜찮습니다.

memory_get_peak_usage에 의해보고 된 메모리는 기본적으로 항상 사용해야하는 가상 맵의 "마지막"메모리 비트입니다. 따라서 항상 성장하고 있기 때문에 빠른 성장을보고합니다. 각 할당은 현재 사용 된 메모리 블록의 "끝"에 해당합니다.


PHP> = 5.3.0에서는 gc_collect_cycles()GC 패스를 강제로 호출 할 수 있습니다 .

참고 : zend.enable_gc활성화 된 상태에서 활성화했거나을 php.ini호출 gc_enable()하여 순환 참조 수집기를 활성화해야합니다.


해결책을 찾았습니다 : 문자열 연결이었습니다. 일부 변수를 연결하여 한 줄씩 입력을 생성했습니다 (출력은 CSV 파일 임). 그러나 PHP는 문자열의 이전 사본에 사용 된 메모리를 해제하지 않는 것처럼 보이므로 사용하지 않는 데이터로 RAM을 효과적으로 막습니다. 배열 기반 접근 방식으로 전환 (그리고 출력 파일에 넣기 직전에 쉼표로 분해)하면이 동작을 피할 수 있습니다.

어떤 이유로-나에게 분명하지 않은-PHP는 json_decode 호출 중에 증가 된 메모리 사용량을보고하여 json_decode 함수가 문제라는 가정으로 오해했습니다.


PHP의 내부 메모리 관리자가 함수 완료시 호출 될 가능성이 가장 높다는 것을 발견했습니다. 그것을 알고, 나는 다음과 같은 루프에서 코드를 리팩토링했습니다.

while (condition) {
  // do
  // cool
  // stuff
}

...에

while (condition) {
  do_cool_stuff();
}

function do_cool_stuff() {
  // do
  // cool
  // stuff
}

편집하다

이 빠른 벤치 마크를 실행했지만 메모리 사용량이 증가하지 않았습니다. 이것은 누출이 아니라고 믿게 만듭니다.json_decode()

for($x=0;$x<10000000;$x++)
{
  do_something_cool();
}

function do_something_cool() {
  $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
  $result = json_decode($json);
  echo memory_get_peak_usage() . PHP_EOL;
}

memory_get_peak_usage()각 진술 후에 전화 하고 가능한 unset()모든 것을 확인하십시오 . 로 반복 foreach()하는 경우 참조 된 변수를 사용하여 원본 ( foreach () ) 의 복사본을 만들지 마십시오 .

foreach( $x as &$y)

PHP가 실제로 메모리를 누출하는 경우 강제 가비지 수집은 아무런 차이가 없습니다.

IBM 에서 PHP 메모리 누수 및 감지에 대한 좋은 기사가 있습니다.


방금 동일한 문제가 발생하여 가능한 해결 방법을 찾았습니다.

상황 : db 쿼리에서 csv 파일로 작성했습니다. 저는 항상 $ row 하나를 할당하고 다음 단계에서 다시 할당했습니다. $ row를 설정 해제해도 도움이되지 않았습니다. 5MB 문자열을 $ row에 먼저 넣는 것은 (조각화를 피하기 위해) 도움이되지 않았습니다. $ row-s 배열을 생성 (많은 행을로드 + 5000 번째 단계마다 전체 설정 해제)하는 것은 도움이되지 않았습니다. 정말 몇 가지를 시도했습니다.

그러나.

파일을 여는 별도의 함수를 만들고 (전체 메모리를 소모하지 않을 정도로 충분) 100.000 줄을 전송하고 파일을 닫았을 때이 함수에 대한 후속 호출을 수행했습니다 (기존 파일에 추가). 모든 함수 종료, PHP는 쓰레기를 제거했습니다. 그것은 지역 변수 공간이었습니다.

결론 : 함수가 종료 될 때마다 모든 지역 변수가 해제됩니다.

This is the rule, as far as I found out. Just one side note however: when I tried to make my "do_only_a_smaller_subset()" function get some variables by reference (namely the query object and the file pointer), garbage collection did not happen. Now maybe I'm misunderstanding something and maybe the query object (mysqli) was leaking, well, I don't know. However, since it was passed by ref, obviously it couldn't get cleaned up since it existed afer the small function's exit point.

So, worth a try! It saved my day to find this out.


I was going to say that I wouldn't necessarily expect gc_collect_cycles() to solve the problem - since presumably the files are no longer mapped to zvars. But did you check that gc_enable was called before loading any files?

I've noticed that PHP seems to gobble up memory when doing includes - much more than is required for the source and the tokenized file - this may be a similar problem. I'm not saying that this is a bug though.

I believe one workaround would be not to use file_get_contents but rather fopen()....fgets()...fclose() rather than mapping the whole file into memory in one go. But you'd need to try it to confirm.

HTH

C.


There recently was a similar issue with System_Daemon. Today I isolated my problem to file_get_contents.

Could you try using fread instead? I think this may solve your problem. If it does, it's probably time to do a bugreport over at PHP.

ReferenceURL : https://stackoverflow.com/questions/2461762/force-freeing-memory-in-php

반응형