【VB.NET極意】なぜそのループでアプリが止まるのか?StringとStringBuilderの決定的な「メモリの深淵」
こんにちは。現場で「なぜか画面が固まる」「メモリ使用率が異常に高い」という相談を受けるたび、私はまずそのコードの文字列連結を確認します。
VB.NETを学び始めた皆さんが、最初に出会う「見えない壁」。それが `String` 型の扱いです。今回は、数万件のデータを扱う現場で、アプリをフリーズさせないための「メモリ効率の極意」を伝授します。
—
1. なぜ「&」でつなぐと地獄を見るのか?
VB.NETにおいて、`String` 型は「イミュータブル(不変)」です。これが何を意味するか、イメージできますか?
皆さんが `str = str & “追加データ”` と書くたびに、裏側では以下のような凄惨なドラマが繰り広げられています。
1. 新しいメモリの確保: 今までの文字列よりも長い、新しい箱(メモリ領域)を確保する。
2. コピー: 古い文字列を、新しい箱にせっせとコピーする。
3. 合体: 新しく追加したいデータをその末尾に書き込む。
4. 死体処理: 古い文字列が入っていた箱を「ゴミ」として捨てる(ガベージコレクションの対象にする)。
これを1万回繰り返すとどうなるか。1万個の「古い箱」がメモリ上に散乱し、プログラムはゴミ拾い(GC)に追われて処理が停止する。これが、あなたのプログラムがフリーズする正体です。
—
2. 救世主 `StringBuilder` の登場
そこで登場するのが `System.Text.StringBuilder` です。
こいつは「可変」です。あらかじめ巨大なバケツ(メモリ領域)を用意しておき、そこに文字をどんどん放り込んでいくだけ。箱を新調する必要がないため、性能は劇的に向上します。
比較コード:1万回の連結実験
まずは、やってはいけない例と、推奨される例を見比べてみましょう。
Imports System.Text
Imports System.Diagnostics
Public Sub ComparePerformance()
Dim count As Integer = 50000
Dim sw As Stopwatch
‘ — パターンA:String連結(絶対にやってはいけない) —
sw = Stopwatch.StartNew()
Dim resultA As String = “”
For i As Integer = 1 To count
resultA &= “データ” & i.ToString() & “,”
Next
sw.Stop()
Console.WriteLine($”String連結: {sw.ElapsedMilliseconds}ms”)
‘ — パターンB:StringBuilder使用(現場の標準) —
sw = Stopwatch.StartNew()
Dim sb As New StringBuilder()
For i As Integer = 1 To count
sb.Append(“データ”).Append(i).Append(“,”)
Next
Dim resultB As String = sb.ToString()
sw.Stop()
Console.WriteLine($”StringBuilder連結: {sw.ElapsedMilliseconds}ms”)
End Sub
結果の衝撃:
環境にもよりますが、`String` 連結が数秒〜数十秒かかる一方で、`StringBuilder` は一瞬(数ミリ秒)で終わります。この差が、数万件のCSV出力では「業務が止まる」か「即座に終わる」かの分かれ道になります。
—
3. 実務で「StringBuilder」を使いこなす鉄則
ただ使うだけでなく、さらに一歩進んだプロの技を教えます。
① 初期容量(Capacity)を指定せよ
`StringBuilder` はデフォルトだと、容量が足りなくなると自動的に拡張します。しかし、拡張する際にもやはり「メモリの再確保」が発生します。出力するデータの規模が概ね予測できるなら、最初にバケツのサイズを指定しましょう。
‘ あらかじめ1MB程度の容量を確保して無駄な再確保を防ぐ
Dim sb As New StringBuilder(1024 1024)
② 連結の「メソッドチェーン」を利用せよ
`StringBuilder.Append` は自分自身(StringBuilderオブジェクト)を返すように設計されています。これを利用して、コードをスマートに書きましょう。
‘ 読みやすく、かつ効率的
sb.Append(“ID:”).Append(id).AppendLine() _
.Append(“Name:”).Append(name)
—
4. まとめ:ここをクリアすれば、もう中級者!
VB.NETを掌握する第一歩は、「プログラムがメモリの裏側で何をしているか」を想像することです。
- String型: 少量(数個〜数十個)の結合ならOK。直感的で読みやすい。
- StringBuilder: ループ内や大量のデータ処理では必須。これを使わないとアプリのパフォーマンスは担保できない。
「とりあえず動くコード」を書く段階から、「なぜ速いのかを理解して書くコード」へ。この視点さえあれば、あなたはもうマクロの記録を卒業した、立派なエンジニアです。
次回の記事では、このStringBuilderをさらに加速させる「メモリ最適化の裏技」についてお話ししましょう。それでは、良いコーディングライフを!
