OutOfMemoryError: GC Overhead Limit Exceededエラー解決。
簡単に説明すると
Garbage Collection (GC)
は
JVM
使われなくなったオブジェクトを再利用して、メモリを解放する処理。
GC Overhead Limit Exceeded error
は
java.lang.OutOfMemoryError
ファミリーであることを示す
JVM
のメモリが枯渇しています。次に、このエラーの原因となった
java.lang.OutOfMemoryError: GC Overhead Limit Exceeded
のエラーとその修正方法について説明します。
GC Overhead Limit Exceeded Errorの紹介
OutOfMemoryError
は
java.lang.VirtualMachineError
のサブクラスです。
JVM
は、リソースの使用状況に問題がある場合にスローされます。より具体的には、このエラーは
JVM
を実行するのに時間がかかりすぎる。
GC
で、ヒープメモリがわずかしか回収できない場合にスローされます。このため
Oracle
の公式ドキュメントでは、デフォルトで、もし
Java
プロセスで消費される
98%
実行時間が長くなる
GC
よりも少ないだけであり
2% of the time
のヒープが回収された場合
JVM
はこのエラーを投げます。言い換えれば、これはアプリケーションが利用可能なメモリをほとんど使い果たし、ガベージコレクタがそれを掃除するのに時間がかかりすぎて、複数回失敗したことを意味します。
この場合、ユーザーはアプリケーションからの応答が非常に遅くなり、通常は数ミリ秒で完了する操作が、このケースでは非常に長くかかることになります。
CPU
はガベージコレクションされるため、他のタスクを実行することができません。
エラーの再発
次のコードを再現することができます。
java.lang.OutOfMemoryError: GC Overhead Limit Exceeded
を以下のコードでエラーにします。
package com.galaxy.concurrency.jvm;
import java.util.HashMap;
import java.util.Map;
import java.util;
public class OutOfMemoryGCLimitExceed {
public static void addRandomDataToMap() {
Map<Integer, String> dataMap = new HashMap<>();
Random r = new Random();
while (true) {
dataMap.put(r.nextInt(), String.valueOf(r.nextInt())));
}
}
public static void main(String[] args) {
addRandomDataToMap();
}
}
このコードはシンプルで
while
に行き続けるデッドループ。
HashMap
乱数を追加していく。を実行した後
main
メソッドを設定します。
JVM
パラメータを
-Xmx300m -XX:+UseParallelGC
(その
JVM
ヒープが
300MB
と
GC
このアルゴリズムは
ParallelGC
) を実行します。
main
メソッドを呼び出すと
java.lang.OutOfMemoryError: GC Overhead Limit Exceeded
エラーになります。
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.HashMap.newNode(HashMap.java:1747)
at java.util.HashMap.putVal(HashMap.java:631)
at java.util.HashMap.put(HashMap.java:612)
at com.galaxy.concurrency.jvm.OutOfMemoryGCLimitExceed.addRandomDataToMap(OutOfMemoryGCLimitExceed.java:13)
at com.galaxy.concurrency.jvm.OutOfMemoryGCLimitExceed.main(OutOfMemoryGCLimitExceed.java:18)
を使っています。
MacOS
オペレーティングシステムを指定します。
2-core 8G
のハードウェア構成は、以下のとおりです。
JDK
バージョンは
1.8
. テスト環境の違いから、もし
java.lang.OutOfMemoryError: Java heap space
エラーが発生した場合は
-Xmx
を再現できるように適切に調整してください。
java.lang.OutOfMemoryError: GC Overhead Limit Exceeded
というエラーが発生します。異なるガベージコレクションアルゴリズムをより理解するために (
Garbage Collection Algorithms
) を参照するとよいでしょう。
Oracle
s
Javaガーベッジコレクションの基本
チュートリアルをご紹介します。
ソリューション
理想的な解決策は、メモリリークの可能性があるコードを調査することで、アプリケーションが抱える問題を発見することです。
-
アプリケーション内のどのオブジェクトがヒープスペースをほとんど占有しているか?(その
What are the objects in the application that occupy large portions of the heap?
) -
アプリケーションの中でヒープの大部分を占めているオブジェクトは何ですか? (
In which parts of the source code are these objects being allocated?
)
また、以下のような自動化されたグラフィックツールも使用できます。
JVisualVM
は、その
JConsole
を含む、コードのパフォーマンス問題を検出するのに役立ちます。
java.lang.OutOfMemoryError
.
最後の方法は
JVM
の起動時設定を変更してヒープサイズを大きくするか、または、ヒープサイズを変更するために
JVM
スタートアップのコンフィギュレーションでヒープサイズを増加させるか、または
-XX:-UseGCOverheadLimit
オプションで
GC Overhead limit exceeded
. 例えば、次のような
JVM
パラメータは
Java
アプリケーションが提供する
1GB
ヒープ空間。
java -Xmx1024m com.xyz.TheClassName
以下は
JVM
パラメータは
Java
アプリケーションは
1GB
ヒープスペースが増え、さらに
-XX:-UseGCOverheadLimit
をオフにするオプションがあります。
GC Overhead limit exceeded
: は、その
java -Xmx1024m -XX:-UseGCOverheadLimit com.xyz.TheClassName
しかし
-XX:-UseGCOverheadLimit
オプションは問題を解決しません。
JVM
を投げることになります。
java.lang.OutOfMemoryError: Java heap space
というエラーが発生します。もしあなたが
OutOfMemoryGCLimitExceed
クラスのテスト結果は
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.lang.Integer.toString(Integer.java:403)
String.valueOf(String.java:3099)
at com.galaxy.concurrency.jvm.OutOfMemoryGCLimitExceed.addRandomDataToMap(OutOfMemoryGCLimitExceed.java:16)
at com.galaxy.concurrency.jvm.OutOfMemoryGCLimitExceed.main(OutOfMemoryGCLimitExceed.java:21)
まとめると、実際のアプリケーションのコードにメモリリークがある場合、上記のような方法では問題を解決できないどころか、バグを先送りしてしまうことになります。したがって、アプリケーションのメモリ使用量を完全に見直す方が賢明です。
インラインでのインシデント解決プロセスと概要
例外ログ
最近、LINEでこの問題が発生したのですが、以下がその例です。
java.lang.OutOfMemoryError: GC overhead limit exceeded
その時の例外ログです。
2019-04-03 10:48:21,253 [http-nio-8080-exec-121] ERROR c.u.n.s.c.c.GlobalExceptionHandler 44 - Server-side exception!
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: GC overhead limit exceeded
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1006)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
at org.apache.phoenix.schema.types.PDataType.isBytesComparableWith(PDataType.java:92)
at org.apache.phoenix.schema.types.PDataType.coerceBytes(PDataType.java:832)
at org.apache.phoenix.schema.types.PDataType.coerceBytes(PDataType.java:822)
at org.apache.phoenix.compile.UpsertCompiler$4.execute(UpsertCompiler.java:1011)
at org.apache.phoenix.jdbc.PhoenixStatement$2.call(PhoenixStatement.java:355)
at org.apache.phoenix.jdbc.PhoenixStatement$2.call(PhoenixStatement.java:338)
at org.apache.phoenix.call.CallRunner.run(CallRunner.java:53)
at org.apache.phoenix.jdbc.PhoenixStatement.executeMutation(PhoenixStatement.java:336)
at org.apache.phoenix.jdbc.PhoenixPreparedStatement.executeUpdate(PhoenixPreparedStatement.java:199)
at com.ucar.nosql.spacex.yarn.plugins.metric.timeline.PhoenixHBaseAccessor.commitMetrics(PhoenixHBaseAccessor.java:188)
at com.ucar.nosql.spacex.yarn.plugins.metric.timeline.PhoenixHBaseAccessor.commitMetricsFromCache(PhoenixHBaseAccessor.java:138)
at com.ucar.nosql.spacex.yarn.plugins.metric.timeline.PhoenixHBaseAccessor.insertMetricRecordsWithMetadata(PhoenixHBaseAccessor. java:348)
at com.ucar.nosql.spacex.yarn.plugins.metric.timeline.HBaseTimelineMetricsService.putMetrics(HBaseTimelineMetricsService.java:299)
at com.ucar.nosql.spacex.yarn.modules.monitor.controller.SinkConsumerController.postMetrics(SinkConsumerController.java:71)
at sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle( ServletInvocableHandlerMethod.java:102)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod( RequestMappingHandlerAdapter.java:877)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod( org.springframework.web.servlet.mvc.method.annotation.Re
トラブルシューティング
Linux
の下で発生します。
OOM
は、必ずしも
Java
サービスがメモリを大量に消費するためだけでなく、他のプログラムが大量のメモリを要求しているため、すべてのアプリケーションが物理メモリよりも多くのメモリを必要とする場合、そして
Java
サービスがメモリ不足であることと
Linux
オペレーティングシステムによって発見されます。
kill
である
Linux
物理メモリの過負荷によるクラッシュを回避するためのメモリ保護機構で、この機構は
OOM Killer
理由は、この記事の最後にある参考文献に記載されています。
OOM Killer on Linux
のセクションを参照してください。以前、仕事で遭遇したことがある
ElasticSearch
データストレージサービスや
Fluentd
ログ収集サービスが同じサーバーに展開されている場合は
Fluentd
によるメモリリーク
ElasticSearch
サービスは
kill
の場合、Laterは
review
アップ
Fluentd
というメモリリーク問題を解決するコードです。
ElasticSearch
サービスを別途デプロイして解決しました。このサービスを別にデプロイしていることを確認した後、次に目を向けるのは
JVM
のメモリ構成です。このアプリケーションはアクセスが多くないので、オンラインサーバーのメモリは
4G
でスタートし
JDK
に付属するコマンドツールは
JVM
コンフィギュレーションです。
# Find the process number of spacex.jar
sudo jps
#Find the jvm parameter, pid is the process number of spacex.jar
sudo jinfo -flags pid
JVM
Attaching to process ID 16022, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13
Non-default VM flags: -XX:CICompilerCount=2 -XX:InitialHeapSize=62914560 -XX:+ManagementServer -XX:MaxHeapSize=1006632960 -XX:MaxNewSize= 335544320 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=20971520 -XX:OldSize=41943040 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
Command line: -Dcom.sun.management.jmxremote -Dcom.sun.management.snmp.port=8044 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun. management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=8045 -Djava.rmi.server.hostname=10.212.27.54
構成は以下の通りです。
-XX:MaxHeapSize=1006632960KB
ここで
960MB
である。
JAVA_OPTS
ブートスクリプトを確認したところ
java
が追加されていませんでした。
JAVA_OPTS
は、start コマンドの内部で
-Xms2048m -Xmx2048m
ヒープメモリを
java
という変数がありますが、その変数は
nohup java
startコマンドを参照しているため、アプリケーションの起動後はデフォルトのメモリ構成が使用されます。
JAVA_OPTS="-server -Xms2048m -Xmx2048m -Xmn512m -Xss256k -XX:PermSize=256m -XX:MaxPermSize=256m -XX:SurvivorRatio=8 -XX:+ UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX: CMSInitiatingOccupancyFraction=70 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${SPACEXLOG}/"
cd /usr/local/frame/spacex
nohup java -Dcom.sun.management.jmxremote -Dcom.sun.management.snmp.port=8044 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun. management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=8045 -Djava.rmi.server.hostname=$LOCAL_IP -jar ${SPACEXJAR } > ${SPACEXLOG}/stdout.log 2>&1 &
1行の違いです。
(1)修正前。
JAVA_OPTS="-server -Xms2048m -Xmx2048m -Xmn512m -Xss256k -XX:PermSize=256m -XX:MaxPermSize=256m -XX:SurvivorRatio=8 -XX:+ UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX: CMSInitiatingOccupancyFraction=70 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${SPACEXLOG}/"
cd /usr/local/frame/spacex
nohup java ${JAVA_OPTS} -Dcom.sun.management.jmxremote -Dcom.sun.management.snmp.port=8044 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.ssl=false Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=8045 -Djava.rmi.server.hostname=$LOCAL_IP -jar ${ SPACEXJAR} > ${SPACEXLOG}/stdout.log 2>&1 &
(2) 改造後。
jps
イベント後の対策とまとめ
1. 修正スクリプトが完了し、本番環境でサービスを開始した後に
jinfo
と
JVM
コマンドで表示します。
1-3
パラメータは有効です
2. サービスを開始するスクリプトは、コマンドのすべての行と関連する変数が有効であることを確認するために、完全かつ厳密にテストする必要があります。
参考文献
OOM
1. インテキスト
Oracle
セクションは
OutOfMemoryError: GCオーバーヘッドの制限を超えました
記事
2.
OOM
の公式サマリーです。
OOM
例外の発生と処理
OutOfMemoryError例外を理解する
LinuxでのOOMキラー
関連
-
要素 popover がクリックされると表示されない 問題が報告される 未定義のプロパティ '$refs' を読み取ることができない
-
shell あいまい出力リダイレクト
-
AttributeError: 'dict' オブジェクトは 'append' という属性を解決していません。
-
Python標準ライブラリ(各種モジュールの超定番入門書)
-
python reports an error: 'list' object has no attribute 'shape'
-
id 'com.android.library' を持つプラグインが見つかりません。
-
raise NotImplementedError
-
簡単な操作でprotobufのバージョンの問題を解決するために、コピーして貼り付けることができます。
-
init()でエラーが発生しました。ログはすでに使用中です エラー解決
-
STM32 学習 0 未定義識別子 "..." 使用時のエラー 解決方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
react error TypeError: 未定義のプロパティ 'setState' を読み取ることができません。
-
スレッド "main "での例外を伴うEclipseでのMain関数の実行をテストする java.lang.ArrayIndexOutOfBoundsException:0
-
Handlerが抽象的でインスタンス化できないエラーの対処法について!
-
Android Studio が GIT をコミットするとき、このリポジトリでは別の git プロセスが実行されているようです(例:エディタを開いている)。
-
AssertionError [ERR_ASSERTION]: タスク関数を指定しなければならない、gulpのバージョンが一致しない
-
JAVA の小さな問題を解決する
-
ImportError: virtualenv を使用して仮想環境を作成する際に、urllib3 という名前のモジュールがないエラーが発生します。
-
liunx, makeでmysqlをインストール *** ターゲットが指定されておらず、makefileも見つかりませんでしたので停止しました。
-
[Python Basic] ValueError: 非キーワード引数は2つしか受け付けません。
-
VSコードデバッグが開始できない