Quel est l’intérêt d’utiliser GC.AddMemoryPressure avec une ressource non gérée?

J’ai lu ce problème sur MSDN et sur le CLR via c #.

Imaginez que nous ayons un HBITMAP non géré de 2 Mo alloué et un bitmap géré de 8 octets pointant dessus. À quoi servirait-il d’indiquer cela au gouvernement avec AddMemoryPressure s’il ne peut jamais rien créer à propos de l’object, car il est alloué en tant que ressource non gérée, donc non susceptible d’être détruit par une récupération de place?

Le but de AddMemoryPressure est d’indiquer au ramasse-miettes qu’une grande quantité de mémoire est allouée avec cet object. S’il n’est pas géré, le ramasse-miettes ne le sait pas. seule la partie gérée. La partie gérée étant relativement petite, le CPG peut la laisser passer plusieurs fois pour la récupération de place, ce qui gaspille essentiellement de la mémoire qui pourrait devoir être libérée.

Oui, vous devez toujours allouer et désallouer manuellement la mémoire non gérée. Vous ne pouvez pas sortir de ça. Vous utilisez simplement AddMemoryPressure pour vous assurer que le GC sait qu’il existe.

Modifier:

Eh bien, au cas où, je pourrais le faire, mais cela ne ferait aucune différence, car le GC ne pourrait rien faire à propos de mon type, si je le comprends bien: 1) je déclarerais ma variable , 8 octets gérés, 2mb d’octets non gérés. Je l’utilisais alors, appelais dispose, de sorte que la mémoire non gérée est libérée. À l’heure actuelle, il n’occupera que 8 octets. Maintenant, à mes yeux, avoir appelé les débuts AddMemoryPressure et RemoveMemoryPressure à la fin n’aurait rien fait de différent. Qu’est-ce que je me trompe? Désolé d’être si énervant à ce sujet. – Jorge Branco

Je pense que je vois votre problème.

Oui, si vous pouvez garantir que vous appelez toujours Dispose , alors vous n’avez pas besoin de vous embêter avec AddMemoryPressure et RemoveMemoryPressure. Il n’y a pas d’équivalence, car la référence existe toujours et le type ne serait jamais collecté.

Cela dit, vous souhaitez toujours utiliser AddMemoryPressure et RemoveMemoryPressure, par souci d’exhaustivité. Et si, par exemple, l’utilisateur de votre classe avait oublié d’appeler Dispose? Dans ce cas, si vous avez correctement implémenté le modèle de mise au rebut, vous récupérerez vos octets non gérés lors de la finalisation, c’est-à-dire lorsque l’object géré sera collecté. Dans ce cas, vous voulez que la pression de mémoire rest toujours active, de sorte que l’object a plus de chances d’être récupéré.

Il est fourni pour que le GC connaisse le coût réel de l’object lors de la collecte. Si l’object est réellement plus grand que la taille gérée ne l’indique, il peut être candidat à une collecte rapide.

Brad Abrams écrit à ce sujet est assez clair:

Considérons une classe qui a une très petite taille d’instance gérée, mais qui contient un pointeur sur un très gros bloc de mémoire non gérée. Même si personne ne fait référence à l’instance gérée, celle-ci peut restr active pendant un certain temps, car le GC ne voit que la taille de l’instance gérée. Nous devons donc «enseigner» au GC le coût réel de cette instance afin qu’il sache exactement quand lancer une collection pour libérer davantage de mémoire.

En d’autres termes, en supposant que les objects gérés de 8 octets se réfèrent chacun à une image non gérée de 2 Mo. Le GC peut attendre longtemps avant de collecter des centaines, voire des milliers, de petits objects gérés, car ils sont si petits. Cela signifierait que des centaines, voire des milliers, de morceaux de 2 Mo non gérés liés restront en vie, en attente de suppression. Cela pourrait devenir un énorme problème. En ajoutant 2 Mo de pression de mémoire au constructeur, vous allez faire croire à l’object géré que l’object géré n’a pas une taille de 8 octets, mais plutôt 8 octets + 2 Mo. Cela déclenchera la collecte de manière plus tôt.

N’oubliez pas l’appel Supprimer.

Bien sûr, si vous vous en débarrassez, vous n’aurez pas besoin de tout cela.

Ces méthodes permettent au moteur d’exécution d’avoir une idée de la quantité de mémoire non gérée allouée par le processus. Sans appeler ces fonctions, il peut ne pas être en mesure de voir la quantité réelle de mémoire non gérée utilisée dans le processus.

Cependant, je suis en désaccord avec les autres réponses ici concernant une association entre la mémoire à laquelle il est fait référence et un object GC particulier.

Considérer:

 var buffer = IntPtr.Zero; try { buffer = Marshal.AllocHGlobal(size); GC.AddMemoryPressure(size); // ... use buffer ... } finally { Marshal.FreeHGlobal(buffer); GC.RemoveMemoryPressure(size); } 

Le CPG n’a aucun moyen d’atsortingbuer la size à un object spécifique. Ce code pourrait même exister dans une méthode statique.

Par conséquent, j’affirme que la déclaration selon laquelle nous devons «enseigner» au GC le coût réel de cette instance afin qu’elle sache exactement quand lancer une collection pour libérer de la mémoire au cours du processus est incorrecte et trompeuse.

Au lieu de cela, cette méthode peut amener le CPG à collecter plus tôt qu’elle ne le ferait autrement, dans le but d’éviter de manquer de mémoire.