序文
前回のオンライン CPU アラームに続いて、今回はサービスが再開されました. 数日落ち着いた後、運用ログ サービスの実行を監視すると、OutOfMemoryError が頻繁に表示されました.これは、一般的に OOM と呼ばれています. OOM が頻繁に発生すると、サービスが利用できない状況に直接陥ります. Skywalking を介してリンク呼び出しを確認すると、基本的にすべてのレポートが赤くなり、基本的に麻痺状態になります.直ちにサービスを停止し、再起動します、B サイド製品であるため、まず会社のビジネスに使用させ、サービスの正常な使用を確保してから、問題を緊急に確認し、根本原因を見つけて修正します。
OutOfMemoryError の理由
まず OutOfMemoryError の理由を理解しましょう。これは 2 つのタイプにすぎません堆内存空间不足
。元空间不足
- ヒープ メモリ領域が不足しています: を意味します
程序存在一直有引用的对象(强引用),主要对象在引用的状态就无法被GC回收,撑爆了-Xmx堆拓展的最大值,内存不足自然就会触发堆内存溢出
。 - メタスペース: Java 8 では、以前のヒープの永続的な生成を置き換えるメタスペースの概念が導入されました. メタスペースはオフヒープ メモリに属しているため、オブジェクト参照は必要ありません. クラスとメタデータはポインターによって表されます. その理由この種の JDK アップグレードの最適化により、永続世代のメモリ オーバーフローが回避されます。
一般的なヒープ メモリ オーバーフローのいくつかの状況
- クエリ データベースから返されるデータの量が多すぎるため、メモリに読み込まれ、メモリ オーバーフローが発生します。
- コードに無限ループがあり、ラージ オブジェクトが参照され、GC で再利用できません。
- リソース リンク プールと io ストリームは、使用後に手動で解放されません。
- 静的コレクション クラスには参照オブジェクトがあり、クリアされていない参照関係が常に存在します。
上記は、一般的なヒープ メモリ オーバーフローのシナリオの一部です. もちろん、時々、私たちが遭遇する問題は奇妙で奇妙です. 一般的な問題は常にめったに遭遇しません.
現象解析
本番環境のエラーログによると、これはMybatisが報告しているメモリオーバーフローで、Mybatisのソースコードを見ると、最下層もいくつかのコレクションクラスを使ってつなぎ合わせたSQLを格納していることがわかるので、もちろんヒープ メモリ オーバーフローが発生する可能性があり、比較的大きな sql ボリュームの場合、受信 sql のセットが非常に大きくなり、再利用できない場合はメモリ オーバーフローが発生します。
主な理由Mybatis拼接SQL的时候生成的占位符和参数对象,存放在Map里,当SQL的参数多导致SQL太长的时候,Map持有这些SQL时间较长,并且多线程同时操作,这时候内存占用就很高,从而发生OOM
Mybatis ソースコード分析
DynamicContext クラスのソース コードを見ると、DynamicContext には、
HashMap を継承し、Map コレクションに相当する ContextMap タイプの別のパラメーター バインディングがあります。 . 簡単に言えば, ForEachSqlNode pass getBindings メソッドは SQL パラメーターとパラメーター プレースホルダーを ContextMap コレクションに均一に配置します. 主な理由は、パラメーターとプレースホルダーが GC によって再利用できず、多数の同時クエリがある場合に OOM が発生するためです.
シナリオの再発
次にオンラインの様子を再現したところ、SQL文をつなぎ合わせてIN内のパラメータを大きくし、実行スレッドを50スレッド作成し、JVMヒープメモリを-Xmx256m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryErrorに設定
ここでコンソールに出力されたログを見ると、サービスは頻繁にフル GC を実行しており、OOM が発生しています。
要約する
問題の原因が判明したので、次のステップはコード SQL を最適化することです.SQL をつなぎ合わせるときにボリュームが大きくなりすぎないようにします.ここで、コードをむやみに記述しないように注意し、SQL文は勝手に書いてはいけません. 時々私たちは問題について考えます.