文字列インターンのプロセスと内部について知りたい。Netフレームワークに固有。また、インターンを使用するメリットと、パフォーマンスを改善するために文字列インターンを使用する必要があるシナリオ/状況についても知りたいと思います。 Jeffery RichterのCLRの本からインターンを勉強しましたが、まだ混乱していて、もっと詳しく知りたいのですが。
[編集]以下のサンプルコードで特定の質問をする:
private void MethodA()
{
string s = "String"; // line 1 - interned literal as explained in the answer
//s.intern(); // line 2 - what would happen in line 3 if we uncomment this line, will it make any difference?
}
private bool MethodB(string compareThis)
{
if (compareThis == "String") // line 3 - will this line use interning (with and without uncommenting line 2 above)?
{
return true;
}
return false;
}
インターンは内部実装の詳細です。 ボクシングとは異なり、知るにメリットはないと思いますリヒターの本で読んだものよりも。
文字列を手動でインターンすることのマイクロ最適化の利点はminimalであるため、通常はお勧めしません。
これはおそらくそれを説明しています:
class Program
{
const string SomeString = "Some String"; // gets interned
static void Main(string[] args)
{
var s1 = SomeString; // use interned string
var s2 = SomeString; // use interned string
var s = "String";
var s3 = "Some " + s; // no interning
Console.WriteLine(s1 == s2); // uses interning comparison
Console.WriteLine(s1 == s3); // do NOT use interning comparison
}
}
一般に、インターンは、リテラル文字列値を使用するときに自動的に発生するものです。インターンには、使用頻度に関係なく、メモリ内にリテラルのコピーが1つしかないという利点があります。
そうは言っても、実行時に生成される独自の文字列をインターンする理由があることはまれであり、通常の開発のために文字列インターンについて考えることさえあります。
潜在的に同一のランタイム生成文字列の比較で多くの作業を行う場合、いくつかの利点があります(インターンはReferenceEqualsによる比較を高速化できるため)。ただし、これは非常に特殊な使用法であり、かなりの量のプロファイリングとテストが必要になるため、適切な測定された問題がない限り、私が検討する最適化にはなりません。
これは「古い」質問ですが、私は別の見方をしています。
小さなプールから多くのlong-lived文字列を取得する場合、インターンによりメモリ効率を向上させることができます。
私の場合、静的ディクショナリで別のタイプのオブジェクトを頻繁に再利用していたため、ディスクに永続化する前に高速キャッシュとして機能しました。
これらのオブジェクトのほとんどのフィールドは文字列であり、値のプールはかなり小さいです(とにかくインスタンスの数よりもはるかに小さい)。
これらが一時的なオブジェクトである場合、文字列フィールドはガベージコレクションされることが多いため、問題になりません。しかし、それらへの参照が保持されていたため、メモリ使用量が蓄積され始めました(新しい一意の値が追加されていなくても)。
そのため、オブジェクトをインターンすると、メモリ使用量が大幅に削減され、インターン中の文字列値もインターンしました。
文字列の内部化はメモリ消費に影響します。
たとえば、文字列を読み取り、それをリストに保持してキャッシュする場合、まったく同じ文字列が10回出現します。string.Internが使用されている場合、文字列は実際にはメモリに1回だけ格納されます。そうでない場合、文字列は10回保存されます。
以下の例では、string.Internバリアントは約44 MBを消費し、バージョンなし(コメントなし)は1195 MBを消費します。
static void Main(string[] args)
{
var list = new List<string>();
for (int i = 0; i < 5 * 1000 * 1000; i++)
{
var s = ReadFromDb();
list.Add(string.Intern(s));
//list.Add(s);
}
Console.WriteLine(Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024 + " MB");
}
private static string ReadFromDb()
{
return "abcdefghijklmnopqrstuvyxz0123456789abcdefghijklmnopqrstuvyxz0123456789abcdefghijklmnopqrstuvyxz0123456789" + 1;
}
内部化は、equals-compareのパフォーマンスも向上させます。以下の例では、インターンバージョンは約1時間単位、非インターンバージョンは7時間単位です。
static void Main(string[] args)
{
var a = string.Intern(ReadFromDb());
var b = string.Intern(ReadFromDb());
//var a = ReadFromDb();
//var b = ReadFromDb();
int equals = 0;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 250 * 1000 * 1000; i++)
{
if (a == b) equals++;
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed + ", equals: " + equals);
}
インターンされた文字列には、次の特性があります。
これらの特性の結果は次のとおりです。
文字列内の各文字を比較するよりもはるかに高速なアドレスポインタを比較するだけで、2つのインターンされた文字列が等しいかどうかをテストできます。これは、文字列が非常に長く、同じ文字で始まる場合に特に当てはまります。インターンされた文字列はObject.ReferenceEquals
メソッドで比較できますが、string ==
演算子は、文字列がインターネットかどうかを最初に確認するため、より安全です。
アプリケーションで同じ文字列を何度も使用すると、アプリケーションは文字列のコピーを1つだけメモリに格納するため、アプリケーションの実行に必要なメモリが削減されます。
多くの異なる文字列をインターンする場合、解放されない文字列にメモリが割り当てられ、アプリケーションはますます多くのメモリを消費します。
非常に多数のインターンされた文字列がある場合、文字列のインターンが遅くなる可能性があり、インターンされた文字列ディクショナリにアクセスするときにスレッドが互いにブロックします。
文字列インターニングは、次の場合にのみ使用してください。