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

 Delphi Discussion :

[D 10.4 update2] [Windows 10 21h1] JSON Marshaling et fuite mémoire


Sujet :

Delphi

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2016
    Messages : 15
    Points : 15
    Points
    15
    Par défaut [D 10.4 update2] [Windows 10 21h1] JSON Marshaling et fuite mémoire
    Bonjour à tous,

    J’ai un petit souci de fuite mémoire dans un projet lors de la sérialisation/désérialisation de mes classes en JSON.

    J’ai résumé au plus court le problème, et j’aimerais savoir si je n’ai pas omis quelque chose ou si je n’y ai, au final, rien entendu ?

    Les unités ajoutées
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Uses … Data.DBXJSONReflect;
    La classe traitée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    type
      TUser = class
       public
         Name: string;
         constructor Create;
      end;
    ….
    constructor TUser.Create;
    begin
      Name := 'Maxi';
    end;
    Et la méthode où j'utilise le marshaling pour transformer une instance de la classe TUser en chaîne JSON.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    procedure TForm1.DoTest;
    begin
      ReportMemoryLeaksOnShutdown := true;
      var JSONMarshal : TJSONMarshal := TJSONMarshal.Create(TJSONConverter.Create);
     
      var User: TUser := TUser.Create;
      try
        ShowMessage( JSONMarshal.Marshal(User).ToString );  <-- problème ici
      finally
        User.Free;
        JSONMarshal.free;
      end;
    end;
    Lors de la fermeture de l'application on peut constater une fuite mémoire.

    Nom : Fuite.png
Affichages : 129
Taille : 7,1 Ko

    Je ne vois pas comment corriger ce problème. Pourriez-vous me suggérer des pistes ?

  2. #2
    Membre actif Avatar de oneDev
    Homme Profil pro
    dilettant
    Inscrit en
    Mars 2019
    Messages
    213
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Maritime (Haute Normandie)

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

    Informations forums :
    Inscription : Mars 2019
    Messages : 213
    Points : 222
    Points
    222
    Par défaut
    Perso, j'utilise madExcept pour vérifier les fuites mémoires. Cela donne exactement, quel objet n'est pas libéré.

  3. #3
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 691
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 691
    Points : 13 121
    Points
    13 121
    Par défaut
    JSONMarshal.Marshal() retourne un objet et tu es responsable de sa libération.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var JSon := JSONMarshal.Marshal(User);
    ShowMessage(JSON.ToString);
    JSon.Free;
    Et à titre d'info, il est rarement nécessaire de spécifier le type dans des var inline.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    var JSONMarshal := TJSONMarshal.Create(TJSONConverter.Create);
    var User := TUser.Create;

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 455
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 455
    Points : 24 867
    Points
    24 867
    Par défaut
    Plusieurs choses à vérifier déjà pour TJSONConverter.Create
    La libération est assuré parce que OwnConverter à True dans le TJSONMarshal.Create
    Cela peut être une source confusion pour la suite pour comprendre quels objets sont libérés implicitement et pas les autres, d'où l'intérêt de s'intéresser à tous les paramètres des fonctions.

    La méthode Marshal retourne une instance de <TSerial> soit TJSONValue (issu du TJSONConverter étant TConverter<TJSONValue>) et cet objet n'est possédé ni par TJSONConverter ni par TJSONMarshal pour permettre de l'utiliser ultérieurement


    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
    procedure TForm1.DoTest;
    begin
      ReportMemoryLeaksOnShutdown := true;
      var UserSerial: TJSONValue := nil;
      try
        var JSONMarshal : TJSONMarshal := TJSONMarshal.Create(TJSONConverter.Create);
        try
          var User: TUser := TUser.Create;
          try
           UserSerial := JSONMarshal.Marshal(User);
          finally
            User.Free();
          end;
        finally
          JSONMarshal.Free();
        end;
     
        ShowMessage(UserSerial.ToString()); // la durée de vie de UserSerial ne dépend pas du TJSONMarshal\TJSONConverter et heureusement
     
      finally
          UserSerial.Free();
      end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  5. #5
    Membre émérite
    Avatar de ALWEBER
    Homme Profil pro
    Expert Delphi
    Inscrit en
    Mars 2006
    Messages
    1 496
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Expert Delphi

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 496
    Points : 2 762
    Points
    2 762
    Billets dans le blog
    10
    Par défaut
    Petite note de mémoire : utiliser
    plutôt que

  6. #6
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 455
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 455
    Points : 24 867
    Points
    24 867
    Par défaut
    Citation Envoyé par ALWEBER Voir le message
    Petite note de mémoire : utiliser JSon.DisposeOf;plutôt queJSon.Free;



    DisposeOf c'est sur un compilateur mobile avec compteur de référence activé, d'ailleurs, l'ARC pour les objets n'est-il pas devenu obsolète ?
    Sous Windows 10, comme c'est écrit dans l'aide et si on lit le code, c'est un Free
    Dans les compilateurs de bureau Delphi (DCC32, DCC64, DCCOSX), l'effet d'un appel de DisposeOf reste le même, puisqu'elle appelle Free.
    Je suis même surpris que DisposeOf ne soit pas marqué deprecated, pprem qui l'avait déjà indique pour la 10.4, nous le confirmera pour la version 11
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  7. #7
    Membre expert
    Avatar de pprem
    Homme Profil pro
    MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Inscrit en
    Juin 2013
    Messages
    1 876
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2013
    Messages : 1 876
    Points : 3 611
    Points
    3 611
    Par défaut
    DisposeOf appelle Free.
    Free appelle Destroy si Self est renseigné.
    Reprendre des habitudes cohérentes avec la pratique historique de la VCL en appelant Free (ou FreeAndNil) me semble être une bonne chose.

    La remarque faite en mars concernant la suppression d'une instance à l'intérieur d'une de ses méthodes reste d'actualité. Ca peut mal se passer sur iOS ou Android dans certains cas. Vaut donc mieux différer les suppression en les mettant en file d'attente.

    D'ailleurs je suis aussi étonné qu'Embarcadero ne l'ait pas déprécié, mais comme le code lié à ARC y est toujours, ça se fera probablement lorsqu'ils feront du ménage sur tout ce qui touche à AUTOREFCOUNT s'ils décident de complètement l'annihiler un jour.

  8. #8
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2016
    Messages : 15
    Points : 15
    Points
    15
    Par défaut
    Je m’étais déjà assuré que le problème ne venait pas de TJSONConverter.

    Merci ShaiLeTroll, je n’avais pas vu la subtilité en effet j’aurais dû mieux lire les commentaires de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    function Marshal(Data: TObject): TSerial; virtual; dans l’unité Data.DBXJSONReflect.
     
    <summary>Marshals an object into an equivalent representation.</summary>
    It uses the converter passed in the constructor</remarks>
    <param name="Data">Object instance to be serialized</param>
    <returns> the serialized equivalent</returns>
    Par conséquent j’ai modifié le code de la façon suivante en tenant compte des remarques de AndNotOr et cela résout mon problème.

    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
    procedure TForm1.DoTest;
    begin
      ReportMemoryLeaksOnShutdown := true;
      var JSONMarshal := TJSONMarshal.Create(TJSONConverter.Create);
     
      var User:= TUser.Create;
      with JSONMarshal.Marshal(User) do
      try
        ShowMessage( ToString );
      finally
        Free;
        User.Free;
        JSONMarshal.free;
      end;
    end ;
    Problème résolu, merci à tous pour vos conseils productifs et votre grande réactivité.

  9. #9
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 455
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 455
    Points : 24 867
    Points
    24 867
    Par défaut
    J'ai failli proposer aussi un with mais avec le var d'une déclaration inline, j'y ai renoncé pour montrer plutôt la durée de vie de l'objet TJSONValue (TSerial)

    On pourrait pousser le vice avec un with var ? je ne sais pas, je n'ai pas de Delphi assez récent, donc le code ci-dessous avec les with var est peut-etre faux, je l'espère pas, je trouve que cela rend le with tout aussi pratique et concis tout en ajoutant une variable facilitant le débogage et aussi le besoin de passer l'objet en paramètre (évitant un ItSelf)
    Cependant, je recommande de séparer les finally, oui c'est lourd mais pour gérer les exceptions entre-temps

    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
    procedure TForm1.DoTest;
    begin
      with var JSONMarshal := TJSONMarshal.Create(TJSONConverter.Create) do
      try
        var User:= TUser.Create();
        try
          with var UserSerial := Marshal(User) do
          try
            ShowMessage(ToString());
          finally
            UserSerial.Free();
          end;
        finally
          User.Free;
        end;
      finally 
         JSONMarshal.Free();
      end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  10. #10
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2016
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2016
    Messages : 15
    Points : 15
    Points
    15
    Par défaut
    Je viens de tester avec le "with var" avec delphi 10.4.2, hélas il y a une erreur à la compilation
    Dcc32 -> E2018 Type Record, Object ou class requis


    Pour info avec le Unmarshal, j'ai procédé ainsi pour éviter la fuite mémoire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function TClassObject<T>.ToObject(ASt: string): T;
    begin
      UnConverters;  //Pour les transformations non définies par défaut
      var JSONObject := TJSONObject.ParseJSONValue(ASt);
      try
        Result := JSONUnMarshal.Unmarshal(JSONObject) as T;
      finally
        JSONObject.Free;
      end;
    end;

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 10/05/2011, 15h59
  2. [C++] Logiciel détection fuite mémoire pour Windows
    Par Aspic dans le forum Autres éditeurs
    Réponses: 5
    Dernier message: 10/12/2010, 18h49
  3. [C++ Windows]fuite mémoire
    Par mehdi_me85 dans le forum C++
    Réponses: 1
    Dernier message: 18/06/2010, 15h29
  4. [Service Windows et WCF] Aucune purge de la mémoire
    Par slim dans le forum Windows Communication Foundation
    Réponses: 0
    Dernier message: 19/06/2009, 14h22
  5. Fuites mémoire windows
    Par J_Lennon dans le forum Général Python
    Réponses: 8
    Dernier message: 13/07/2007, 17h00

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