docker+spring boot线上环境内存爆涨问题排查
docker中只运行了一个spring boot应用程序,但运行一段时间后内存涨到5G使用jmap -histo 1查看,得到total的总消耗才1.5G使用jmap -histo:live 1查看后,得到total的总消耗才150M,说明JVM一直没有对内存中可回收的对象进行回收处理,经过jmap -histo:live后相当于手动调用了一次GC,内存显著下降,初步断定是docker中的...
docker中只运行了一个spring boot应用程序,但运行一段时间后内存涨到5G
使用jmap -histo 1查看,得到total的总消耗才1.5G
使用jmap -histo:live 1查看后,得到total的总消耗才150M,说明JVM一直没有对内存中可回收的对象进行回收处理,经过jmap -histo:live后相当于手动调用了一次GC,内存显著下降,初步断定是docker中的java应用程序没有设置jvm参数,这会导致由JVM根据运行情况去自动分配了内存,在物理内存足够的情况下,JVM出于对应用程序性能的考虑并没有调用FGC,这才会导致内存一直增长。
使用jmap -dump:[live,]format=b,file=<filename>导出dump文件,并在eclipse中使用MAT工具进行分析,也没有发现明显示的内存泄漏
然后再通过jstat -gc 1 5000进行查看,发现内存在增长时,FGC一直未被调用过
解决方案:
要设置堆栈大小,我们可以在JAVA_OPTS环境变量设置Xmx参数:
docker run -e "JAVA_OPTS=-Xmx700M -Xms700M -Xmn200M -XX:MaxMetaspaceSize=100M -XX:MetaspaceSize=100M -XX:HeapDumpPath=/tmp/document_server.dump" app-image
这就OK了吗? 非也!Java实际上忽略了我们的变量,并启用了默认值。
其原因在于Spring Boot。 Spring Boot会将任何环境变量传递给应用程序 - 但是我们的JAVA_OPTS并非是针对应用程序的,而是针对Java runtime本身的。 所以我们需要使用$ JAVA_OPTS变量来 `exec java`。 这需要对Dockerfile进行一些小改动:
ENTRYPOINT exec java $JAVA_OPTS -jar build/libs/{app name}.jar
现在,当我们运行容器时,$JAVA_OPTS被传递给runtime,正如我们所料。 我们设置的其他任何环境变量当然也可以被Spring Boot应用拿到。
再通过top命令查看,内存一直控制在1G左右,查看jstat -gc 发现FGC也有被多次调用,当然为了减少FGC的次数,可以适当调高JVM的内存参数
在docker中运行应用程序一定要设置jvm的参数。
更多推荐
所有评论(0)