Outils pour utilisateurs

Outils du site


java:memoire

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
java:memoire [2013/11/04 11:34] – [Les types pointeurs] brunojava:memoire [2013/11/04 16:11] (Version actuelle) – [Java l'amnésique] bruno
Ligne 297: Ligne 297:
 Seul le premier octet de l'adresse est colorié. En effet, les types construits n'ont pas de taille fixe, nous allons maintenant voir comment java leur alloue la mémoire dont ils ont besoin. Seul le premier octet de l'adresse est colorié. En effet, les types construits n'ont pas de taille fixe, nous allons maintenant voir comment java leur alloue la mémoire dont ils ont besoin.
  
-==== La mémoire des pointeurs ====+==== La mémoire et les tableaux ==== 
 + 
 +Vous avez appris qu'un tableau en java ne peut pas changer de taille: il faut lui indiquer combien d'éléments il va contenir à sa création. 
 + 
 +<code java> 
 + //tableau de 3 entiers 
 + int[] tabInt = new int[3]; 
 +  
 + //tableau de 4 Object 
 + Object[] tabObject = new Object[4]; 
 + 
 +</code> 
 + 
 +Pourquoi donc? 
 + 
 +Hé bien c'est facile à comprendre. Java va mémoriser la taille du tableau, et pour chaque case va réserver une zone mémoire qui contiendra une adresse vers la donnée. 
 + 
 +Les adresses sont en général codées avec 4 octets. Un tableau de 20 éléments aura donc une taille de 20*4 = 80 octets, **quel que soit le type de données qu'il contient**. 
 +Si vous essayez de lire une donnée en dehors de la plage déclarée (par exemple //tabInt[120]//), java vous interdit cette opération car vous tomberiez dans une zone mémoire qui contient d'autres données que celles du tableau (120*4=480, soit le 480ème octet après le premier octet du tableau). 
 + 
 +Java génère dans ce cas une erreur de type //ArrayIndexOutOfBoundsException//: l'index demandé est en dehors des limites du tableau. 
 + 
 +Voici quelques exemples illustrés/ 
 + 
 +=== Tableau de types primitifs === 
 + 
 +En mémoire, chaque case du tableau "pointe" (c'est à dire contient l'adresse) d'une zone mémoire qui contient la donnée recherchée. 
 + 
 +L'**adresse** de la case du tableau fait 4 octet. 
 + 
 +La **zone pointée** fait la taille du type primitif (2 octets pour un short, 4 pour un int, etc.). 
 + 
 +{{ :java:memoire4.png?600 | exemple d'un tableau d'entier: chaque case du tableau contient l'adresse de la zone mémoire où est réellement stocké l'entier.}} 
 + 
 +=== Tableau de dimension 2, 3, n... === 
 + 
 +Soit le code suivant:  
 +<code java> 
 +short [][] tab = new tab[6][4]; 
 +</code> 
 + 
 +//tab// est un tableau de dimension 6 qui contient des tableaux de dimension 4. 
 + 
 +Nous avons vu qu'un tableau est identifié par son adresse en mémoire. 
 + 
 +Donc, chacune des 6 cases du tableau //tab// va contenir l'adresse d'un tableau de dimension 4. 
 +Chaque tableau de dimension 4 contiendra l'adresse où se situe la donnée de type //short// 
 + 
 +On devrait donc parler d'un tableau de 6 adresses de tableaux de 4 adresses de short. 
 + 
 +{{ :java:memoire5.png?600 | Un tableau de tableau}} 
 + 
 + 
 ==== Construction d'objets ==== ==== Construction d'objets ====
  
 +Nous avons vu que pour qu'il y ait création, il faut nécessairement que **new** soit utilisé.
 +
 +Mais que fait cet opérateur **new**?
 +
 +**new** attend en paramètre un **constructeur** (pas un nom de classe!!!).
 +
 +Prenons par exemple la hiérarchie de classes suivante:
 +
 +{{ :java:heritageobject.png?200 |Pingouin hérite de Animal qui hérite de Object}}
 +
 +Voici le code des classes Animal et Pingouin:
 +
 +<code java Animal.java>
 +/**
 + Une toute petite classe représentant un Animal
 + @author Bruno Mascret
 +*/
 + public class Animal{
 + /**le nom de l'animal*/
 + private String nom ="";
 + /** l'espèce de l'animal*/
 + private String espece="";
 +
 + /**
 + Constructeur d'Animal
 + @param n valeur pour nom
 + @param e valeur pour espece
 + */
 + public Animal(String n, String e){
 + nom = n;
 + espece = e;
 + }
 +
 + /**
 + Constructeur d'Animal
 + L'animal est d'espèce inconnue
 + @param n valeur pour nom
 + */
 + public Animal(String n){
 + nom = n;
 + espece = "inconnue";
 + }
 +
 + /**
 + Pour faire jouer l'Animal.
 + Affiche un petit message en console.
 + */
 + public void jouer(){
 + System.out.println("Je joue et c'est vraiment l'éclate");
 + }
 +
 +}
 +</code>
 +
 +et Pingouin:
 +
 +<code java Pingouin.java>
 +/** Une toute petite classe pour représenter un Pingouin*/
 + public class Pingouin extends Animal{
 +
 + /**
 + Constructeur de Pingouin, qui affecte automatiquement la chaîne "pingouin" à l'attribut hérité "espece"
 + Utilise le constructeur d'Animal Animal(String, String)
 + @param n le nom du pingouin
 + */
 + public Pingouin(String n){
 + super(n,"pingouin");
 + }
 +
 + /**
 + Ce qui est bien quand on est Pingouin, c'est qu'on peut glisser sur le ventre
 + */
 + public void glisser(){
 + System.out.println("Waou, je glisse sur le ventre");
 + }
 +
 +}
 +</code>
 +
 +De quels constructeurs disposons-nous?
 +
 +Classe Object: //Object()//
 +
 +Classe Animal: //Animal(String n, String e)// et //Animal(String n)//
 +
 +Classe Pingouin: //Pingouin(String n)//
 +
 +Pour construire (créer) un //Object// (une instance d'//Object//), je peux utiliser n'importe lequel d'entre eux (un Pingouin **est un** Animal qui **est un** Object).
 +<code java>
 +Object o1 = new Object();
 +Object o2 = new Animal("Serge");
 +Object o3 = new Animal("Tryphon", "Pingouin");
 +Object o4 = new Pingouin("Thérèse");
 +</code>
 +
 +J'ai utilisé 4 fois l'opérateur **new**: j'ai donc 4 instances en mémoire.
 +
 +Parmi ces instances:
 +  * //o1// pointe vers une instance de type //Object//
 +  * //o2// et //o3// pointent chacune vers une instance de type //Animal//
 +  * //o4// pointe vers une instance de type //Pingouin//.
 +
 +Mais est-ce que pour autant j'ai la même chose en mémoire?
 +
 +**NON!**
 +
 +Regardons ce qui se passe en détail.
 +
 +<code java>
 +Object o1 = new Object();
 +</code>
 +
 +Java va créer une zone de mémoire pour les attributs de //o1//, une autre pour ses méthodes (qui sont celles de la classe Object).
 +
 +Les attributs (caractéristiques) de chaque instance d'Object **diffèrent**, les méthodes (comportement) restent les mêmes.
 +
 +On peut schématiser cette opération de la manière suivante:
 +
 +{{ :java:memoireobj1.png?600 | création après appel au constructeur Object}}
 +
 +<code java>
 +Object o1 = new Object();
 +Object o2 = new Animal("Serge");
 +</code>
 +
 +Cette fois-ci, on utilise le constructeur de Animal. Un Animal **est un** Object, donc java va commencer par construire un Object en appelant le constructeur //Object()// hérité par Animal.
 +
 +Ensuite, il va ajouter les attributs de l'instance o2 venant de Animal, et les méthodes de Animal:
 +
 +{{ :java:memoireobj2.png?600 | état de la mémoire après appel du constructeur Animal("Serge")}}
 +
 +<code java>
 +Object o1 = new Object();
 +Object o2 = new Animal("Serge");
 +Object o3 = new Animal("Tryphon", "Pingouin");
 +</code>
 +
 +On utilise maintenant le constructeur //Animal(String n, String e)//.
 +Il n'y a pas de différence avec le cas précédent. Notez cependant que la zone mémoire contenant les méthodes de Animal n'est pas dupliquée (les méthodes/comportement sont les mêmes pour toutes les instances d'Animal).
 +
 +{{ :java:memoireobj3.png?600 | état de la mémoire après appel du constructeur Animal("Tryphon", "Pingouin")}}
 +
 +<code java>
 +Object o1 = new Object();
 +Object o2 = new Animal("Serge");
 +Object o3 = new Animal("Tryphon", "Pingouin");
 +Object o4 = new Pingouin("Thérèse");
 +</code>
 +
 +Cette fois-ci, le constructeur //Pingouin(String n)// va appeler **explicitement** le constructeur //Animal(String n, String e)// qui va lui-même appeler (implicitement) le constructeur //Object()//.
 +
 +Il suffit alors de "dépiler" les appels et voici ce qu'on obtient au final en mémoire:
 +
 +{{ :java:memoireobj4.png?600 | état de la mémoire après appel du constructeur Animal("Thérèse")}}
 +
 +Vous vous rappelez du "Garbage Collector" (le ramasse-miettes en français).
 +
 +C'est le processus de java qui s'occupe de libérer la mémoire.
 +
 +La dernière figure vous permettra de bien comprendre comment il fonctionne: tant qu'une zone de la mémoire reçoit une flèche, ça veut dire qu'elle est "pointée", donc encore utilisée.
 +
 +Une zone de mémoire qui ne reçoit plus de flèche est une zone dont les données ne pourront jamais être récupérées: le garbage collector va donc pouvoir effacer cette zone, et indiquer qu'elle est libre pour une nouvelle utilisation. C'est simple avec un dessin, non?
 +
 +
 +==== Et les tableaux d'objets? ====
 +
 +Que se passe-t-il dans le cas de tableaux d'objets?
 +
 +Réfléchissez, vous avez déjà la réponse!
 +
 +===== Java l'amnésique =====
 +
 +Et pour finir, parlons des pertes de mémoires de java! Je veux parler du problème suivant:
 +
 +<code java>
 +Object o1 = new Object();
 +Object o2 = new Animal("Serge");
 +Object o3 = new Animal("Tryphon", "Pingouin");
 +Object o4 = new Pingouin("Thérèse");
 +
 +o4.jouer();
 +
 +o4.glisser();
 +</code>
 +
 +Le compilateur java nous indique deux erreurs: 
 +
 +<code>
 +TypeConstruit.java:62: error: cannot find symbol
 + o4.jouer();
 +   ^
 +  symbol:   method jouer()
 +  location: variable o4 of type Object
 +TypeConstruit.java:64: error: cannot find symbol
 + o4.glisser();
 +   ^
 +  symbol:   method glisser()
 +  location: variable o4 of type Object
 +2 errors
 +
 +</code>
 +
 +Pourtant, //o4// a été construit avec un constructeur de //Pingouin//!
 +
 +Rappelez-vous que javac n'est qu'un programme, pas toujours très intelligent donc!
 +
 +Ce que javac voit, c'est que //o4// est déclaré comme //Object//.
 +
 +Il cherche donc les méthodes //jouer()// et //glisser()// dans la zone de mémoire d'Object... et bien sûr ne les trouve pas car elles n'y sont pas!
 +
 +Pourtant, si on regarde le dernier schéma, on voit bien que ces méthodes sont chargées dans la mémoire!
 +
 +Il va donc falloir explicitement dire à Java de regarder un peu plus en détail. C'est le fameux **cast** ou **transtypage** que nous avons déjà vu avec les types primitifs, sauf que là il ne peut y avoir perte de données: soit l'opération est possible, soit elle est impossible et génerera une erreur de type //ClassCastException//.
 +
 +Nous allons donc définir une variable et lui dire de pointer dans la même zone mémoire que //o4//, en demandant à java d'étendre un peu son champ de vision, ou de //retrouver la mémoire// ;-)
 +
 +<code java>
 + Object o4 = new Pingouin("Thérèse");
 + /* ça marche pas!
 + o4.jouer();
 + o4.glisser();*/
 +
 + Animal a = (Animal)o4;
 + a.jouer();
 +
 + ((Pingouin)o4).glisser();
 +</code>
 +
 +Je vous ai mis deux manières de faire:
 +  * dans le premier cas, je déclare une variable //a//, et après j'appelle jouer();
 +  * dans le deuxième cas, je fais directement le cast sans passer par une variable: java créera une variable temporaire et appellera ensuite la méthode glisser().
 +
 +Et la question de la fin pour voir si vous avez bien compris: 
 +
 +//**"Combien d'instances ont été créées en tout dans le dernier code???"**//
 +
 +===== Codes complet des exemples =====
 +
 +Archive du code au format tar.gz : {{:java:memoire.tar.gz|code des fichiers java utilisés}}
 +
 +Vous pouvez exporter ce TP en pdf en utilisant le lien du menu de gestion de cette page (le dernier, petite icône en crayon)
  
  
  
java/memoire.1383564881.txt.gz · Dernière modification : 2013/11/04 11:34 de bruno