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 Java Discussion :

deux petits test sur le gc: je ne comprend pas leurs différences


Sujet :

Langage Java

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2008
    Messages : 30
    Points : 29
    Points
    29
    Par défaut deux petits test sur le gc: je ne comprend pas leurs différences
    Voilà, en voulant tester le gc, j'ai fini par faire deux petits test:
    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
    package gc_test_case;
     
    import java.lang.management.*;
     
    public class Main {
    	public static void main(String[] args) {
    		test0();
                    //test1(); selon ce que je veux tester.
    	}
     
    	private static void test0() {
    		byte[] b_;
    		b_ = new byte[1];
    		for (int i = 0; i < 100; i++) {
    			b_ = new byte[1000 * 1000 * 40];
    			System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
    		}
    	}
     
    	private static void test1() {
    		byte[] b_;
    		// b_ = new byte[1];
    		for (int i = 0; i < 100; i++) {
    			b_ = new byte[1000 * 1000 * 40];
    			System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
    		}
    	}
     
    	private static void test2() {
    		byte[] b_;
    		b_ = new byte[1];
    		for (int i = 0; i < 100; i++) {
    			b_ = new byte[1000 * 1000 * 30];
    			System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
    		}
    	}
    }
    Le test0 m'envoie un message d'erreur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    	at gc_test_case.Main.test0(Main.java:14)
    	at gc_test_case.Main.main(Main.java:7)
    init = 0(0K) used = 40128792(39188K) committed = 45178880(44120K) max = 66650112(65088K)
    Le test1 et test2 fonctionnent.

    Pourquoi cette différence et surtout pourquoi le test0 ne fonctionne pas?

  2. #2
    Membre actif Avatar de bidi
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    262
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2004
    Messages : 262
    Points : 266
    Points
    266
    Par défaut
    Intéressant :-)

    A mon avis c'est juste du a l'algorithme utilisé par le GC...

    Si tu lance ton prog avec l'argument jvm -verbosegc, tu peux observer que pour test0, le Second full GC ne fonctionne pas correctement...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    [GC 169K->105K(1984K), 0.0027662 secs]
    [Full GC 105K->105K(1984K), 0.0063434 secs]
    [GC 39167K->39167K(41048K), 0.0004147 secs]
    [Full GC 39167K->39167K(41048K), 0.0058626 secs]
    [Full GC 39167K->39166K(65088K), 0.0343285 secs]
    Pour test1, ça fonctionne bien:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    [GC 169K->105K(1984K), 0.0027325 secs]
    [Full GC 105K->105K(1984K), 0.0053987 secs]
    [GC 39167K->39167K(41048K), 0.0004454 secs]
    [Full GC 39167K->105K(41048K), 0.0057935 secs]
    je sais pas trop pourquoi, sans doute dû à l'algo utilisé.

    A mon avis ça dépend d'une jvm à l'autre :-)

  3. #3
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Dans "test0", ton tableau de byte est déclaré à l'extérieur de la boucle.
    Tu me diras : "mais euh.... c'est pareil pour test1 !!"
    En fait.... non. Le compilateur prend la liberté de déclarer le tableau de byte à l'intérieur de la boucle de test1 car il a observé, à juste titre, que tu ne l'utilises pas du tout à l'extérieur de la boucle. Par contre, pour "test0", puisque tu instancies "b_" à l'extérieur de la boucle, le compilateur place "b_" dans le contexte de la méthode, et plus dans la boucle.

    Pour test1, à chaque itération de boucle, le contexte de la boucle est supprimer, donc l'espace mémoire de "b_" aussi. Ensuite, la JVM déclare en mémoire un nouveau tableau.
    Par contre, pour "test0", a chaque itération de boucle, la JVM va d'abord instancier un tableau de byte, et ensuite l'affecter à "b_". Pour faire tourner "test0", il faut donc une mémoire égale à deux fois la taille du tableau.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  4. #4
    Expert éminent sénior
    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
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,

    Je pense plutôt que le compilateur optimise la portée de la variable b_.


    Pourquoi le test0 ne marche pas ? En fait la première itération marche bien, on le voit bien car on a une des traces du HeapMemoryUsage :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    init = 0(0K) used = 40130072(39189K) committed = 45178880(44120K) max = 66650112(65088K)
    On s'aperçoit qu'on utilise environ 39Mo sur les 65Mo disponible au maximum, ce qui est logique pour un tableau de 40 millions de bytes...

    Et cela plante à la seconde iteration, car on essaye d'allouer un second tableau de 39Mo alors que le premier ne peut pas être libéré par le GC. En effet les opérations se font de droite à gauche, donc dans la ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    b_ = new byte[1000 * 1000 * 40];
    On a deux étapes :
    1. On crée le tableau
    2. On l'affecte à la référence b_

    Pendant un très court instant on va donc d'avoir deux tableaux de 39Mo... ce qui est trop gros pour les 64Mo maximum de la JVM...

    Pour "corriger" cela on pourrait indiquer à chaque iteration que le tableau est libérable en mettant la référence à null :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    	private static void test0() {
    		byte[] b_;
    		b_ = new byte[1];
    		for (int i = 0; i < 100; i++) {
    			b_ = null; // on déréférence le tableau
    			b_ = new byte[1000 * 1000 * 40];
    			System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
    		}
    	}
    Le GC pourra le libérer avant de créer le nouveau tableau







    Pourquoi test2 marche ? Tout simplement car le tableau ne comporte "que" 30 millions d'éléments et que cela représente un peu moins de 30Mo... donc on peut très bien avoir deux tableaux en mémoire à un instant T. Et une fois l'affectation faite le tableau précédent peut être supprimé par le GC pour réutiliser l'espace pour l'itération suivante.







    Pourquoi le test1 marche ? Là c'est plus compliqué et je pense qu'il s'agit d'une optimisation du compilateur.
    Comme ta variable n'est pas du tout utilisé en dehors de ta boucle, le compilateur doit interprété cela de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    	private static void test1() {
    		for (int i = 0; i < 100; i++) {
    			byte[] b_ = new byte[1000 * 1000 * 40];
    			System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage());
    		}
    	}
    A la fin de chaque itération du for, l'objet pointé par b_ est libérable par le GC, donc on peut très bien créer un nouveau tableau de 39Mo car le précédent pourra être libéré avant !






    On voit très bien ici que le choix de la porté des variables est très important en Java, et qu'en limitant au minimum requis on facilite le travaille du GC et on économise l'espace


    a++

    [edit] Bon le temps de taper tout ca je me suis fait grillé

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    30
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2008
    Messages : 30
    Points : 29
    Points
    29
    Par défaut
    OK j'ai compris. Maintenant c'est très claire. Merci à tous.

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

Discussions similaires

  1. Deux petites questions sur les niveaux de gris
    Par jujuj dans le forum Traitement d'images
    Réponses: 9
    Dernier message: 07/07/2011, 13h49
  2. Deux petites questions sur CSS
    Par coaut dans le forum Mise en page CSS
    Réponses: 3
    Dernier message: 21/09/2010, 17h01
  3. Deux petites questions sur REALM
    Par ahmed.normal.tn dans le forum Tomcat et TomEE
    Réponses: 1
    Dernier message: 11/04/2008, 15h13
  4. Petit test sur les listes
    Par Erwane dans le forum Scheme
    Réponses: 6
    Dernier message: 19/02/2008, 23h55
  5. Réponses: 2
    Dernier message: 28/07/2007, 01h21

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