サーバーの終了時にオブジェクトを正しく破棄する


9

大規模なC ++プロジェクトに取り組んでいます。REST APIを公開するサーバーで構成され、他の多くのサーバーで構成される非常に広範なシステムにシンプルでユーザーフレンドリーなインターフェースを提供します。コードベースは非常に大きく複雑であり、適切な設計を事前に行わずに時間をかけて進化しました。私の仕事は、新しいコードを実装し、古いコードをリファクタリング/修正して、より安定して信頼できるようにすることです。

現時点では、サーバーは、プロセスが終了しても終了も破棄もされない、長期間存続するオブジェクトを多数作成します。これにより、Valgrindはリーク検出にほとんど使用できなくなります。何千もの(疑わしい)正当なリークと「危険な」リークを区別することは不可能だからです。

私の考えは、すべてのオブジェクトが終了前に確実に破棄されるようにすることですが、私がこの提案をしたとき、私の同僚と上司は、OSがとにかくそのメモリを解放することを指摘し(誰にとっても明らかです)、オブジェクトを破棄することに反対しましたサーバーのシャットダウンを遅くします(現時点では、これは基本的にへの呼び出しですstd::exit)。私は、「クリーンな」シャットダウン手順を持っているからといって、それを使用しなければならないということを必ずしも意味しないと答えました。焦りを感じたらいつでも呼んでstd::quick_exitもいいしkill -9、ただプロセスを呼んでもいい。

彼らは、「ほとんどのLinuxデーモンとプロセスはシャットダウン時にわざわざメモリを解放しない」と答えました。私はそれを見ることができますが、私はすでにメモリ破損、二重解放、および初期化されていない変数を見つけたので、プロジェクトが正確なメモリデバッグを必要とすることも事実です。

あなたの考えは何ですか?私は無意味な努力を追求していますか?そうでない場合、同僚や上司をどのように説得できますか?もしそうなら、なぜ、そして代わりに私は何をすべきですか?


パフォーマンスの議論(これは合理的です!)に加えて、長く存続するオブジェクトを分離してそれらのクリーンアップコードを追加するのは大変なことですか?
Doc Brown

回答:


7

すべてのメモリを解放するvalgrind測定中に使用できるサーバープロセスにスイッチを追加します。このスイッチをテストに使用できます。通常の運用では影響は最小限です。

1000のオブジェクトを解放するのに数分かかる長い実行プロセスがありました。単に終了して彼らを死なせたほうがはるかに効率的でした。残念ながら、ご指摘のとおり、これにより、valgrindまたはその他のツールを使用して真のメモリリークを検出することが困難になりました。

これは、通常のパフォーマンスに影響を与えずに、テストにとって適切な妥協案でした。


1
+1実用主義FTW。測定には価値がありますが、迅速なシャットダウンにも価値があります。
ロスパターソン

2
コマンドラインスイッチの代わりに、#ifdef DEBUGブロック内の永続オブジェクトの削除の実装を検討することもできます。
Jules 14

3

ここでの鍵はこれです:

私はそれを見ることができますが、私はすでにメモリ破損、二重解放、および初期化されていない変数を見つけたので、プロジェクトが正確なメモリデバッグを必要とすることも事実です。

これは、コードベースが希望と文字列以外のものからまとめられていることを直接意味します。有能なC ++プログラマーには、二重解放はありません。

あなたは絶対に無意味な努力を続けています-あなたは実際の問題の1つの小さな兆候に対処しています。

サーバーをRAIIで正しくプログラムすれば、これらの問題は発生せず、質問の問題は解消されます。さらに、コードは実際には時々正しく実行される場合があります。したがって、それは明らかに最良の選択肢です。


確かに問題は全体像にあります。ただし、プロジェクトをより適切な形にリファクタリング/書き直すためのリソースと権限を見つけることができるのは非常にまれです。
Cengiz Can 2014

@CengizCan:バグを修正したい場合は、リファクタリングする必要があります。それはそれが機能する方法です。
DeadMG 14

2

1つの優れたアプローチは、分類によって同僚とのディスカッションを絞り込むことです。コードベースが大きい場合、単一の理由ではなく、生きているオブジェクトが複数の(識別可能な)理由があります。

例:

  • 誰からも参照されない長い生存オブジェクト(実際のリーク)。これはプログラミングロジックエラーです。時間の経過とともにメモリフットプリントが増大する(およびアプリケーションの品質が低下する)責任がある場合を除き、優先度の低いものを修正します。時間の経過とともにメモリフットプリントが大きくなる場合は、優先順位を高くして修正してください。

  • (プログラムロジックにより)まだ参照されているが使用されていないが、メモリフットプリントが大きくならない、長寿命のオブジェクト。コードレビューを行い、その原因となる他のバグを見つけてください。意図的な(パフォーマンス)最適化である場合は、コードベースにコメントを追加します。

  • 「設計による」長命のオブジェクト。たとえば、シングルトンパターン。特にマルチスレッドアプリケーションの場合、それらを取り除くことは実際には困難です。

  • リサイクルされたオブジェクト。長生き物は必ずしも悪いものである必要はありません。それらはまた有益である場合もあります。高頻度のメモリ割り当て/割り当て解除の代わりに、現在未使用のオブジェクトをコンテナに追加して、そのようなメモリブロックが再び必要になったときにそこから描画すると、アプリケーションの速度が上がり、ヒープの断片化を回避できます。これらは、シャットダウン時に簡単に解放できるはずです。おそらく、特別な「計測/チェック」ビルドであるはずです。

  • 「共有オブジェクト」-他の複数のオブジェクトによって使用(参照)され、オブジェクトを解放するためにいつ保存されるかを正確に知らないオブジェクト。それらを参照カウント対象オブジェクトに変換することを検討してください。

それらの解放されていないオブジェクトの実際の理由を分類すると、ケースバイケースのディスカッションに入り、解決策を見つけることがはるかに簡単になります。


0

私見では、これらのオブジェクトのライフタイムは、システムがシャットダウンしたときに作成され、そのままにしておくべきではありません。グローバル変数のこの悪臭は、私たち全員が知っている悪い悪い悪い悪い悪いです。特にスマートポインターの時代には、怠惰以外にこれを行う理由はありません。しかし、より重要なことは、誰かがいつか対処しなければならない可能性のあるレベルの技術的負債をシステムに追加することです。

「技術的負債」の考え方は、このような近道をたどると、誰かが将来コードを変更したいときです(クライアントを「オフラインモード」または「スリープモードにしたい」 「または、プロセスを再起動せずにサーバーを切り替えられるようにしたいのですが)彼らはあなたがスキップしていることをするために努力をしなければなりません。しかし、彼らはあなたのコードを維持するので、彼らはあなたのようにそれについてほとんど知らないでしょう、それで彼らはずっと長くかかります(私は20%長く話していない、私は20倍長く話しています!)。それがあなたであっても、この特定のコードに何週間も何ヶ月も取り組んでいないことになり、正しく実装するためにクモの巣をほこりから取り除くのにはるかに長い時間がかかります。

この場合、サーバーオブジェクトと「長期間有効な」オブジェクトとの間の結合が非常に緊密であるように見えます...レコードがサーバーへの接続よりも長く存続できる(そして存続する必要がある)場合があります。サーバー内のオブジェクトへのすべての変更を維持することは法外にコストがかかる可能性があるため、通常、生成されたオブジェクトは、サーバーを実際に変更するsaveおよびupdate呼び出しを使用して、サーバーオブジェクトへのハンドルを追跡する方が優れています。これは一般にアクティブレコードパターンと呼ばれます。見る:

http://en.wikipedia.org/wiki/Active_record_pattern

C ++では、アクティブな各レコードにサーバーへのweak_ptrがあり、サーバー接続が暗くなった場合にインテリジェントにスローすることができます。これらのクラスは、ニーズに応じて、遅延またはバッチで入力できますが、これらのオブジェクトの存続期間は、それらが使用される場所のみにする必要があります。

以下も参照してください。

プロセスを終了する前にリソースを解放するのは時間の無駄ですか?

その他


This reeks of global variables「解放する必要があるオブジェクトは何千もある」から「グローバルでなければならない」にどうやって行くのですか?それは論理の飛躍です。
ドヴァル2014

0

無期限に存続する必要のあるオブジェクトがどこに割り当てられているかを簡単に特定できる場合は、代替の割り当てメカニズムを使用してオブジェクトを割り当てて、valgrindリークレポートに表示されないようにするか、単一の割り当てのように見える可能性があります。

アイデアに慣れていない場合は、c ++カスタムメモリ割り当てを無力化する方法についての記事を次に示します。ただし、削除をまったく処理する必要がないため、ソリューションはその記事の例よりも単純である可能性があります。 !

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.