記事ディレクトリ
1.理由
毎日コードを入力すると、運用および保守の同僚が突然チームメンバーをグループにまとめて言ったオンラインマシンのメモリが不足し、OOMにより、サービスによって登録されたメッシュクライアントが強制終了され、一部のサービスが異常に呼び出されます。運用と保守の同僚がマシンの負荷をチェックしたところ、私たちのグループのJavaサービスが占有しているメモリが少し異常であることがわかりました。起動コマンド-Xmx128m
は、最大ヒープメモリがわずか128Mであることを指定していますが、プロセス全体が占めるメモリは640Mに達します。これは、明らかに問題です。
2.オンライン調査
操作とメンテナンスのスクリーンショットを投げた後、ポットを捨てることはできません。正直にトラブルシューティングするためにオンラインマシンにログインしてください。メモリ使用量が高すぎる最初に考えたのは、何が起こったのか、ファイルへの出力統計ヒープオブジェクト内内存泄露
で使用jmap -histo $pid > heap.log
し、ファイルを表示して、さまざまなアレイのヒープメモリの大部分を検出し、重大な問題は検出されなかったことです。何でもして、top -H p $pid
コマンドを使用してプロセスで実行されているスレッドのステータスを確認し、最終的に疑わしい場所を見つけました。このJavaサービスでは実際には5000の子スレッドが実行されており、それらのほとんどすべてがスリープ状態です。
この状況で最初に頭に浮かぶのは、スレッドのデッドロックが発生し、リソースの競合によって多数のスレッドがブロックされることです。使用しjstack -l $pid > stack.log
、検索が失望されたファイルを開いて、ファイルにスレッドスタック関連する状態の出力をデッドロックはまったくありません。スレッドのステータスのほとんどはそこにありますがTIMED_WAITING
、行ごとに見下ろすと、疑わしい点も見つかります。次のそのようなOkHttp ConnectionPool
スレッドは、スレッドの数にさえ、あまりにも多く表示されます1082707
"OkHttp ConnectionPool" #1082707 daemon prio=5 os_prio=0 tid=0x00007f564c18f000 nid=0x1a4d in Object.wait() [0x00007f5602cb4000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:460)
at okhttp3.ConnectionPool$1.run(ConnectionPool.java:67)
- locked <0x00000000fc30fb30> (a okhttp3.ConnectionPool)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- <0x00000000fc305f98> (a java.util.concurrent.ThreadPoolExecutor$Worker)
このときのヒープメモリファイルを振り返ると、次のようなレコードが見つかりました。okhttp3.ConnectionPool
この接続プールオブジェクトインスタンスが占めるメモリはそれほど多くはありませんが、300Kしかありません。インスタンスの総数は7876です、問題があることは間違いありません
40: 7876 315040 okhttp3.ConnectionPool
3.コードのトラブルシューティング
疑わしい点は、初期化コンストラクターを呼び出すConnectionPool
、プロジェクトConnectionPool
オブジェクト初期化呼び出しサイトで見つかった検索のオブジェクトの番号です。つまり、各インスタンスが作成され、接続プールが作成されます。プロジェクトコードでは、各RPC呼び出しは再作成されたオブジェクトになるため、非常に明確になります。OkHttpClient
ConnectionPool
OkHttpClient
ConnectionPool
OkHttpClient
OkHttpClient
オブジェクトの原因はJVMの使用後に回復されますがOkHttp
、ソースコードConnectionPool
コンストラクターはデフォルトの最大スレッドアイドル数は5で、keepAlive時間は5分です。つまり、ネットワーク接続が開始された後、接続は5分以内に切断されません。。このように、OkHttpClient
オブジェクトがJVMによって再利用されるConnectionPool
と、サーバーに接続されたActive
状態のスレッドが存在するため、5分以内に再利用されず、当然、スレッドリソースは解放されません。スレッドはメモリスペースを占有するため、時間が蓄積されるとスレッドの数が増加し、プロセスが占有するメモリが自然に増加します。/** * Create a new connection pool with tuning parameters appropriate for a single-user application. * The tuning parameters in this pool are subject to change in future OkHttp releases. Currently * this pool holds up to 5 idle connections which will be evicted after 5 minutes of inactivity. */ public ConnectionPool() { this(5, 5, TimeUnit.MINUTES); }
4.解決策
問題の根本が発見され、修復は自然で簡単です。RPCがOkHttpClient
オブジェクトを再作成するために呼び出すたびにコードをプロジェクトします。これは、静的コードOkHttpClient
オブジェクトを保存する限り、実際にはパフォーマンスの大きな浪費です。各ネットワーク要求は、そのオブジェクトと多重化されます。