2010-02-15 6 views
3

Il y a parfois un problème de manque de mémoire lorsqu'il est fragmenté.Trouver le plus grand bloc de mémoire libre

Est-il possible de trouver le plus grand bloc de mémoire libre? J'utilise Delphi 2007 avec FastMM. Le développement sur l'application sous Windows XP sur Windows 2003.

Cordialement

EDIT: Je pourrais ajouter les informations que l'application est en cours d'exécution sur un serveur avec 32 Go de mémoire sur un ordinateur Windows Server 2003 x64. Mais l'application est une application 32 bits, donc la mémoire allouée maximum théorique pour chaque instance est de 2 Go. De nombreuses instances sont exécutées en même temps. Je ne pense pas que ce soit la mémoire physique totale qui soit trop petite. Je suppose que quand a commencé l'application a eu un espace de mémoire virtuelle de 32 bits. Cela peut alors être trop fragmenté pendant l'exécution.

J'ai également trouvé la méthode FastGetHeapStatus qui retourne un THeapStatus avec quelques champs pour la mémoire libre. Peut-être que je pourrais les utiliser.

EDIT2: J'ai trouvé ceci How to get the largest available continues memory block. Le code est C mais peut-être pourrait-il être traduit en Delphi.

+2

Petite correction: Sur les versions d'OS 64 bits obtenir la plage d'adresses complète de 4 Go pour les processus 32 bits. Le processus peut ne pas être en mesure de l'utiliser (en raison des types de données signés utilisés dans les routines de gestion de la mémoire), mais la limite est différente de celle d'un système d'exploitation 32 bits. AFAIR Delphi * a * des problèmes avec cela. – mghie

+0

Oui, Delphi utilise beaucoup de valeurs signées partout dans le RTL –

Répondre

4

C'est la traduction du code Delphi que vous vouliez:

function GetLargestFreeMemRegion(var AAddressOfLargest: pointer): LongWord; 
var 
    Si: TSystemInfo; 
    P, dwRet: LongWord; 
    Mbi: TMemoryBasicInformation; 
begin 
    Result := 0; 
    AAddressOfLargest := nil; 
    GetSystemInfo(Si); 
    P := 0; 
    while P < LongWord(Si.lpMaximumApplicationAddress) do begin 
    dwRet := VirtualQuery(pointer(P), Mbi, SizeOf(Mbi)); 
    if (dwRet > 0) and (Mbi.State and MEM_FREE <> 0) then begin 
     if Result < Mbi.RegionSize then begin 
     Result := Mbi.RegionSize; 
     AAddressOfLargest := Mbi.BaseAddress; 
     end; 
     Inc(P, Mbi.RegionSize); 
    end else 
     Inc(P, Si.dwPageSize); 
    end; 
end; 

Vous pouvez l'utiliser comme ceci:

procedure TForm1.FormCreate(Sender: TObject); 
var 
    BaseAddr: pointer; 
    MemSize: LongWord; 
begin 
    MemSize := GetLargestFreeMemRegion(BaseAddr); 
    // allocate dynamic array of this size 
    SetLength(fArrayOfBytes, MemSize - 16); 

    Caption := Format('Largest address block: %u at %p; dynamic array at %p', 
    [MemSize, BaseAddr, pointer(@fArrayOfBytes[0])]); 
end; 

Notez que je devais soustraire 16 octets de la taille maximale, probablement parce que le tableau dynamique lui-même utilise quelques octets qui ont été alloués à partir du même bloc de mémoire, l'allocation suivante était basée sur le multiple suivant de 16.

6

Non, c'est le « maxavail » dans le vieux Turbo Pascal, une caractéristique souvent demandé, mais malheureusement, il est un concept inutile dans un multi-utilisateur, environnement multi-tâches

Le heapmanager peut connaître le plus grand bloc dans la mémoire, il se maintient lui-même, mais ce sera généralement petit, puisque les gros morceaux sont directement attribués à partir de (et retournés à) des fenêtres.

Et les schémas pour tenter d'allouer progressivement des blocs plus gros échoueront car le système d'exploitation accordera des requêtes même si cela signifie un échange sur le disque (ce que vous ne voulez pas). Idem pour les astuces qui tentent d'extraire de telles valeurs avec les appels Windows API.

L'ensemble de l'environnement en mode protégé a comme base que la mémoire est partagée, et chaque application utilise seulement autant que nécessaire. Ignorer cela et prétendre que tout est toujours comme sous DOS ne produira que des plaintes massives de la part de personnes qui exécutent plusieurs applications à la fois. Si votre application dépend vraiment de cela, faites-en un paramètre de configuration (combien de mémoire allouer au démarrage pour quelque chose) avec une valeur par défaut (petite). Si c'est VRAIMENT crucial, confrontez-le à l'utilisateur pendant la configuration

On peut bien sûr toujours commencer par les tentatives heuristiques en faisant quelques appels Winapi et en supposant qu'aucune autre application ne fonctionne. Mais toujours laisser la décision finale à l'utilisateur, surtout pour les applications serveur.

3

Sur un système de mémoire virtuelle, l'espace d'adressage virtuel signifie que les pages virtuelles peuvent être mappées n'importe où. Vous n'avez pas besoin de grands blocs contigus de mémoire physique. Si vous rencontrez des problèmes de fragmentation de votre espace d'adressage virtuel, vous devrez peut-être utiliser une autre stratégie de gestion de la mémoire. Toutefois, la plupart des options nécessitent que votre code d'application soit conscient de la stratégie de gestion de la mémoire à un certain niveau. Je ne crois pas qu'il existe une solution rapide à ce problème - vous êtes probablement prêt pour une intervention chirurgicale assez importante pour le réparer. Aucune de ces options n'est simple à mettre en œuvre, vous devrez trouver celui qui est le plus susceptible de fonctionner dans votre cas particulier.

Les principales options que je peux voir sont: les allocateurs de mémoire personnalisés, quelque chose impliquant AWE (voir ci-dessous) ou la reconstruction de la stratégie d'allocation de mémoire dans l'application.

Option 1: répartiteurs mémoire personnalisée

répartiteurs de mémoire personnalisée ne sont pas rares dans les cercles C et C++. Vous pourriez être en mesure de mettre en œuvre quelque chose de similaire. Deux possibilités vous sont ouvertes:

  • Construire un allocateur de mémoire avec un mécanisme qui tente de fusionner des blocs libres adjacents en un seul bloc plus grand (vous pouvez exécuter cela comme une partie de tenter de se remettre d'une allocation de mémoire a échoué). Cela peut vous permettre de gérer la mémoire de manière transparente sans que l'application ne doive être au courant. La mise en œuvre de ce serait fastidieux et technique mais est probablement réalisable.

    Le principal avantage de cette approche est qu'elle est la seule qui ne vous obligerait pas à modifier le code d'application existant. L'inconvénient est qu'il n'est pas garanti de travailler; il est toujours possible que l'opération de fusion échoue à consolider un bloc de mémoire suffisamment grand pour satisfaire la requête. L'opération de fusion peut également provoquer des pauses significatives dans la réponse de l'application pendant son exécution.

  • Vous devrez peut-être créer votre application de manière à compacter la structure de données. Cela vous obligerait à gérer des poignées qui prennent en charge les objets déplacés, c'est-à-dire un mécanisme d'indirection double. Je suppose qu'il y a probablement un ou un petit nombre de structures de données différentes qui causent ce problème de fragmentation, donc il peut être possible de localiser n'importe quel travail de ré-architecture au sein de votre application.

Option 2:

PAE

Windows ne les installations de soutien pour manipuler directement la MMU, et il y a deux possibilités où cela pourrait appliquer à votre application. Cela nécessiterait certainement un support architectural explicite de votre application, mais offre la possibilité d'utiliser un pool de mémoire de plus de 2 Go.

Sur les versions serveur de Windows, regardez dans PAE, qui est pris en charge par API's qui vous permet de manipuler manuellement la MMU du système et de mapper des blocs de mémoire. Cette pourrait être utile pour vous dans l'une des deux façons

  • Vous pouvez construire le gestionnaire de la structure de données d'une manière qui utilise ce mécanisme comme une partie intégrante de la gestion des données.

  • Si vous pouvez adapter les éléments de votre structure de données aux limites de la page, vous pourrez peut-être l'utiliser pour consolider la mémoire.

Cependant, cette approche vous avez besoin de repenser votre application de sorte que les références de l'objet d'informations suffisantes pour gérer le processus d'échange en explicite (peut-être une sorte de gestionnaire superposition d'un mécanisme de proxy pour les objets étant référencé à travers ce système). Cela signifie que toute solution impliquant PAE n'est pas un remplacement direct pour FastMM - vous devrez modifier l'application pour soutenir explicitement PAE.Toutefois, un mécanisme de proxy de ce type peut signifier que ce sous-système peut être relativement transparent pour les clients. Mis à part les frais généraux de gestion de l'indirection et des recouvrements (qui peuvent ou non être un problème important), les procurations pourraient être pratiquement indiscernables de l'API d'origine. Ce type d'approche fonctionnerait mieux pour un nombre relativement petit de grands objets lourds avec une interconnexion minimale - l'application canonique pour cela est la mise en cache de disque. Les proxies doivent rester dans un emplacement fixe en mémoire, les objets les plus volumineux étant accessibles via le mécanisme de superposition. YMMV.

Option 3: Correction du problème à la source

stratégie d'allocation Une possibilité est que votre objet peut être optimisé à partir du code d'application (peut-être à partir de pools d'objets alloués en vrac puis gérés dans l'application). Cela peut vous permettre de gérer la fragmentation de la mémoire à partir de votre application sans tenter de réécrire le gestionnaire de mémoire.

Encore une fois, cette approche signifie que vous devrez reconstruire des parties de votre application, et l'applicabilité de l'approche dépend vraiment de la nature de votre application. Vous seul pouvez être le juge de comment cela pourrait fonctionner.

+0

1) La fusion de blocs adjacents ne cède pas beaucoup si les blocs sont inamovibles. Un memmanager propre peut être une solution désespérée, mais vous devez probablement ajouter des connaissances sur le modèle d'accès des applications au gestionnaire pour faire beaucoup mieux que fastmm. 2) PAE n'aide pas parce que le compilateur Delphi est 32, pas 64 bits, et suppose des pointeurs lineair 32 bits. Les schémas comme ne pas stocker les 4 bits inférieurs vont casser l'arithmétique du pointeur. Factoriser des bits intensifs en mémoire sur les DLL FPC 64 bits est alors une solution plus sûre. J'irais pour l'option 3 en combinaison avec l'allocation du plus grand bloc nécessaire au démarrage –

+0

Merci pour la réponse. Nous ne construisons pas notre propre gestionnaire de mémoire, j'en suis sûr. Ce que nous espérons vraiment, c'est un compilateur Delphi 64 bits car la mémoire est bon marché aujourd'hui. En attendant, j'ai ajouté un memorymeter dans l'application afin que l'utilisateur puisse observer la situation. En cas de EOutOfMemory je suggère un redémarrage ... :) –

Questions connexes