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

JDBC Java Discussion :

OutOfMemoryError sur insertions MySQL


Sujet :

JDBC Java

  1. #1
    Membre averti
    Inscrit en
    Octobre 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 14
    Par défaut OutOfMemoryError sur insertions MySQL
    Bonjour à tous,

    J'ai un soucis de fuite mémoire avec le driver JDBC MySQL.
    Dans une boucle, je fais environ 150 000 INSERT.
    Avant d'arriver à la fin, l'appli sort sur un OutOfMemoryError. J'ai inspecté mon code, je ne vois pas de fuite mémoire. Après analyse de la mémoire heap, il semble que ce soit le driver MySQL qui consomme toute la mémoire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    One instance of "com.mysql.jdbc.Connection" loaded by
    "sun.misc.Launcher$AppClassLoader @ 0x2xxxxxxxx8" occupies
    759 916 912 (99,79%) bytes. The memory is accumulated in one
    instance of "java.util.HashMap$Entry[]" loaded by "<system class loader>".
    A priori, pas de fuite mémoire non plus dans ma fonction d'insertion. J'ai essayé pas mal de chose : fermer mon preparedStatement et mon resultSet, forcer le passage du GC, rien n'y fait. En désespoir de cause, j'ai tenté une déconnexion / reconnexion, et là, ca fonctionne.

    Qu'est-ce que j'ai bien pu oublier ? Quelqu'un a t-il déjà été confronté à ce problème, quelle solution (je trouve la déconnexion / reconnexion pas élégante du tout) ?

    Merci

  2. #2
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    code?

  3. #3
    Membre averti
    Inscrit en
    Octobre 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 14
    Par défaut
    Voilà le code de la fonction d'insertion dans la BDD

    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
    58
    59
    60
    61
    62
        public int insertAIPref(int idSync, String identifiantVersement, long idLotArcsys) throws ArcheckException
        {
            if (idSync < 1 ||
                identifiantVersement == null ||
                idLotArcsys < 1)
            {
                handleSQLException("<insertAIPref> Une ou plusieurs données sont nulles : idSync [" + idSync + "], identifiantVersement [" + identifiantVersement + "], idLotArcsys [" + idLotArcsys + "]");
            }
            int id = -1;
            try
            {
                // on insert la nouvelle definition
                logger.debug("<insertAIPref> insertion de la référence sur l'AIP d'identifiant Arcsys [" + idLotArcsys + "] dans le referentiel");
     
                sql = "INSERT INTO AIPref (id_sync, id_versement, id_lot_arcsys) VALUES (?,?,?)";
                preparedStatement = conn.prepareStatement(sql);
                compteur = 1;
                preparedStatement.setInt(compteur++, idSync);
                preparedStatement.setString(compteur++, identifiantVersement);
                preparedStatement.setLong(compteur++, idLotArcsys);
     
                preparedStatement.executeUpdate();
                rs = preparedStatement.getGeneratedKeys();
                // recupération de l'identifiant
                if (rs.next())
                {
                    id = rs.getInt(1);
                }
                else
                {
                    handleSQLException("<insertAIPref> erreur lors du chargement de l'identifiant de l'AIPref [" + idLotArcsys + "]");
                }
            }
            catch (SQLException exc)
            {
                   if (exc.getErrorCode() == 1062)
                   {
                    logger.warn("Le lot d'identifiant Arcsys [" + idLotArcsys + "] est déjà présent dans le référentiel.");
                    String sql = "SELECT id FROM AIPref WHERE id_lot_arcsys=?";
                    try
                    {
                        preparedStatement = conn.prepareStatement(sql);
                        compteur = 1;
                        preparedStatement.setLong(compteur++, idLotArcsys);
                        rs = preparedStatement.executeQuery();
                        if (rs.next())
                        {
                            // l'extension existe déjà, on récupere l'identifiant
                            id = rs.getInt("id");
                        }
                    }
                    catch (SQLException e)
                    {
                        handleSQLException("<insertAIPref> Erreur SQL [" + exc.getMessage() + "]", exc);
                    }
     
                   }
                else
                    handleSQLException("<insertAIPref> Erreur SQL [" + exc.getMessage() + "]", exc);
            }
            return id;
        }
    Et régulièrement (toutes les 1000 insertions), je commit, j'appelle cette fonction, puis je lance le GC.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        public void releaseStatements() throws SQLException
        {
            preparedStatement.close();
            rs.close();
            disconnect();
            connect();
        }
    J'aurai aimé éviter le disconnect() / connect() que je ne trouve pas élégant du tout !...

  4. #4
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    dans ton insertAIPref, tu oublie de fermer tes perparedStatement et tes resultset, cve qui consomme de la mémoire. Un code correct doit être basé sur ce shéma.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    preparedStatement = conn.prepareStatement(sql);
    try{
        //....
    } finally {
        preparedStatement.close();
    }
    même chose avec les resultset
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    rs = preparedStatement.xxxxx();
    try{
       //.....
    } finally {
        rs.close();
    }

  5. #5
    Membre averti
    Inscrit en
    Octobre 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 14
    Par défaut
    Bon... J'ai ajouté un finally à chaque try, dans lequel je ferme mon ResultSet et mon PreparedStatement. J'ai relancé, et au bout d'un moment (un peu plus tard que d'habitude il me semble), il me sort un beau OutOfMemoryError...

    D'ailleurs, ca ne métonne qu'à moitié : je faisais manuellement (toutes les 1000 requètes) un close() sur mon rs et preparedStatement, et en dehors de ces fermetures explicites, j'écrasais les références (donc passage du GC et libération mémoire).

    Ca semble indiquer une gestion sous-jacente de la mémoire par le driver un peu faiblarde : une plage mémoire déréférencée dans java, et libérée par le GC resterait affectée dans le driver ?

    D'autres idées ?

  6. #6
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    Citation Envoyé par ben_cines Voir le message
    Bon... J'ai ajouté un finally à chaque try, dans lequel je ferme mon ResultSet et mon PreparedStatement. J'ai relancé, et au bout d'un moment (un peu plus tard que d'habitude il me semble), il me sort un beau OutOfMemoryError...
    Tu peux nous donner le code corriger, ainsi que le code appelant

    en dehors de ces fermetures explicites, j'écrasais les références (donc passage du GC et libération mémoire).
    Si tu ne fais pas un close, ça ne sera libéré que lorsque tu fera un close de la connection. Tu n'est pas le seul à garder des référence sur ces objet, la connexion en garde aussi! D'ou l'intérêt du close()

  7. #7
    Expert confirmé
    Avatar de sinok
    Profil pro
    Inscrit en
    Août 2004
    Messages
    8 765
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2004
    Messages : 8 765
    Par défaut
    Ca semble indiquer une gestion sous-jacente de la mémoire par le driver un peu faiblarde : une plage mémoire déréférencée dans java, et libérée par le GC resterait affectée dans le driver ?
    Le driver MySQL étant écrit en pur java et étant soumis aux limites de ta JVM, il ne peut en aucun cas être soumis à des limite différentes que celle de ta JVM niveau mémoire.

    Le premier réflexe à avoir quand tu as une fuite mémoire tele que celle ci est de sortir un profiler et de le brancher sur ta JVM.

    En l'occurence VisualVM qui est fournit avec les JDK6 récents (exécutable jvisualvm dans le dossier bin du répertoire d'installation du JDK) fera tout à fait l'affaire pour monitorer la mémoire de ton application, pour cibler où se trouve la fuite.

    Je doute franchement que le driver soit responsable de la fuite, en effet c'est un composant utilisé plus que régulièrement dans des environnements de production, cela m'étonnerait quelques peu.

  8. #8
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,

    Citation Envoyé par ben_cines Voir le message
    D'ailleurs, ca ne métonne qu'à moitié : je faisais manuellement (toutes les 1000 requètes) un close() sur mon rs et preparedStatement, et en dehors de ces fermetures explicites, j'écrasais les références (donc passage du GC et libération mémoire).
    Ce que je vois surtout, c'est que ton PreparedStatement et ton ResultSet ne sont pas déclarée dans le corps de la méthode. Je suppose donc qu'il s'agit d'attribut d'instance de ta classe.

    Tu utilises des attributs d'instances pour stocker des variables locales

    Et lorsque tu dis que tu fais un close() toutes les 1000 requêtes... tu te contentes en fait de fermer 1 requête sur 1000 !!!


    a++

  9. #9
    Membre averti
    Inscrit en
    Octobre 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 14
    Par défaut
    Merci pour vos réponses.

    Effectivement, j'ai une classe en singleton qui gère tout ce qui est accès aux données de la base, et j'ai préféré utiliser des variables d'instance pour mon ResultSet et mon PreparedStatement, plutôt que multiplier les lignes de déclaration.

    Pour ce qui est du profiling, à priori, la fuite mémoire est identifiée (cf. premier post)

    Entendu pour les références conservées par le driver, donc obligation de faire un close après chaque requète. Une question : si le driver garde des références, y a t'il une fonction qui permet d'accéder à cette table, plus exactement de la vider ?

    Je vous met tout le code impliqué :

    Ma boucle appellante :
    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
            for (long idLot = 1; idLot < lastArcsysId; idLot++)
            {
                if (!api.existsArcsysId(idLot)) continue;
                String idendifiantVersement = api.getIdentifiantVersementLotbyId(idLot);
                int idAIPRef = db.insertAIPref(this.id, idendifiantVersement, idLot);
                ArrayList<String> envPathList = api.getEnvPathListByLot(idLot);
                if (envPathList != null && !envPathList.isEmpty())
                    db.insertAIPpathList(idAIPRef, envPathList);
                if ((idLot % 1000) == 0)
                {
                    db.commit();
    //                db.releaseStatements();
    //                System.gc();
                }
            }
    Mes deux fonctions d'insertion en base :
    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
        public int insertAIPref(int idSync, String identifiantVersement, long idLotArcsys) throws ArcheckException
        {
            if (idSync < 1 ||
                identifiantVersement == null ||
                idLotArcsys < 1)
            {
                handleSQLException("<insertAIPref> Une ou plusieurs données sont nulles : idSync [" + idSync + "], identifiantVersement [" + identifiantVersement + "], idLotArcsys [" + idLotArcsys + "]");
            }
            int id = -1;
            try
            {
                // on insert la nouvelle definition
                logger.debug("<insertAIPref> insertion de la référence sur l'AIP d'identifiant Arcsys [" + idLotArcsys + "] dans le referentiel");
     
                sql = "INSERT INTO AIPref (id_sync, id_versement, id_lot_arcsys) VALUES (?,?,?)";
                preparedStatement = conn.prepareStatement(sql);
                compteur = 1;
                preparedStatement.setInt(compteur++, idSync);
                preparedStatement.setString(compteur++, identifiantVersement);
                preparedStatement.setLong(compteur++, idLotArcsys);
     
                preparedStatement.executeUpdate();
                rs = preparedStatement.getGeneratedKeys();
                // recupération de l'identifiant
                if (rs.next())
                {
                    id = rs.getInt(1);
                }
                else
                {
                    handleSQLException("<insertAIPref> erreur lors du chargement de l'identifiant de l'AIPref [" + idLotArcsys + "]");
                }
            }
            catch (SQLException exc)
            {
                   if (exc.getErrorCode() == 1062)
                   {
                    logger.warn("Le lot d'identifiant Arcsys [" + idLotArcsys + "] est déjà présent dans le référentiel.");
                    String sql = "SELECT id FROM AIPref WHERE id_lot_arcsys=?";
                    try
                    {
                        preparedStatement = conn.prepareStatement(sql);
                        compteur = 1;
                        preparedStatement.setLong(compteur++, idLotArcsys);
                        rs = preparedStatement.executeQuery();
                        if (rs.next())
                        {
                            // l'extension existe déjà, on récupere l'identifiant
                            id = rs.getInt("id");
                        }
                    }
                    catch (SQLException e)
                    {
                        handleSQLException("<insertAIPref> Erreur SQL [" + exc.getMessage() + "]", exc);
                    }
                    finally
                    {
                        try
                        {
                            preparedStatement.close();
                            rs.close();
                        }
                        catch (SQLException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
     
                   }
                else
                    handleSQLException("<insertAIPref> Erreur SQL [" + exc.getMessage() + "]", exc);
            }
            finally
            {
                try
                {
                    preparedStatement.close();
                    rs.close();
                }
                catch (SQLException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return id;
        }
    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
        public void insertAIPpathList(int idAIPref, ArrayList<String> envPathList) throws ArcheckException
        {
            if (idAIPref < 1 || envPathList == null || envPathList.isEmpty())
            {
                handleSQLException("<insertAIPpathList> Une ou plusieurs données sont nulles.");
            }
     
            try
            {
                // on insert la nouvelle definition
                logger.debug("<insertAIPpathList> insertion des chemin des enveloppes des AIPref id [" + idAIPref + "] dans le referentiel : " + envPathList);
     
                Date date = new Date(System.currentTimeMillis());
     
                for (Iterator<String> itEnvPathList = envPathList.iterator(); itEnvPathList.hasNext();)
                {
                    String envPath = itEnvPathList.next();
     
                    sql = "INSERT INTO AIPpath (id_aip_ref, date, env_path) VALUES (?,?,?)";
                    preparedStatement = conn.prepareStatement(sql);
                    compteur = 1;
                    preparedStatement.setInt(compteur++, idAIPref);
                    preparedStatement.setTimestamp(compteur++, new Timestamp(date.getTime()));
                    preparedStatement.setString(compteur++, envPath);
     
                    preparedStatement.executeUpdate();
                 }
            }
            catch (SQLException exc)
            {
                   if (exc.getErrorCode() == 1062)
                    logger.warn("<insertAIPpathList> Le lot AIPref d'id [" + idAIPref + "] est déjà présent dans le référentiel.");
                else
                    handleSQLException("<insertAIPpathList> Erreur SQL [" + exc.getMessage() + "]", exc);
            }
            finally
            {
                try
                {
                    preparedStatement.close();
                }
                catch (SQLException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    Je vais essayer en utilisant un ResultSet et PreparedStatement local à chaque fonction...

  10. #10
    Membre averti
    Inscrit en
    Octobre 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 14
    Par défaut
    Hum... Il doit me manquer un close lors de chaque iteration de mon envPathList. J'essaye ca...

  11. #11
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ben_cines Voir le message
    Effectivement, j'ai une classe en singleton qui gère tout ce qui est accès aux données de la base, et j'ai préféré utiliser des variables d'instance pour mon ResultSet et mon PreparedStatement, plutôt que multiplier les lignes de déclaration.
    C'est une FBI (Fausse Bonne Idée) !!!

    Tu augmentes inutilement la portée des variables ainsi que leurs durées de vie tu compliques donc le travail du GC, ainsi que le risque d'erreur de ta part...


    De plus dans ton code la fermeture des ressources n'est pas correcte : tu dois avoir un try/finally par ressources.

    Bref cela devrait ressembler à ceci :
    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
     
    try {
        PreparedStatement ps = con.prepareStatement(sql);
        try {
            // utilisation du "ps"
     
            ResultSet rs = preparedStatement.getGeneratedKeys();
            try {
                // utilisation du "rs"
            } finally {
                rs.close();
            }
     
        } finally {
            ps.close();
        }
     
    } catch (SQLException e) {
        // traitement des exceptions
    }
    a++

  12. #12
    Expert éminent
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 482
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 482
    Par défaut
    je rajoute en plus de ce que dit adiguba: tu ne close que la moitié de tes statement / rs, puisque je vois dans le code deux conn.prepareStatement(sql);
    pour un seul close, même chose pour les resultset.


    Quand à la question du vidage du driver -> C'est justement à ça que sert le close, dire au driver que tu n'en a plus beosin!

  13. #13
    Membre averti
    Inscrit en
    Octobre 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 14
    Par défaut
    Entendu, merci à tous pour votre aide.
    C'est bien de travailler sur des centaines de milliers de tuples, ca oblige à reprendre quelques bases, dont l'oubli est rarement problématique dans les cas courant.

    Ceci dit, je pense que le déréférencement des objets par java devrait suffir, ca serait plus simple (destructeur appelant la méthode close() ).

  14. #14
    Expert confirmé
    Avatar de sinok
    Profil pro
    Inscrit en
    Août 2004
    Messages
    8 765
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Août 2004
    Messages : 8 765
    Par défaut
    Citation Envoyé par ben_cines Voir le message

    Ceci dit, je pense que le déréférencement des objets par java devrait suffir, ca serait plus simple (destructeur appelant la méthode close() ).

    Absolument pas.

    Les connexions à des flux (que ce soit fichier, streams reseaux, SGBCD) doivent être fermées de façon explicites si ont veut être sur qu'elles sont fermées... De fait tant qu'une connexion est ouverte elle n'est pas déréférencée par le GC, donc ne sera jamais vidée de la mémoire.

    le triumvira try {}catch{}finally{} est la seule façon propre de procéder.

  15. #15
    Membre averti
    Inscrit en
    Octobre 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 14
    Par défaut
    Citation Envoyé par sinok Voir le message
    Les connexions à des flux (que ce soit fichier, streams reseaux, SGBCD) doivent être fermées de façon explicites si ont veut être sur qu'elles sont fermées...
    Pourquoi ?
    (je me doutais que ma petite phrase allait lancer le troll...)

  16. #16
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ben_cines Voir le message
    Ceci dit, je pense que le déréférencement des objets par java devrait suffir, ca serait plus simple (destructeur appelant la méthode close() ).
    Il n'y a pas de destructeur en Java. La méthode finalize() n'en est pas vraiment un, car on ne peut pas prévoir le moment où elle sera exécutée.

    Toutefois, en règle générale dans ces cas là la méthode finalize() fait bien appel à close(), mais on ne peut pas prévoir son exécution.
    Or les ressources mémoires allouées par ces objets peut être minime, alors que cela implique l'utilisation d'un grand nombre de ressources "externes".

    C'est pour cela que l'utilisation des try/finally pour libérer les ressources est obligatoire (à noter que Java 7 apportera une syntaxe try-with-resources qui devrait simplifier cela).

    a++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Datetime + Mysql problème sur insertion
    Par hirochirak dans le forum ASP.NET
    Réponses: 12
    Dernier message: 08/12/2008, 17h34
  2. [MySQL] memory limit sur insertion dans MySQL
    Par bru2336 dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 13/11/2007, 08h32
  3. Problème sur requête insert mysql
    Par kcizth dans le forum Requêtes
    Réponses: 5
    Dernier message: 04/02/2006, 18h37
  4. Réponses: 4
    Dernier message: 28/09/2002, 00h00
  5. Réponses: 4
    Dernier message: 28/09/2002, 00h00

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