php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #39037 Memory leaks when using curl_multi
Submitted: 2006-10-04 14:08 UTC Modified: 2006-10-06 09:40 UTC
From: eugen at id dot com dot ua Assigned:
Status: Closed Package: cURL related
PHP Version: 5.1.6 OS: Fedora Core 5
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: eugen at id dot com dot ua
New email:
PHP Version: OS:

 

 [2006-10-04 14:08 UTC] eugen at id dot com dot ua
Description:
------------
Possible memory leaks when using curl_multi_* functions. 

I start 10 tasks with curl_multi and at completion of one of them add a new task. The use of memory is gradually increased thus, although $GLOBALS has a permanent size approximately.

In particular, at the call of curl_close() or curl_multi_remove_handle() memory is not freed (I guess it should be...)

Reproduce code:
---------------
http://www.follow.net.ua/bug.phps
(see MEMORY_IS_NOT_RELEASED_HERE string near the end of file).

Expected result:
----------------
Downloading queue of documents with 10 threads

Actual result:
--------------
Gradual increasing of memory usage (sizeof $GLOBALS is *NOT* increasing)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2006-10-04 14:25 UTC] tony2001@php.net
Please try using this CVS snapshot:

  http://snaps.php.net/php5.2-latest.tar.gz
 
For Windows:
 
  http://snaps.php.net/win32/php5.2-win32-latest.zip


 [2006-10-04 15:32 UTC] eugen at id dot com dot ua
I've tried to do the same with PHP 5.2.0RC5-dev - no changes.
Memory usage before and after code

curl_close($handle);
curl_multi_remove_handle($mh, $handle);

is the same. 

Looks like curl doesn't release memory which containing downloaded files.

P.S. ????? ?? ??????? :)
 [2006-10-04 15:35 UTC] tony2001@php.net
Thank you for this bug report. To properly diagnose the problem, we
need a short but complete example script to be able to reproduce
this bug ourselves. 

A proper reproducing script starts with <?php and ends with ?>,
is max. 10-20 lines long and does not require any external 
resources such as databases, etc. If the script requires a 
database to demonstrate the issue, please make sure it creates 
all necessary tables, stored procedures etc.

Please avoid embedding huge scripts into the report.

>P.S. ????? ?? ??????? :)
No, it's a public system.
 [2006-10-04 16:01 UTC] eugen at id dot com dot ua
Here is short script without external resources requipment:

<?php
echo "Initial memory usage: ".memory_get_usage()."\n";

$tasks = array(
	"http://www.google.com",
	"http://www.php.net"
);

$curl_handles = array();
$mh = curl_multi_init();

reset($tasks);
while (list($index, $row)=each($tasks)) {
	$cid = curl_init($row);
	curl_setopt($cid, CURLOPT_RETURNTRANSFER, 1);
	curl_multi_add_handle($mh, $cid);
	$curl_handles[ $index ] = $cid;
}

do { 
	$n = curl_multi_exec($mh, $active);
	$info = curl_multi_info_read($mh);
	
	if (!empty($info)) {
		reset($curl_handles);
		while (list($task_id, $handle)=each($curl_handles)) {
			if ($handle == $info['handle']) {
				$done_task_id = $task_id;
				break;
			}
		}
		
		echo "Task $tasks[$done_task_id] completed\n";
		$memory_usage = memory_get_usage();
		// this should free up resources
		curl_close($info['handle']);
		$diff = memory_get_usage() - $memory_usage;
		echo "Memory usage was: $memory_usage, after curl_close: $diff bytes more\n";
	}

} while ($active);
?>
 [2006-10-04 17:22 UTC] tony2001@php.net
All memory leaks are reported on shutdown.
What you see is just the way zend memory manager works trying to reduce malloc/realloc/free calls and caching some memory.
No bug here.
 [2006-10-05 08:22 UTC] eugen at id dot com dot ua
I think curl_close() MUST release memory. And it does when using curl_exec() like this:

<?php

$ch = curl_init();

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, "http://www.php.net/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// grab URL and pass it to the browser
curl_exec($ch);

$memory_usage = memory_get_usage();
// close curl resource, and free up system resources
curl_close($ch);
echo "Memory usage was: $memory_usage, after curl_close - ".(memory_get_usage() - $memory_usage)." bytes more\n";
?>

In this code curl_close() releases used memory. But when I use it with curl_multi it doesn't. Why? If it is caching then it must be a bad caching, i guess...
 [2006-10-05 15:37 UTC] tony2001@php.net
I've commited a small patche which decreases refcount for curl handles added to the multi handle (previously they were destroyed on shutdown).
The patch will appear in the next snapshot.
Thanks you for your help.

Meanwhile lets look into your code:
$curl_handles[ $index ] = $cid; // refcount++
...
if ($handle == $info['handle']) { // refcount++ 
...
curl_close($info['handle']); // refcount--

1 + 1 - 1 = 1, so curl_close() doesn't actually destroy the handle, but just reduces its refcount. To destroy the handles you need to unset() numerous arrays they were added to.
..Or you can put this code into a function, this way all variables will be destroyed automatically when execution goes out of the scope.
Also, notice that curl_close() IS NOT called for the last handle, because $active is false at the moment.
 [2006-10-06 09:31 UTC] eugen at id dot com dot ua
In php5.2 2006-10-06 06:30 snapshot problem is not solved.
Even this code, where each handle is destroyed after use doesn't release memory:

<?php
$tasks = array("http://www.google.com","http://www.php.net");
$mh = curl_multi_init();

reset($tasks);
while (list($index, $row)=each($tasks)) {
	$cid = curl_init($row);
	curl_setopt($cid, CURLOPT_RETURNTRANSFER, 1);
	curl_multi_add_handle($mh, $cid);
	unset($cid); // unset curl handle
}

do { 
	$n = curl_multi_exec($mh, $active);
	$info = curl_multi_info_read($mh); 
	
	if (!empty($info)) {
		$memory_usage = memory_get_usage();
		// this should free up resources
		curl_close($info['handle']);
		$diff = memory_get_usage() - $memory_usage;
		echo "Memory usage was: $memory_usage, after curl_close: $diff bytes more\n";
	}
	unset($info);
} while ($active);
?>

> Meanwhile lets look into your code ...

Does it matter how much refs for curl connection I have?
Lets look at curl_close documentation (http://ua2.php.net/manual/en/function.curl-close.php):
This function closes a CURL session and (!!!)frees all resources(!!!).

There's nothing abount refcounts.

P.S. In submitted script curl releases memory when after while loop is finished I call curl_multi_init() again. %|
 [2006-10-06 09:40 UTC] tony2001@php.net
>Even this code, where each handle is destroyed 
$mh is not destroyed again and please re-read my previous post: "Also, notice that curl_close() IS NOT called for the last handle, because $active is false at the moment."

>Does it matter how much refs for curl connection I have?
Yes, it does.

>This function closes a CURL session and (!!!)frees all resources(!!!).
The handle is still referenced in $mh and we can't destroy it, because it would leave garbage in $mh.
Please keep this report as closed.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri May 24 14:01:30 2024 UTC