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

Langage Delphi Discussion :

RAM non utilisée


Sujet :

Langage Delphi

  1. #21
    Membre expérimenté Avatar de guillemouze
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    876
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2004
    Messages : 876
    Points : 1 448
    Points
    1 448
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    si tu alloues tout d'un coup oui,

    sinon tu réalloues à chaque fois un bloc plus grand que le précédent qui ne tient donc pas dans la place libérée.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // a = alloué, "-" est libéré
    [aaa]
    [---][aaaaaa]
    [---------][aaaaaaaaa]
    [------------------][aaaaaaaaaaaa]
     
    //au lieu d'avoir
    [aaa.........]
    [aaaaaa......]
    [aaaaaaaaa...]
    [aaaaaaaaaaaa]
    Ok, donc pour les performances, c'est moyen, mais la mémoire de la chaîne précédente est tout de même libérée. Mais selon ce que dit Franck, ce n'est pas forcement le cas. Dans ce cas, dans quelles mesures la mémoire reste non rendue au système ? Il va sérieusement falloir que je me penche sur la question du rapport entre la libération dans l'exe et la mémoire effectivement désallouée.

  2. #22
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

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

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Citation Envoyé par guillemouze Voir le message
    Ah ouais ?!
    Je croyais que les GetMem géraient en direct la memoire Mais bon, crois-tu que les blocs soient si gros que, malgré les libérations, la mémoire ne soit pas désallouée ? A moins que, au pire des cas, il reste dans chaque bloque réellement alloué au système, il reste un objet qui lock sa libération ! Il y a peut être un processus de remappage de la mémoire pour essayer de libérer des blocks. Si tu as plus de précisions, ca m’intéresse, sinon, je creuserai un peu plus la question de mon coté
    Et non, GetMem n'est pas du tout en direct sur la mémoire...
    Ouvre les sources de l'unité System. Tu as une variable globale (privée) MemoryManager qui est une structure qui contient des pointeurs vers les routines d'allocation du gestionnaire mémoire.
    Tu peux remplacer le gestionnaire mémoire par défaut par le tient en utilisant SetMemoryManager.

    La gestion de la mémoire est une tâche très complexe. C'est d'ailleurs parmi les opérations les plus complexes que réalise une application.
    Allouer de la mémoire pour le système d'exploitation est très compliqué. Il faut gérer la protection pages mémoires d'un processus à l'autre. Il faut gérer la pagination, la virtuallisation (les pointeurs utilisés dans un processus ne contiennent pas des adresses de la mémoire physique)...
    Allouer un bloc mémoire auprès de Windows est une opération très très lente. C'est inenvisageable avec les applis en POO moderne, qui passent leur temps à faire des allocations mémoires de très petites taille.
    C'est pourquoi tu es obligé d'allouer des gros blocs et de les redécouper en interne pour les besoins de l'appli.

    Comme tu l'as dit, s'il reste un objet qui utilise un bloc mémoire plus important, le bloc ne peut pas être rendu à Windows... C'est le problème de la fragmentation. Dans FastMM, un bloc médium représente entre 3 Ko et quelques centaines de Ko. Il suffit d'une seule allocation pour interdire de libérer le bloc.
    De plus, si le bloc est recyclé pour servir les smallblocks, il suffit d'une chaîne de caractère de 1 octets pour vérouiller le bloc complet en mémoire...

    Je suis sous BDS2006. Depuis BDS2006, FastMM est le gestionnaire de mémoire par défaut. Tu peux utiliser GetMemoryManagerState pour obtenir un état des lieux assez précis de l'utilisation de la mémoire faite par l'application.
    Je l'utilise pour faire un calcul précis de la consommation mémoire de mon appli en production, et je trace et enregistre cette consommation en permanence en production.
    Je constate très clairement un taux "d'efficacité" du gestionnaire mémoire qui varie de 30% à 80%. C'est à dire que la mémoire réellement allouée par l'application (la somme des GetMem) représente 30% à 80% de la mémoire que FastMM s'est réservé auprès de Windows...
    Sur les graph que je trace à partir de ces mesures, on voit très clairement la mémoire allouée qui varient très rapidement au rythme des Create et des Free.
    Par contre la mémoire réservée par FastMM (et la mémoire consommée auprès de Windows) varient quasiment pas. De temps en temps, le processus va consommer un peu plus de mémoire. FastMM va se réserver quelques blocs médiums en plus... et lorsque cette mémoire est libérée dans l'application... et bien FastMM la garde qu'en même pour lui...
    Il me fait le coup trois ou quatre fois dans la journée.

    Sinon non, il n'y a pas de processus de remappage des blocs en Win32. FastMM utilise trois stratégies différentes de gestion de la mémoire :
    - Les petites allocations : Il utilise des pools de blocs mémoire préalloués de taille fixe. Les petits blocs sont généralement allouer/libérés très fréquemment. Leur durée de vie est assez réduite. La gestion sous cette forme permet de limiter la fragmentation de la mémoire et facilite la réutilisation des trous...
    - Les allocations moyennes : C'est la gestion classique de la mémoire. FastMM alloue des blocs mémoires de quelques centaines de Ko auprès de Windows. Puis le bloc et découpé en gérant des listes chaînées de blocs alloués et de blocs libres. Le principe étant d'essayer de boucher les trous lors des allocations successives.
    - Les allocations de grandes tailles : Il est rare qu'une appli passe son temps à allouer des blocs mémoire d'1 Mo ou plus (d'un seul bloc). Dans ce cas, FastMM alloue la mémoire directement auprès de Windows.

    Par contre, en .Net, tu as le garbage collector qui gère la mémoire totalement différement : Une appli ne peut faire que des allocations, jamais de désallocation. Donc les allocations sont gérées comme une pile. Chaque fois qu'il faut allouer de la mémoire, il suffit de la prendre immédiatement après le dernier bloc alloué. Les allocations sont ainsi quasiment instantanées.
    Lorsque la collecte se déclenche, le GC identifie une liste de trous, puis il compacte la mémoire en déplacant les objets alloués pour qu'il n'y ait aucun trou.
    De cette façon, avec le GC, la mémoire ne se fragmente pas.

  3. #23
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

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

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Un petit exercice pour le fun :

    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
    24
    25
    26
    27
    28
    29
     
    var
      lst : TStrings;
      i : integer;
      GUID : TGUID;
    begin
      lst := TStringList.Create;
      ShowMessage('Etape 0'); // 3016 Ko.
      for i := 0 to 10000000 do
      begin
        CreateGUID(GUID);
        lst.Add(GUIDToString(GUID));
      end;
      ShowMessage('Etape 1');  // 1 026 948 Ko
      for i := 0 to 10000000 do
      begin
        if i mod 10<>0
        then lst[i] := '';
      end;
      ShowMessage('Etape 2');   // 1 026 940 Ko
      for i := 0 to 10000000 do
      begin
        if i mod 10=0
        then lst[i] := '';
      end;
      ShowMessage('Etape 3'); // 86 880 Ko
      lst.Free;
      ShowMessage('Etape 4'); // 5 624 Ko.
    end;
    L'appli démarre. Lorsque le message Etape 0 s'affiche, elle consomme 3 Mo auprès de Windows.
    On alloue 10 000 000 de chaînes de caractères (je crée des GUID convertis en chaînes pour avoir des chaînes alléatoires différentes).

    A l'étape 1, l'appli consomme 1 Go, c'est le résultat de toutes les allocations précédentes.

    Je libère ensuite 90% des chaînes allouées. On devrait s'attendre à ce que la mémoire soit immédiatement rendue, et donc libérer 90% de la mémoire utilisée.

    Mais lorsqu'on arrive à l'étape 2 (90% des chaînes libérées), l'appli consomme toujours 1 Go de mémoire.

    Je libère alors les 10% de chaînes qui n'ont pas été détruite lors du passage précédent.

    Cette fois à l'étape 3, la consommation mémoire tombe à 86 Mo. On vérifie ainsi que définir les chaînes à vide dans la stringlist libère bien la mémoire.
    Cependant, on n'a récupéré que 90% de la mémoire.

    Lorsqu'on détruit la TStringList, qui n'est pourtant qu'un tableau de 10 000 000 de pointeur à nil, on récupère 80 Mo (alors qu'on aurait dû en récupérer que 40 Mo).

    A la fin du code, on est revenu à la situation initiale. Pourtant l'appli consomme qu'en même 5 Mo au lieu des 3 Mo du départ, sans le moindre memory leak !

    Maintenant si on fait :
    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
    24
    25
    26
    27
    28
    29
     
    var
      lst : TStrings;
      i : integer;
      GUID : TGUID;
    begin
      lst := TStringList.Create;
      ShowMessage('Etape 0'); // 3016 Ko.
      for i := 0 to 10000000 do
      begin
        CreateGUID(GUID);
        lst.Add(GUIDToString(GUID));
      end;
      ShowMessage('Etape 1');  // 1 026 948 Ko
      for i := 0 to 10000000 do
      begin
        if i<9000000
        then lst[i] := '';
      end;
      ShowMessage('Etape 2');   // 180 492 Ko
      for i := 0 to 10000000 do
      begin
        if i>=9000000
        then lst[i] := '';
      end;
      ShowMessage('Etape 3'); // 86 880 Ko
      lst.Free;
      ShowMessage('Etape 4'); // 5 624 Ko.
    end;
    Cette fois ci, au lieu de libérer 90% des chaînes allouées à raison de 9 sur 10, on libère les 9 000 000 de chaînes allouées en premier.
    On remarque que cette fois, arrivé à l'étape 2, l'appli a rendu 80% de la mémoire allouée (alors que précédemment, on gardait tout !).

    On voit ainsi clairement l'effet de la fragmentation de la mémoire...

  4. #24
    Membre expérimenté Avatar de guillemouze
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    876
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Novembre 2004
    Messages : 876
    Points : 1 448
    Points
    1 448
    Par défaut
    Merci pour toutes ces infos Franck, c'est vraiment très intéressant
    Et ton exemple illustre parfaitement le principe.
    Merci beaucoup

  5. #25
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    Citation Envoyé par guillemouze Voir le message
    Ok, donc pour les performances, c'est moyen, mais la mémoire de la chaîne précédente est tout de même libérée. Mais selon ce que dit Franck, ce n'est pas forcement le cas. Dans ce cas, dans quelles mesures la mémoire reste non rendue au système ? Il va sérieusement falloir que je me penche sur la question du rapport entre la libération dans l'exe et la mémoire effectivement désallouée.
    le problème n'est pas de savoir si elle est rendue à Windows, mais si elle est disponible pour l'application.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // a = alloué, "-" est libéré
    [aaa]
    [---][aaaaaa]
    [---------][aaaaaaaaa]
    [------------------][aaaaaaaaaaaa]
     
    // l'espace est trop petit pour la nouvelle chaîne, mais suffisant pour des chaînes plus courtes
    [bbb][---------------][aaaaaaaaaaaa]
     
    // qui subissent également la fragmentation la cas échéant
    [---][bbbbbbb][---------][aaaaaaaaaaaa]
    Le but est donc de chercher à limiter la fragmentation pour exploiter au mieux la mémoire.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    [aaa.........][......]
    [aaaaaa......][......]
    [aaaaaaaaa...][bbb...]
    [aaaaaaaaaaaa][bbbbbb]
    Mais ce n'est possible que si on peut estimer la taille finale de la variable évidemment

    dans le même genre voir TList.Capacity qui permet de prévoir de la place pour les éléments à ajouter.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  6. #26
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 447
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    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 447
    Points : 24 849
    Points
    24 849
    Par défaut
    En Delphi 4 à 7, j'avais remarqué que pré-calculer la taille d'un tableau, d'une chaine, ou TList par une 1ère analyse au lieu de ré-allouer à chaque itération, typiquement dans fonction comme Explode ou FullTrim !

    En Delphi 7 + FastMM ou Delphi2006, un simple SetLength était 100 fois plus rapide !
    Mes astuces de prédiction d'allocation devenait souvent inutile et pénalisante car les volumes restaient assez faibles lorsque l'on manipule des string

    Par contre, je n'avais jamais poussé l'analyse de la libération partielle !
    Très Intéressant !
    +1 pour Paul Toth et son explication de la ré-allocation
    +1 pour Franck SORIANO et son explication de la libération partielle !

    Négrier, j'ignore quel est ton code !

    Effectivement, conserver la trace en mémoire est extrêmement coûteux comme dans un TMemo\TRichEdit, perso, je les vidais arrivé à 10000 lignes, cela ne servait que pour l'affichage, le vrai log étant dans une table de la DB ou dans un fichier
    Idem pour OutputDebugString qui consomme de la mémoire du côté du déboggueur Delphi

    Je crois que tu avoir un gros travail de refactoring de ton code, soit pour trouver l'éventuelle fuite mémoire soit si c'est la nature même de ton code de gérer un recycleur d'objet comme évoqué dans ce sujet
    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

Discussions similaires

  1. [TUNING] pb non utilisation de l'index
    Par ruthene dans le forum Oracle
    Réponses: 10
    Dernier message: 13/04/2006, 17h02
  2. Javascript : non utilisation du CSS
    Par lafracas dans le forum Mise en page CSS
    Réponses: 1
    Dernier message: 12/04/2006, 09h49
  3. [SERVICE INTERACTIF] Form non utilisable apres logoff/logon
    Par Spart64 dans le forum API, COM et SDKs
    Réponses: 4
    Dernier message: 05/03/2006, 13h32
  4. Installation SP2 + RAM non prise en compte
    Par laure_belette dans le forum Windows XP
    Réponses: 3
    Dernier message: 13/10/2005, 12h46
  5. Réponses: 1
    Dernier message: 28/04/2004, 19h36

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