IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Fuites mémoire + boucle


Sujet :

Langage PHP

  1. #1
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut Fuites mémoire + boucle
    Pour résumer c'est une vulgaire extraction d'une table envoyé au navigateur en fichier CSV à la volé.


    Le code se situe dans une fonction d'une class :


    Garbage Colletor enabled (défaut) : OUI
    utilisation de la mémoire : 3139.66 Mo

    exécution de la requête (environ 200 000 lignes sur 25 colones)
    22.28 Mo

    jusqu'à là pas de problème


    ensuite vient le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    $tab = array();
    		while ($tab = mysql_fetch_array($res,MYSQL_NUM)) { 
    			//echo implode("`|", $tab) . "\n<br />";
    			echo round(memory_get_usage()/(1024*1024),2)." Mo<br />";
    			unset($tab);
    			gc_collect_cycles();
    		}
    résultats :

    1er ligne : 22.28 Mo
    60164 ligne : 127.85 Mo


    j'ai retirer les lignes dans la boucle et juste ce code là pose problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $tab = array();
    		while ($tab = mysql_fetch_array($res,MYSQL_NUM)) { 
     
    		}
    j'ai essayé avec d'autre fonction mysql comme mysql_fetch_object etc... mais cela ne change rien.

  2. #2
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    Salut,

    je ne sais pas si tu travaille sur MySQL mais il t'est possible de créer le .csv avec le MySQL et de servir le résultat tel quel avec PHP (plus de problème de mémoire)
    Quelque chose du genre :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT ...
    FROM table
    INTO OUTFILE '/tmp/csv_output.csv'
    FIELDS TERMINATED BY ','
    ENCLOSED BY '"'
    LINES TERMINATED BY '\n'
    A creuser...

  3. #3
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    Effectivement ça résout ce problème là, quoi que non car le serveur BDD est pas sur la même machine que le serveur PHP. Enfin ya toujours moyen de faire de la bidouille, mais ça ne résout pas le problème de fond.

    Mais c'est un chose récurrente chez moi, car je commence de plus en plus à utiliser php en langage de batch pour des traitements de données (grab etc...)

    En écrivant comme un goret avec une fonction appelé dans une boucle on arrive à récupérer de la mémoire, mais c'est beaucoup moins performant en vitesse d’exécution.

  4. #4
    Expert éminent sénior
    Avatar de rawsrc
    Homme Profil pro
    Dev indep
    Inscrit en
    Mars 2004
    Messages
    6 142
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Dev indep

    Informations forums :
    Inscription : Mars 2004
    Messages : 6 142
    Points : 16 545
    Points
    16 545
    Billets dans le blog
    12
    Par défaut
    C'est clair qu'avec ton code tu vas récupérer en mémoire l'intégralité de ton tableau. Je ne sais pas l'impact d'une autre approche : si par exemple tu remplissais au fur et à mesure un fichier .csv toutes les 1000 lignes :
    avec quelque chose comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    file_put_contents($file, $data, FILE_APPEND | LOCK_EX);

  5. #5
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    Juste le fait de boucler dans rien faire pose problème, alors le fait d'ajouter le résultat dans un fichier ne ferait qu'empirer.


    petite précision :
    php -v
    PHP 5.3.17-1~dotdeb.0 with Suhosin-Patch (cli) (built: Sep 14 2012 11:40:39)
    Copyright (c) 1997-2012 The PHP Group
    Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies
        with Suhosin v0.9.33, Copyright (c) 2007-2012, by SektionEins GmbH
    
    serveur : 
    3GHz
    4Go de ram
    

  6. #6
    Membre émérite
    Avatar de gene69
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 769
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 769
    Points : 2 446
    Points
    2 446
    Par défaut
    ça ne sert à rien, c'est pas l'appel adapté:

    d'une part la collecte doit te retourner 0 à chaque fois (ton tableau c'est pas des variables à référence circulaire), d'autre par le garbage collector s’exécute de façon (pseudo) non déterministe, va voir les parametres php.ini.

    La mémoire est consommée jusqu'à ce qu'il n'y en ai plus dispo. tant que tu es dans la limite autorisée, laisse php consommer ce dont il a besoin. Quand il arrive au max, il libère les variables non référencées et recycle la mémoire.

    L'idée générale c'est que le GC consomme plus de perf que laisser les mémoires en place et attendre la fin de l'exec du processus php et que l'os récupère la mémoire.

    si ton php utilise trop de mémoire, tu lui baisses la mémoire autorisée. Tu ne reçois pas d'erreur? alors go.
    PHP fait nativement la validation d'adresse électronique .
    Celui qui a inventé mysql_connect(...) or die() est déjà mort plusieurs fois.

    Utilisez le bouton résolu!

  7. #7
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    justement je reçois ce msg : Fatal error: Allowed memory size of XXXX bytes exhausted (tried to allocate YY bytes)



    Le résultat fait ~ 200 000 lignes et j'ai que les 60 000 premieres

  8. #8
    Membre émérite
    Avatar de gene69
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 769
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 769
    Points : 2 446
    Points
    2 446
    Par défaut
    juste avec le code que tu montres?

    PHP fait nativement la validation d'adresse électronique .
    Celui qui a inventé mysql_connect(...) or die() est déjà mort plusieurs fois.

    Utilisez le bouton résolu!

  9. #9
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    phpMyAdmin depuis un serveur distant y arrive, il y plus qu'a lire le code ...

  10. #10
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    Citation Envoyé par gene69 Voir le message
    juste avec le code que tu montres?


    Oui c'est bien ça le problème.


    code de la fonction dans ça globalité :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    function export_csv() {
     
    		echo "Garbage Colletor enabled (défaut) : " . (gc_enabled() ? 'OUI' : 'NON') . "\n";
    		echo round(memory_get_usage()/(1024^2),2)." Mo";
     
    		/*
    		header("Cache-Control: public");
    		header("Content-Description: File Transfer");
    		header("Content-Disposition: attachment; filename=CapGemini_".date("Y-m-d_His").".csv");
    		header("Content-Type: text/plain");
    		header("Content-Transfer-Encoding: binary");
    		*/
    		ini_set("memory_limit", "256M");
     
    		$_SQL = Singleton::getInstance(SQL_DRIVER);
    		$sql = "SHOW COLUMNS FROM capgemini";
     
    		$res = $_SQL->sql_query($sql);
    		$field = $_SQL->sql_to_array($res);
     
    		$rr = array();
    		foreach ($field as $line)
    		{
     
    			$rr[] = $line['Field'];
    		}
     
    		echo implode("`|", $rr) . "\n";
     
    		echo round(memory_get_usage()/(1024^2),2)." Mo";
     
    		$sql = "SELECT * FROM capgemini LIMIT 2000";
     
     
    		$res = $_SQL->sql_query($sql);
     
    		$GLOBALS['_DEBUG']->save("Query capgemini");
     
     
     
    		$tab = array();
     
     
    		$nbcount = mysql_num_rows($res);
    		for ($i =0 ; $i < $nbcount; $i++) { //MYSQL_NUM
    			$tab = mysql_fetch_object($res);
    			//echo implode("`|", $tab) . "\n<br />";
    			//echo round(memory_get_usage()/(1024*1024),2)." Mo<br />";
     
    			gc_collect_cycles();
     
    			//$GLOBALS['_DEBUG']->save($i);
    			unset($tab);
    		}
     
    		//exit;
    	}

    précision utilisé dans un framework maison optimisé pour les perfs et la mémoire (MVC classique), et ne prend pas plus de 2.5Mo pour une page classique.

    Les fonctions MySQL sont encapsulé afin d'avoir des infos (nombre de requêtes, temps d’exécutions, nombre de ligne affecté, et id retourné en cas d'insert et de pouvoir changer de SGBD (Sybase, PostgreSQL & Oracle).

    $GLOBALS['_DEBUG']->save=> permet d'obtenir des infos sur la mémoires et le temps d’exécution, et sort un graphique avec ces données.

  11. #11
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    j'ai essayé ça : http://blog.preinheimer.com/index.ph...ge-in-PHP.html

    (visible dans le code précédent) mais cela ne semble pas marcher avec les tableaux

  12. #12
    Membre émérite
    Avatar de gene69
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 769
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 769
    Points : 2 446
    Points
    2 446
    Par défaut
    ok,

    il y a deux boucles dans ta fonction, laquelle est concernée la levée d'exception ?

    si c'est la premiere, je comprends pourquoi, si c'est la seconde, il faut patcher ton framework.

    seconde question: pourquoi pas mysqli ?
    PHP fait nativement la validation d'adresse électronique .
    Celui qui a inventé mysql_connect(...) or die() est déjà mort plusieurs fois.

    Utilisez le bouton résolu!

  13. #13
    Membre émérite
    Avatar de gene69
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 769
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 769
    Points : 2 446
    Points
    2 446
    Par défaut
    dernière question, ta requete sql contient une étoile. est ce que le bug se reproduit quand tu ne requetes que la clé primaire qui sera (comme cap' sont des concurrents qui savent travailler) un auto-integer ?
    PHP fait nativement la validation d'adresse électronique .
    Celui qui a inventé mysql_connect(...) or die() est déjà mort plusieurs fois.

    Utilisez le bouton résolu!

  14. #14
    Membre émérite
    Avatar de gene69
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 769
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 769
    Points : 2 446
    Points
    2 446
    Par défaut
    http://php.net/manual/en/function.mysql-free-result.php
    Citation Envoyé par le manuel
    Use of this extension is discouraged. Instead, the MySQLi or PDO_MySQL extension should be used. See also MySQL: choosing an API guide and related FAQ for more information.
    PHP fait nativement la validation d'adresse électronique .
    Celui qui a inventé mysql_connect(...) or die() est déjà mort plusieurs fois.

    Utilisez le bouton résolu!

  15. #15
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    Citation Envoyé par gene69 Voir le message
    ok,

    il y a deux boucles dans ta fonction, laquelle est concernée la levée d'exception ?

    si c'est la premiere, je comprends pourquoi, si c'est la seconde, il faut patcher ton framework.

    seconde question: pourquoi pas mysqli ?
    heureusement que la première ne casse pas car ça retourne uniquement les 20 noms de colonnes de la table.

    c'est bien la 2eme qui casse. (patcher avec mysqli tu veux dire?)

  16. #16
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    Citation Envoyé par gene69 Voir le message
    dernière question, ta requete sql contient une étoile. est ce que le bug se reproduit quand tu ne requetes que la clé primaire qui sera (comme cap' sont des concurrents qui savent travailler) un auto-integer ?
    c'est une vue sans la moindre clef (les jointures sont bien faites, normal c'est moi qui les ai faites ), le nom est comme cela car ça leur est destiné. car rajouter/intervertir un champ ou prendre les caractères d’échappement étaient apparemment trop coûteux pour eux.

    PS : la réunion pour ça à prise plus de temps que de faire les modifs de mon coté.


    Je vais ajouter un connecteur avec mysqli ça ne prendra pas longtemps.

  17. #17
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    Après ajout du connecteur mysqli. Cela ne change ... strictement rien niveau mémoire.


    pour 50 000 lignes

    100.34 avec mysql
    100.35 avec mysqli

    pour la table au complet même plantage.


    j'ai essayé avec 2000 et 10000 lignes cela ne change rien niveau mémoire et temps de traitement.

  18. #18
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    J'ai réduit le code à :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    $con = mysqli_connect("XXXXXXX", "XXXX", "XXXXX", "XXXXX");
     
    ini_set("memory_limit", "128M");
    $con = mysqli_connect("XXXXXXX", "XXXX", "XXXXX", "XXXXX");
     
    $sql = "SELECT * FROM capgemini LIMIT 50000";
    $res = mysqli_query($con, $sql);
     
     
    while ($tab = mysqli_fetch_object($res))
    { //MYSQL_NUM
            //echo implode("`|", $tab) . "\n<br />";
            //echo round(memory_get_usage()/(1024*1024),2)." Mo<br />";
     
            //$GLOBALS['_DEBUG']->save($i);
            //unset($tab);
    }
    echo round(memory_get_peak_usage()/ (1024 ^ 2), 2) . " Mo\n";
    mysqli_close($con);

    et ça ne passe toujours pas :/

    La consommation est passé de 100.35 Mo à 97.53 Mo (les 2.5~3 Mo de consommation du framework) pour 50 000 lignes

  19. #19
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    j'ai mis les résultats dans une table réel et non plus une vue et la table ne prend que 22,5 Mio en InnoDB. La mémoire ne bouge pas d'un poil

  20. #20
    Membre habitué
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2011
    Messages
    146
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Directeur technique
    Secteur : Transports

    Informations forums :
    Inscription : Février 2011
    Messages : 146
    Points : 172
    Points
    172
    Par défaut
    La réponse se trouve dans phpmyadmin, en reprenant exactement les même fonctions pour l'extractions je ne parviens pas au même résultats (toujours la mémoire qui croit).

    La solution se trouve p-e dans le header ? Ce qui est sur c'est que j'ai loupé un truc annexe qui est capitale.

Discussions similaires

  1. [tomcat][memoire] java.net.URL et fuite mémoire
    Par Seiya dans le forum Tomcat et TomEE
    Réponses: 6
    Dernier message: 09/03/2009, 10h41
  2. [Fuites mémoire] Je cherche un utilitaire
    Par 10_GOTO_10 dans le forum C++Builder
    Réponses: 8
    Dernier message: 10/02/2005, 10h03
  3. Outil de recherche de fuite mémoire
    Par eag35 dans le forum MFC
    Réponses: 4
    Dernier message: 02/02/2005, 12h46
  4. [SWT]SWT et fuite mémoire(ou pas)
    Par menuge dans le forum SWT/JFace
    Réponses: 2
    Dernier message: 22/06/2004, 21h40
  5. [debug] fuites mémoires
    Par tmonjalo dans le forum C
    Réponses: 3
    Dernier message: 28/07/2003, 17h20

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo