2 votes

PHP Simple HTML Dom Parser Memory Leak / Usage

J'essaie d'utiliser PHP Simple HTML Dom Parser pour analyser les informations de certains sites. Peu importe quoi et où. Mais il semble qu'il y ait un énorme problème de mémoire. J'ai réussi à réduire le code html à seulement 6kB, mais script qui trouve certains éléments et les enregistre dans la base de données prend même 700MB de ram et plus de 1GB de mémoire virtuelle ! J'ai lu quelque part que je devrais utiliser ->clear() pour libérer de la mémoire, mais il semble que ce ne soit pas le cas.

J'utilise str_get_html() une fois et 5 fois en utilisant ->find() en assignant le résultat à une variable.

$main_html = str_get_html($main_site);
$x = $main_html->find(...);
$y = $main_html->find(...);

etc.

J'ai essayé d'utiliser par exemple $y->clear() après avoir utilisé $y mais j'obtiens une erreur PHP Fatal error: Call to a member function clear() on a non-object même si $y existe et if($y) est vrai. Même foreach($y) echo $y->plaintext retourne plaintext de $y .

De htop :

PID USER     PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
8839 username    20   0 1068M  638M   268 R 23.0  8.0  0:08.41 php myscript.php

Qu'est-ce qui ne va pas ?

Un test simple :

echo "(MEM:".memory_get_usage()."->";
$product = $p->find('a',0)->href;
echo memory_get_usage()."->";
unset($product);
$p->clear();
unset($p);
echo memory_get_usage().")";

Le résultat est :

(MEM:11865648->11866192->11865936)

Forme plus lisible :

11865648->
11866192-> (+544 in total)
11865936 (+288 in total)

Bien sûr, je ne peux pas utiliser $product->clear() car il est dit que PHP Fatal error: Call to a member function clear() on a non-object

9voto

Flash Thunder Points 2704

Il semble qu'il y ait des problèmes de mémoire lors de l'utilisation str_html_get ou une fonction similaire qui crée simple_html_dom plusieurs fois sans effacer et détruire l'objet précédent. Surtout lorsque l'on utilise ->find qui crée un tableau de simple_html_dom_node objets. Même la FAQ sur le site des auteurs dit qu'il faut effacer et détruire les objets précédents. simple_html_dom avant d'en créer un nouveau, mais parfois cela ne peut pas être fait sans code et mémoire supplémentaires.

C'est pourquoi j'ai créé cette fonction, pour supprimer de la mémoire toutes les traces de PHP Simple HTML Dom Parser :

function clean_all(&$items,$leave = ''){
    foreach($items as $id => $item){
        if($leave && ((!is_array($leave) && $id == $leave) || (is_array($leave) && in_array($id,$leave)))) continue;
        if($id != 'GLOBALS'){
            if(is_object($item) && ((get_class($item) == 'simple_html_dom') || (get_class($item) == 'simple_html_dom_node'))){
                $items[$id]->clear();
                unset($items[$id]);
            }else if(is_array($item)){
                $first = array_shift($item);
                if(is_object($first) && ((get_class($first) == 'simple_html_dom') || (get_class($first) == 'simple_html_dom_node'))){
                    unset($items[$id]);
                }
                unset($first);
            }
        }
    }
}

Utilisation :

Nettoyer TOUTES les traces de PHP Simple HTML Dom Parser de la mémoire : clean_all($GLOBALS);

Nettoyer toutes les traces de PHP Simple HTML Dom Parser de la mémoire, sauf $myobj : clean_all($GLOBALS,'myobj');

Nettoyer toutes les traces de PHP Simple HTML Dom Parser de la mémoire, sauf la liste des objets ($myobj1,$myobj2...) : clean_all($GLOBALS,array('myobj1','myobj2'));

J'espère que cela aidera d'autres personnes.


En général, je l'utilise lorsque j'utilise str_to_html() deux fois comme :

$site=file_get_contents('http://google.com');
$site_html=str_get_html($site);
foreach($site->find('a') as $a){
   $site2=file_get_contents($a->href);
   $site2_html=str_get_html($site2);
   echo $site2->find('p',0)->plaintext;
}
clean_all($_GLOBALS);

Dans cet exemple, je ne peux pas $site_html->clear() avant foreach{} parce que foreach alors il échouera. Et parce qu'appeler plusieurs str_get_html() sans effacer les précédentes, les dépendances redondantes sont rompues et l'effacer après tout laisse des fuites de mémoire. C'est pourquoi ma fonction doit rechercher dans les variables définies les objets simple_html_dom et les effacer manuellement.

Dans mon cas, j'ai forké à l'intérieur de foreach et après quelques étapes, le php principal script a utilisé environ 100MB de mémoire. Et quand j'ai forké plusieurs fois, cela a augmenté et augmenté et finalement tué mon serveur à mort. Enfin presque. Bien sûr, lorsque PHP script se termine, il libère de la mémoire. Mais lorsque j'utilise 8 Go de mémoire, cela prend des siècles à se terminer.

3voto

Paul Dessert Points 4294

Je crois que vous devez appeler clear() en $main_html

Extrait de la documentation ...

Q : Ce script perd sérieusement de la mémoire... Après son exécution, il ne nettoie pas correctement l'objet dom de la mémoire...

R : En raison de la fuite de mémoire des références circulaires de PHP5, après avoir créé l'objet DOM, vous devez appeler $dom->clear() pour libérer la mémoire si l'appel file_get_dom() plus d'une fois.

Exemple :

$html = file_get_html(...); 
// do something... 
$html->clear(); 
unset($html);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X