In short, garbage collection (GC)
is the process of JVM
recycling objects that are no longer used and releasing memory GC overhead limit exceeded error
is a member of the java.lang.outofmemoryerror
family, indicating that the JVM
memory is exhausted. Next, let’s look at the causes of the java. Lang. outofmemoryerror: GC overhead limit exceeded
error and how to solve it
Introduction to GC overhead limit exceeded error
outofmemoryerror
is a subclass of java. Lang. virtualmachineerror
, which is thrown when JVM
resource utilization has problems. More specifically, this error is thrown when JVM
takes too long to execute GC
and can only recover very little heap memory. According to the Oracle
official documents, by default, if the java
process takes more than 98%
time to execute GC
, and less than 2%
heap is recovered each time, then JVM
throws this error. In other words, this means that our application runs out of almost all available memory, and the garbage collector takes too long to try to clean it up and fails many times
In this case, users will experience that the application response is very slow, and some operations that usually take only a few milliseconds to complete will take longer to complete. This is because all CPUs
are garbage collecting, so they cannot perform other tasks
Error recurrence
The following code can reproduce the java. Lang. outofmemoryerror: GC overhead limit exceeded
error, the code is as follows:
package com.galaxy.concurrency.jvm;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
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();
}
}
This code is very simple, that is to use a while
dead loop to add random numbers to HashMap
continuously. Before executing the main
method, first set the JVM
parameter to - xmx300m - XX: + useparallelgc
( JVM
heap to 300MB
, GC
algorithm to parallelgc
), and then run the main
method, you will encounter java.lang.outofmemoryerror: GC overhead limit exceeded
error:
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)
I use the MacOS
operating system, 2-core 8g
hardware configuration, JDK
version is 1.8
. Due to different test environments, if you encounter java.lang.outofmemoryerror: Java heap space
errors, you can adjust - Xmx
appropriately to reproduce java.lang.outofmemoryerror: GC overhead limit exceeded
errors. In order to better understand different garbage collection algorithms ( garbage collection algorithms
), you can refer to Oracle
java garbage collection basics tutorial
Solutions
The ideal solution is to find the problems in the application by checking the code that may have memory leak
Which objects in the application occupy most of the heap spaceWhat are the objects in the application that occupy large portions of the heap?
)
In which parts of the source code are these objects usedIn which parts of the source code are these objects being allocated?
)
We can also use automated graphical tools, such as jvisualvm and jconsole, which can help detect performance problems in code, including java. Lang. outofmemoryerror
The last method is to increase the heap size by changing the JVM
startup configuration, or to turn off GC overhead limit exceeded
by adding the - XX: - usegcoverhead limit
option in the JVM
startup configuration. For example, the following JVM
parameters provide 1GB
heap space for java
Applications:
java -Xmx1024m com.xyz.TheClassName
The following JVM
parameters not only provide 1GB
heap space for java
applications, but also add the - XX: - usegcoverhead limit
option to turn off GC overhead limit exceeded
:
java -Xmx1024m -XX:-UseGCOverheadLimit com.xyz.TheClassName
However, adding the - XX: - usegcoverheadlimit
option will cure the symptoms but not the root cause. JVM
will eventually throw out the java.lang.outofmemoryerror: Java heap space
error. If the above outofmemorygclimitexceed
class test results are as follows:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.lang.Integer.toString(Integer.java:403)
at java.lang.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)
In a word, if there is a memory leak in the actual application code, the methods listed above will not solve the problem. On the contrary, we will delay the error. Therefore, it is wiser to thoroughly reassess the memory usage of the application
Online accident solving process and summary
Exception log
This problem has recently occurred online. The following is the exception log of 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 - 服务器端异常!
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.handleInternal(RequestMappingHandlerAdapter.java:783)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
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)
Trouble shooting
oom
occurs under linux
, not necessarily because java
service consumes memory, but also because other programs apply for a lot of memory. At this time, all applications need more memory than physical memory. Then java
service consumes a lot of memory and is found by linux
operating system, it will be kill
, This is a memory protection mechanism adopted by linux
to avoid system crash caused by physical memory overload. This mechanism is called oom killer
. For the specific reasons, please refer to the oom killer under linux in the resources at the end of this paper. In my previous work, I encountered the situation that
elasticsearch
data storage service and fluent
log collection service were deployed on the same server, and elasticsearch
service was killed
due to fluent
memory leak. Later, Review
created fluent
, solved the memory leak problem, and deployed it separately from elasticsearch
service. After confirmation, our service is deployed separately, so we will turn our attention to JVM
memory configuration. This application has a small amount of visits, and the online server memory is 4G
. First, we used the command tool of JDK
to check the JVM
configuration
#Find the process number of spacex.jar
sudo jps
# Check the jvm parameter, pid is the process number of spacex.jar
sudo jinfo -flags pid
JVM
is configured as follows.
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
Where - XX: maxheapsize = 1006632960kb
, that is, 960mb
, check the startup script and find java_ Opts
is not added to the java
startup command, java_ Opts
we set the heap memory as - xms2048m - xmx2048m
, but this variable is not referenced by java
startup command, so the application uses the default memory configuration after startup. The comparison before and after the startup script modification is as follows. Pay attention to the difference of nohup Java
line:
(1) Before revision:
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 &
(2) After modification:
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.authenticate=false -Dcom.sun.management.jmxremote.port=8045 -Djava.rmi.server.hostname=$LOCAL_IP -jar ${SPACEXJAR} > ${SPACEXLOG}/stdout.log 2>&1 &
Measures and summary after the event
1. After modifying the script and starting the service in the production environment, use the JPS
and Jinfo
commands to check whether the JVM
parameters are effective
2. The script to start the service must be tested completely and rigorously to make sure that each line of command and related variables take effect
References
OOM
1. The 1-3
section is compiled from outofmemoryerror: GC overhead limit exceeded
2. Oracle
official summary of oom
exception and handling method: understand the outofmemoryerror exception
Oom killer under Linux
1. Understanding and configuring oom killer under Linux
2、LinuxMM: OOM_ Killer