Глубокая утечка рекурсивной памяти - сбросить открытый ресурс? - PullRequest
2 голосов
/ 26 ноября 2011

У меня есть рекурсивная функция для перебора 11 миллионов записей базы данных, по 1000 за раз. Когда он приблизился к 9М, он остановился. Мое предположение о проблеме с памятью подтвердилось, когда я отображал get_memory_usage() после каждых 1000 записей.

Функция работает примерно так:

<?
get_data_block();

function get_data_block($id=0);
{
    //open a csv file for writing
    $packages_sorted_csv=fopen("./csv/packages_sorted.csv", "a");

    //get 1000 records and process them
    //$unsorted = array of 1000 records from database
    foreach($unsorted as $row);
    {
        $ct++;
        $id++;
        //$packages_sorted = array of processed data

        //write output
        fputcsv($packages_sorted_csv, $packages_sorted);
    }
    fclose($packages_sorted_csv);

    if($ct==1000)
    {
        unset($unsorted);
        echo 'Mem usage: '.memory_get_usage();
        get_data_block($id);    //call the function again
    }else{
        //finished
    }
}

?>

У кого-нибудь есть совет, как освободить все ресурсы с рекурсивными функциями? ... или есть способ вызвать ту же функцию снова, чтобы она не вызывалась сама по себе?

Примечания:

  • Мне нужно разбить данные на блоки, чтобы освободить занятый сервер MySQL.
  • Я попытался сбросить все определенные переменные, которые не входят в глобальную область.
  • Единственное, что я не могу сбросить, это ресурс fopen.
  • Объем памяти увеличивается примерно на 400 КБ за каждую итерацию.

Ответы [ 2 ]

2 голосов
/ 26 ноября 2011

Хороший язык программирования должен распознавать хвостовую рекурсию и превращать ее в неявный цикл.

PHP, очевидно, нет, поэтому в какой-то момент вы получите переполнение стека. Попробуйте переписать свой код, чтобы использовать вместо него цикл, или, например, очередь обработки для итерации.

2 голосов
/ 26 ноября 2011

Исходя из кода, который вы разместили, рекурсия не выглядит правильным подходом.

Создайте вторую функцию и измените ее так, чтобы вы могли сделать что-то вроде (псевдокод):

while (!$done) {
  $id = get_data_block($id);
  $done = // determine if finished or not
}

т.е. вернуть $id из get_data_block, присвоив ему недопустимое значение, если больше не требуется выполнять обработку. Вам не придется беспокоиться о накоплении кадров стека таким образом.

...