JVM——hibernate sql 缓存过大致内存溢出

问题描述

最近遇到内存泄漏的问题:在阿里服务器上部署了一个定时爬虫,用 springboot 写的项目;使用 webmagic 爬虫框架,最终数据写入 mysql 并且添加 elasticsearch 索引;当跑到一个月,服务就宕机。所有内存耗尽,程序一直在 FULL GC , 日志中抛出 OOM 异常。

附上一天的 gcviewer 图:

图1-1 gc 最近日志


图1-2 gc 20小时前日志


图1-3 程序一直full gc

从上面的 gc 日志得出:老年代小幅度只增不减,存在内存泄漏。为了快速准确的定位到问题,使用到内存分析工具。


内存分析工具 MAT

MAT 一款功能强大的 java 堆内存分析器,常用于查找内存泄漏以及性能调优。

浅堆(shallow heap)和深堆(retained heap)

浅堆只与对象结构有关,如下图所示,在32位系统中 int 4 字节,对象引用4字节,对象头8字节;那么 String 对象一共是3*4 + 4 + 8 = 24字节,这就是他的浅堆大小,不管char[] 有多少个字符始终是24字节。

图2.2-1 String 结构

深堆是指对象的保留集(直接或间接访问到的所有对象)中所有对象的浅堆大小之和。

支配树(dominator tree)

图2.2-2 左边是引用关系,右边是支配树

简单理解,对象的直接支配者是引用该对象的公共祖先(注意上图中的H)。

删除支配对象,意味着释放以支配者为根节点的整棵对象树。

实际分析:

内存泄漏存在以下特点:老年代越来越大,某些类内存占比大。

程序跑了3天,内存转储文件经MAT 内存泄漏分析报告如下:

图3.1-1 内存泄漏报告

结果查到 QueryPlanCache 中缓存有大量的sql。猜测:程序员使用 id in (....),由于参数数量不同所致 :

图3.1-2 in-sql语句

果然在代码里找到了那段根据id查询的 sql `select ... from .. in ( ....) ` ,参数没有每页处理。

如果采用暴力处理,直接限制了sql的缓存大小就可以防止溢出;或修改查询方式,保证降低sql的缓存数量。

解决方案参考:https://stackoverflow.com/questions/31557076/spring-hibernate-query-plan-cache-memory-usage


参考资料

MAT 使用参考《Java 程序性能优化》 葛一鸣等编著,清华大学出版社。

你可能感兴趣的:(JVM——hibernate sql 缓存过大致内存溢出)