分析Python中的内存泄漏

   分析Python中的内存泄漏

  引子

  之前一直盲目的认为 Python 不会存在内存泄露, 但是眼看着上线的项目随着运行时间的增长 而越来越大的内存占用, 我意识到我写的程序在发生内存泄露, 之前 debug 过 logging 模块导致的内存泄露.

  目前看来, 还有别的地方引起的内存泄露. 经过一天的奋战, 终于找到了内存泄露的地方, 目前项目 跑了很长时间, 在业务量较小的时候内存还是能回到刚启动的时候的内存占用.

  什么情况下不用这么麻烦

  如果你的程序只是跑一下就退出大可不必大费周章的去查找是否有内存泄露, 因为 Python 在退出时 会释放它所分配的所有内存, 如果你的程序需要连续跑很长时间那么就要仔细的查找是否 产生了内存泄露.

  场景

  如何产生的内存泄露呢, 项目是一个 TCP server, 每当有连接过来时都会创建一个连接实例来进行 管理, 每次断开时连接实例还被占用并没有释放. 没有被释放的原因肯定是因为有某个地方对连接 实例的引用没有释放, 所以随着时间的推移, 连接创建分配内存, 连接断开并没有释放掉内存, 所以 就会产生内存泄露.

  调试方法

  由于不知道具体是哪里引起的内存泄露, 所以要耐心的一点点调试.

  由于知道了断开连接时没有释放, 所以我就不停的模拟创建连接然后发送一些包后断开连接, 然后通过下面一行 shell 来观察内存占用情况:

  PID=50662;while true; do; ps aux | grep $PID | grep -v grep | awk '{print $5" "$6}' >> t; sleep 1; done

  如果在增长了一定的量后保持住就说明已经没有产生泄露.

  同时可以在对象该释放的时候查看对象的引用计数, 通过 sys.getrefcount(obj). 如果引用计数变为了 2 则说明该对象在跳出命名空间后就会被正确回收.

  产生原因

  项目中两种情况导致对象没有被正确回收:

  被退出才回收的对象引用

  交叉引用

  被退出才回收的对象引用

  为了追踪连接所以把连接对象同时放在一个列表里, 而这个列表只有在程序退出时才会被回收, 如果不正确处理, 那么分配的对象将也会只在程序退出时才会被回收.

  全局变量和类变量都只会在程序退出的时候才会被回收:

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14

_CONNECTIONS = []
 
# ...
class Connection(object):
def __init__(self, sock, address)
pass
 
def server_loop():
# ...
sock, address = server_sock.accept()
connection = Connection(sock, address)
_CONNECTIONS.append(connection)
# ...
sock.close()

  上面把所有建立的连接都放在全局变量 _CONNECTIONS 里, 如果在关闭的时候不从这个列表 里取出(减少引用)则 connection 对象就不会被回收, 则每建立一次连接就会有个连接对象和连接 对象引用的对象不会被回收.

  如果把对象放在一个类属性里也是一样的, 因为类对象在程序一开始就分配, 并在程序退出时才被回收.

  解决办法就是在退出时从列表(或其他对象)里解除对对象的引用(删除)

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

_CONNECTIONS = []
 
# ...
class Connection(object):
def __init__(self, sock, address)
pass
 
def server_loop():
# ...
sock, address = server_sock.accept()
connection = Connection(sock, address)
_CONNECTIONS.append(connection)
try:
# ...
sock.close()
finally:
_CONNECTIONS.remove(connection) # XXX

  交叉引用

  有时候我们为对象分配一个实例属性时需要将自己本身赋值给实例属性, 作为实例属性的实例属性, 说着很拗口, 看一下代码:

  ?

1
2
3
4
5
6
7
8

class ConnectionHandler(object):
def __init__(self, connection):
self._conn = connection
 
 
class Connection(object):
def __init__(self, sock, address)
self._conn_handler = ConnectionHandler(self) # XXX

  上面的代码就会产生交叉引用, 交叉引用会让解释器困惑, 从而之后只能靠2代和3代回收, 这个过程可能会很慢.

  解决这种问题的方法就是使用 弱引用

  ?

1
2
3
4
5
6
7
8
9
10

import weakref
 
class ConnectionHandler(object):
def __init__(self, connection):
self._conn = connection
 
 
class Connection(object):
def __init__(self, sock, address)
self._conn_handler = ConnectionHandler(weakref.proxy(self)) # XXX

时间: 2024-09-08 10:44:26

分析Python中的内存泄漏的相关文章

浅析Node.js中的内存泄漏问题

  这篇文章是由Mozilla的Identity团队带来的 A Node.JS Holiday Season系列文章的首篇,该团队上个月发布了 Persona的第一个测试版本.在开发Persona时我们构建了一系列的工具,包括了从调试,到本地化,到依赖管理以及更多的方面.在这一系列的文章中我们将与社区分享我们的经验和这些工具,这对任何想用node.js建立一个高可用性服务的人都很有用.我们希望您能喜欢这些文章,并期待看到您的想法和贡献. 我们将从一篇关于Node.js的实质性问题:内存泄漏的主题

在 JNI 编程中避免内存泄漏

简介: 本文详细论述如何在 JNI 编程中避免内存泄漏.论述了 JNI 编程中可能引发的明显的内存泄漏.本文的重点是阐述 JNI 编程中潜在的内存泄漏,希望读者通过本文对 Local reference 有更深刻的理解,了解 Local reference 表的存在,区分 Local reference 和局部变量,从而认识到 Local reference 可能引发的 native memory 内存泄漏 JNI 编程简介 JNI,Java Native Interface,是 native

浅析Node.js中的内存泄漏问题_node.js

 这篇文章是由Mozilla的Identity团队带来的 A Node.JS Holiday Season系列文章的首篇,该团队上个月发布了 Persona的第一个测试版本.在开发Persona时我们构建了一系列的工具,包括了从调试,到本地化,到依赖管理以及更多的方面.在这一系列的文章中我们将与社区分享我们的经验和这些工具,这对任何想用node.js建立一个高可用性服务的人都很有用.我们希望您能喜欢这些文章,并期待看到您的想法和贡献. 我们将从一篇关于Node.js的实质性问题:内存泄漏的主题文

Java中关于内存泄漏出现的原因汇总及如何避免内存泄漏(超详细版)_java

Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题.内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收.最近自己阅读了大量相关的文档资料,打算做个 总结 沉淀下来跟大家一起分享和学习,也给自己一个警示,以后 coding 时怎么避免这些情况,提高应用的体验和质量. 我会从 java 内存泄漏的基础知识开始,并通过具体例子来说明 Android 引起内存泄漏的各种原因,以

【转贴】了解 JavaScript 应用程序中的内存泄漏

转贴:http://www.ibm.com/developerworks/cn/web/wa-jsmemory/ 检测和解决内存问题 Ben Dolmar, 软件开发人员, The Nerdery   简介: 垃圾回收解放了我们,它让我们可将精力集中在应用程序逻辑(而不是内存管理)上.但是,垃圾收集并不神奇.了解它的工作原理,以及如何使它保留本应在很久以前释放的内存,就可以实现更快更可靠的应用程序.在本文中,学习一种定位 JavaScript 应用程序中内存泄漏的系统方法.几种常见的泄漏模式,以

JavaScript中的内存泄漏以及如何处理

随着现在的编程语言功能越来越成熟.复杂,内存管理也容易被大家忽略.本文将会讨论JavaScript中的内存泄漏以及如何处理,方便大家在使用JavaScript编码时,更好的应对内存泄漏带来的问题.   概述 像C语言这样的编程语言,具有简单的内存管理功能函数,例如malloc( )和free( ).开发人员可以使用这些功能函数来显式地分配和释放系统的内存. 当创建对象和字符串等时,JavaScript就会分配内存,并在不再使用时自动释放内存,这种机制被称为垃圾收集.这种释放资源看似是"自动&qu

mfc-MFC中视频流内存泄漏问题

问题描述 MFC中视频流内存泄漏问题 void CHLDlg::OnBnClickedOpen(){ // TODO: 在此添加控件通知处理程序代码 CFileDialog dlg(TRUENULLNULLNULLNULL); pCapture = NULL; if(dlg.DoModal()==IDOK)// { PathName = dlg.GetPathName(); FileName = dlg.GetFileName(); } else { return; } c=(LPCSTR)Pa

调试内存泄漏的应用程序: 发现并防止托管代码中出现内存泄漏

本文讨论: 理解托管应用程序中的内存泄漏问题 .NET 应用程序中所用的非托管内存 帮助 .NET 垃圾收集器发挥应有功效 本文使用了以下技术: .NET Framework 目录 .NET 应用程序中的内存 检测泄漏 堆栈内存泄漏 非托管堆内存泄漏 "泄漏"托管堆内存 总结 一提到托管代码中出现内存泄漏,很多开发人员的第一反应都认为这是不可能的.毕竟垃圾收集器 (GC) 会负责管理所有的内存,没错吧?但要知道,垃圾收集器只处理托管内存.基于 Microsoft .NET Framew

Android中导致内存泄漏的竟然是它----Dialog

一. 内存泄漏的 Bug 猛增 最近在 App 进行 mokey 测试的时候检测到一些内存泄漏问题.在前天的测试中,楼主一瞬间收到了4个这样的 Bug 单,瞬间心理无比纠结,真有千万只羊驼向我奔来. 登录页面出现内存泄漏??!!楼主的代码是如此的完美而无懈可击,这么可能出现这么多泄漏的问题? 插播什么是 Activity 泄漏:Android 中 Activity 代表一个页面,拥有一段生命周期,生命周期结束后,Activity 对象应当在之后某个合适的时机被 VM 回收内存.出现了泄漏就意味着