Trouver la taille de l’instance d’object en octets dans c #

Pour toute instance arbitraire (collections d’objects, compositions, objects uniques, etc.)

Comment puis-je déterminer sa taille en octets?

(J’ai actuellement une collection d’objects divers et j’essaie de déterminer la taille agrégée de celui-ci)

EDIT: Quelqu’un a-t-il écrit une méthode d’extension pour Object qui pourrait le faire? Ce serait vraiment bien.

Tout d’abord, un avertissement: ce qui suit est ssortingctement dans le domaine des hacks laids et sans papiers. Ne vous fiez pas à cela – même si cela fonctionne pour vous maintenant, il pourrait ne plus fonctionner demain, avec une mise à jour .NET mineure ou majeure.

Vous pouvez utiliser les informations de cet article sur les internes du CLR – la dernière fois que j’ai vérifié, il était toujours applicable. Voici comment cela se fait (il récupère le champ interne “Basic Instance Size” via TypeHandle du type).

 object obj = new List(); // whatever you want to get the size of RuntimeTypeHandle th = obj.GetType().TypeHandle; int size = *(*(int**)&th + 1); Console.WriteLine(size); 

Cela fonctionne sur 3.5 SP1 32 bits. Je ne sais pas si les tailles de champs sont les mêmes sur 64 bits – vous devrez peut-être ajuster les types et / ou les décalages s’ils ne le sont pas.

Cela fonctionnera pour tous les types “normaux”, pour lesquels toutes les instances ont les mêmes types bien définis. Ceux pour lesquels ce n’est pas vrai sont les tableaux et les chaînes à coup sûr, et je crois aussi à SsortingngBuilder . Pour eux, vous devrez append la taille de tous les éléments contenus à la taille de leur instance de base.

Vous pourrez peut-être approcher la taille en faisant semblant de la sérialiser avec un sérialiseur binary (mais en acheminant la sortie vers l’oubli) si vous travaillez avec des objects sérialisables.

 class Program { static void Main(ssortingng[] args) { A parent; parent = new A(1, "Mike"); parent.AddChild("Greg"); parent.AddChild("Peter"); parent.AddChild("Bobby"); System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); SerializationSizer ss = new SerializationSizer(); bf.Serialize(ss, parent); Console.WriteLine("Size of serialized object is {0}", ss.Length); } } [Serializable()] class A { int id; ssortingng name; List children; public A(int id, ssortingng name) { this.id = id; this.name = name; children = new List(); } public B AddChild(ssortingng name) { B newItem = new B(this, name); children.Add(newItem); return newItem; } } [Serializable()] class B { A parent; ssortingng name; public B(A parent, ssortingng name) { this.parent = parent; this.name = name; } } class SerializationSizer : System.IO.Stream { private int totalSize; public override void Write(byte[] buffer, int offset, int count) { this.totalSize += count; } public override bool CanRead { get { return false; } } public override bool CanSeek { get { return false; } } public override bool CanWrite { get { return true; } } public override void Flush() { // Nothing to do } public override long Length { get { return totalSize; } } public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override int Read(byte[] buffer, int offset, int count) { throw new NotImplementedException(); } public override long Seek(long offset, System.IO.SeekOrigin origin) { throw new NotImplementedException(); } public override void SetLength(long value) { throw new NotImplementedException(); } } 

Pour les types non gérés, c’est-à-dire les types de valeur, les structures:

  Marshal.SizeOf(object); 

Pour les objects gérés, plus je me rapproche, plus c’est approximatif.

  long start_mem = GC.GetTotalMemory(true); aclass[] array = new aclass[1000000]; for (int n = 0; n < 1000000; n++) array[n] = new aclass(); double used_mem_median = (GC.GetTotalMemory(false) - start_mem)/1000000D; 

N'utilisez pas de sérialisation. Un formateur binary ajoute des en-têtes, vous pouvez donc changer votre classe et charger un ancien fichier sérialisé dans la classe modifiée.

En outre, il ne vous indiquera pas la taille réelle en mémoire et ne tiendra pas compte de l’alignement de la mémoire.

[Edit] En utilisant BiteConverter.GetBytes (valeur-prop) de manière récursive sur toutes les propriétés de votre classe, vous obtiendrez le contenu en octets, qui ne compte pas le poids de la classe ou des références, mais est beaucoup plus proche de la réalité. Je recommanderais d'utiliser un tableau d'octets pour les données et une classe proxy non gérée pour accéder aux valeurs en utilisant le pointage si la taille est importante, notez que ce serait de la mémoire non alignée donc sur les anciens ordinateurs considérablement plus rapide, car minimiser la taille à lire à partir de la RAM aura un impact plus important que non aligné.

Cela ne s’applique pas à l’implémentation actuelle de .NET, mais une chose à garder à l’esprit avec les temps d’exécution collectés / gérés est la taille allouée d’un object qui peut changer pendant toute la durée de vie du programme. Par exemple, certains collecteurs de déchets générationnels (tels que le collecteur hybride de comptage de référence générationnel / ultérieur ) doivent uniquement stocker certaines informations après le déplacement d’un object de la pépinière vers l’espace mature.

Cela rend impossible la création d’une API générique fiable pour exposer la taille de l’object.

solution sûre avec quelques optimisations Code CyberSaving / MemoryUsage . certains cas:

 /* test nullable type */ TestSize.SizeOf(null) //-> 4 B /* test SsortingngBuilder */ SsortingngBuilder sb = new SsortingngBuilder(); for (int i = 0; i < 100; i++) sb.Append("わたしわたしわたしわ"); TestSize.SizeOf(sb ) //-> 3132 B /* test Simple array */ TestSize.SizeOf(new int[100]); //-> 400 B /* test Empty List*/ var list = new List(); TestSize>.SizeOf(list); //-> 205 B /* test List with 100 items*/ for (int i = 0; i < 100; i++) list.Add(i); TestSize>.SizeOf(list); //-> 717 B 

Cela fonctionne aussi avec les classes:

 class twossortingng { public ssortingng a { get; set; } public ssortingng b { get; set; } } TestSize.SizeOf(new twossortingng() { a="0123456789", b="0123456789" } //-> 28 B 

C’est impossible à faire lors de l’exécution.

Il existe divers profileurs de mémoire qui affichent la taille de l’object, cependant.

EDIT : Vous pouvez écrire un deuxième programme qui décrit le premier à l’aide de l’ API de profilage CLR et communique avec lui par le biais de l’access distant ou autre.

AFAIK, vous ne pouvez pas, sans compter réellement la taille de chaque membre en octets. Mais encore une fois, la taille d’un membre (comme les éléments à l’intérieur d’une collection) compte-t-elle dans la taille de l’object ou un pointeur sur ce membre compte-t-il dans la taille de l’object? Cela dépend de la façon dont vous le définissez.

J’ai déjà rencontré cette situation où je voulais limiter les objects dans mon cache en fonction de la mémoire qu’ils consommaient.

Eh bien, s’il y a un truc pour faire ça, je serais ravi de le savoir!

Pour les types de valeur, vous pouvez utiliser Marshal.SizeOf . Bien sûr, il renvoie le nombre d’octets requirejs pour regrouper la structure dans la mémoire non gérée, ce qui n’est pas nécessairement le cas du CLR.

Vous pouvez utiliser la reflection pour rassembler toutes les informations de membre public ou de propriété (en fonction du type de l’object). Il est toutefois impossible de déterminer la taille sans parcourir chaque élément de données de l’object.

Utilisez Son Of Ssortingke qui a une commande ObjSize .

Notez que la mémoire réelle consommée est toujours supérieure aux rapports ObjSize raison d’un synkblk qui réside directement avant les données d’object.

En savoir plus sur les deux ici http://msdn.microsoft.com/en-us/magazine/cc163791.aspx

Ma classe n’a pas été marquée comme sérialisable, certaines des réponses ne fonctionnaient pas. Je l’ai contourné comme ça.

 var dump = JsonConvert.SerializeObject(obj); 

Pour ceux qui recherchent une solution ne nécessitant pas de classes [Serializable] et où le résultat est une approximation plutôt qu’une science exacte. La meilleure méthode que je pourrais trouver est la sérialisation de json dans un stream de mémoire utilisant le codage UTF32.

 private static long? GetSizeOfObjectInBytes(object item) { if (item == null) return 0; try { // hackish solution to get an approximation of the size var jsonSerializerSettings = new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.IsoDateFormat, DateTimeZoneHandling = DateTimeZoneHandling.Utc, MaxDepth = 10, ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; var formatter = new JsonMediaTypeFormatter { SerializerSettings = jsonSerializerSettings }; using (var stream = new MemoryStream()) { formatter.WriteToStream(item.GetType(), item, stream, Encoding.UTF32); return stream.Length / 4; // 32 bits per character = 4 bytes per character } } catch (Exception) { return null; } } 

Non, cela ne vous donnera pas la taille exacte qui serait utilisée en mémoire. Comme mentionné précédemment, ce n’est pas possible. Mais cela vous donnera une estimation approximative.

Notez que c’est aussi assez lent.