美文网首页
实战:一次疑似内存泄漏的问题排查

实战:一次疑似内存泄漏的问题排查

作者: 搬运工来架构 | 来源:发表于2019-02-13 00:28 被阅读0次

    1、问题背景

    我的blog:  https://blog.verysu.com,有兴趣的可以看下。

    最近服务器到期等因素,进行了迁移。租了其它的外国厂商,但是由于资费问题,购买了1.5G 内存的服务器(现)。因为原本用惯了4G内存的服务器(原),现在压缩成这样,似乎不太能支持我的使用,囧!

    现在就来说下blog服务分配的内存情况:

    原:4G 内存,分配给blog,1.5G。

    现:1.5G内存,分配给blog,500M。

    由于此次调整,原本以为资源需求压力会更大。实际上应该不会,仔细想想,blog也没多少内容的。那么根据此次调整之后,已经出现了2次OutOfMemoryError了。

    由于JVM参数配置有加上-XX:+HeapDumpOnOutOfMemoryError,所以出现OutOfMemoryError时会将堆栈输出到指定的文件,这样可以方便以后排查问题。

    OS:CentOS7

    Web容器:Tomcat 8

    ORM:Hibernate3

    数据库:MySQL5

    2、问题解决过程

    2.1 诊断排查出现内存泄漏对象

    这个文件占用了差不多535M,说明里面的对象占用空间很大。这里我使用了MAT工具来排查。

    1)Histogram (可以查看每个类的实例(即对象)的数量和大小)

    通过Histogram图,我们初步看出,实际上跟我们自己的代码好像是没有关系,因为这里没找到我们对应的包名和类名。不过可以看到一些疑惑就是Tomcat和mysql相关的类占用了很大内存。

    2)Dominator Tree(列出Heap Dump中处于活跃状态中的最大的几个对象,默认按 retained size进行排序)

    从这里可以更加明确是哪些对象占用了大部分资源了,似乎也是跟Tomcat和mysql有关。

    3)Top Consumer(按类、类加载器和包分别进行查询,并以饼图的方式列出最大的几个对象)

    通过上面这个饼图,我们更加明确的是哪些对象占用了大部分资源。其它一些视图这里就不展开了。

    小结:上面MAT的各种视图表明,并不是说占用资源大的对象是就是内存泄漏的罪魁祸首,它这里只是做了个统计,方便你观察和发现问题,只是提示你可能这些对象存在泄漏的可能性。

    2.2 诊断排查占用大资源内因

    由上面的图中,我从TaskThread这个类入手:

    发现主要占用的资源在StatefulPersistenceContext这个类,很明显这个是Hibernate持久化相关的。

    查看下类的注释,大致意思是:

    PersistenceContext表示Hibernate正在跟踪的持久化“内容”的状态。这包括持久实体、集合以及生成的代理。

    SessionImpl和PersistentContext之间应该是一对一的对应关系。SessionImpl使用PersistentContext来跟踪其上下文的当前状态。事件监听器使用PersistentContext来执行处理。

    因为Hibernate的一级缓存就是在Session层面上,所以StatefulPersistenceContext跟一级缓存有关系,网上有些资料也有说StatefulPersistenceContext存在问题可能造成内存泄漏等原因。

    可以在使用完Session时进行clear清除。这样就能防止占用过大资源,但是,使用一级缓存是能够大大的增强性能,如果这么做的话,似乎有违背Hibernate的设计初衷,因此,这里我们不能采取这种做法。

    继续往下挖,看这里面存放的是具体什么对象吧!

    从这里可以看出,持久化的主要对象实体是ShareArticle,并且有1706个实体之多!~,这里只是TaskThread这个线程所持有这么多的实体,要知道下面还有几个大对象TaskThread,里面也是有包含这些的,所以不仅仅1千个多实体。

    2.3 找出问题根源(“真凶”)

    看到这个实体的每个字段,才知道问题出现在哪!这张表有一个content字段,类型是mediumtext,存放文章内容。所以一旦加载到内存里,自然的需要占用大部分资源了。

    找到问题的点在哪了,接下来就得看怎么优化,毕竟在资源急缺的我,需要优化下当前的服务了。

    2.4 解决问题,优化!优化!

    2.4.1 优化之一:查询不返回content字段

    特别是哪些查询列表相关的。这能大大的减少占用内存。因为有些查询List结果实际上是没有使用到content字段,再次查询出来也是一种浪费。

    说下这个过程吧,由于使用的是Hibernate,虽说有其优点,但是使用起来极其不灵活!在公司用惯了Mybatis,才知道Mybatis的好,哈哈!

    2.4.2 优化之二:只查询需要的字段

    在查询当前文章的上下文时,基本上也是不用用到content字段,这里只需要返回id和title,即可。其实跟上面2.4.1的类似。

    2.4.3 优化之三:延迟加载指定字段

    我们知道,Hibernate在一对多、多对多等关系中,是支持延迟加载的。查资料发现Hibernate3也能支持指定字段进行延迟加载,在需要的时候再次去查询数据库指定的字段再返回。

    所以就动手干!但是也遇到使用时出现不生效,资料表明需要再次使用字节码进行增强才能正常使用。具体操作可以看下下面这篇文字:https://blog.verysu.com/article/414

    小结:经过上面的优化,服务再也不会出现OutOfMemeryError了,而且资源占用也大幅度降低。

    3、总结(经验与优化)

    1)针对占用资源多的,是否能够不存在数据库,比如生成静态HTML文件,访问的时候直接包含在页面直接返回,这样能快速返回,占用内存少,提升性能。

    2)针对大字段(占用资源大),没用到时不返回。

    3)只返回需要的字段,在SQL优化的上必有,这样也能提升mysql的吞吐量,也不会浪费资源。

    4)如果追求灵活性,ORM建议使用mybatis,毕竟互联网公司基本用它。Hibernate更适合在企业系统里面使用。

    5)如果已经使用了Hibernate了,可以增加字段延迟加载机制,进而在需要的时候再去查询。

    好了,为了写这篇文章,花了两个多小时,觉得不错的话,点个赞呗^_^!

    推荐工具1:

    MAT:Eclipse Memory Analyzer  下载地址:

    http://www.eclipse.org/mat/

    推荐工具2:

    IBM HeapAnalyzer 下载地址:

    https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=4544bafe-c7a2-455f-9d43-eb866ea60091

    参考资料:

    PS:如果上面的链接点击不了,可以点击左下角的阅读原文进行查看。

    相关文章

      网友评论

          本文标题:实战:一次疑似内存泄漏的问题排查

          本文链接:https://www.haomeiwen.com/subject/vuoleqtx.html