TObjectList , Remove [Résolu]

Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 28 janv. 2009 à 13:41 - Dernière réponse : Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention
- 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

32 réponses

Meilleure réponse
Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 2 févr. 2009 à 18:05
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

Merci Nicolas___ 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 70 internautes ce mois-ci

Commenter la réponse de Nicolas___
Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 28 janv. 2009 à 13:45
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___
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 28 janv. 2009 à 14:27
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
Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 28 janv. 2009 à 14:36
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___
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 28 janv. 2009 à 14:45
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
Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 28 janv. 2009 à 15:08
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___
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 28 janv. 2009 à 15:32
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
Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 28 janv. 2009 à 15:53
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___
WhiteHippo 1270 Messages postés samedi 14 août 2004Date d'inscription 5 avril 2012 Dernière intervention - 28 janv. 2009 à 16:41
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
Bacterius 3869 Messages postés samedi 22 décembre 2007Date d'inscription 3 juin 2016 Dernière intervention - 28 janv. 2009 à 17:25
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
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 28 janv. 2009 à 17:27
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
Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 28 janv. 2009 à 21:43
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___
Bacterius 3869 Messages postés samedi 22 décembre 2007Date d'inscription 3 juin 2016 Dernière intervention - 28 janv. 2009 à 21:51
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
cs_rt15 3982 Messages postés mardi 8 mars 2005Date d'inscription 7 novembre 2014 Dernière intervention - 29 janv. 2009 à 14:16
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
cs_rt15 3982 Messages postés mardi 8 mars 2005Date d'inscription 7 novembre 2014 Dernière intervention - 29 janv. 2009 à 14:18
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
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 29 janv. 2009 à 14:42
0
Merci
J'ai enfin compris le mystère du gestionnaire des tâches !

Merci  rt15
Commenter la réponse de Caribensila
Nicolas___ 1039 Messages postés jeudi 2 novembre 2000Date d'inscription 24 avril 2013 Dernière intervention - 29 janv. 2009 à 14:54
0
Merci
Merci rt15 pr ts ces infos très utile
Commenter la réponse de Nicolas___
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 29 janv. 2009 à 15:04
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
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 29 janv. 2009 à 15:18
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
Caribensila 2684 Messages postés jeudi 15 janvier 2004Date d'inscription 26 juillet 2018 Dernière intervention - 29 janv. 2009 à 15:24
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

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.