高同時実行サービスの最適化: 読み取り/書き込みロックによって発生するメモリ リークの詳細な説明

高同時実行サービスの最適化: 読み取り/書き込みロックによって発生するメモリ リークの詳細な説明

[[414812]]

JVM 関連の例外は、最前線の開発者にとって常に頭痛の種でした。なぜなら、ビジネス コードの場合、JVM の動作は基本的にブラック ボックスだからです。例外が発生した場合、直感的に問題を確認して見つけることは困難です。だからこそ、その内部ロジックを研究する必要があるのです。

この記事では、最近のオンライン JVM メモリ リークの例を使用して分析します。

パート1 オンラインサーバーが警報を発令

ある日、同僚が助けを求めて私に来ました。あるシステムでは、何の警告もなく一連のアラームが発生していることが判明しました。旧世代のマシンのメモリ使用量がしきい値を超えました。

1.1 まずはパフォーマンスを見る

旧世代のメモリ使用量

7 月中旬までは、メモリ使用量は比較的正常であり、各 GC で古い世代のオブジェクトの大部分をリサイクルできたことがわかります。

月の中旬以降、旧世代のメモリが徐々に増加し、解放できなくなります。明らかに、これはオブジェクトが正常にリサイクルされていないことが原因であるはずです。

メモリリーク〜

1.2 何をすべきか

新しく立ち上げたプロジェクトでこのような問題が発生した場合、影響は比較的小さいため、コードを直接ロールバックして出血を止めることが最優先となります。

しかし、このプロジェクトは明らかに何日もオンラインになっており、その間にどれだけの要求があったかは不明です。また、最近のトラフィックの増加により問題が発生しているため、トラフィックが顧客に開放されたことを意味します。

ロールバックは不可能なので、急いで問題を特定し、オンラインで修正してください。

パート2 位置決めの問題

一般的な手順:

  • ダンプファイルを取得する
  • MATなどのツールを使用して、メモリを過剰に占有する異常なオブジェクトとその参照関係を見つける
  • 例外オブジェクトに関連付けられたコードで起こりうる問題を分析する

ただし、ダンプされたファイルは 10G を超えて大きすぎるため、MAT は基本的に無力であり、手動で分析するために印刷することしかできません。

2.1 問題コードを見つける

jmap 結果ビュー

幸いなことに、異常な物体は非常に明白です。 Point オブジェクトと GeoDispLocal オブジェクトのインスタンスは数百万あります。まず、これら 2 つのオブジェクトがコード内でどのように使用されるかを見てみましょう。

  1. プライベート静的最終 CacheMap<String, List<GeoDispLocal>> NEAR_DISTRICT_CACHE = 新しい CacheMap<String, List<GeoDispLocal>>(3600 * 1000, 1000);
  2.  
  3. プライベート静的最終 CacheMap< Integer , Point> LOCAL_POINT_CACHE = new CacheMap< Integer , Point>(3600 * 1000, 6000);

それらはすべて現在のキャッシュ CacheMap に格納されており (メモリ リークの一般的な原因は、静的コレクションによって保持され、リサイクルできないことです)、ダンプ ファイル内の CacheMap.Entry も非常に高くなります。

CacheMap が最優先の容疑者です。まず、このキャッシュ クラスがどのようなものかを見てみましょう。

  1. パブリッククラスCacheMap<K, V> {
  2. プライベートファイナルロング有効期限Ms;
  3. プライベート LRUMap<K, CacheMap.Entry<V>> valueMap;
  4. //その他は省略
  5. }

内部的には LRU 機能を備えたマップに依存します。どのように実装されますか?

  1. パブリッククラスLRUMap<K, V>はLinkedHashMap<K, V>を拡張します。
  2. プライベート静的最終long serialVersionUID = 1L;
  3. プライベート最終int最大容量;
  4. // このマップは拡大しません
  5. プライベート静的最終フロートLOAD_FACTOR = 0.99f;
  6. プライベート最終ReadWriteLockロック = 新しいReentrantReadWriteLock();
  7.  
  8. パブリックLRUMap( int最大容量){
  9. super(maxCapacity、LOAD_FACTOR、 true );
  10. this.maxCapacity = 最大容量;
  11. }
  12.  
  13. @オーバーライド
  14. 保護されたブール値の長男エントリを削除します(java.util.Map.Entry<K, V> 長男) {
  15. 戻る サイズ() > 最大容量;
  16. }
  17.  
  18. @オーバーライド
  19. パブリックV get(オブジェクトキー) {
  20. 試す {
  21. ロック。読み取りロック()。ロック();
  22. super.get(キー)を返します
  23. ついに
  24. ロックを解除します。
  25. }
  26. }
  27.  
  28. @オーバーライド
  29. 公開V put(Kキー、V値) {
  30. 試す {
  31. ロック。writeLock()。ロック();
  32. super.put(キー、値)を返します
  33. ついに
  34. ロック。writeLock()。ロック解除();
  35. }
  36. }
  37. //クリアを削除
  38. }

内部的には、LinkedHashMap を使用して実装された LRU キャッシュです。コメントによると、容量制限があり拡張性のないMAPを構築するのが目的だそうです(Baiduで調べたらネット上の実装と全く同じですね~)。では、実際の状況は本当に想像通りなのでしょうか?

2.2 LinkedHashMap によって実装された LRUMap は便利ですか?

容量と拡張に関する設定を見てみましょう。なぜ設計者は LRUMap が拡張しないと考えるのでしょうか?

  1. //**容量と拡張に関連するパラメータを抽出**
  2. //ユーザーが期待する最大容量
  3. プライベート最終int最大容量;
  4. //荷重係数
  5. プライベート静的最終フロートLOAD_FACTOR = 0.99f;
  6. //コンストラクタでLinkedHashMapを呼び出して初期化する
  7. super(maxCapacity、LOAD_FACTOR、 true );
  8.  
  9. @Override // 最も古い要素条件を削除するメソッドをオーバーライドします
  10. 保護されたブール値の長男エントリを削除します(java.util.Map.Entry<K, V> 長男) {
  11. //LinkedHashMap.size が制限容量より大きい場合は削除を実行します
  12. 戻る サイズ() > 最大容量;
  13. }

実際の使用法に応じてインスタンス化してみましょう。

  • maxCapacity=6000 は必要な要素の最大容量です。
  • load_factor=0.99 負荷係数。
  • マップの内部しきい値 = 8192 * 0.99 = 8110 は、次の拡張の容量サイズです。 (マップ内のテーブル容量の実際のサイズは、6000 に最も近い 2 の累乗、つまり 8192 です)。

LRU 条件関数がオーバーライドされるため、サイズが 6000 を超えると LRU 置換が実行されます。したがって、理論上、サイズが 8110 に達することはありません。

同時読み取り/書き込み競合を解決するにはどうすればよいでしょうか?

  1. // 読み取り/書き込みロック
  2. プライベート最終ReadWriteLockロック = 新しいReentrantReadWriteLock();
  3.   
  4. パブリックV get(オブジェクトキー) {
  5. 試す {
  6. ロック。読み取りロック()。ロック();
  7. super.get(キー)を返します
  8. ついに
  9. ロックを解除します。
  10. }
  11. }
  12.  
  13. 公開V put(Kキー、V値) {
  14. 試す {
  15. ロック。writeLock()。ロック();
  16. super.put(キー、値)を返します
  17. ついに
  18. ロック。writeLock()。ロック解除();
  19. }
  20. }

同時実行時の読み取り/書き込み競合を解決するために、設計者はクエリおよび変更メソッドにロックを追加しました。パフォーマンスを考慮するために、読み取り/書き込みロックが使用されました。つまり、読み取りロックは get 中に追加され、書き込みロックは put/remove 中に追加されました。

全体的な設計により、LRUMap の固定容量と同時操作の問題がうまく解決されているように見えますが、真実は何でしょうか?

実際、この問題はずっと以前から分析されてきました[1]。これは、LinkedHashMap が読み取り/読み取り操作を実行するときに、LRU を維持するために要素を変更するため、つまり、要素をリンク リストの末尾に移動するためです。これは読み取り書き込み同時実行の問題につながりますが、この説明は漠然とした感じがするので、これに基づいて読み取り書き込み同時実行の問題をより詳しく説明することにしました。

2.3 LinkedHashMap メモリリーク解析

読み取り/書き込みロックが追加されても機能しないのはなぜですか?

ここでは、読み取り/書き込みロックの概念と適用可能なシナリオを明確にする必要があります。読み取り/書き込みロックを使用すると、複数のスレッドが読み取りロックを共有できるため、書き込みよりも読み取りが多い状況に適しています。 (読み取り操作によってストレージ構造が変更されないことが前提です)

したがって、get 操作で問題が発生します。 LinkedHashMap の get 操作は、LRU 機能を実装するために書き直されます。取得後、現在のノードはリンク リストの末尾に移動されます。

さあ、同志たち、これは明らかに書き込み操作なので、読み取りロックを追加することはまだ役に立ちますか?

つまり、複数のスレッドが入り込み、変更が行われると、どのような影響があるのでしょうか?同時実行の問題は発生しないでしょうか?

次に、ノード移動コードを比較して、マルチスレッドでの同時実行の問題を詳細に分析します。

最後に到達したらノードを移動する

実際の逆アセンブリ分析は次のとおりです。マルチスレッドの状況でメモリ リークが発生する理由は次のとおりです。

タイムスライスでのマルチスレッドの取得実行

スレッド 1 が最初の 2 つのステートメントを実行した後、タイム スライスを放棄したことがわかります。スレッド 2 が p.after=null を実行すると、タイム スライスが再度放棄されました。このように、a は次の <2,B> ノードになるはずでしたが、マルチスレッドでは null になりました。最終的に、次の 2 つのノードがリンク リストから除外され、削除操作がそれらに到達できず、メモリ リークが発生しました。

認証コードはここには掲載しません。ご興味がございましたら、ぜひご自身でお試しください。

パート3の要約

問題が特定されたので、このメモリ リークをどのように修正すればよいでしょうか?

読み取り/書き込みロックはミューテックス ロックに変更できます。あるいは、分散ストレージを直接使用した場合、速度はどのくらい遅くなるのでしょうか?これは便利で簡単で、マシンのメモリを節約するために LRUMap を自分で構築する必要がありません。

すべてのエッセイは面接のためだけのものではなく、あらゆるオンラインの問題のトラブルシューティングの基礎となります。八本足のエッセイの役割を誤解しないでください。 。 。

この記事はWeChatの公開アカウント「Coder's Technical Journey」から転載したものです。以下のQRコードからフォローできます。この記事を転載する場合は、Coder’s Technology Road のパブリック アカウントにお問い合わせください。

<<:  クラウドコンピューティングの8つの一般的な用途

>>:  クラウドネイティブはクラウドコンピューティングの質的変化を促進し、デジタル変革への道を提供します

推薦する

Zhihu クラウドネイティブアーキテクチャプラクティス

著者 |王 陸編集者 |王瑞平この記事は、Zhihu のクラウド ネイティブ アーキテクチャの責任者...

感情マーケティングとソーシャルメディアマーケティングを組み合わせた新しいモデルについての簡単な議論

この世に愛とは何でしょう?それは、生死を問わず人々を結びつけるものです!すべては「愛」という言葉から...

AppleがiOS 9を正式にリリース

Appleは今朝早く、iOS 9の正式版を世界中のユーザーに公開し、長い間待ち望んでいた多くのユーザ...

uuuvps: 香港 cn2 無制限トラフィック VPS、最低 40% オフ、Windows システムをサポート

uuuvpsは3年以上運営されています。公式が大量メールの形でプロモーション活動を発表するのはこれが...

男女の恋愛観からみたウェブサイトSEOの原則をまとめる

愛は素晴らしく、至福のものです。誰もが情熱的な愛を望んでいるので、安定した関係を築くためには、多くの...

ブランドマーケティングにおける製品のジレンマ!

インターネット上での情報交換の加速により、さまざまなコンテンツを入手する時間が短縮され、あらゆる面で...

セルフメディアで働く人はQQグループで遊ぶ人ほど優秀ではない:簡単に月に10万元を稼ぐ

時々、私はいくつかのQQグループに引き込まれます。そこでは、服の組み合わせやショッピング体験について...

URL 送信チャネル ツールは記事の掲載を促進できますか?

最近のBaiduのアルゴリズムの継続的な改善により、多くのウェブサイトが破壊されました。ほとんどのウ...

SEOも違う

山を正面から見ると山頂が見えます。SEOも同じです!警察は赤警察と黒警察に分かれていますが、SEOも...

Weiboマーケティング:お金とブランドを作る方法についての面白い話

前回の記事では、「SEOについて:WeiboマーケティングにおけるSEOランキング」を書き、多くの友...

2021年から2022年にかけて、14人のCEOから見た中国のSaaS市場

SaaS が 2021 年に入ると、風が再び強まります。資本市場の熱狂はそれ自体が物語っているが、そ...

これらの刺激的なコピーライティングのテクニックを学べば、あなたの心をつかめない人はいないでしょう。

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています人をからか...

InceptionHosting-1GメモリXEN PV/月額5.2ドル

inceptionhosting は評判が良く、VPS 品質が保証されているサービス プロバイダーで...

ソフト記事プロモーション:ユーザーの感情を動かすには?

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています「一生懸命...