文字列連結の StringBuilder が OutOfMemoryException をスローする
-
21-08-2019 - |
質問
私たちは主に上記のベスト プラクティスに従う傾向があります。
見て 文字列と文字列ビルダー
ただし、StringBuilder はスローする可能性があります 十分なメモリがある場合でも OutOfMemoryException が発生する. 。「連続したメモリブロック」が必要なため、OOM 例外がスローされます。
参考のためのいくつかのリンクStringBuilder OutOfMemoryException
他にもたくさんあります....
この問題に直面している人、または気づいている人は何人いますか?また、それを解決するために何をしましたか?
何か足りないものはありますか?
追伸:これは知りませんでした。
質問を言い換えました。
*** 同じことが手動連結でも機能しました (これを確認して SO を更新します)。もう 1 つ気になったのは、システムに十分なメモリがあるかどうかです。それが、この問題に直面した人がいるかどうか、またはコードに何か重大な問題があるかどうかを確認するためにここでこの質問を提起した理由です。
解決
作成する基礎となる文字列は、文字の配列として表されるため、連続したメモリ ブロックも必要になります (配列には連続したメモリが必要です)。StringBuilder が OOM 例外をスローした場合、それなしでは基礎となるものを構築できません。
文字列の作成によって OOM が発生する場合は、アプリケーションにさらに深刻な問題がある可能性があります。
説明に応じて編集:
手動連結が成功しても、StringBuilder を使用した文字列の構築が失敗するケースの小さなサブセットが存在します。手動連結では 2 つの文字列を結合するために必要な正確な長さが使用されますが、StringBuilder ではメモリを割り当てるための異なるアルゴリズムが使用されます。これはより積極的で、文字列に実際に必要なメモリよりも多くのメモリを割り当てる可能性があります。
StringBuilder を使用すると、文字列が System.String フォームと StringBuilder に短時間同時に存在するため、必要なメモリが一時的に 2 倍になります。
ただし、一方の方法で OOM が発生し、もう一方の方法では OOM が発生しない場合でも、プログラム内のより深刻な問題を示している可能性があります。
他のヒント
特定の状況で StringBuilder が OutOfMemoryException をスローする場合、手動で文字列を連結することはより良い解決策ではありません。それははるかに悪いです。これはまさに、StringBuilder が使用されるべきケース (非常に長い文字列の作成) です。これほど大きな文字列を手動で連結すると、StringBuilder で文字列を作成する場合に必要となるメモリの何倍も必要になります。
つまり、最新のコンピューターでは、文字列が 連続したメモリが不足しているコンピュータを実行している あなたのデザインには深く、深く欠陥があります。どうすればこれほど大きな文字列が作成されるのか想像できません。
メモリの量について話しているのでしょうか?システムの空きメモリや総メモリについて話しているのではありませんが、連結している文字列の長さはどれくらいですか?
メモリ オーバーフロー例外は、連続メモリが利用できないために経験したことがあるように、実際にメモリが使い果たされるずっと前に失敗した場合でも、ほとんどの場合、コードに関する非常に悪い兆候です。
その時点で、実際にコードを再構築する必要があります。
たとえば、問題に対処するさまざまな方法を次に示します。
- 一度に多くのデータをメモリに保存せず、ディスクか何かに保存します。
- それを分割し、文字列/文字列ビルダーのリストを保持し、新しいリストに切り替える前に一定の長さまでにのみ追加することで、「連続メモリ」の問題を回避します。
- メモリ内にギガバイトのデータがまったく蓄積されないようにアルゴリズムを再構築する
これが懸念されるほどメモリ制限に近づいて実行している場合は、おそらく別のアーキテクチャを検討するか、より強力なマシンを購入する必要があります。
様子を見てみると StringBuilder
が実装されると、実際に String
データを保持するため (String
内部メソッドがあるため、 StringBuilder
その場で変更します)。
つまり、どちらも同じ量のメモリを使用します。ただし、以来、 StringBuilder
基礎となる配列を自動的に拡張し、必要に応じて必要に応じてコピーします (ただし、容量は 2 倍になります)。これがメモリ不足エラーの原因である可能性が高くなります。しかし、他の人がすでに指摘しているように、両方とも連続したメモリブロックを必要とします。
さて、実際の疑問は、なぜそんなに長い文字列を扱う必要があるのかということです。この問題に遭遇した場合は、おそらく概念を変更する必要があります。
この問題は System.String クラスなので、入力をチャンク化する必要があります リスト<文字列> データを並列処理するため、適切に記述されていれば全体的なパフォーマンスが向上します。
さまざまな stringbuilder で連続して構築された非常に大きな文字列 (匿名関数内で宣言されているため問題は発生しませんでした) でこの例外に遭遇しましたが、最終的には匿名関数の外部で宣言された単一の StringBuilder を再利用することで解決しました。
私も、文字列を追加していたのに String.Format を追加するのを忘れたという非常に似た経験をしました。したがって:
myStringBuilder.Append("1,""{0}""", someVeryLargeIntVariable)
になるはずだった:
myStringBuilder.Append(String.Format("1,""{0}""", someVeryLargeIntVariable))
これは失敗した私の vb.net コードであることに注意してください。同様のテストを C# で複製しました。
myStringBuilder.Append('a', 1564544656);
対
myStringBuilder.Append(string.Format("1,\"{0}\"", 1564544656));
しかし、私の場合、vb.net は暗黙的な変換のせいでトラブルに見舞われました (並列処理できませんでした)。 ちょうど C#でも同じ問題があります)。
それが誰かの役に立てば幸いです。