2010-10-13 5 views
9

Je suis un peu confus, peut-être cette question est très stupide. Où la mémoire est-elle allouée pour un composant non géré?Gestion de mémoire de composant non géré par CLR

Dans mon code .net si j'ai initié un composant non géré, où ce composant va être chargé et la mémoire est allouée?

Comment CLR marshall appel entre le segment géré et non géré?

EDIT

Merci pour votre réponse, mais ce que je demande est de dire suppose que je fais DllImport de user32.dll, ce qui est manifestement une dll non géré et j'appeler une fonction dans user32.dll maintenant ma question , comment CLR marshall mon appel à cette DLL non démontée?

Répondre

10

Il commence assez facile. Le marshaller PinVoke appelle d'abord LoadLibrary et transmet le nom de la DLL que vous avez spécifié, la propriété DllImportAttribute.Value. Dans votre cas, user32.dll est déjà chargé car il est chargé par le programme d'amorçage .NET, son compteur de références est simplement incrémenté. Mais normalement, le chargeur Windows obtient la DLL mappée dans l'espace d'adressage du processus afin que les fonctions exportées puissent être appelées.

Ensuite, vous obtenez GetProcAddress pour obtenir l'adresse de la fonction à appeler, la propriété DllImportAttribute.EntryPoint. Le marshaller fait quelques essais, sauf si vous avez utilisé ExactSpelling. Un nom de fonction comme "foo" est testé de plusieurs manières possibles, foo et fooW ou fooA. Détails d'implémentation Nasty de Win32 liés à la différence entre les caractères Unicode et Ansi. La propriété CharSet est importante ici.

Maintenant j'ai besoin d'agiter les mains un peu parce que ça devient compliqué. Le marshaller construit un cadre de pile, définissant les arguments devant être transmis à la fonction exportée. Cela nécessite un code de bas niveau, soigneusement exclu des regards indiscrets. Prenons la valeur nominale qu'il effectue le genre de traductions que la classe Marshal prend en charge pour convertir entre les types gérés et non gérés. La propriété DllImportAttribute.CallingConvention est importante car elle détermine la valeur de l'argument à placer de sorte que la fonction appelée puisse la lire correctement.

Ensuite, il configure un gestionnaire d'exceptions SEH afin que les exceptions matérielles déclenchées par le code appelé puissent être interceptées et traduites en une exception gérée. Celui qui génère le plus commun, AccessViolationException. Et d'autres. Ensuite, il pousse un cookie spécial sur la pile pour indiquer que le code non géré est sur le point de commencer à utiliser la pile. Cela empêche le garbage collector de déformer les cadres de pile non gérés et d'interpréter les pointeurs qu'il trouve en tant que références d'objets gérés. Vous pouvez voir ce cookie dans la pile d'appels du débogueur, [Managed to Native Transition].

Ensuite, juste un appel indirect à l'adresse de fonction trouvée avec GetProcAddress(). Cela fait fonctionner le code non managé. Après l'appel, un nettoyage peut être nécessaire pour libérer la mémoire qui a été allouée pour transmettre les arguments non gérés. La valeur de retour peut devoir être convertie en une valeur gérée. Et c'est tout, en supposant que rien de méchant ne soit arrivé, l'exécution continue sur la prochaine instruction de code managé.

8

Les allocations de mémoire non managées proviennent du tas de processus. Vous êtes responsable de l'allocation/désaffectation de la mémoire, car elle ne récupère pas les données collectées car le GC ne connaît pas ces objets.

+0

Vous pouvez créer une enveloppe autour du composant non géré, et mettre en œuvre l'interface 'IDisposable', pour un moyen plus propre de le désaffecter. Le garbage collector appelle la fonction définie dans cette interface pour éviter de "oublier" de nettoyer. –

+1

Vous pouvez l'insérer dans IDisposable, mais veillez à appeler Dispose ou à remplacer le finaliseur. Il ne suffit pas d'implémenter IDispose, le GC n'appelle pas Dispose, il appelle Object.Finalize (voir http://stackoverflow.com/questions/45036/will-the-gc-call-idisposable-dispose-for-me) – pstrjds

0

Michael répond à une partie de votre question. Je réponds à l'autre partie.

Si CLR est chargé dans un processus non géré, il est appelé hébergement CLR. Cela implique généralement l'appel d'un point d'entrée dans mscoree DLL, puis le domaine AppDomain par défaut est chargé. Dans un tel cas, CLR demande un bloc de mémoire du processus et lorsqu'il est donné, cela devient son espace mémoire et aura une pile et un tas.

1

Tout comme une recherche académique d'information en expansion sur ce qui a été écrit ici:

Il y a environ 8 tas différents que le CLR utilise:

  1. Loader Heap: contient des structures CLR et le type système

  2. haute fréquence Heap: statique, MethodTables, FieldDescs, carte d'interface

  3. bas Fréq parence Heap: EEClass, ClassLoader et tables de consultation

  4. Stub Heap: talons pour CAS, emballages COM, P/Invoke

  5. Large Object Heap: allocations de mémoire qui nécessitent plus de 85 duodecies octets

  6. GC Heap: mémoire utilisateur tas privé alloué à l'application

  7. code JIT Heap: mémoire allouée par mscoreee (exécution Engine) et le compilateur JIT pour le code managé

  8. Processus/Base de Heap: Interop/allocations non gérés, la mémoire native, etc

HTH