web-dev-qa-db-fra.com

Erreur système. Code: 8. Pas assez de mémoire disponible pour traiter cette commande

Nous avons quelques applications Win32 (codées dans Delphi 2006) où l'utilisateur reçoit parfois un message d'erreur indiquant "Erreur système. Code: 8. Le stockage disponible est insuffisant pour traiter cette commande.".

Depuis la pile, il semble que ce soit toujours le cas lors de l'appel CreateWnd

Main ($1edc):
004146cc +070 app.exe SysUtils               RaiseLastOSError
00414655 +005 app.exe SysUtils               RaiseLastOSError
004ce44c +130 app.exe Controls               TWinControl.CreateWnd
00535a72 +022 app.exe cxControls             TcxControl.CreateWnd
004ce82a +016 app.exe Controls               TWinControl.CreateHandle
00553d21 +005 app.exe cxContainer            TcxContainer.CreateHandle
00586ef1 +005 app.exe cxEdit                 TcxCustomEdit.CreateHandle
005c331d +005 app.exe cxDropDownEdit         TcxCustomDropDownEdit.CreateHandle
004ceaf0 +074 app.exe Controls               TWinControl.UpdateShowing
004ceb1e +0a2 app.exe Controls               TWinControl.UpdateShowing
004cebdc +03c app.exe Controls               TWinControl.UpdateControlState
004d118a +026 app.exe Controls               TWinControl.CMVisibleChanged
004cb713 +2bb app.exe Controls               TControl.WndProc
004cf569 +499 app.exe Controls               TWinControl.WndProc
004b727d +4c1 app.exe Forms                  TCustomForm.WndProc
004cb3a0 +024 app.exe Controls               TControl.Perform
004c9f6a +026 app.exe Controls               TControl.SetVisible
004b6c46 +03a app.exe Forms                  TCustomForm.SetVisible
004baf1b +007 app.exe Forms                  TCustomForm.Show
004bb151 +14d app.exe Forms                  TCustomForm.ShowModal
007869c7 +0d3 app.exe UfrmPrice      770 +19 TfrmPrice.EditPrice
0078655d +009 app.exe UfrmPrice      628  +0 TfrmPrice.actNewBidExecute
00431ce7 +00f app.exe Classes                TBasicAction.Execute
004c2cb5 +031 app.exe ActnList               TContainedAction.Execute
004c397c +050 app.exe ActnList               TCustomAction.Execute
00431bb3 +013 app.exe Classes                TBasicActionLink.Execute
004af384 +090 app.exe Menus                  TMenuItem.Click
004b059f +013 app.exe Menus                  TMenu.DispatchCommand
004b16fe +082 app.exe Menus                  TPopupList.WndProc
004b164d +01d app.exe Menus                  TPopupList.MainWndProc
004329a8 +014 app.exe Classes                StdWndProc
7e4196b2 +00a USER32.dll                     DispatchMessageA
004bea60 +0fc app.exe Forms                  TApplication.ProcessMessage
004bea9a +00a app.exe Forms                  TApplication.HandleMessage
004becba +096 app.exe Forms                  TApplication.Run
008482c5 +215 app.exe AppName        129 +42 initialization

Je n'ai jamais été en mesure de cerner les causes de ceci et, comme cela se produit assez rarement, je ne suis pas inquiet, mais j'aimerais savoir ce qui les cause et, espérons-le, le corriger ...

EDIT: Full Stacktrace

EDIT 2: Plus d'infos ... Le client qui a vécu cela aujourd'hui a mon application installée depuis environ 4 mois et s'exécute sur son PC 8 heures par jour. Le problème est seulement apparu aujourd'hui et a continué à réapparaître même s'il a tué mon application et l'a redémarré. Aucune des autres applications de son système ne s'est comportée étrangement. Après un redémarrage, le problème disparaît complètement. Est-ce que cela indique la pénurie de tas mentionnée par Steve?

EDIT 3: Intéressant billet de blog msdn ici et ici sur le sujet du segment de bureau. Bien que je ne sois pas sûr que ce soit la cause du problème, cela semble certainement probable.

36
Marius

Si votre programme utilise beaucoup de ressources Windows, cela pourrait être une pénurie de ressources.

Il existe une entrée de registre qui peut être augmentée pour augmenter la taille de segment de mémoire pour XP. Pour Vista, Microsoft définit déjà la valeur par défaut plus élevée. Je recommande de changer le défaut 3072 à au moins 8192.

Ces informations sont documentées dans la base de connaissances MS (ou recherchez «Mémoire insuffisante»). Vous trouverez plus de détails concernant les valeurs de paramètre dans l'article KB184802 .

Je vous suggère de lire l'article de la base de connaissances, mais les informations de base sur le changement sont les suivantes: 

  1. Exécutez l'Éditeur du Registre (REGEDT32.EXE). 

  2. Dans la sous-arborescence HKEY_ LOCAL_MACHINE, accédez à la clé suivante: 

    \System\CurrentControlSet\Control\Session Manager\SubSystem
    
  3. Sur le côté droit de l'écran, double-cliquez sur la clé:

    windows 
    
  4. Dans la fenêtre contextuelle, vous verrez un très long champ sélectionné. Déplacez le curseur vers le début de la chaîne en recherchant ceci (les valeurs peuvent varier): 

    SharedSection=1024,3072,512
    
  5. SharedSection spécifie les segments de mémoire système et de bureau au format suivant: SharedSection=xxxx,yyyy,zzzxxxx définit la taille maximale du segment de mémoire système (en kilo-octets), yyyy définit la taille du segment de mémoire par bureau et zzz définit la taille du segment de une station de fenêtre "non interactive".

  6. Modifiez SEULEMENT la valeur yyyy en 8192 (ou plus) et appuyez sur OK. 

  7. Quittez l'Éditeur du Registre et redémarrez le PC pour que la modification soit prise en compte. 

Bonne chance.

27
Steve Black

En réalité, c'est un problème avec la table ATOM. J'ai signalé ce problème à Embarcadero car il me cause beaucoup de chagrin.

Si vous surveillez la table des atomes globaux, vous constaterez que les applications Delphi perdent des atomes, laissant l'identifiant de votre application sans le supprimer de la mémoire:

Vous verrez des tonnes d'articles suivants:

**Delphi000003B4*

*Controlofs0040000000009C0**

En principe, comme vous ne pouvez pas enregistrer plus de 0xFFFF différents identifiants de messages Windows dès que vous en demandez un autre, le système renvoie "Erreur système. Code: 8. Le stockage disponible est insuffisant pour traiter cette commande" . Ensuite, vous ne serez pas en mesure de démarrer une application qui crée une fenêtre.

Un autre problème a été signalé à Embarcadero QC Central.

Ce problème se présente sous Windows 7/Windows Server 2008. Le fait que sur Windows Server 2003 et avant son exécution est dû à une implémentation incorrecte, qui recycle les ATOM une fois que leur index est entouré d'un maximum de 16384 unités.

N'hésitez pas à utiliser mon Global Atom Monitor pour vérifier si vos applications Delphi ont des fuites d'atomes ou non.

Pour résoudre ce problème, vous aurez besoin d'un correctif d'Embarcadero.

30
Jordi Corbilla

Je cherche depuis 2 ans et grâce à Jordi Corbilla répond je l’ai enfin compris!

En quelques mots: La source Delphi a des bugs qui vous causent ce problème! 

Comprenons ce qui se passe:

Windows a une zone de mémoire appelée "Table Atom", qui sert aux applications se communiquer ( voir plus ).

En outre, Windows possède une autre "zone de mémoire", appelée "Système de messagerie Windows", qui a la même fonction ( voir plus ).

Ces deux zones de mémoire ont chacune "16k slots". Dans le premier, il est possible de SUPPRIMER un atome, en utilisant l'API Windows suivante:

GlobalDeleteAtom // for removing an atom added by "GlobalAddAtom"

Dans la seconde "zone", nous ne faisons que IMPOSSIBLE DE RETIRER!

La fonction RegisterWindowMessage est généralement utilisée pour enregistrer messages pour la communication entre deux applications coopérantes. Si deux applications différentes enregistrent la même chaîne de message, le les applications renvoient la même valeur de message. Le message reste enregistré jusqu'à la fin de la session.

Les applications compilées Delphi (au moins par D7) placent un enregistrement dans "Zone de messagerie" et quelques autres enregistrements dans "Table Atom" À chaque fois qu'ils sont démarrés. L'application tente de les supprimer lors de la fermeture de l'application, mais j'ai trouvé beaucoup (et beaucoup) de "fuites atomiques", même après la fermeture de l'application.

À ce stade, vous pouvez voir que si vous avez un serveur qui lance des milliers d'applications par jour, vous devriez probablement atteindre bientôt la limite de 16k, et le problème commence! La solution à ce stade? Rien qu'un redémarrage simple.

Alors, que pouvons-nous faire? Eh bien, mon ami, je suis désolé de vous le dire, mais nous devons corriger le code source de Delphi et recompiler toutes les applications.

Tout d’abord, ouvrez le fichier Controls.pas et remplacez la ligne suivante:

RM_GetObjectInstance := RegisterWindowMessage(PChar(ControlAtomString));

pour:

RM_GetObjectInstance := RegisterWindowMessage('RM_GetObjectInstance');

puis recompilez les packages Delphi et vos applications.

Comme j'ai trouvé des fuites d'atomes même après la fermeture de l'application, j'ai créé une application qui permet à Garbage de collecter tous les atomes laissés. Il ne fait que lancer le code suivant toutes les heures:

procedure GarbageCollectAtoms;
var i, len : integer;
    cstrAtomName: array [0 .. 1024] of char;
    AtomName, Value, procName: string;
    ProcID,lastError : cardinal;
    countDelphiProcs, countActiveProcs, countRemovedProcs, countCantRemoveProcs, countUnknownProcs : integer;

    // gets program's name from process' handle
    function getProcessFileName(Handle: THandle): string;
    begin
      Result := '';
      { not used anymore
      try
        SetLength(Result, MAX_PATH);
        if GetModuleFileNameEx(Handle, 0, PChar(Result), MAX_PATH) > 0 then
          SetLength(Result, StrLen(PChar(Result)))
        else
          Result := '';
        except
      end;
      }
    end;

    // gets the last 8 digits from the given atomname and try to convert them to and integer
    function getProcessIdFromAtomName(name:string):cardinal;
    var l : integer;
    begin
      result := 0;
      l := Length(name);
      if (l > 8) then
      begin
        try
          result := StrToInt64('$' + copy(name,l-7,8));
          except
            // Ops! That should be an integer, but it's not!
            // So this was no created by a 'delphi' application and we must return 0, indicating that we could not obtain the process id from atom name.
            result := 0;
        end;
      end;
    end;

    // checks if the given procID is running
    // results: -1: we could not get information about the process, so we can't determine if is active or not
    //           0: the process is not active
    //           1: the process is active
    function isProcessIdActive(id: cardinal; var processName: string):integer;
    var Handle_ID: THandle;
    begin
      result := -1;
      try
        Handle_ID := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, id);
        if (Handle_ID = 0) then
        begin
          result := 0;
        end
        else
        begin
          result := 1;
          // get program's name
          processName := getProcessFileName(Handle_ID);
          CloseHandle(Handle_ID);
        end;
        except
          result := -1;
      end;
    end;

    procedure Log(msg:string);
    begin
      // Memo1.Lines.Add(msg);
    end;


begin

  // initialize the counters
  countDelphiProcs := 0;
  countActiveProcs := 0;
  countRemovedProcs := 0;
  countUnknownProcs := 0;

  // register some log
  Log('');
  Log('');
  Log('Searching Global Atom Table...');

  for i := $C000 to $FFFF do
  begin
    len := GlobalGetAtomName(i, cstrAtomName, 1024);
    if len > 0 then
    begin
      AtomName := StrPas(cstrAtomName);
      SetLength(AtomName, len);
      Value := AtomName;
      // if the atom was created by a 'delphi application', it should start with some of strings below
      if (pos('Delphi',Value) = 1) or
         (pos('ControlOfs',Value) = 1) or
         (pos('WndProcPtr',Value) = 1) or
         (pos('DlgInstancePtr',Value) = 1) then 
      begin
        // extract the process id that created the atom (the ProcID are the last 8 digits from atomname)
        ProcID := getProcessIdFromAtomName(value);
        if (ProcId > 0) then
        begin
          // that's a delphi process
          inc(countDelphiProcs);
          // register some log
          Log('');
          Log('AtomName: ' + value + ' - ProcID: ' + inttostr(ProcId) + ' - Atom Nº: ' + inttostr(i));
          case (isProcessIdActive(ProcID, procName)) of
            0: // process is not active
            begin
              // remove atom from atom table
              SetLastError(ERROR_SUCCESS);
              GlobalDeleteAtom(i);
              lastError := GetLastError();
              if lastError = ERROR_SUCCESS then
              begin
                // ok, the atom was removed with sucess
                inc(countRemovedProcs);
                // register some log
                Log('- LEAK! Atom was removed from Global Atom Table because ProcID is not active anymore!');
              end
              else
              begin
                // ops, the atom could not be removed
                inc(countCantRemoveProcs);
                // register some log
                Log('- Atom was not removed from Global Atom Table because function "GlobalDeleteAtom" has failed! Reason: ' + SysErrorMessage(lastError));
              end;
            end;
            1: // process is active
            begin
              inc(countActiveProcs);
              // register some log
              Log('- Process is active! Program: ' + procName);
            end;
            -1: // could not get information about process
            begin
              inc(countUnknownProcs);
              // register some log
              Log('- Could not get information about the process and the Atom will not be removed!');
            end;
          end;
        end;
      end;
    end;
  end;
  Log('');
  Log('Scan complete:');
  Log('- Delphi Processes: ' + IntTostr(countDelphiProcs) );
  Log('  - Active: ' + IntTostr(countActiveProcs) );
  Log('  - Removed: ' + IntTostr(countRemovedProcs) );
  Log('  - Not Removed: ' + IntTostr(countCantRemoveProcs) );
  Log('  - Unknown: ' + IntTostr(countUnknownProcs) );

  TotalAtomsRemovidos := TotalAtomsRemovidos + countRemovedProcs;

end;

(Ce code ci-dessus était basé sur ce code )

Après cela, je n'ai plus jamais cette erreur f **!

Mise à jour tardive:

En outre, c’est la source de cette erreur: Erreur d’application: adresse de défaillance 0x00012afb

21
Christian

Vous pouvez utiliser Desktop Heap Monitor de Microsoft pour afficher des statistiques sur le tas (utilisez%, etc.). Il est disponible à l'adresse suivante: 

http://www.Microsoft.com/downloads/details.aspx?familyid=5cfc9b74-97aa-4510-b4b9-b2dc98c8ed8b&displaylang=en

2
HS1

J'ai remarqué cette erreur (Erreur système. Code: 8. Stockage insuffisant ...) lors de l'utilisation d'un code Twain, cela se passait sur mon ordinateur et non sur celui de mon collègue, et la seule différence réelle entre nos machines est que j'utilise le écran d'ordinateur portable en tant que deuxième moniteur, mes dimensions totales de bureau sont donc plus grandes.

J'ai trouvé la documentation du problème évoqué ci-dessus par Steve Black, mais j'ai trouvé un moyen (qui corrige au moins l'erreur sur ma machine) qui ne nécessite pas de modification du registre:

L'ancien code utilisait 

  DC := GetDC(Owner.VirtualWindow);
  // ...
  ReleaseDC(Owner.VirtualWindow, DC);

et j'ai trouvé que le remplacer par cela m'a débarrassé de mes erreurs

  DC := CreateCompatibleDC(Owner.VirtualWindow);
  // ...
  DeleteDC(DC);


Je ne sais pas si cela a un rapport avec votre problème, mais cela pourrait être utile pour d'autres à l'avenir.

2
jasonpenny

Il pourrait y avoir des bugs dans le compilateur, il y a fort à parier que c'est quelque chose dans votre application qui pose problème. Se pourrait-il que votre application présente des fuites de poignées de fenêtre ou d'un autre objet d'interface graphique tel que des stylos/pinceaux? Cela pourrait être une cause.

0
Aikislave

Pour moi, c’était juste un tas de TJPEGImages qui décompressaient en diaporama et qui manquaient finalement de mémoire.

0
Anton Duzenko