I'm working on a project where I pull data (JSON) from an API. The problem I'm having is that the memory is slowly growing until I get the dreaded fatal error:
Fatal error: Allowed memory size of * bytes exhausted (tried to allocate * bytes) in C:... on line *
I don't think there should be any memory growth. I tried unsetting everything at the end of the loop but no difference. So my question is: am I doing something wrong? Is it normal? What can I do to fix this problem?
<?php
$start = microtime(true);
$time = microtime(true) - $start;
echo "Start: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br/>";
include ('start.php');
include ('connect.php');
set_time_limit(0);
$api_key = 'API-KEY';
$tier = 'Platinum';
$threads = 10; //number of urls called simultaneously
function multiRequest($urls, $start) {
$time = microtime(true) - $start;
echo " start function: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br>";
$nbrURLS = count($urls); // number of urls in array $urls
$ch = array(); // array of curl handles
$result = array(); // data to be returned
$mh = curl_multi_init(); // create a multi handle
$time = microtime(true) - $start;
echo " Creation multi handle: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br>";
// set URL and other appropriate options
for($i = 0; $i < $nbrURLS; $i++) {
$ch[$i]=curl_init();
curl_setopt($ch[$i], CURLOPT_URL, $urls[$i]);
curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1); // return data as string
curl_setopt($ch[$i], CURLOPT_SSL_VERIFYPEER, 0); // Doesn't verifies certificate
curl_multi_add_handle ($mh, $ch[$i]); // Add a normal cURL handle to a cURL multi handle
}
$time = microtime(true) - $start;
echo " For loop options: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br>";
// execute the handles
do {
$mrc = curl_multi_exec($mh, $active);
curl_multi_select($mh, 0.1); // without this, we will busy-loop here and use 100% CPU
} while ($active);
$time = microtime(true) - $start;
echo " Execution: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br>";
echo ' For loop2<br>';
// get content and remove handles
for($i = 0; $i < $nbrURLS; $i++) {
$error = curl_getinfo($ch[$i], CURLINFO_HTTP_CODE); // Last received HTTP code
echo " error: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br>";
//error handling if not 200 ok code
if($error != 200){
if($error == 429 || $error == 500 || $error == 503 || $error == 504){
echo "Again error: $error<br>";
$result['again'][] = $urls[$i];
} else {
echo "Error error: $error<br>";
$result['errors'][] = array("Url" => $urls[$i], "errornbr" => $error);
}
} else {
$result['json'][] = curl_multi_getcontent($ch[$i]);
echo " Content: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br>";
}
curl_multi_remove_handle($mh, $ch[$i]);
curl_close($ch[$i]);
}
$time = microtime(true) - $start;
echo " after loop2: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br>";
curl_multi_close($mh);
return $result;
}
$gamesId = mysqli_query($connect, "SELECT gameId FROM `games` WHERE `region` = 'EUW1' AND `tier` = '$tier ' LIMIT 20 ");
$urls = array();
while($result = mysqli_fetch_array($gamesId))
{
$urls[] = 'https://euw.api.pvp.net/api/lol/euw/v2.2/match/' . $result['gameId'] . '?includeTimeline=true&api_key=' . $api_key;
}
$time = microtime(true) - $start;
echo "After URL array: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br/>";
$x = 1; //number of loops
while($urls){
$chunk = array_splice($urls, 0, $threads); // take the first chunk ($threads) of all urls
$time = microtime(true) - $start;
echo "<br>After chunk: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br/>";
$result = multiRequest($chunk, $start); // Get json
unset($chunk);
$nbrComplete = count($result['json']); //number of retruned json strings
echo 'For loop: <br/>';
for($y = 0; $y < $nbrComplete; $y++){
// parse the json
$decoded = json_decode($result['json'][$y], true);
$time = microtime(true) - $start;
echo " Decode: ". memory_get_peak_usage(true) . " | " . memory_get_usage() . "<br/>";
}
unset($nbrComplete);
unset($decoded);
$time = microtime(true) - $start;
echo $x . ": ". memory_get_peak_usage(true) . " | " . $time . "<br>";
// reuse urls
if(isset($result['again'])){
$urls = array_merge($urls, $result['again']);
unset($result['again']);
}
unset($result);
unset($time);
sleep(15); // limit the request rate
$x++;
}
include ('end.php');
?>
PHP Version 5.3.9 - 100 loops:
loop: memory | time (sec)
1: 5505024 | 0.98330211639404
3: 6291456 | 33.190237045288
65: 6553600 | 1032.1401019096
73: 6815744 | 1160.4345710278
75: 7077888 | 1192.6274609566
100: 7077888 | 1595.2397520542
EDIT:
After trying it with PHP 5.6.14 xampp on windows:
loop: memory | time (sec)
1: 5505024 | 1.0365679264069
3: 6291456 | 33.604479074478
60: 6553600 | 945.90159296989
62: 6815744 | 977.82566595078
93: 7077888 | 1474.5941500664
94: 7340032 | 1490.6698410511
100: 7340032 | 1587.2434458733
EDIT2: I only see the memory increase after json_decode
Start: 262144 | 135448
After URL array: 262144 | 151984
After chunk: 262144 | 152272
start function: 262144 | 152464
Creation multi handle: 262144 | 152816
For loop options: 262144 | 161424
Execution: 3145728 | 1943472
For loop2
error: 3145728 | 1943520
Content: 3145728 | 2095056
error: 3145728 | 1938952
Content: 3145728 | 2131992
error: 3145728 | 1938072
Content: 3145728 | 2135424
error: 3145728 | 1933288
Content: 3145728 | 2062312
error: 3145728 | 1928504
Content: 3145728 | 2124360
error: 3145728 | 1923720
Content: 3145728 | 2089768
error: 3145728 | 1918936
Content: 3145728 | 2100768
error: 3145728 | 1914152
Content: 3145728 | 2089272
error: 3145728 | 1909368
Content: 3145728 | 2067184
error: 3145728 | 1904616
Content: 3145728 | 2102976
after loop2: 3145728 | 1899824
For loop:
Decode: 3670016 | 2962208
Decode: 4980736 | 3241232
Decode: 5242880 | 3273808
Decode: 5242880 | 2802024
Decode: 5242880 | 3258152
Decode: 5242880 | 3057816
Decode: 5242880 | 3169160
Decode: 5242880 | 3122360
Decode: 5242880 | 3004216
Decode: 5242880 | 3277304
that's been allocated to your PHP script.
perhaps part of it, is cached memory. So even tough you unset your code, actions else where could have raised the memory but that doesn't reflect the actual memory in use but rather the most you ever used during processing the data? For example the activity u do in your "manipulate data" could raise it, that memory was used but is no longer used after, but the system will still cache it temporarily – RodriquemultiRequest
function, beforereturn
try tounset($ch)
. – Guerezamemory_get_peak_usage(true)
in your code and show us the results. We have to know where the memory actually grows so we can pinpoint the exact place. One iteration is enough because at the first loop it's already at 55MB. The growth in the other loops is not significant. – Teutonizememory_get_peak_usage(true)
I only see the memory grow afterjson_decode()
now im going to do a big loop to see what happens. – Minicamemory_get_usage
. I think it's weird that there is such a big increase fromStart
toAfter URL array
. Can you also update the code you posted with the memory debug lines so we can see exactly where they are? – Teutonizejson_decode()
but once it grew at the second for loop ofmultiRequest()
– Minica$ch
and PHP’s idiomatic way of handlingforeach
s. For debugging memory problems,xdebug
is useful.kcachegrind
works for reading its output well. Without exact code to review, it’s really hard to look at this any more, but even then, I’d want to vote to close this as “Why this code is not working?” question... As there is no simple piece of code you’d want to know why it works like it works. – Punke(any suggestions?)
You've already been given a suggestionI’d really hope you’d make a runnable test case;
, giving up doesn't make the problem go away. – Rodrique