内存溢出是指向JVM申请内存空间时没有足够的可用内存了,就会抛出OOM即内存溢出。
内存泄漏是指,向JVM申请了一块内存空间,使用完后没有释放,由于没有释放,这块内存区域其他类加载的时候无法申请,同时当前类又没有这块内存空间的内存地址了也无法使用,相当于丢了一块内存,这就是内存泄漏。
值得注意的是内存泄漏最终会导致内存溢出,很好理解,内存丢了很多最后当然内存不够用了。
像Java程序一样,虽然Python本身也有垃圾回收的功能,但是同样也会产生内存泄漏的问题。
对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是有了“内存泄露”。
对于 python 这种支持垃圾回收的语言来说,怎么还会有内存泄露? 概括来说,有以下三种原因:
为什么循环引用的对象定义了__del__方法后 collect 就不起作用了呢?
gc模块最常使用的方法就是gc.collect()方法,使用collect方法对循环引用的对象进行垃圾回收。
如果我们在类中重载了__del__方法。__del__方法定义了在用del语句删除对象时除了释放内存空间以外的操作。
一般而言,在使用del语句的时候解释器首先会看要删除对象的引用计数,如果为0,那么就释放内存并执行del方法。
在这里,首先del语句出现时本身引用计数就不为0(因为有循环引用的存在),所以解释器不释放内存;再者,执行collect方法时应该会清除循环引用所产生的无效引用计数从而达到del的目的,对于这两个循环引用对象而言,python无法判断调用它们的del方法时会不会要用到对方那个对象,比如在进行b.del()时可能会用到b._a也就是a,如果在那之前a已经被释放,那么就彻底GG了。
为了避免这种情况,collect方法默认不对重载了__del__方法的循环引用对象进行回收,而它们俩的状态也会从unreachable转变为uncollectable。由于是uncollectable的,自然就不会被collect处理,所以就进入了garbage列表。
无论是哪种方式的内存泄露,最终表现的形式都是某些 python 对象在不停的增长;因此,首先是要找到这些异常的对象。
用到的工具: gc 模块和 objgraph 模块
gc模块 是Python的垃圾收集器模块,gc使用标记清除算法回收垃圾
objgraph 是一个用于诊断内存问题的工具
1、 在服务程序的循环逻辑中,选择出一个诊断点
2、 在诊断点,插入如下诊断语句
import gc import objgraph ### 强制进行垃圾回收 gc.collect() ### 打印出对象数目最多的 50 个类型信息 objgraph.show_most_common_types(limit=50)
运行加入诊断语句的服务程序,并将打印到屏幕上的统计信息重定向到日志中。运行一段时间后,就可以来分析日志,看看哪些对象在不停的增长。
比如,排查结果可能是:
一个多线程程序,多个线程作为生产者,一个线程作为消费者,通过将一个 tuple 对象送入异步队列进行通信。
由于消费者的处理速度跟不上生产者的速度,又没有进行同步, 导致异步队列中的对象越来越多。
第一步,修改JVM启动参数,直接增加内存(-Xms,-Xmx参数一定不要忘记加)
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其 它异常或错误
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置
重点排查以下几点:
第四步,使用内存查看工具动态查看内存使用情况
原文:https://www.cnblogs.com/sunny0824/p/13736749.html