きな粉もち.net

.NET関連仕事に携わっています。OSSのソースを読んで気がついたことを中心に呟いたりブログに投稿したりしています。最近はUiPathを使ったRPAも研究中。気軽にフォローやツッコミよろしくおねがいします! Gitはここを使っています https://github.com/kinakomotitti

log4net ✖️ 出力処理編 ✖️String.Internによるメモリ節約術

この記事の目的

この記事は、
StringクラスのInternメソッドの使い方のメモ
を目的としています。

本題

  1. なぜString.Intern?
  2. String.Internとは?
  3. 実際に使ってみよう
  4. 利用方法を考えてみよう
  5. 注意点など
なぜString.Intern?

log4netでログを出力する処理の実装を見ていた時、以下の実装を見つけました。

protected Logger(string name) 
{
#if NETCF || NETSTANDARD1_3
// NETCF: String.Intern causes Native Exception
m_name = name;
#else
m_name = string.Intern(name);
#endif
}

String型のLoggerの名前を受け取って、m_nameに代入するとき・・・
string.Intern(name);
何かしている!・・が、何が起こっているかわからない!
ということで、String.Internを調べてみました。

String.Internとは?

公式ドキュメントは以下のURLとなります。
https://msdn.microsoft.com/ja-jp/library/system.string.intern(v=vs.110).aspx

共通言語ランタイムは、それぞれ一意のリテラル文字列宣言またはプログラムによって作成された、プログラムで 1 つの参照を含むインターン プールと呼ばれる、テーブルを維持することで文字列ストレージを節約できます。 その結果、特定の値を持つリテラル文字列のインスタンスのみ 1 回、システムに存在します。

インターンプールというテーブルのようなもので、文字列を管理しているというところまでわかりました。
後は、手を動かして体で感じよう。

実際に使ってみよう

ということで、実際にやってみました。
f:id:kinakomotitti:20180225010004p:plain

Stringクラスのコンストラクタを使ってインスタンスを生成することってあんまりないなぁ。とか
Stringクラスの参照先を比較するためには、==やequalsではだめで、Objectクラスのメソッドを利用しなくてはいけないことなど、Intern以外の要素でも勉強不足な点が見つかりました。

利用方法を考えてみよう

1ケースぐらいしか考えがまとまらなかったです。
・クラス、メソッドまたいで同じ文字列を繰り返し利用する場合
 同じ参照先情報を利用することでメモリの節約ができます。

注意点など

Internメソッド利用の注意点があります。

アプリケーションによって確保されるメモリの全体的な使用量を抑えるという観点で見た場合、文字列への参照をインターン プールに格納すると、思わぬ副作用が生じることに注意してください。第一に、String オブジェクト用にインターン プールに確保されるメモリは、共通言語ランタイム (CLR) が終了するまで解放されない可能性があります。これは、CLR は、インターン プールに格納された String オブジェクトを、アプリケーション (またはアプリケーション ドメイン) の終了後も参照し続ける場合があるためです。第二に、文字列の参照をインターン プールに格納した場合、最初に文字列を作成する必要があります。String オブジェクトによって使用されたメモリは、最終的にガベージ コレクタによって解放されるまで割り当てられたままになります。

何でもかんでもパフォーマンスを向上させてくれるわけではないということですね。
用法用量を守って正しく利用することが大事ですね!

まとめ

String.Internメソッドで、同じ値を持つ文字列への参照先を取得することが可能。
文字列と、参照先は、インターンプールで管理。
インターンプールの参照は、CLRが終了するまで解放されない可能性あり!

おまけ
参照の比較には、Object.RefernceEqualsメソッドを利用!