Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

3 Stron V   1 2 3 >  
Reply to this topicStart new topic
> [PHP] Wyciek pamięci
SmokAnalog
post 4.02.2018, 13:09:10
Post #1





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Witajcie,

nie do końca wiem jak w PHP narazić się na wyciek pamięci, ale chyba właśnie padłem jego ofiarą. Mam skrypt konsolowy, który czyta po kolei strony z zewnętrznego serwera i zawsze po około 40-50-ciu tysiącach iteracji otrzymuję błąd w stylu:

Cytat
PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 421888 bytes) in ... on line 36


A to mój kod (uprościłem dla przykładu):

  1. for ($id = $initialId;; $id += 1) {
  2. echo 'Trying #' . $id . ': ';
  3.  
  4. try {
  5. $html = file_get_contents('http://domena.com/page/' . $id);
  6.  
  7. // ...
  8. } catch (Exception $exception) {
  9. echo 'FAIL';
  10. }
  11. }


Linia 36. to:
  1. $html = file_get_contents('http://domena.com/page/' . $id);


Czy po kroku iteracji zawartość $html dalej jest trzymana w pamięci? Jakoś nie chce mi się w to wierzyć.

Ten post edytował SmokAnalog 4.02.2018, 13:09:52
Go to the top of the page
+Quote Post
com
post 4.02.2018, 14:35:22
Post #2





Grupa: Zarejestrowani
Postów: 3 032
Pomógł: 366
Dołączył: 24.05.2012

Ostrzeżenie: (0%)
-----


PHP to język interpretowany, on przetwarza pokolei, a na końcu zwraca Ci dopiero wynik, wiec gdzieś te informację musi w pamieć przechować, dodatkowo domyślnie masz ograniczenia zużycia pamieć dla skryptu, które zawsze można próbować sobie podnieść.

zrób to jakimś systemem kolejkowym najlepiej smile.gif
Go to the top of the page
+Quote Post
SmokAnalog
post 4.02.2018, 14:43:12
Post #3





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Dzięki, ale to chyba nie jest odpowiedź na moje pytanie? Co ma do tego system kolejkowy? Poza tym to, że przetwarza po kolei nie znaczy, że też nie ma wewnętrznego zarządzania pamięcią?
Go to the top of the page
+Quote Post
trzczy
post 4.02.2018, 15:05:00
Post #4





Grupa: Zarejestrowani
Postów: 460
Pomógł: 49
Dołączył: 5.06.2011

Ostrzeżenie: (0%)
-----


Czasami ograniczenia pamięci wkurzają. Np. niezajęta partycja swap, a komunikat, że brakuje pamięci. Z ciekawości: jakie jest tam ustawienie memory_limit w php.ini?
Go to the top of the page
+Quote Post
com
post 4.02.2018, 15:07:53
Post #5





Grupa: Zarejestrowani
Postów: 3 032
Pomógł: 366
Dołączył: 24.05.2012

Ostrzeżenie: (0%)
-----


https://github.com/php/php-src/blob/master/...ard/file.c#L522 tutaj jest kod za to odpowiedzialny biggrin.gif
Jest zarządzie pamięcią robi malloc i gdzieś tam pewnie jest ta pamieć zwalniana ale niekoniecznie od razu.

No system kolejkowy, żeby wrzucić te operacje na kolejkę i przetworzyć a nie robić to w pętli tak jak masz teraz
Go to the top of the page
+Quote Post
darko
post 4.02.2018, 15:15:16
Post #6





Grupa: Zarejestrowani
Postów: 2 885
Pomógł: 463
Dołączył: 3.10.2009
Skąd: Wrocław

Ostrzeżenie: (0%)
-----


Wyciek pamięci, a wyczerpanie dozwolonego limitu zużycia pamięci dla skryptu to dwie różne kwestie. Zwróć uwagę na to, że nie podałeś warunku zakończenia pętli for, zatem nie dziw się, że mieli w nieskończoność aż dojdzie do limitu pamięci.


--------------------
Nie pomagam na pw, tylko forum.
Go to the top of the page
+Quote Post
SmokAnalog
post 4.02.2018, 15:25:30
Post #7





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


@darko tylko co ma do tego brak warunku? Ma mielić w nieskończoność i dobrze napisany kod nie powinien doprowadzić do wycieku nawet gdy mieli przez trylion lat. Twoim tokiem rozumowania to każda pierwsza lepsza gra komputerowa powinna wywoływać wyciek.
Go to the top of the page
+Quote Post
trueblue
post 4.02.2018, 15:36:30
Post #8





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Coś się poprawia dodając unset($html) i/lub http://php.net/manual/en/function.gc-collect-cycles.php ?

Ten post edytował trueblue 4.02.2018, 15:36:43


--------------------
Go to the top of the page
+Quote Post
com
post 4.02.2018, 16:03:15
Post #9





Grupa: Zarejestrowani
Postów: 3 032
Pomógł: 366
Dołączył: 24.05.2012

Ostrzeżenie: (0%)
-----


Ale zacznijmy od tego że PHP, nie koniecznie jest dobrze napisanym kodem, poza tym gry w PHP się nie pisze to zupełnie dwa rożne światy wink.gif
Go to the top of the page
+Quote Post
darko
post 4.02.2018, 16:10:39
Post #10





Grupa: Zarejestrowani
Postów: 2 885
Pomógł: 463
Dołączył: 3.10.2009
Skąd: Wrocław

Ostrzeżenie: (0%)
-----


Nie zrozumieliśmy się. Problemy ze zwalnianiem pamięci w pętlach w PHP są tak stare jak świat. Na podstawie tego fragmentu kodu trudno określić co dzieje się ze zmienną $html dalej i czy robisz tam jakiś unset czy nie. Użyj profilera, poświęć czas na analizę logu, poeksperymentuj z garbage collectorem. W sieci można znaleźć wiele tematów dotyczących problemów z pamięcią i używaniem takich funkcji jak json_decode, file_get_contents i funkcjami operującymi na xmlu. PHP generalnie średnio nadaje się do pisania rozwiązań działających na zasadzie demonów właśnie przez znane problemy ze zwalnianiem pamięci.


--------------------
Nie pomagam na pw, tylko forum.
Go to the top of the page
+Quote Post
phpion
post 4.02.2018, 16:42:02
Post #11





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Nie jest przypadkiem tak, że dla konkretnego adresu wczytywanie jego zawartości powoduje przekoreczenie pamięci? Może jest tam tak duża zawartość ze wczytanie jej do zmiennej w PHP powoduje przekoreczenie limitu. Ustal czy błąd powstaje każdorazowo dla tego samego adresu ($id).
Go to the top of the page
+Quote Post
SmokAnalog
post 4.02.2018, 17:03:35
Post #12





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


@phpion Jestem pewien. Jedna strona nie ma 512 MB. Rozumiem, że podważasz zdanie kolegów wyżej?

@darko Nie wiem czy PHP się aż tak różni, żeby uniemożliwiać użycie pętli idących w tysiące iteracji. Zalicza się go jednak do języków ogólnego przeznaczenia. Świat PHP nie kończy się na requestach i response'ach.

@trueblue Muszę właśnie zrobić taki test. Zmienię limit pamięci na malutki, np. 1 MB, dam unset i zobaczę. Do głowy przychodzi mi jeszcze jedna możliwość. Używam w tej pętli kilka razy preg_match z trzecim argumentem, na przykład:

  1. preg_match('#personId=(\d+)#', $html, $matches);


Może ten trzeci argument, podawany w końcu jako referencja, powoduje wyciek? Tak naprawdę nie jest powiedziane, że to właśnie zmienna $html wyciekła. To ona spowodowała przekroczenie limitu, ale być może zapchało go coś innego? Tylko że poza preg_match, zapytaniami do bazy i echo nic tam innego nie ma, a bez echo też występuje ten błąd.

Ten post edytował SmokAnalog 4.02.2018, 17:04:17
Go to the top of the page
+Quote Post
phpion
post 4.02.2018, 20:40:46
Post #13





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Do zmiennej $html wczytujesz ponad 500 Mb danych co powoduje przekroczenie pamięci. Żadne unsety tutaj nie pomogą bo sama zmienna zawiera w sobie zbyt wiele danych.
Go to the top of the page
+Quote Post
SmokAnalog
post 4.02.2018, 20:58:58
Post #14





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Eee, jakim cudem wczytuję do zmiennej $html 500 mega? haha.gif Przecież nie appenduję do tego stringa, tylko na nowo ustawiam.
Go to the top of the page
+Quote Post
markuz
post 4.02.2018, 21:15:21
Post #15





Grupa: Zarejestrowani
Postów: 1 240
Pomógł: 278
Dołączył: 11.03.2008

Ostrzeżenie: (0%)
-----


memory_get_usage

Po prostu sprawdź w którym momencie ilość pamięci wzrasta. Może masz jakąś tablicę której nie czyścisz, może to jakiś wbudowany mechanizm PHP dla danej funkcji. Wywołaj tą funkcję po każdym "bloku", następnie w danym "bloku" (tym który zwiększa zużycie) po każdej linijce itp. Jak znajdziesz linijkę to już pewnie sobie poradzisz.

Podawanie fragmentu kodu jest dobre w większości przypadków - ale nie tutaj, możesz pokazać całość. Pokazałeś tylko file_get_contents a na 99% przyczyna jest w innym miejscu.

To, że w tej lini występuje błąd też nic nie mówi - jeżeli mamy 50 kb dostępnej pamięci, a file_get_contents zabiera tylko 5 kb pamięci, to chcemy się dowiedzieć skąd pochodzi reszta tj. 45 kb. A, że akurat została przekroczona w tym miejscu - to bez znaczenia.

Ten post edytował markuz 4.02.2018, 21:22:28


--------------------
Go to the top of the page
+Quote Post
SmokAnalog
post 4.02.2018, 21:24:55
Post #16





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Masz rację, że to nie dowód. Sam o tym napisałem powyżej. A co myślisz o koncepcji z preg_match?
Go to the top of the page
+Quote Post
markuz
post 4.02.2018, 21:27:05
Post #17





Grupa: Zarejestrowani
Postów: 1 240
Pomógł: 278
Dołączył: 11.03.2008

Ostrzeżenie: (0%)
-----


Nic nie myślę, po prostu to sprawdź tą funkcją smile.gif


--------------------
Go to the top of the page
+Quote Post
SmokAnalog
post 4.02.2018, 21:29:15
Post #18





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Jasne. Sprawdzę jak dotrę do domu smile.gif
Go to the top of the page
+Quote Post
phpion
post 4.02.2018, 21:38:39
Post #19





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Mój błąd, przeczytałem ze wczytujesz więcej niż 500Mb. Tak czy inaczej piszesz ze błąd dotyczy linii z file_get_contents wiec chyba w tym miejscu przekraczasz pamięć. Użyj podanej przez markuza funkcji badając zużycie pamięci w poszczególnych liniach/iteracjach. Teoretycznie przy każdej iteracji pamięć niekoniecznie powinna wzrastać bo jest to zależne od rozmiaru obrabianych danych. Ustal tez czy problem tyczy konkretnego $id czy pojawia sie losowo.
Go to the top of the page
+Quote Post
SmokAnalog
post 4.02.2018, 23:04:46
Post #20





Grupa: Zarejestrowani
Postów: 1 707
Pomógł: 266
Dołączył: 3.07.2012
Skąd: Poznań

Ostrzeżenie: (0%)
-----


Owszem, ale na logikę: to nie znaczy, że to ta linia powoduje wyciek. Ona przekracza, co jest zrozumiałe, bo potrzebuje jej najwięcej. Ale kumulować pamięć może cokolwiek imnego.

Dobra Panowie. Już wiem co powoduje wyciek.

Przepraszam, że dopiero teraz to mówię, ale tak obsługuję wyjątki w tym skrypcie:

  1. set_error_handler(function ($severity, $message, $file, $line) {
  2. if (!(error_reporting() & $severity)) {
  3. // This error code is not included in error_reporting
  4. return;
  5. }
  6.  
  7. throw new ErrorException($message, 0, $severity, $file, $line);
  8. });


Ilekroć strona nie istnieje (404), mój file_get_contents wyrzuca wyjątek. Tak ma być, ale jest jeden problem. Każdy poprzedni $html jest zapisywany jako stack trace tego wyjątku. Zrobiłem test polegający na tym, że wypisuję ilość zużywanej pamięci i dodatkowo ustawiłem na sztywno numerek dla URL-a na taki, który zwraca 404. Pamięć rośnie wtedy jak szalona. W moim normalnym użyciu, tych 404 nie ma aż tak dużo, ale wystarczająco, by w końcu przepełnić pamięć.

Jak mogę sprawić, by te wyjątki nie przechowywały całego stosu? Nie ukrywam, że najchętniej zostałbym przy wyjątkach zamiast klasycznych błędów. Próbowałem dać xdebug_disable();, ale nie istnieje u mnie taka funkcja.
Go to the top of the page
+Quote Post

3 Stron V   1 2 3 >
Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 29.03.2024 - 14:53