内存泄漏问题及解决

来源:互联网 发布:大堀惠 知乎 编辑:程序博客网 时间:2024/06/07 07:08

背景

之前只是在书上,或者网上资料中看到过内存泄漏,日常开发还没有注意或经历过这种问题,使用常规的SpringMVC做常规的项目,很少会发生。但最近的一次项目中发生了一次内存泄露问题。下面就把经历的内容和解决的过程记录一下,以后来参考下。如果原因不准确,以后再来修改。项目经过一段时间,会自动关停。GC后有时日志里会记录 Out of Memory Error

解决过程

第一阶段

开始怀疑的点包括

1. 线程中的httpclient连接失败,反复请求,占内存太高2. 大量线程开启,没有释放

调查后,上述问题均没有出现异常,开始采用逐行代码测试,监控内存状况(使用 jstat -gcutil 26256 2000)

第二阶段

经过排查,发现问题出现在JsonString转object的过程。
项目中使用jackson的ObjectMapper做解析

    ObjectMapper jsonMapper = new ObjectMapper();    jsonMapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);    jsonMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);    JdpResponse jdpResponse = jsonMapper.readValue(strJdpResponse, JdpResponse.class);

查了很多资料,没发现转换过程有内存问题,也是用Gson进行过测试

 Gson gson = new Gson();    JdpResponse jdpResponse = gson.fromJson(strJdpResponse, JdpResponse.class);

问题照旧

手写JsonObject,同样的问题。但是去掉这部分数据转换,内存几乎不再增长。而单独测试json转换是没有内存溢出的问题。

开始怀疑这里是内存溢出的现象点,但不是根本原因

第三阶段

开始重新排查逻辑。

观察jstat增长,在开始业务处理时,新生代大量增长,很快将新生代和伊甸区装满,开始在持久代增长。开始怀疑是业务处理时存内存太多,在旧内存还没有释放,GC就发生,导致很多应该再伊甸区释放的内存块,被留在持久代中,一直无法释放。业务逻辑分为几步,第一步是查询数据,第二步转换格式,第三步同步到新表中。发现问题的根源是,第一步把所有的数据都查询出来,第二部转换的时候,由于在一个大的list中,生成的对象一直有索引,直到第三部所有数据更新到新表后才释放,中间大量占用内存。

解决办法

修改业务逻辑,

数据一次查询部分数据(分页查询),查询完,立即执行第二步和第三步,然后释放就内存,进行下一部分的数据查询处理。形成一个流数据形式,而不是块数据形式。在伊甸区GC的时候几乎所有内存都得到释放,不会进入到持久代

后记

工作很长时间,几乎没有发生处理过内存溢出的问题。关于内存存活性还没有理解深刻。本项目中也反映了伊甸区设置可能不足,有可能调整这部分,会缓解一部分本次的问题。之后需要增加关于内存调优这方面的学习。

后续补充

20170421补有读到文档讲,jackson也好Gson也好,转化实体对象的过程中,为了速度快,会把所有对象都放到内存中,进行转化,这可能是导致中间伊甸区经常占满GC的原因,通常情况下会很快结束,也很快释放掉,但是本项目由于数据量大,同时进行了数据库的存储,对象一直被引用中。关于JsonObject猜测也是同样问题,但没有经过测试。
0 0