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的参数。

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐