Disruptorのようにメモリオーバーフローも発生しますか?


前言OutOfMemoryError問題は多くの友人が遭遇したことがあると信じており、一般的なビジネス異常(配列境界、空のポインタなど)に比べて、このような問題は位置決めと解決が難しい.
本文は最近出会ったオンラインメモリオーバーフローの位置決め、問題解決の方式で展開する.似たような問題に直面した学生に構想と助けをもたらすことを望んでいる.
主に --> --> --> の4つのステップから問題を分析し、解決する.
イメージ
最近、デルが生産しているアプリケーションでは、メモリオーバーフローが絶えず発生し、ビジネス量の増加に伴う頻度が高まっています.
このプログラムのビジネスロジックは非常に簡単で、Kafkaからデータを消費してロット化して操作します.
現象はKafkaのメッセージが多ければ多いほど,異常の頻度が速くなることである.当時は他にも仕事があったので、メンテナンスを再起動させ、スタックメモリやGCの状況を監視するしかありませんでした.
大法を再起動するのはよいが,依然として根本的に問題を解決することはできない.
チェック
そこで,運用前に収集したメモリデータ,GCログに基づいてどこに問題があるかを判断しようとする.
その結果,古い世代のメモリ使用はGCが発生してもずっと高く,時間が経つにつれてますます高くなっていることが分かった.
jstatのログを合わせると、FGCが発生しても古い世代では回収できず、メモリが頂点に達していることがわかります.
何台かのアプリFGCが百回にも達していて、時間も高いのが怖いです.
これは、アプリケーションのメモリ使用に問題があるに違いありません.多くのライフルオブジェクトが回収できないことを示しています.
位置
生産上のメモリdumpファイルが非常に大きいため、数十Gに達した.メモリの設定が大きすぎるためです.
そのため、MAT分析を使用するには多くの時間がかかります.
そのため、私たちは現地で再現できるかどうかを考えています.そうすれば、より多くの位置を特定することができます.
問題を早期に再現するために、ローカルアプリケーションの最大スタックメモリを150 Mに設定します.
そして消費KafkaではMockはwhileサイクルのためにデータを生成し続けた.
同時に、アプリケーションが起動した後、VisualVM接続アプリケーションを利用してメモリ、GCの使用状況をリアルタイムで監視する.
結局10数分走ってもメモリの使用に問題はありませんでした.図から分かるように、GCメモリが発生するたびに有効に回収できるため、問題は再現されていない.
問題が再現できないと位置づけにくい.そこで私たちはreviewコードを使って、生産の論理は私たちがwhileでMockデータを循環するのとはまだ違います.
生産ログを見ると、Kafkaから取り出すたびに数百件のデータが表示されますが、私たちがMockをするたびに1つしか生成されません.
可能な限り生産状況をシミュレートするためにサーバ上で生産者プログラムを走り,Kafkaにデータを送信し続けた.
やはり意外にも1分以上走っただけでメモリが耐えられなくなり、左図を見るとGCの頻度は非常に高いが、メモリの回収は下手だ.
また、バックグラウンドでも印刷メモリがオーバーフローし始め、問題が再現されます.
解決する
現在の表現から見ると、メモリに多くのオブジェクトが強い参照関係を維持しているため、回収されません.
そこで、どのオブジェクトがこんなに多くのメモリを消費しているのかを見てみたいと思います.VisualVMのHeapDump機能を利用して、現在のアプリケーションのメモリ状況をすぐにdumpすることができます.
その結果、com.lmax.disruptor.RingBuffer型のオブジェクトが50%近くのメモリを消費していることが分かった.
このバッグを見るとDisruptor環状の列が思い浮かぶ.
再度reviewコードを発見:Kafkaから取り出した700個のデータは直接Disruptorに捨てられた.
ここでは、なぜ最初のシミュレーションデータが問題を再現しなかったのかを説明することができます.
シミュレーションではオブジェクトがキューに格納され,本番では700個のデータがキューに格納される.このデータ量は700倍の差です.
一方,Disruptorはリングキューとして,再オブジェクトが上書きされないまで存在していた.
私も実験をして、確かにそうであることを証明しました.
私はキューサイズを8に設定し、0~9から中に10個のデータを書き、8に書くと前の0の位置を上書きし、後ろのはこのように押します(HashMapの型取り位置決めに似ています).
だから、本番で私たちのキューサイズが1024だと仮定すると、システムの実行に伴って最終的に1024の位置にオブジェクトがいっぱいになり、各位置は700個になります.
そこで,生産上のDisruptorのRingBuffer構成を調べたところ,1024*1024であった.
この数級はとても怖いです.
この問題であるかどうかを確認するために、ローカルで値を2に変更し、最小値を試してみました.
同じ128 Mメモリでも、Kafkaを介してデータがどんどん取り出されています.以下を監視します.
20分ほど走るとシステムはすべて正常で、GCごとにメモリの大部分を回収することができ、最終的に鋸歯状になります.
これで問題が見つかりましたが、生産上この値を具体的に設定するには、業務状況に応じてテストしなければなりませんが、従来の1024*1024は絶対に使用できません.
まとめ
最後に1行のコード(まだ変更されていないので、構成を直接変更)を変更しましたが、このチェックプロセスは意味があると思います.
JVMのようなブラックボックスが手に入らないと感じている学生の多くにも直感的な感じがします. Disruptor , !
関連プレゼンテーションコードの表示:
https://github.com/crossoverJie/JCSprout/tree/master/src/main/java/com/crossoverjie/disruptor
あなたのいいねと転送は最大のサポートです.