PHP,MySQL内存泄漏问题

3

Faced with a problem, I can not find a memory leak when working with a database. The script takes a large data from database and therefore the memory leak critical. This problem occurs when working with mysqli, mysql or PDO. Here is test code:

$link = mysqli_connect('localhost', 'root', '');
if (!$link) {
    die('Connection error: ' . mysql_error());
}
mysqli_select_db($link, 'coolstat.my') or die ('Can\'t use coolstat.my: ' . mysql_error());


for($ii=0; $ii<20000; $ii+=1000){
    $sql= "SELECT `codes_data`.* FROM `codes_data` INNER JOIN codes ON codes.siteid= 20     AND codes.codeid=codes_data.codeid LIMIT ".$ii.", ".($ii+1000)."";
    ///

    $data= array();
    $result = mysqli_query($link, $sql);
    while (($row = mysqli_fetch_array($result))){
        $data[]= $row;
    }
    mysqli_free_result($result);
    unset($result);
    unset($data);
    echo "Memory get_data usage: ".convert_memory_val(memory_get_peak_usage(true))."<br />\n";
}
mysqli_close($link);


function convert_memory_val($size){
    $unit = array('b', 'kb', 'mb', 'gb', 'tb', 'pb');
    return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . ' ' . $unit[$i];
}

It output:

Memory get_data usage: 3.25 mb
Memory get_data usage: 6 mb
Memory get_data usage: 9 mb
Memory get_data usage: 11.75 mb
Memory get_data usage: 14.75 mb
Memory get_data usage: 17.75 mb
Memory get_data usage: 20.5 mb
Memory get_data usage: 23.5 mb
Memory get_data usage: 26.5 mb
Memory get_data usage: 29.5 mb
Memory get_data usage: 32.25 mb
Memory get_data usage: 35.25 mb
Memory get_data usage: 38.25 mb
Memory get_data usage: 41.25 mb
Memory get_data usage: 44 mb
Memory get_data usage: 47 mb
Memory get_data usage: 50 mb
Memory get_data usage: 53 mb
Memory get_data usage: 56 mb
Memory get_data usage: 58.75 mb


3
不是主题,但你需要用mysqli_error($link)替换mysql_error。 - Alain Tiemblo
这不是一个生产脚本,只是一个快速复制粘贴。 - Vlad Fedosov
不,你不能这样做。即使你将所有对一个对象的引用设置为null,你也无法控制垃圾回收器何时运行。 - JvdBerg
在这种情况下,我无法更改内存限制。 - Vlad Fedosov
哪个平台?具体是哪个版本的PHP? - Kaii
显示剩余6条评论
2个回答

4

你的错误在于Limit子句:第二个数应该是常数,比如1000。使用你目前的写法,查询将会是

LIMIT 0, 1000
LIMIT 1000, 2000
LIMIT 2000, 3000
...

这不是分页,而是逐步获取数据,这些数据也存在重叠。因此内存使用量的增加是正确的。


谢谢,你救了我的一天! - syl.fabre

2
当垃圾回收器被打开时,上述描述的循环查找算法会在根缓冲区满时执行。根缓冲区有一个固定的大小,最多可容纳10,000个可能的根节点[...]。此外,即使可能的根缓冲区尚未填满,也可以强制收集循环引用。为此,您可以使用gc_collect_cycles()函数。该函数将返回算法收集到的循环次数。
因此,在循环体末尾尝试强制进行垃圾回收。
for ($ii = 0; $ii < 20000; $ii += 1000) {
    // ...

    mysqli_free_result($result);
    unset($result);
    unset($data);
    echo "Memory before GC run: ".convert_memory_val(memory_get_peak_usage(true))."<br />\n";

    $n = gc_collect_cycles();
    echo "GC collected $n garbage cycles<br />\n";
    echo "Memory after GC run: ".convert_memory_val(memory_get_peak_usage(true))."<br />\n";
}

@ВладФедосов 尽管如此,这是您在长时间运行的脚本中手动释放内存的方法。该答案可能有助于其他人学习。 - Kaii

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接