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. #1
    Membre régulier
    RAM non utilisée
    Bonjour,

    Résumé du problème :

    Je suis confronté a deux problèmes mémoire sur une application delphi.
    1°) une dérive mémoire au cours de l'exécution (je me charge de ce pb)
    2°) l'incapacité, pour mon application à utiliser plus de 230 Mo. (c'est pour ce pb que je sollicite votre analyse)

    Mon contexte informatique est le suivant :

    Windows XP pro SP3 - 2Go de RAM
    Delphi7 Entreprise.

    Description du problème

    Dans mon application j'ai un code de calcul qui dérive en mémoire. Pour bien mettre en évidence le problème j'ai encapsulé ce code dans une boucle de 1000 itérations.

    Situation en début de boucle :
    le processus occupe environ 190 Mo
    environ 670 Mo sont occupés sur le poste

    Situation aprè environ 500 boucles :
    le processus occupe environ 220 Mo
    environ 700 Mo sont occupés sur le poste

    J'obtiens "Runtime erreur, mémoire insuffisante"

    En lançant plusieurs fois la boucle j'obtiens toujours le même résultat, c'est à dire un plantage après environ 500 boucles et 220 Mo occupés par le processus.

    J'ai essayé de changer $MINSTACKSIZE et $MAXSTACKSIZE mais le résultat reste le même sinon très voisin.

    Ma question (enfin ) existe-t-il une limitation intrinsèque aux exécutables produits avec delphi7 ? S'agit-il d'une limitation de XP ?

    Merci de votre (longue) attention

  2. #2
    Expert confirmé
    Il va être assez difficile de répondre à ton problème sans voir le code et la gestion mémoire effectuée...

    Voici qu'en même quelque éléments :


    Ma question (enfin ) existe-t-il une limitation intrinsèque aux exécutables produits avec delphi7 ? S'agit-il d'une limitation de XP ?
    Oui, en Win32 un processus ne peut pas utiliser plus de 2 Go de RAM, et ce quel que soit la mémoire disponible sur la machine.


    J'obtiens "Runtime erreur, mémoire insuffisante"
    Logiquement, ça signifie que c'est la taille maximal du tas (heap) qui a été atteinte.


    J'ai essayé de changer $MINSTACKSIZE et $MAXSTACKSIZE mais le résultat reste le même sinon très voisin.
    Ces directives servent à déterminer la taille de la pile. A priori, ton problème vient de l'utilisation du tas, donc ne devrait être influencé par ces directives.


    Situation en début de boucle :
    le processus occupe environ 190 Mo
    environ 670 Mo sont occupés sur le poste
    Je ne pense pas qu'il s'agisse d'un bon indicateur.
    Si tu n'as rien d'autre, prend le gestionnaire des tâches Windows et regarde la consommation mémoire du processus.
    Mais attention, par défaut, le gestionnaire des tâches affiche la taille du working-set et non pas la taille de la mémoire utilisée par le processus. Sous XP, il suffit généralement de minimiser l'application pour voir sa consommation mémoire tomber à quelques Ko dans le gestionnaire des tâches...
    Reconfigure le gestionnaire des tâches et demande lui d'afficher une colonne qui s'appelle "taille de la mémoire virtuelle".
    Logiquement, si tu as rencontré une erreur out-of-memory, la taille de la mémoire virtuelle a dû augmenter rapidement pour atteindre les 2 Go. Arrivé à 2 Go (voir moins si la machine ne dispose pas de suffisament de mémoire virtuelle), tu rencontres l'erreur.

    Malgré tout, il faut aussi savoir que contrairement à une idée répendu, il ne suffit pas de faire un Free pour que la mémoire soit rendu à Windows.
    Entre le Create/Free du code, et la mémoire de Windows se trouve le gestionnaire mémoire de Delphi.
    ce dernier alloue des gros blocs mémoires auprès de Windows et les redécoupe pour servir les allocations de l'application.
    La mémoire ne peut être rendu à Windows que lorsque toutes les allocations qui ont été effectuées pour un de ces "gros" blocs mémoire ont été libérée.
    De facto, si la mémoire se fragmente pendant le traitement, il se peut que la mémoire ne soit pas rendue à Windows, alors qu'elle est bien disponible pour servir d'autres allocations.

    Pour faire la chasse aux fuites, je te suggère d'installer FastMM4 et d'activer le mode FullDebug. Il te permettra de trouver rapidement les fuites.

  3. #3
    Membre expérimenté
    Citation Envoyé par Franck SORIANO Voir le message
    La mémoire ne peut être rendu à Windows que lorsque toutes les allocations qui ont été effectuées pour un de ces "gros" blocs mémoire ont été libérée.
    De facto, si la mémoire se fragmente pendant le traitement, il se peut que la mémoire ne soit pas rendue à Windows, alors qu'elle est bien disponible pour servir d'autres allocations.
    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é

  4. #4
    Membre régulier
    Pour connaître la taille occupée par le processus j'ai utilisé l'API "GetProcessMemoryInfo"
    Je n'ai utilisé le gestionnaire de tâches que pour connaître la mémoire restante, mais je crois devoir passer à des primitives Windows pour avoir des informations plus fiables.

    En ce qui concerne la chasse aux fuites (très dépendante de mon application) je m'y colle en parallèle. Mais ma question était plutôt relative à la non utilisation (apparente ?) d'une grosse partie de la mémoire disponible sur mon ordi (2Go) par mon application qui ne semble pas vouloir (pouvoir ?) en prendre plus de 300Mo !

  5. #5
    Membre expérimenté
    pour avoir une vue generale de la memoire utilisée par mon processus, j'utilise process explorer de sysinternals. tu vas dans les proprietes de ton process, onglet "performance graph", et tu vois la memoire utilisée

  6. #6
    Expert confirmé
    Intéressant comme outil !

    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. Écrivez dans un français correct !!

    Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise
    OpenGL 2.1 - Oracle 10g - Interbase (7 - XE) - PostgreSQL 11.6

  7. #7
    Membre expérimenté
    Citation Envoyé par Lung Voir le message
    Intéressant comme outil !

    Très. Toute la suite de SysInternals est très bien.

  8. #8
    Membre régulier
    J'ai voulu tester "SetProcessWorkingSetSize". L'image jointe montre
    le résultat obtenu (j'ai fait tourner le processus pendant la nuit).

    Le résultat est le même j'ai un arrêt avec "Mémoire insuffisante"
    après environ 450 boucles. Je ne comprends toujours pas pourquoi
    cela arrive avec au maximum 800 Mo occupés sur 2Go.


    Que pensez vous de la manipulation suivante : incrémenter la taille du tas
    pour Windows XP ? Il s'agit de modifier, dans le registre,la
    "SharedSection=1024,3072,512" en remplaçant 3072 par 8192.

  9. #9
    Expert éminent sénior
    Je te déconseille ce type de modification !
    Le message d'erreur « Mémoire insuffisante » s'affiche lorsqu'un grand nombre de programmes sont en cours d'exécution
    Dans ce sujet, Microsoft explique comment modifier SharedSection, mais ton problème n'est pas lié au nombre de programme mais à un seul programme gourmand ! Ce n'est donc pas cela !


    As-tu beaucoup d'éléments affichés à l'écran ?
    Pense à libérer les fenêtres (ainsi que de les allouer uniquement quand c'est utile)
    Pense que tu auras du mal a dépasser 1960Mo sur les 2Go théorique (limite d'un processus Windows et du MemoryManager de Delphi 32 bits)

    si ton erreur n'est pas lié à la RAM cela aurait pu être lié à une grande consommation de Handle et d'objets GDI mais 1805 et 303 reste inférieur à celui que consomme mon instance C++Builder d'aujourd'hui 1843 et 1116
    J'ai quelques programmes qui monte nettement plus haut !
    Je n'ai que 1Go sur mon poste de travail !

    Cela doit être encore autre chose !
    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 régulier
    Grace à l'outil (fantastique!) PsList (de la suite PsTools de SysInternal mentionné dans le fil de discussion) j'ai pu avoir une vision beaucoup plus satisfaisante de la mémoire consommée (mon process a pour nom "Ocap"):

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Process memory detail for SCU33900: 
     
    Name		Pid	VM	WS	Priv	Priv	Pk	Faults	NonP
    Ocap		3344	2021308	249408	241528	242956	2024425	78	278
    svchost		1116	293124	95264	37428	311612	2306326	67	242
    commu		4092	201468	54560	39112	39296	30165	27	303
    Util		3704	187424	6560	28828	28828	12205	8	120
     
    Total (40proc)	  	4472304


    Je suis donc pleinement satisfait les 2 Go de ma machine sont effectivement bien utilisés ! C'est quand même beaucoup plus satisfaisant d'avoir une vision réelle de la mémoire consommée... Il ne me reste plus qu'à corriger ce problème de dérive mémoire

    La mesure avec PsList a été faite après lancement et fin d'exécution habituelle de mon processus :

    Pas n° : 372 : Utilisation mémoire du processus courant en Kilo Octets : 242740

    Runtime Error: Mémoire insuffisante [line: 364, column: 1, file: u:\scripts\construction.dws]

  11. #11
    Expert éminent sénior
    Tu as vraiment des résultats différents entre le Gestionnaire des Taches, GetProcessMemoryInfo (WorkingSetSize et PagefileUsage), Process Explorer et PsList ?

    EDIT, la colonne VM de PsList, correspond dans Process Explorer, Properties, Onglet Performance, Group "Virtual Memory", élément "Virtual size"

    Cela correpondant dans "Performance" des outils d'administration à la courbe Processus\Taille Virtuelle

    Je n'avais jamais regardé ce chiffre, en même temps, je n'ai jamais vu ce phénomène de "mémoire insuffisante" sans le voir dans le Gestionnaire des Taches (j'ai rarement consommé autant sans le faire exprès)
    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

  12. #12
    Membre régulier
    Je n'ai pas encore regardé "process explorer". La colonne "WS" de PsList
    correspond à ce que j'obtiens avec GetProcessMemoryInfo (WorkingSetSize ).

    En revanche après l'arrêt du processus pour mémoire insuffisante le gestionnaire des tâches indique moins de 1Go consommé. Sur mon image jointe on voit qu'il affiche une VM de 232 Mo pour le processus bien loin du 2000Mo affiché par PsList.

  13. #13
    Membre régulier
    Un petit comparatif sur tous les "instruments" de mesure :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        		Gestionnaire de tâches           ProcessExplorer                              PsList                 GetMemoryUsage (Ko)
     
                    Util. Mém       Taille MV        Virtual Size   PrivateBytes   Peak WS        VM        Priv                               
     
    Avant Boucle    4234      	188116           378800  	188116  	202876        378800    188116       10936
     
    Après Boucle    5200      	725400           2036372         725400  	567156        2036372   725400       556348


    La "Virtual Size" de Process Explorer et VM" de PSList sont quand même des informations importantes....

  14. #14
    Membre régulier
    Petites nouvelles de la mémoire.

    Tout d'abord j'avais une variable string (trace) qui grossissait de 4014 caractères à chaque boucle. En la supprimant et en traçant sur fichier à la place (pour un processus pouvant tourner toute la nuit c'est même mieux) j'arrive à faire 1500 boucles au lieu de 400. Dans ce cas (sans cette variable de trace) l'augmentation de la taille du WorkingSet suit beaucoup mieux la taille de mémoire virtuelle utilisée.


    J'ai tracé également sur fichier certains paramètres récupérés à l'aide de l'API Windows : GlobalMemoryStatus. Sur l'image jointe on peut voir l'évolution de certains paramètres en fonction du nombre de boucles. Il apparaît clairement que la mémoire virtuelle disponible suit une "descente" parabolique contrairement à la mémoire physique qui diminue linéairement. Mais là, et c'est
    désormais bien clair, les 2Go de mémoire physique semblent effectivement utilisés par le processus.

    Vos expériences confirment-elles ces phénomènes ? J'imagine que sur Windows 7 je pourrais faire mes 5000 boucles sans problème...

  15. #15
    Membre expérimenté
    Je suis loin d’être un expert en mémoire, mais je pense que l'observation du problème est mal ciblée. Si je ne m'abuse, tes 2Go de RAM n'ont rien a voir dans l'histoire, ce qui te bloque, c'est la limite d'adressage du pointer limité à 2Go en 32bits. Rien ne t’empêche d'avoir 5 process de 1Go alors que tu n'as que 2Go de mémoire physique, l'OS se charge de mettre la mémoire en cache. Par contre, si tu dépasse les 2Go de mémoire virtuelle, alors tu a utilisé toutes les adresses pointables par un pointeur de 4octets, d'ou la mémoire insuffisante. Ou alors, peut-être que la limite mémoire d'un process est limité à Min(MemoirePhysique, ~2Go).

    Je ne suis pas sûr du tout de ce que j'avance, ce ne sont que des suppositions, alors n’hésitez pas a me corriger ou a confirmer mes dires.

  16. #16
    Expert éminent sénior
    Citation Envoyé par Négrier Voir le message
    Petites nouvelles de la mémoire.

    Tout d'abord j'avais une variable string (trace) qui grossissait de 4014 caractères à chaque boucle. En la supprimant et en traçant sur fichier à la place (pour un processus pouvant tourner toute la nuit c'est même mieux) j'arrive à faire 1500 boucles au lieu de 400. Dans ce cas (sans cette variable de trace) l'augmentation de la taille du WorkingSet suit beaucoup mieux la taille de mémoire virtuelle utilisée.


    J'ai tracé également sur fichier certains paramètres récupérés à l'aide de l'API Windows : GlobalMemoryStatus. Sur l'image jointe on peut voir l'évolution de certains paramètres en fonction du nombre de boucles. Il apparaît clairement que la mémoire virtuelle disponible suit une "descente" parabolique contrairement à la mémoire physique qui diminue linéairement. Mais là, et c'est
    désormais bien clair, les 2Go de mémoire physique semblent effectivement utilisés par le processus.

    Vos expériences confirment-elles ces phénomènes ? J'imagine que sur Windows 7 je pourrais faire mes 5000 boucles sans problème...
    si tu sais devoir allouer 4014 octets 5000 fois, autant en allouer 20070000 dès le départ, le gestionnaire de mémoire ne s'en portera que mieux.
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  17. #17
    Membre expérimenté
    Citation Envoyé par Paul TOTH Voir le message
    si tu sais devoir allouer 4014 octets 5000 fois, autant en allouer 20070000 dès le départ, le gestionnaire de mémoire ne s'en portera que mieux.
    De plus, ca ne fait que 20Mo, on est bien loin des 2Go

  18. #18
    Membre éprouvé
    Avec la allocation de string or tableaux dynamique en boucle tu risque la fragmentation de la mémoire... passe directement par pointer
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    PHugeString=^THugeString;
    THugeString =array[0..0]of Char;

  19. #19
    Membre régulier
    Bonsoir Guillemouse : oui effectivement il s'agit bien de "la limite d'adressage du pointer limité à 2Go en 32bits" par processus.

    Effectivement la variable trace n'est qu'un petite partie de la dérive mémoire
    mais rien qu'avec cette correction (redirection de la trace vers un fichier) je passe plus de 1500 pas au lieu de 400 ce qui m'arrange beaucoup

    La correction plus profonde de la dérive mémoire est de bien plus long terme.

    Ceci dit, de connaitre précisément la mémoire consommée (par GlobalMemoryStatus) m'a permis d'arrêter proprement le processus sans avoir une exception. Je fais un test sur le pourcentage de mémoire utilisé et j'arrête si c'est supérieur à 80%

  20. #20
    Expert éminent sénior
    Citation Envoyé par guillemouze Voir le message
    De plus, ca ne fait que 20Mo, on est bien loin des 2Go
    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]
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store