TObjectList , Remove [Résolu]

Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
- - Dernière réponse : Nicolas___
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
- 3 févr. 2009 à 09:07
Bonjour ,
J'ai un gros problème de libération de mémoire et je ne vois pas ou est mon erreur ...

J'ai une structure de ce type :

type
  TMyObject  = class
 private
 public
 constructor Create ;
 destructor Destroy ;
end;

  TMyList = class(TObjectList)
  protected
    function Get( Index : Integer ) : TMyObject;
    procedure Put( Index : Integer; Item : TMyObject);
  public
    property Items[ Index : Integer ] : TMyObject read Get  write Put; default;
    constructor Create;
  end;

 TAutreObject = class
 private
 MyList : TMyList;
// blabalbla
public
 construtor Create ;
 procedure RemoveAll;
...

implementation
{ - TMyObject ---------------------------------------------------------- }
constructor TMyObject .Create
begin
 inherited Create;
end;

destructor TMyObject .Destroy
begin
 // je supprime bien sur ts ce que j'ai pu creer ici, mais pr l'exemple, ca n'en vaut pas la peine ...
 inherited Destroy;
end;

 
{ - TMyList ---------------------------------------------------------- }
constructor TMyList .Create;
begin
  inherited Create(True);// OwnObject est a True, Donc libération des Objects Automatique !
end;

function TMyList .Get( Index : Integer ) : TMyObject;
begin
  Result := inherited Get( Index );
end;

procedure TMyList .Put( Index : Integer; Item : TMyObject);
begin
  inherited Put( Index, Item );
end;

{ - TAutreObject ---------------------------------------------------------- }
construtor TAutreObject .Create ;
begin
 inherited Create;
MyList  := TMyList.Create;
// blablabla
end;

procedure TAutreObject .RemoveAll;
var
 i : integer;
begin
 for i:=0 to MyList.Count-1 do begin
   //Supprime un objet spécifié de la liste
  
// et (si OwnsObjects est à true) libère l'objet.
   MyList.Rmove(MyList.Items[i]);
 end;

 MyList.Capacity := MyList.Count;
// Mais il ne supprime rien ! Enfin si , mon vecteur est vide
//mais ma mémoire est toujours autant occupée , prq ?!!
end;

Voila ben ma question est simple , Pourquoi ca ne me libère rien coté mémoire ?!

Merci
Afficher la suite 

Votre réponse

20/32 réponses

Meilleure réponse
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
3
Merci
Bon ça y est ,
j'ai résolu mon problème et attention c'était encore plus con que tous , et ça prouve qu'il faut toujours faire gaffe au(x) message(s) d'erreur(s)

J'avais ceci mais je ne faisait jamais très attention :
[Avertissement]  La méthode 'Destroy' cache la méthode virtuelle du type de base 'TObject'

Hé oui , tt simplement oublié destructor Destroy;override
; !

Merci à tous quand même

Nico

Dire « Merci » 3

Quelques mots de remerciements seront grandement appréciés. Ajouter un commentaire

Codes Sources 154 internautes nous ont dit merci ce mois-ci

Commenter la réponse de Nicolas___
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
0
Merci
Bien sur pour ajouter qqch à ma liste je fais comme ceci :

procedure  TAutreObject.AddObject ( Item : TMyObject ) ;
begin
 MyList.Add ( Item );
end;
Commenter la réponse de Nicolas___
Messages postés
1270
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
0
Merci
Bonjour

Une petite question : Pourquoi tu n'emploies pas tout simplement MyList.Clear pour supprimer les éléments ?

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Commenter la réponse de WhiteHippo
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
0
Merci
Parce que ici je ne montre pas ts le code
(il est un peu long) ,
et que je ne dois pas effacer toute la liste , juste un certain nombre objet de la liste avec un Tag spécial ...
(Dans mon RemoveAll j'ai une condition , et si elle est a true -> Remove )

J'ai l'instruction  TAutreObject.Clear qui appele tous simplement MyList.Clear , mais la encore , ce n'est aps libéré !

(J'ouvre le gestionnaire de tâches en même tps, je créé +- 500 object , ma mémoire occupée grimpe et quand je fais un Clear ou un RemoveAll, pas de différences , mais pourtant mon vecteur est vide ... )
Commenter la réponse de Nicolas___
Messages postés
1270
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
0
Merci
Et avec un petit coup de MyList.Pack ?

N.B. Attention les objets contenus doivent avoir été assigné à NIL après leur suppression.

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Commenter la réponse de WhiteHippo
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
0
Merci
Pack
Supprime tous les éléments nil (Delphi) ou NULL (C++) du tableau Items.

Description

La méthode Pack déplace tous les éléments non-nil (Delphi) ou non-NULL (C++) au début du tableau Items, puis réduit la propriété Count au nombre d'éléments réellement utilisés. Pack ne libère pas la mémoire utilisée pour stocker les pointeurs nil (Delphi) ou NULL (C++). Pour libérer la mémoire allouée aux entrées inutilisées qui sont supprimées par Pack, affectez la nouvelle valeur de Count à la propriété Capacity.

Donc j'ai fais ca :

procedure TAutreObject .RemoveAll;
var
 i : integer;
begin
 for i:=0 to MyList.Count-1 do begin
 MyList.Items[i] := nil;
 end;
 MyList.Pack;
 MyList.Capacity := MyList.Count;
end;

Ca ne change rien !

Et quand je veux faire

procedure TAutreObject .RemoveAll;

var

 i : integer;

begin

 for i:=0 to MyList.Count-1 do begin
 MyList.Items[i] .free;// Forcement ca plante royalement !

 end;

 MyList.Capacity := MyList.Count;

end;
Commenter la réponse de Nicolas___
Messages postés
1270
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
0
Merci
Moi j'aurais plutôt écrit quelquechose comme :

procedure TAutreObject .RemoveAll;
var
 i : integer;
begin
  for i:=0 to MyList.Count-1 do 
  begin
    MyList.Remove(MyList.Items[i]); 
    MyList.Items[i] := nil; // Assignation à nil après la libération.

  end;
  MyList.Pack;
  MyList.Capacity := MyList.Count;
end;

N.B. Désolé, j'ai pas de quoi testé pour l'instant, alors c'est tout en live !!!

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Commenter la réponse de WhiteHippo
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
0
Merci
MyList.Remove(MyList.Items[i]);


MyList.Items[i] := nil; // Assignation à nil après la libération.

Je pense pas que ça marche , enfin ça plante même

Aide : "Après qu'un objet a été supprimé, tous les objets qui suivent sont déplacés et Count est décrémenté"

Donc dans ton code , le




MyList.Items[i]:=nil , l'indice "i" ne fait plus référence au même object , Si ?

Merci de ton aide
Commenter la réponse de Nicolas___
Messages postés
1270
Date d'inscription
samedi 14 août 2004
Statut
Membre
Dernière intervention
5 avril 2012
0
Merci
Je ferais des tests ce soir, ça m'évitera peut etre de t'aiguiller vers des fausses pistes et/ou de dire ces betises.

Cordialement.
<hr />"L'imagination est plus importante que le savoir." Albert Einstein
Commenter la réponse de WhiteHippo
Messages postés
3869
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
5
0
Merci
Salut Nicolas.

for i:=0 to MyList.Count-1 do
  begin
   //Supprime un objet spécifié de la liste
   // et (si OwnsObjects est à true) libère l'objet.
   MyList.Remove(MyList.Items[i]);
   asm DEC I end;
  end;

Faut utiliser l'asm sinon Delphi va grogner. Chez moi, 50000 éléments, application passe à 8000Ko mémoire, après RemoveAll, retombe à 4000.
La raisonest que quand tu supprime un objet, Count est automatiquement décrémenté : donc, tu te retrouves avec une belle V.A, et aucune libération.

Cordialement, Bacterius !
Commenter la réponse de Bacterius
Messages postés
2684
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
26 juillet 2018
10
0
Merci
Salut,

Tiens! C'est le monstre du
Loch Ness qui ré-apparaît ! 

A mon avis, la mémoire est bien libérée, ne t'inquiète pas. C'est juste un mystère du gestionnaire de tâches de Windows...
Tu trouveras quelques informations dans les commentaires, ICI
Commenter la réponse de Caribensila
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
0
Merci
Bacterius , utiliser l'asm comme ca , c'est totalement inutile

soit 1 fonction est pur asm soit elle est en pascal...
Commenter la réponse de Nicolas___
Messages postés
3869
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
5
0
Merci
Ok, je m'en souviendrai, ça pourrait peut-être m'aider ce conseil.
Bon ... sinon ça marche si on recule de 1 à chaque fois ?
Sinon tu peux partir de la fin, ça marche aussi et sans asm.

Cordialement, Bacterius !
Commenter la réponse de Bacterius
Messages postés
3982
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
8
0
Merci
Salut,

Il y a effectivement fuite de mémoire !

Et vous avez de la chance que f0xi ou autre ne soit pas encore passé là !
Ils vous renieraient en tant que padawan jusqu'à votre 57 ème génération.

Prenons un exemple simple en pseudo code.

liste[0] = tata;
liste[1] = titi;
liste[2] = toto;

for i:= 0 to liste.Count - 1 do
  liste.Remove(liste[i]);

Première boucle. liste.Count vaut 3, i vaut zéro :
liste.Remove(liste[0]);

[Aide de Delphi]
Appelez Remove pour supprimer un objet spécifique de la liste lorsque l'indice est inconnu. La valeur renvoyée est l'indice de l'objet dans le tableau Items avant qu'il ne soit supprimé. Si l'objet spécifié n'est pas trouvé dans la liste, Remove renvoie –1. Si OwnsObjects est à True, Remove libère l'objet en plus de le supprimer de la liste.

Après qu'un objet a été supprimé, tous les objets qui suivent sont déplacés et Count est décrémenté.
de Delphi

Donc le contenu de liste devient :
liste[0] = titi;

liste[1] = toto;

Bin oui, tata est libéré, titi et toto sont déplacés (Déplacement de pointeur, coûte pas grand chose).

Deuxième boucle. liste.Count vaut 2, i vaut 1 :
  liste.Remove(liste[1]);

contenu de liste après Remove :

liste[0] = titi;

et on sort de la boucle.

Bilan titi n'est pas libéré.

Il faut par exemple coder ça comme ça :

procedure TAutreObject .RemoveAll;
var
  nCount: Integer;
  i: Integer;
begin
  nCount:= MyList.Count;
 for i:= 0 to nCount - 1 do
   MyList.Remove(MyList.Items[0]);
end;

Remarquez le 0 : on supprime toujours le premier élément de la liste et les autres se décalent. Le deuxième (1) devient premier (0) et est supprimé au tour suivant. Remarquez aussi la récupération de count dans une vériable afin d'empècher la réévaluation.

Mais côté perf, c'est tout pourri. Il vaut bien mieux attaquer à la fin de la liste, pour éviter les décalages :

procedure TAutreObject .RemoveAll;

var

  i: Integer;

begin

 for i:= nCount - 1 downto 0 do

   MyList.Remove(MyList.Items[i]);

end;

Ainsi on supprime toujours le dernier élément.

Finalement, une petite remarque concernant Windows ou le gestionnaire des tâche qui dit que la mémoire n'est pas rendu. C'est vrai : Windows ne la pas vraiment récupéré, et ne peut pas la réaffecter à un autre processus.

C'est à cause des pages. La mémoire est divisée en pages de 4 ko. C'est l'unité de mémoire que Windows peut donner à un processus. Les allocations sont réalisées par VirtualAlloc. Après, par dessus ça, vient un tas, fourni par Windows (HeapAlloc, GlobalAlloc, LocalAlloc...) ou par le langage : Delphi propose certainement son propre tas.

En interne, le tas peut utiliser un autre tas, mais au final, c'est VirtualAlloc qui est utilisé et qui ne peut que délivrer 4 ko, rien, ou un multiple de 4ko.

Les tas subdivise les pages qui lui sont données. Supposons que l'on demande deux variables de 2 ko. Le tas peut demander une page de 4 ko, et stocker les deux dedans. Problème si une des deux variables est libérée, la page ne peut être rendu au système : la moitié est encore utilisée. Donc le processus consomme toujours 4 ko dans le gestionnaire des tâches, alors qu'il a libéré la moitié de sa mémoire ! Par contre, le tas sait que la moitié vide est vide, et peut la réutiliser pour des demandes ultérieures.

C'est ainsi que sur une application qui tourne depuis longtemps, un grand nombre de pages peut être bouffé par une ou deux variables qui restent dessus. Le processus à l'air donc de consommer beaucoup de mémoire, alors qu'en fait la plupart de sa mémoire allouée est vide.

L'utilisation de pages par le système n'est pas du tout spécifique à Windows, mais est dûe à l'architecture du processeur et du fait que la mémoire est virtualisée : chaque processus à son propre espace, et il faut tout traduire en adresse physique à chaque accès. Il y a des tables de traduction page virtuelle -> page physique utilisées par le processeur, bien que gérées par Windows.
Commenter la réponse de cs_rt15
Messages postés
3982
Date d'inscription
mardi 8 mars 2005
Statut
Modérateur
Dernière intervention
7 novembre 2014
8
0
Merci
Erratum, dans la bonne méthode, nCount non définit bien sûr :

procedure TAutreObject .RemoveAll;
var
  i: Integer;
begin
 for i:= MyList.Count - 1 downto 0 do
   MyList.Remove(MyList.Items[i]);
end;
Commenter la réponse de cs_rt15
Messages postés
2684
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
26 juillet 2018
10
0
Merci
J'ai enfin compris le mystère du gestionnaire des tâches !

Merci  rt15
Commenter la réponse de Caribensila
Messages postés
1039
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
0
Merci
Merci rt15 pr ts ces infos très utile
Commenter la réponse de Nicolas___
Messages postés
2684
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
26 juillet 2018
10
0
Merci
@rt15
« Windows ne la pas vraiment récupéré, et ne peut pas la réaffecter à un autre processus »

- Imaginons une application qui doit créer de très nombreux objets et les libérer presque aussitôt. Si on n'a pas bcp de chance, ne risque t-on pas d'arriver à un plantage du système, même si le code est irréprochable du côté des libérations



- Existe t-il un moyen ou une méthode de codage pour se prémunir de ça
Commenter la réponse de Caribensila
Messages postés
2684
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
26 juillet 2018
10
0
Merci
... Une sorte ne défragmentation de la ram est-elle possible
 
(  J'espère ne pas abuser de ta patience, rt15. Qu'on sait infinie, mais quand même...  ;)
Commenter la réponse de Caribensila
Messages postés
2684
Date d'inscription
jeudi 15 janvier 2004
Statut
Membre
Dernière intervention
26 juillet 2018
10
0
Merci
... D'ailleurs, n'est-ce pas exactement le même phénomène au niveau du DD qui nous oblige à le défragmenter de temps en temps?

Allez! Je vais arrêter là (pour le moment).
 
Commenter la réponse de Caribensila