JavaプログラムでCPU 100%を引き起こすよくある原因と診断の考え方

1987 ワード

テキストリンク
先週、あるチームのある業務の新機能が1%オンラインになり、オンラインになってから1日も経たないうちに、開発者はこのプロセスの中で下流のアプリケーションが絶えずサーバーを切っていることを発見し、症状はCPU 100%で、新しい要求を処理できない.この問題を一時的に迅速に解決するために、同じように事前に2つのheap dumpと2つのCPUがあるintervalのusageのスクリーンショットを行い、開発者にこの新機能をオフにさせ、同時に下流サーバを完全に再起動する.問題が解決する
問題は一時的に解決されたが、その時は最終的な問題点は発見されなかった.後で関連するロゴを調べてみると、ある機械は意外にもその時間帯に、ロゴをセンターロゴサーバーに送っていないと同時に、ローカルのロゴが上書きされていることが分かった.スクリーンショットのCPU usageによると、多くのスレッドがcacheでいくつかの値を検索し、いくつかのスレッドがHashMapでget()のある値があり、HashMapのデッドサイクルの問題に似ているように見えるが、他にも多くのCPUを費やしたスレッドがHashMapには入っていない.しかし、最終的にこれらのスレッドStackを見て、問題はビジネスロジックに次のコードがあることです.
TreeNode curNode = curNode.getParentNode();
Integer curVal = null;
while (null != curNode && null == curVal) {
    curVal = getSomeValueFromConfigCache(curNode);
}

よく見ると、上のコードに問題があり、どのノードがTree構造に見えるかは、現在のノードで調べられない場合はParentに基づいて調べたが、この論理の中には上に置き換えられた論理をwhileループに入れなかった.現在のコードによると、現在のノードがnullを返すことを調べられない場合、このwhileはデッドループである.だから修正は簡単ですが、この代価は少し大きいです.テストコードの99%は現在のノードで結果を調べることができるかもしれませんが、1%のデータがあり、Parentノードを上に見つけなければ調べることができません.
このようなCPU 100%の問題については、以下のようなものが一般的です.
  • Javaメモリ不足またはオーバーフローによるGC overhead問題、GC overheadによるCPU 100%問題.
  • 死循環問題.一般的なHashMapが複数のスレッドによって同時に使用されることによるデッドサイクル、または上記の例のデッドサイクルのようなものである.
  • 特定の特別料金CPUの動作は長期にわたって実行する.従来、正規表現を用いてルールに合致するか否かを判断するcaseがあったが、入力パラメータが数十K以上のデータである場合があり、この正規表現がうまく書けず、CPUがこのような入力に遭遇すると爆発する.

  • 上記の第1のタイプについては、基本的にGC overheadの割合で見ることができます.この場合、CPUは完全にGCによって引き起こされ、GC overheadはほぼ100%に近いので、次のステップはheap dumpをしてverbose GC logとheap dumpを分析します.
    以上の2つ目については、CPUのusage分析を複数回行うことができ、例えば毎回1 sのinterval内のすべてのスレッドがCPUを消費する割合は、CPUが100%であるため、どのスレッドがCPUを大量に消費しているのかを簡単に見ることができ、その後、これらのstacktraceを分析し、基本的にデッドサイクルのコード以外のstackは毎回同じである.デッドサイクル以内の場合はstackに差があります.heap dumpの中の局部変数を結合することによって、パラメータは同時にstackの中の方法の呼び出し関係を読むことができて、死の循環の位置の所在を調べることができます.HashMapについては、基本的に何回かtread dumpで確定し、heap dumpに行ってそのサイクルを見つけることができます.
    第3種については、基本的に第2種と同様に、まずstackの中に特需CPUの操作があるかどうかを観察し、ソースコードを見続けて確認する.最後にheap dumpの中のデータと結びつけて検証します.