Encontre o tamanho da instância do object em bytes em c #

Para qualquer instância arbitrária (collections de objects, composições, objects únicos, etc)

Como posso determinar seu tamanho em bytes?

(Eu atualmente tenho uma coleção de vários objects e estou tentando determinar o tamanho agregado dele)

EDIT: alguém escreveu um método de extensão para o object que poderia fazer isso? Isso seria bem legal.

Primeiro de tudo, um aviso: o que segue é estritamente no reino dos hacks feios e não documentados. Não confie neste trabalho – mesmo que funcione para você agora, ele pode parar de funcionar amanhã, com qualquer atualização menor ou maior do .NET.

Você pode usar as informações contidas neste artigo sobre os componentes internos do CLR – na última vez que verifiquei, ainda era aplicável. Veja como isso é feito (recupera o campo interno “Basic Instance Size” via TypeHandle do tipo).

 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); 

Isso funciona no 3.5 SP1 de 32 bits. Não tenho certeza se os tamanhos dos campos são os mesmos em 64 bits – talvez seja necessário ajustar os tipos e / ou deslocamentos caso eles não sejam.

Isso funcionará para todos os tipos “normais”, para os quais todas as instâncias têm os mesmos tipos bem definidos. Aqueles para os quais isso não é verdade são arrays e strings com certeza, e eu acredito também no StringBuilder . Para eles, você adicionará o tamanho de todos os elementos contidos ao tamanho da instância base.

Você pode aproximar o tamanho fingindo serializá-lo com um serializador binário (mas roteando a saída para o esquecimento) se estiver trabalhando com objects serializáveis.

 class Program { static void Main(string[] 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; string name; List children; public A(int id, string name) { this.id = id; this.name = name; children = new List(); } public B AddChild(string name) { B newItem = new B(this, name); children.Add(newItem); return newItem; } } [Serializable()] class B { A parent; string name; public B(A parent, string 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(); } } 

Para tipos não gerenciados, também conhecidos como tipos de valor, structs:

  Marshal.SizeOf(object); 

Para objects gerenciados, quanto mais perto eu tenho, é uma aproximação.

  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ão use serialization.Um formatador binário adiciona headers, para que você possa alterar sua class e carregar um arquivo serializado antigo para a class modificada.

Também não lhe dirá o tamanho real na memory nem levará em conta o alinhamento da memory.

[Edit] Usando BiteConverter.GetBytes (prop-value) recursivamente em cada propriedade de sua class você obteria o conteúdo em bytes, que não conta o peso da class ou das referências, mas está muito mais próximo da realidade. Eu recomendaria usar uma matriz de bytes para dados e uma class de proxy não gerenciada para acessar valores usando a conversão de ponteiro se o tamanho for importante, observe que seria memory não alinhada, assim, computadores antigos serão lentos, mas conjuntos de dados ENORMES na RAM MODERNA serão consideravelmente mais rápido, pois minimizar o tamanho da leitura da memory RAM será um impacto maior do que o desalinhado.

Isso não se aplica à implementação .NET atual, mas uma coisa a ter em mente com tempos de execução gerenciados / coletados de lixo é que o tamanho alocado de um object pode mudar ao longo da vida útil do programa. Por exemplo, alguns coletores de lixo geracionais (como o coletor Hybrid Counting Counting Hybrid Reference ) precisam apenas armazenar certas informações depois que um object é movido do berçário para o espaço maduro.

Isso torna impossível criar uma API genérica e confiável para expor o tamanho do object.

solução segura com algumas otimizações Código CyberSaving / MemoryUsage . algum caso:

 /* test nullable type */ TestSize.SizeOf(null) //-> 4 B /* test StringBuilder */ StringBuilder sb = new StringBuilder(); 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 

Funciona também com classs:

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

Isso é impossível de fazer em tempo de execução.

Existem vários profilers de memory que exibem o tamanho do object, no entanto.

EDIT : você poderia escrever um segundo programa que perfis o primeiro usando o CLR Profiling API e se comunica com ele através de remoting ou algo assim.

AFAIK, você não pode, sem realmente contar em profundidade o tamanho de cada membro em bytes. Mas, novamente, o tamanho de um membro (como elementos dentro de uma coleção) conta para o tamanho do object, ou um ponteiro para esse membro conta para o tamanho do object? Depende de como você define isso.

Eu corri para esta situação antes de onde eu queria limitar os objects no meu cache com base na memory que eles consumiram.

Bem, se houver algum truque para fazer isso, eu ficaria muito feliz em saber sobre isso!

Para tipos de valor, você pode usar Marshal.SizeOf . Naturalmente, ele retorna o número de bytes necessários para empacotar a estrutura na memory não gerenciada, que não é necessariamente o que o CLR usa.

Você pode usar a reflection para reunir todas as informações públicas de membros ou propriedades (dado o tipo do object). Não há como determinar o tamanho sem percorrer cada parte individual dos dados no object.

Use Son Of Strike que tem um comando ObjSize .

Observe que a memory real consumida é sempre maior que os relatórios ObjSize devido a um synkblk que reside diretamente antes dos dados do object.

Leia mais sobre ambos aqui http://msdn.microsoft.com/pt-br/magazine/cc163791.aspx

Minha aula não foi marcada como serializável, então algumas das respostas aqui não funcionaram. Eu cheguei assim.

 var dump = JsonConvert.SerializeObject(obj); 

Para quem procura uma solução que não requer classs [Serializable] e onde o resultado é uma aproximação em vez da ciência exata. O melhor método que encontrei é a serialização json em um stream de memory usando a codificação 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; } } 

Não, isso não lhe dará o tamanho exato que seria usado na memory. Como mencionado anteriormente, isso não é possível. Mas isso lhe dará uma estimativa aproximada.

Note que isso também é bem lento.