|
ついでに文字列作成操作に絡むメモリの利用のされ方について。
StringBuilderで文字列を作成するとき、CLRの2までは、必要なバッファをそのまま連続メモリ領域として確保していました。 またStringBuilderは容量が不足すると、領域を倍のサイズで確保しなおします。 ということは、1GBの文字列を作成しようとした場合、最悪2GB近い連続領域が確保されるということです。 またその時には元の1GB近い領域も同時に確保した状態ですから、3GBもの連続領域(1GB+2GB)が一時的に必要になります。 また、最後にToStringした際、最悪では2GB近い領域がそのまま文字列に転用されます。 1GBは無駄になるわけです。 また、連続メモリ領域ですので、LOHにも大きな負担がかかります。
CLR4以降では、StringBuilderは、内部バッファを小さいメモ例領域として分割確保するようになりました。 これにより、連続メモリ領域確保による、LOHへの負担はなくなりました。 ただし、ToStringで文字列に変換する時点では文字列バッファの再確保とコピーが必要になるため、やっぱり一時的には最悪で文字列に必要な容量の3倍のメモリ領域が確保されることになります。 とはいえ、連続領域は必要最低限の量だけですので、CLR2と比較すると、LOHへの負担ははるかに小さくなります。 ただし、ToStringで必ずバッファの再確保とコピーが発生するため、ToString自体にかかる時間はCLR2よりも長くなる傾向があります。
今回の例ですと、1GBの文字列を作成するために、実際には3GBの領域が必要になってしまっているかもしれません。 もし最初から文字列の長さが分かっているなら、StringBuilderのキャパシティを明示設定することで、無駄を減らすことはできます。 ただ、CLR4の場合は、それでも一時的に2GBの領域が必要になりますが。 CLR2だとうまくやれば1GBで済むため、場合によっては.NET Framework 3.5などの方が有利なこともあるわけですね。
|