クラスがあり、そのフィールドを調べて、最終的に各フィールドが何バイトかかるかを報告したいと考えています。すべてのフィールドが Int32、byte などの型であると仮定します。
フィールドのバイト数を簡単に調べるにはどうすればよいですか?
次のようなものが必要です:
Int32 a;
// int a_size = a.GetSizeInBytes;
// a_size should be 4
クラスがあり、そのフィールドを調べて、最終的に各フィールドが何バイトかかるかを報告したいと考えています。すべてのフィールドが Int32、byte などの型であると仮定します。
フィールドのバイト数を簡単に調べるにはどうすればよいですか?
次のようなものが必要です:
Int32 a;
// int a_size = a.GetSizeInBytes;
// a_size should be 4
基本的に、できません。これは、使用している CLR バージョンやプロセッサなどに基づいている可能性があるパディングに依存します。他のオブジェクトへの参照がないと仮定すると、オブジェクトの合計サイズを計算する方が簡単です。大きな配列を作成し、基点としてGC.GetTotalMemoryを使用し、配列に型の新しいインスタンスへの参照を入力してから、GetTotalMemory を再度呼び出します。一方の値をもう一方の値から取り、インスタンスの数で割ります。おそらく、事前に単一のインスタンスを作成して、新しい JITted コードが数値に寄与しないようにする必要があります。はい、それは聞こえるほどハッキーですが、私は今までそれを使って効果を上げてきました。
ちょうど昨日、このための小さなヘルパー クラスを作成することをお勧めします。興味があれば教えてください。
編集: 他に 2 つの提案があり、両方に対処したいと思います。
まず、sizeof演算子: これは、その型がアブストラクトで占めるスペースの量のみを示し、その周囲にパディングは適用されません。(構造内のパディングは含まれますが、別の型内のその型の変数に適用されるパディングは含まれません。)
次に、Marshal.SizeOf : これは、メモリ内の実際のサイズではなく、マーシャリング後のアンマネージ サイズのみを示します。ドキュメントに明示的に記載されているように:
返されるサイズは、実際にはアンマネージ型のサイズです。オブジェクトのアンマネージド サイズとマネージド サイズは異なる場合があります。文字タイプの場合、サイズはそのクラスに適用される CharSet 値の影響を受けます。
繰り返しになりますが、パディングは違いを生む可能性があります。
パディングが関連していることの意味を明確にするために、次の 2 つのクラスを考えてみましょう。
class FourBytes { byte a, b, c, d; }
class FiveBytes { byte a, b, c, d, e; }
私の x86 ボックスでは、FourBytes のインスタンスは (オーバーヘッドを含めて) 12 バイトかかります。FiveBytes のインスタンスは 16 バイトを使用します。唯一の違いは「e」変数です。つまり、4 バイトかかるのでしょうか。ええと、そうではありません。明らかに、FiveBytes から任意の変数を 1 つ削除してサイズを 12 バイトに戻すことができますが、それは各変数が 4 バイトを占めるという意味ではありません (すべての変数を削除することを考えてください!)。単一の変数のコストは、ここではあまり意味のある概念ではありません。
質問者のニーズに応じて、Marshal.SizeOf は必要なものを提供する場合と提供しない場合があります。(Jon Skeetが回答を投稿した後に編集)。
using System;
using System.Runtime.InteropServices;
public class MyClass
{
public static void Main()
{
Int32 a = 10;
Console.WriteLine(Marshal.SizeOf(a));
Console.ReadLine();
}
}
jkersch が言うように、 sizeof は使用できますが、残念ながら値型でしか使用できないことに注意してください。クラスのサイズが必要な場合は、Marshal.SizeOf が最適です。
Jon Skeet は、 sizeof も Marshal.SizeOf も完璧ではない理由を明らかにしました。質問者は、どちらかが彼の問題に受け入れられるかどうかを判断する必要があると思います.
彼の答えのJonSkeetsレシピから、私は彼が参照しているヘルパークラスを作成しようとしました。改善のための提案を歓迎します。
public class MeasureSize<T>
{
private readonly Func<T> _generator;
private const int NumberOfInstances = 10000;
private readonly T[] _memArray;
public MeasureSize(Func<T> generator)
{
_generator = generator;
_memArray = new T[NumberOfInstances];
}
public long GetByteSize()
{
//Make one to make sure it is jitted
_generator();
long oldSize = GC.GetTotalMemory(false);
for(int i=0; i < NumberOfInstances; i++)
{
_memArray[i] = _generator();
}
long newSize = GC.GetTotalMemory(false);
return (newSize - oldSize) / NumberOfInstances;
}
}
使用法:
Tの新しいインスタンスを生成するFuncを使用して作成する必要があります。毎回同じインスタンスが返されないようにしてください。例:これで問題ありません:
public long SizeOfSomeObject()
{
var measure = new MeasureSize<SomeObject>(() => new SomeObject());
return measure.GetByteSize();
}
これを IL レベルまで煮詰める必要がありましたが、最終的にこの機能を非常に小さなライブラリで C# に組み込みました。
bitbucketで入手できます (BSD ライセンス) 。
コード例:
using Earlz.BareMetal;
...
Console.WriteLine(BareMetal.SizeOf<int>()); //returns 4 everywhere I've tested
Console.WriteLine(BareMetal.SizeOf<string>()); //returns 8 on 64-bit platforms and 4 on 32-bit
Console.WriteLine(BareMetal.SizeOf<Foo>()); //returns 16 in some places, 24 in others. Varies by platform and framework version
...
struct Foo
{
int a, b;
byte c;
object foo;
}
sizeof
基本的に、私が行ったことは、 IL 命令の周りに簡単なクラス メソッド ラッパーを作成することでした。この命令は、オブジェクトへの参照が使用する生のメモリ量を取得します。たとえば、 の配列があるT
場合、sizeof
命令は各配列要素が何バイト離れているかを示します。
sizeof
これは、C# の演算子とは大きく異なります。1 つには、静的な方法で他のもののサイズを取得することは実際には不可能であるため、C# では純粋な値型のみが許可されます。対照的に、sizeof
命令は実行時レベルで機能します。したがって、この特定のインスタンス中に型への参照が使用するメモリの量が返されます。
私のブログで、いくつかの詳細情報ともう少し詳細なサンプル コードを確認できます。
アラインメントを考慮せずに、間接的に行うことができます。型インスタンスを参照するバイト数は、サービス フィールド サイズ + 型フィールド サイズと等しくなります。サービス フィールド (32x ではそれぞれ 4 バイト、64x では 8 バイト):
したがって、フィールドのないクラスの場合、彼のインスタンスは 32x マシンで 8 バイトを使用します。1 つのフィールドを持つクラスの場合、同じクラス インスタンスを参照するため、このクラスは (64x) を取得します。
Sysblockindex + pMthdTable + クラスの参照 = 8 + 8 + 8 = 24 バイト
値型の場合、インスタンス フィールドがないため、in は自分のフィールド サイズのみを取得します。たとえば、1 つの int フィールドを持つ構造体がある場合、32x マシンでは 4 バイトのメモリしか必要としません。
型がある場合は、sizeof 演算子を使用します。タイプのサイズをバイト単位で返します。例えば
Console.WriteLine(sizeof(int));
出力します:
4
メソッドのオーバーロードをトリックとして使用して、フィールド サイズを決定できます。
public static int FieldSize(int Field) { return sizeof(int); }
public static int FieldSize(bool Field) { return sizeof(bool); }
public static int FieldSize(SomeStructType Field) { return sizeof(SomeStructType); }