最近在项目组测试有开发一个导表检查的需求,大致是在策划提交表更新到svn之后,svn发送post-commit到策划表数据服务,而后策划表数据服务下载excel文件,更新数据之后,将导表检查下游服务所需的检查数据进行整合,然后post到导表检查服务。在这个过程中,由于做的仓促没有考虑设计,最后策划表数据服务的JVM发生了OOM问题。经过一番排查改进了设计,解决了这个问题。
JVM的内存结构跟GC流程是老生常谈的话题。Java8去掉了永久区的设定,更改为了Metaspace存储类的元信息、常量等内容,而剩下来依然是新区eden和老区old。当eden区空间满的时候触发young gc,将eden区根节点不可达的对象清除,存活的对象转到survivor区。survivor区的对象如果经历了某一个数(可配置,最高15)的young gc后仍然存活,就会转到old区。如果短时间内创建大量对象,且eden区放不下,young gc没有清除过多数据的情况下,多余的数据会转到old区。如果old区也放不下,就出现OOM(Out Of Memory)。一般业务层面导致OOM的原因基本上是一次产生大量对象,或者是内存泄露,没有及时清除不需要的数据引用。
策划表数据的业务逻辑上是下载数据->更新数据->进行导表检查的一个流程,其中下载、更新数据这一环节会产生大量的对象,而同样进行导表检查时为了发导表检查所需的数据,也会产生大对象——需要将数据序列化为json字符串。在这个流程里自己犯了几个错误导致OOM的问题,具体如下: