Утечка памяти в php с тремя циклами for - PullRequest
3 голосов
/ 26 декабря 2011

Мой скрипт - это паук, который проверяет, является ли страница «страницей ссылок» или «информационной страницей».если страница является «страницей ссылок», то она продолжается рекурсивным способом (или деревом, если хотите), пока не найдет «информационную страницу».

Я попытался сделать скрипт рекурсивным, и это было легконо я продолжал получать сообщение об ошибке:

Неустранимая ошибка: допустимый объем памяти 33554432 байта исчерпан (попытался выделить 39 байтов) в /srv/www/loko/simple_html_dom.php в строке 1316

Мне сказали, что мне придется использовать для метода цикла , потому что независимо от того, использую ли я функцию unset (), скрипт не освободит память, и у меня есть только три уровня, которые мне нужныперебрать, так что это имеет смысл.Но после того, как я изменил сценарий, ошибка повторяется, , но, может быть, я могу освободить память сейчас?

Что-то должно умереть здесь, пожалуйста, помогите мне уничтожить кого-то!

set_time_limit(0);
ini_set('memory_limit', '256M');
require("simple_html_dom.php");
$thelink = "http://www.somelink.com";
$html1 = file_get_html($thelink);
$ret1 = $html1->find('#idTabResults2');

// first inception level, we know page has only links
if (!$ret1){
    $es1 = $html1->find('table.litab a');
    //unset($html1);
    $countlinks1 = 0;
    foreach ($es1 as $aa1) {
        $links1[$countlinks1] = $aa1->href;
        $countlinks1++;
    }
    //unset($es1);

    //for every link in array do the same
    for ($i = 0; $i < $countlinks1; $i++) {
        $html2 = file_get_html($links1[$i]);
        $ret2 = $html2->find('#idTabResults2');
        // if got information then send to DB
        if ($ret2){
            pullInfo($html2);
            //unset($html2);
        } else {
        // continue inception
            $es2 = $html2->find('table.litab a');
            $html2 = null;

            $countlinks2 = 0;
            foreach ($es2 as $aa2) {
            $links2[$countlinks2] = $aa2->href;
            $countlinks2++;
            }
            //unset($es2);

            for ($j = 0; $j < $countlinks2; $j++) {
                $html3 = file_get_html($links2[$j]);
                $ret3 = $html3->find('#idTabResults2');
                // if got information then send to DB       
                if ($ret3){
                    pullInfo($html3);

                } else {
                // inception level three
                    $es3 = $html3->find('table.litab a');
                    $html3 = null;
                    $countlinks3 = 0;
                    foreach ($es3 as $aa3) {
                        $links3[$countlinks3] = $aa3->href;
                        $countlinks3++;
                    }
                    for ($k = 0; $k < $countlinks3; $k++) {
                        echo memory_get_usage() ;
                        echo "\n";
                        $html4 = file_get_html($links3[$k]);
                        $ret4 = $html4->find('#idTabResults2');
                        // if got information then send to DB       
                        if ($ret4){
                            pullInfo($html4);

                        }
                        unset($html4);                  
                    }
                    unset($html3);
                }

            }
        }
    }
}



function pullInfo($html)
{

$tds = $html->find('td');
$count =0; 
foreach ($tds as $td) {
  $count++;
  if ($count==1){
    $name = html_entity_decode($td->innertext);
   }
  if ($count==2){
        $address = addslashes(html_entity_decode($td->innertext));
   }
  if ($count==3){
    $number = addslashes(preg_replace('/(\d+) - (\d+)/i', '$2$1', $td->innertext));
   }

}
unset($tds, $td);

$name = mysql_real_escape_string($name);
$address = mysql_real_escape_string($address);
$number = mysql_real_escape_string($number);
$inAlready=mysql_query("SELECT * FROM people WHERE phone=$number");
while($e=mysql_fetch_assoc($inAlready))
            $output[]=$e;
    if (json_encode($output) != "null"){ 
        //print(json_encode($output));
    } else {

mysql_query("INSERT INTO people (name, area, phone)
VALUES ('$name', '$address', '$number')");
}
}

А вот картинка роста объема памяти: enter image description here

Ответы [ 5 ]

2 голосов
/ 26 декабря 2011

Я немного изменил код, чтобы освободить как можно больше памяти. Я добавил комментарий над каждой модификацией. Добавленные комментарии начинаются с "#", чтобы вам было проще их найти. Это не связано с этим вопросом, но стоит отметить, что код вставки вашей базы данных уязвим для внедрения SQL.

<?php
require("simple_html_dom.php");
$thelink = "http://www.somelink.co.uk";

# do not keep raw contents of the file on memory
#$data1 = file_get_contents($thelink);
#$html1 = str_get_html($data1);
$html1 = str_get_html(file_get_contents($thelink));

$ret1 = $html1->find('#idResults2');

// first inception level, we know page has only links
if (!$ret1){
    $es1 = $html1->find('table.litab a');

    # free $html1, not used anymore
    unset($html1);

    $countlinks1 = 0;
    foreach ($es1 as $aa1) {
        $links1[$countlinks1] = $aa1->href;
        $countlinks1++;
        // echo (addslashes($aa->href));
    }

    # free memroy used by the $es1 value, not used anymore
    unset($es1);

    //for every link in array do the same

    for ($i = 0; $i <= $countlinks1; $i++) {
        # do not keep raw contents of the file on memory
        #$data2 = file_get_contents($links1[$i]);
        #$html2 = str_get_html($data2);
        $html2 = str_get_html(file_get_contents($links1[$i]));

        $ret2 = $html2->find('#idResults2');

        // if got information then send to DB
        if ($ret2){
            pullInfo($html2);
        } else {
        // continue inception

            $es2 = $html2->find('table.litab a');

            # free memory used by $html2, not used anymore.
            # we would unset it at the end of the loop.
            $html2 = null;

            $countlinks2 = 0;
            foreach ($es2 as $aa2) {
                $links2[$countlinks2] = $aa2->href;
                $countlinks2++;
            }

            # free memory used by $es2
            unest($es2);

            for ($j = 0; $j <= $countlinks2; $j++) {
                # do not keep raw contents of the file on memory
                #$data3 = file_get_contents($links2[$j]);
                #$html3 = str_get_html($data3);
                $html3 = str_get_html(file_get_contents($links2[$j]));
                $ret3 = $html3->find('#idResults2');
                // if got information then send to DB   
                if ($ret3){
                    pullInfo($html3);
                }

                # free memory used by $html3 or on last iteration the memeory would net get free
                unset($html3);
            }
        }

        # free memory used by $html2 or on last iteration the memeory would net get free
        unset($html2);
    }
}



function pullInfo($html)
{
    $tds = $html->find('td');
    $count =0; 
    foreach ($tds as $td) {
      $count++;
      if ($count==1){
        $name = addslashes($td->innertext);
       }
      if ($count==2){
            $address = addslashes($td->innertext);
       }
      if ($count==3){
        $number = addslashes(preg_replace('/(\d+) - (\d+)/i', '$2$1', $td->innertext));
       }

    }

    # check for available data:
    if ($count) {
        # free $tds and $td
        unset($tds, $td);

        mysql_query("INSERT INTO people (name, area, phone)
        VALUES ('$name', '$address', '$number')");
    }

}

Обновление:

Вы можете отслеживать использование памяти, чтобы увидеть, сколько памяти используется в каждом разделе вашего кода. это может быть сделано с помощью вызовов memory_get_usage () и сохранением результата в некоторый файл. например, поместив приведенный ниже код в конец каждого цикла или перед созданием объектов, вызывая тяжелые методы:

file_put_contents('memory.log', 'memory used in line ' . __LINE__ . ' is: ' . memory_get_usage() . PHP_EOL, FILE_APPEND);

Чтобы вы могли отслеживать использование памяти каждой частью вашего кода.

В конце помните, что всей этой трассировки и оптимизации может быть недостаточно, поскольку вашему приложению действительно может потребоваться больше памяти, чем 32 МБ. Я разработал систему, которая анализирует несколько источников данных и обнаруживает спамеров, а затем блокирует их SMTP-соединения, и, поскольку иногда число подключенных пользователей превышает 30000, после большой оптимизации кода мне приходилось увеличивать ограничение памяти PHP до 768. МБ на сервере, что не является обычным делом.

1 голос
/ 27 декабря 2011

Решением было использование метода очистки, такого как: $ Html4-> ясно (); метод simple_html_dom для очистки памяти Когда вы закончите с элементом DOM. Если вы хотите узнать больше, введите этот сайт .

1 голос
/ 26 декабря 2011

Если вашей работе требуется память, а на вашем сервере доступно больше памяти, вы можете вызвать ini_set('memory_limit', '128M'); или что-то подобное (в зависимости от вашей памяти), чтобы увеличить объем памяти, доступный для сценария.не значит, что вы не должны оптимизировать и реорганизовывать свой код :-) это всего лишь одна часть.

0 голосов
/ 26 декабря 2011

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

function findInfo($thelink)
{
    $data = file_get_contents($thelink);  //Might want to make sure that it's a valid link, i.e. that file get contents actually returned stuff, before trying to run further with it.
    $html = str_get_html($data);

    unset($data);  //Finished using it, no reason to keep it around.

    $ret = $html->find('#idResults2');

    if($ret)
    {
         pullInfo($html);
         return true;    //Should stop once it finds it right?
    }
    else
    {
         $es = $html->find('table.litab a');  //Might want a little error checking here to make sure it actually found links.

         unset($html); //Finished using it, no reason to keep it around

         $countlinks = 0;

         foreach($es as $aa)
         {
              $links[$countlinks] = $aa->href;
              $countlinks++;
         }

         unset($es);  //Finished using it, no reason to keep it around.

         for($i = 0; $i <= $countlinks; $i++)
         {
              $result = findInfo($links[$i]);

              if($result === true)
              {
                  return true;  //To break out of above recursive functions if lower functions return true
              }
              else
              {
                  unset($links[$i]); //Finished using it, no reason to keep it around.
                  continue;
              }
         }     
    }
    return false;  //Will return false if all else failed, should hit a return true before this point if it successfully finds an info page.
}

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

О, и если вы хотите, чтобы это было так глубоко, измените объявление функции на что-то вроде:

function findInfo($thelink, $depth = 1, $maxdepth = 3)

Затем при вызове функции внутри функции вызовите ее так:

findInfo($html, $depth + 1, $maxdepth); //you include maxdepth so you can override it in the initial function call, like findInfo($thelink,,4)

, а затем выполните проверку глубины в зависимости от maxdepth в начале функции и получите return false, если глубина > чем maxdepth.

0 голосов
/ 26 декабря 2011

Если использование памяти является вашей основной задачей, вы можете рассмотреть возможность использования парсера на основе SAX.Кодирование с использованием SAX-анализатора может быть немного сложнее, но нет необходимости хранить весь документ в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...