2010-10-27 19 views
1

Je développe en tant que petit programme de diabète en utilisant Delphi 5 et ADO. Je fais une petite requête comme ceci:Delphi: fuite de mémoire TAdoQuery?

function GetLowestGlucoseLevel(StartDate:string;EndDate:string): Integer; 
var 
    Q:TADOQuery; 
begin 
    try 
     Q:=TADOQuery.Create(Application); //Separate unit, owner set to App 
     Q.Connection:=dtMod.ADOCon; 
     Q.DisableControls; 
     Q.Close; 
     Q.SQL.Clear; 
     Q.SQL.Add('SELECT Min(qGlucose.Glucose) AS MinOfGlucose from qGlucose'); 
     Q.Parameters[0].Value:=StartDate; 
     Q.Parameters[1].Value:=EndDate; 
     Q.Open; 

     Result:=Q.FieldByName('MinOfGlucose').AsInteger; 

     Q.Close; 
    finally 
     Q:=nil; 
     Q.Free; 
    end; 
end; 

La requête fonctionne bien et renvoie le résultat comme prévu. Toutefois, lorsque j'ai vérifié le Gestionnaire des tâches Windows, l'utilisation de la mémoire continue à augmenter plutôt que de diminuer après la requête.

Comment résoudre ce problème?

Merci!

+0

commentaire supplémentaire sur votre code: L'objet Q est créé dans votre routine, donc l'appel à DisableControls/Close/Effacer sont totalement inutiles, parce que la requête n'a pas de contrôle associés, n'est pas ouvert et la propriété SQL est _blank_ lors de la création. L'impact sur les performances est quasiment nul en faisant les appels, mais j'évite ce type de code par nature, principalement parce que cela rend un simple aperçu de routine un peu compliqué. – jachguate

+0

Supplémentaire 2: Votre texte de requête ne contient pas de paramètres, mais vous en indiquez deux dans la ligne suivante ... sans Delphi en ce moment, je ne peux pas tester, mais je pense qu'il y aura une exception ou, au moins, un comportement indéfini. – jachguate

+0

@Jachguate: à propos des commandes de désactivation, j'ai juste l'habitude de m'assurer de toujours désactiver les commandes avant de faire appel à sql. Merci de l'avoir fait remarquer :) À propos des paramètres, j'appelle en fait une requête à l'intérieur du fichier d'accès ms avec les paramètres, donc sans définir les paramètres dans mon code, cela déclencherait une exception. – AFF

Répondre

13

Vous traces de fuites l'Comme d'autres l'ont souligné, TADOQuery d'abord la mise à zéro, puis en appelant Free la variable nulle (qui ne fait rien)

+0

quand je supprime le Q: = nil, la fuite est toujours là. Donc je pense que ce n'est pas la cause. – AFF

+4

Windows Gestionnaire des tâches n'affiche pas l'utilisation réelle de la mémoire, http://delphi.about.com/od/delphitips2007/qt/memory_usage.htm. Le code dans la question a seulement la fuite que j'ai décrite. Vous devez utiliser FastMM et définir 'ReportMemoryLeaksOnShutdown' comme décrit dans la réponse de Bharat pour trouver d'autres fuites. – jasonpenny

+3

@AFF Jason a raison. Mettre 'Q' à' nil' avant d'appeler 'Free' ne libérera pas l'objet' TADOQuery'. Lorsque vous l'avez retiré et que vous avez encore été témoin d'une fuite de mémoire, cela signifie que vous avez eu plusieurs fuites et que seule une d'entre elles a été corrigée. (soit cela ou vous ne l'avez pas recompilé). –

0

la section finalement devrait avoir les 2 déclarations inversées, comme si :

finally 
    Q.Free; 
    Q:=nil; // <- not even necessary since this is a local var 
end; 

Ou vous pouvez appeler SysUtils.FreeAndNil (Q) (si cela est disponible dans Delphi 5, pas sûr). A côté de cela, le TaskManager est un instrument terrible pour déterminer l'utilisation de la mémoire quand même. Vous pouvez libérer la mémoire pour Q, mais cela ne signifie pas automatiquement que le gestionnaire de mémoire Delphi libère la mémoire sur le système d'exploitation.

4
  • Avez-vous installé les mises à jour Delphi 5? L'implémentation RTM ADO est connue pour ont des problèmes.
  • Utilisez FastMM4, il devrait fonctionner avec Delphi 5, et vous dire plus sur l'endroit où les fuites sont.
+0

J'ai déjà installé la mise à jour ADO 2, mais la fuite est toujours là. J'ai également installé FastMM4. La fuite apparaît uniquement lorsque j'exécute la requête. Je n'ai pas pu trouver d'autres fuites liées à des formulaires créés dynamiquement, etc. – AFF

+0

Avez-vous configuré FastMM4 pour signaler des fuites? Il va collecter une pile d'appels et beaucoup d'autres informations pour localiser la fuite, mais seulement si on vous demande de le faire, regardez le fichier FastMM4.inc pour les définitions de $ pour permettre d'obtenir des informations détaillées et comment. –

+0

Le rapport de pile d'appel n'apparaît que si vous utilisez FullDebugMode –

2

Quote:

finally 
    Q:=nil; 
    Q.Free; 
end; 

Vous plaisantez? D'abord zéro la variable, puis la libérer? Tu es un génie! :-)

Utilisation:

finally 
    Q.Free; 
    Q:=nil; 
end; 

Ou ne prend pas la peine attribuer zéro à, puisque Q est une variable locale ...


Mais relisant votre code, je remarque vous utilisez Application en tant que propriétaire. En conséquence, ce ne sera pas vraiment une fuite, car il sera libéré lorsque l'application sera libérée. Si vous utilisez un formulaire, il sera libéré lorsque le formulaire propriétaire sera libéré.
Ce que vous devriez essayer est d'appeler cette requête environ 100.000 fois pour vérifier si elle continue à réserver de la mémoire ou si elle augmente juste la mémoire jusqu'à ce qu'une certaine taille ait été atteinte. Ce dernier est plus probable, puisque la mémoire est réservée aux prochains appels ADO.

+0

Hahaha, oui, un génie stupide je suis! :-D ... de toute façon, quand j'appelle la requête encore et encore et encore et encore ..., l'utilisation de la mémoire continue à augmenter. Je soupçonne quelque chose de louche à propos de l'ADO elle-même. – AFF

+1

Et ce n'est pas une erreur. :-) Vous retardez simplement la libération de la mémoire jusqu'à ce que l'application soit libérée. Les composants Delphi ADO sont connus pour avoir quelques fuites de mémoire mineures, cependant. –

+0

@Workshop Alex: Cela dépend de la définition de "fuite de mémoire". Vous dites que c'est une variable qui n'est jamais libérée, donc c'est bien. Je pense qu'une meilleure description est une variable qui reste allouée après que vous ayez fini de l'utiliser. –

0

Outre les lignes inversing comme Arjan, jasonpenny et WorkShop Alex dit, vous pouvez utiliser Process Explorer pour voir la consommation réelle de la mémoire (octets privés) du processus. Le gestionnaire de tâches n'est pas vraiment adapté à cette tâche, car il ne montre que l'ensemble fonctionnel du processus.

3

La façon Delphi:

function GetLowestGlucoseLevel(const StartDate:string; const EndDate:string): Integer; 
var 
    Q:TADOQuery; 

begin 

    Q:=TADOQuery.Create(nil); //(nil) because local use only. Placed before try\finally block 
           //because if it fails to .create then there would be no object to 
           //.Free 
    try 

     Q.Connection := dtMod.ADOCon; 

     //------can erase these------ 
     //Q.DisableControls; //No controls attached so unnecessary 
     //Q.Close;   //Q is local and was never opened so no need to close 
     //Q.SQL.Clear;  //Q is local and was never filled so no need to clear 

     Q.SQL.Add('SELECT Min(qGlucose.Glucose) AS MinOfGlucose from qGlucose'); 
     Q.Parameters[0].Value:=StartDate; 
     Q.Parameters[1].Value:=EndDate; 
     Q.Open; 

     Result := Q.FieldByName('MinOfGlucose').AsInteger; 

     Q.Close; 

    finally 

     Q.Free; 

     //Q := nil   //not needed because Q's scope is local 

    end; 
end;