如何释放Pythonmysql占用内存不释放的内存

Python学习(13)
在maillist里面看到无数次的有人问,python速度为什么这么慢,python内存管理很差。实话说,我前面已经说过了。如果你在意内存/CPU,不要用python,改用C吧。就算C不行,起码也用个go或者java。不过今天还是说说,python的内存为什么不释放。
首先,python的初始内存消耗比C大,而且大很多。这个主要来自python解释器的开销,没什么好解释的。用解释器,就得承担解释器运行开销。然后,python中的每个对象,都有一定的对象描述成本。因此一个long为例,在C下面一般是4个字节(不用int是因为int在不同平台下是变长的),而python下面至少是16个字节。如果你生成100W个对象,那么C的内存消耗是4M,python的是16M。这些都是常规内存消耗,搞不明白的就别问了,不再解释。
下面解释一下python的内存释放情况。
如果是C,通常是用long array[1024 * 1024]的方法来生成1M个对象空间。当然,实际这样是不一定能运行的。因为linux的默认栈空间是8M,而Windows默认栈空间只有1M。所以代码在linux下可以通过,而windows下会跑爆掉。怎么办?下面说。当这个函数执行完毕后,当RET的时候,会自动退栈,空间就会自动释放掉(虽然在逻辑上这部分空间还是保留没有释放的,然而空间不活跃了,不过统计的时候还是占用的)。当然,更好的办法是使用malloc。malloc会从系统中自动提取和管理空间,free自动释放。这样无论是linux还是windows,都没有栈空间不足的问题。free后就会自动交还系统(4M已经超过了交还的最大阀值,一般glibc不会自己闷掉不交给系统的)。如果你忘记free,这部分内存就会一直占用,直到进程退出未知,这就是很有名的内存泄露。
python下的情况更加复杂一些,python没有直接使用malloc为对象分配细粒度内存,而是使用了三层堆结构,加上三色标记进行回收。所谓三层堆,细节我们不说了,在源码阅读笔记里面写的比较详细。但是有一点需要我们记住的——当我们分配某个大小的内存的时候,内存管理器实际上是向上对齐到8字节,然后去对应的内存池中切一块出来用的。也就是说,如果我们运气比较差,申请了10个对象,偏偏每个对象大小差8字节。这样系统就要给我们分配10个堆,而不是刚刚好。如果你的对象粒度都比较散,那么内存开销比较大也不奇怪。
python下还有一个更坑爹的事情,也是大部分内存不释放的根本原因。在int/str等对象的模块中,有个模块级别的对象缓存链表,static PyObject * free_list。当对象释放的时候,压根不会还到池中,而是直接在free_list中缓存。根据我的搜索,python内部没有地方对此进行干预。就是说,一旦你真的生成了1M个数字对象,然后释放。这1M个对象会在free_list链表中等待重用,直到天荒地老,这16M内存压根不会返还。而且,int的对象缓存链表和str的还不通用。如果你又做了1M个str对象,他的开销还是会继续上涨。几乎所有的内建对象都有这种机制,因此对于大规模对象同时生成,python会消耗大量内存,并且永不释放。
解决的机制,基本只有用yield来将列表对象转换为生成器对象。列表对象会同时生成所有元素,从而直接分配所有内存。而生成器则是一次生成一个元素,比较节约内存。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:6320次
排名:千里之外
原创:43篇
(1)(7)(7)(3)(2)(1)(4)(17)(2)(8)(大浪淘沙)
(大浪淘沙)
第三方登录:thanks Cssk8 &&
python内存泄露
centos中:
yum install&graphviz
easy_install xdot
easy_install&objgraph
一、python有自动垃圾回收机制(当对象的引用计数为零时解释器会自动释放内存),出现内存泄露的场景一般是扩展库内存泄露或者循环引用(还有一种是全局容器里的对象没有删除)
前者无需讨论,后者举例如下(Obj('B')和Obj('C')的内存没有回收)(貌似循环引用的内存,Python解释器也会自己回收(标记-清除垃圾收集机制),只是时间早晚的问题,也就是说我们在编码中不需要耗费精力去刻意避免循环引用,具体的内容这两天再细看一下(&源码剖析的垃圾收集那一章还没看完真是心病啊)---)
[dongsong@localhost&python_study]$&cat&leak_test2.py&&&
class&Obj:&&
&&&&def&__init__(self,name='A'):&&
&&&&&&&&self.name&=&name&&
&&&&&&&&print&'%s&inited'&%&self.name&&
&&&&def&__del__(self):&&
&&&&&&&&print&'%s&deleted'&%&self.name&&
if&__name__&==&'__main__':&&
&&&&a&=&Obj('A')&&
&&&&b&=&Obj('B')&&
&&&&c&=&Obj('c')&&
&&&&c.attrObj&=&b&&
&&&&b.attrObj&=&c&&
[dongsong@localhost&python_study]$&vpython&leak_test2.py&&&
A&inited&&
B&inited&&
c&inited&&
A&deleted&&
该模块可以找到增长最快的对象、实际最多的对象,可以画出某对象里面所有元素的引用关系图、某对象背后的所有引用关系图;可以根据地址获取对象
但是用它来找内存泄露还是有点大海捞针的感觉:需要自己更具增长最快、实际最多对象的日志来确定可疑对象(一般是list/dict/tuple等common对象,这个很难排查;如果最多最快的是自定义的非常规对象则比较好确定原因)
1.show_refs() show_backrefs() show_most_common_types() show_growth()
[dongsong@localhost&python_study]$&!cat&&
cat&objgraph1.py&&&
import&objgraph&&
if&__name__&==&'__main__':&&
&&&&&&&&x&=&[]&&
&&&&&&&&y&=&[x,&[x],&dict(x=x)]&&
&&&&&&&&objgraph.show_refs([y],&filename='/tmp/sample-graph.png')&&&
&&&&&&&&objgraph.show_backrefs([x],&filename='/tmp/sample-backref-graph.png')&&&
&&&&&&&&&&
&&&&&&&&objgraph.show_growth(limit=4)&&&
[dongsong@localhost&python_study]$&!vpython&&
vpython&objgraph1.py&&&
Graph&written&to&/tmp/tmpuSFr9A.dot&(5&nodes)&&
Image&generated&as&/tmp/sample-graph.png&&
Graph&written&to&/tmp/tmpAn6niV.dot&(7&nodes)&&
Image&generated&as&/tmp/sample-backref-graph.png&&
tuple&&&&&&&&&&&&&&&&&&&&&&&&&&3393&&&&&+3393&&
wrapper_descriptor&&&&&&&&&&&&&&945&&&&&&+945&&
function&&&&&&&&&&&&&&&&&&&&&&&&830&&&&&&+830&&
builtin_function_or_method&&&&&&622&&&&&&+622&&
sample-graph.png
sample-backref-graph.png
2.show_chain()
[dongsong@localhost&python_study]$&cat&objgraph2.py&&&
import&objgraph,&inspect,&random&&
class&MyBigFatObject(object):&&
&&&&&&&&pass&&
def&computate_something(_cache&=&{}):&&
&&&&&&&&_cache[42]&=&dict(foo=MyBigFatObject(),bar=MyBigFatObject())&&
&&&&&&&&x&=&MyBigFatObject()&&
if&__name__&==&'__main__':&&
&&&&&&&&objgraph.show_growth(limit=3)&&
&&&&&&&&computate_something()&&
&&&&&&&&objgraph.show_growth(limit=3)&&
&&&&&&&&objgraph.show_chain(&&
&&&&&&&&&&&&&&&&objgraph.find_backref_chain(random.choice(objgraph.by_type('MyBigFatObject')),&&
&&&&&&&&&&&&&&&&&&&&&&&&inspect.ismodule),&&
&&&&&&&&&&&&&&&&filename&=&'/tmp/chain.png')&&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&&
[dongsong@localhost&python_study]$&!vpython&&
vpython&objgraph2.py&&&
tuple&&&&&&&&&&&&&&&&&&3400&&&&&+3400&&
wrapper_descriptor&&&&&&945&&&&&&+945&&
function&&&&&&&&&&&&&&&&831&&&&&&+831&&
wrapper_descriptor&&&&&&956&&&&&&&+11&&
tuple&&&&&&&&&&&&&&&&&&3406&&&&&&&&+6&&
member_descriptor&&&&&&&165&&&&&&&&+4&&
Graph&written&to&/tmp/tmpklkHqC.dot&(7&nodes)&&
Image&generated&as&/tmp/chain.png&&
该模块可以确定垃圾回收期无法引用到(unreachable)和无法释放(uncollectable)的对象,跟objgraph相比有其独到之处
gc.collect()强制回收垃圾,返回unreachable object的数量
gc.garbage返回unreachable object中uncollectable object的列表(都是些有__del__()析构函数并且身陷引用循环的对象)If&is set, then all unreachable objects will be added to this list rather than freed.
warning:如果用gc.disable()把自动垃圾回收关掉了,然后又不主动gc.collect(),你会看到内存刷刷的被消耗....
[dongsong@bogon&python_study]$&cat&gc_test.py&&&
import&gc&&
class&MyObj:&&
&&&&&&&&def&__init__(self,&name):&&
&&&&&&&&&&&&&&&&self.name&=&name&&
&&&&&&&&&&&&&&&&print&"%s&inited"&%&self.name&&
&&&&&&&&def&__del__(self):&&
&&&&&&&&&&&&&&&&print&"%s&deleted"&%&self.name&&
if&__name__&==&'__main__':&&
&&&&&&&&gc.disable()&&
&&&&&&&&gc.set_debug(gc.DEBUG_COLLECTABLE&|&gc.DEBUG_UNCOLLECTABLE&|&gc.DEBUG_INSTANCES&|&gc.DEBUG_OBJECTS&|&gc.DEBUG_SAVEALL)&&
&&&&&&&&a&=&MyObj('a')&&
&&&&&&&&b&=&MyObj('b')&&
&&&&&&&&c&=&MyObj('c')&&
&&&&&&&&a.attr&=&b&&
&&&&&&&&b.attr&=&a&&
&&&&&&&&a&=&None&&
&&&&&&&&b&=&None&&
&&&&&&&&c&=&None&&
&&&&&&&&if&gc.isenabled():&&
&&&&&&&&&&&&&&&&print&'automatic&collection&is&enabled'&&
&&&&&&&&else:&&
&&&&&&&&&&&&&&&&print&'automatic&collection&is&disabled'&&
&&&&&&&&rt&=&gc.collect()&&
&&&&&&&&print&"%d&unreachable"&%&rt&&
&&&&&&&&garbages&=&gc.garbage&&
&&&&&&&&print&"\n%d&garbages:"&%&len(garbages)&&
&&&&&&&&for&garbage&in&garbages:&&
&&&&&&&&&&&&&&&&if&isinstance(garbage,&MyObj):&&
&&&&&&&&&&&&&&&&&&&&&&&&print&"obj--&%s&name--&%s&attrrMyObj--&%s"&%&(garbage,&garbage.name,&garbage.attr)&&
&&&&&&&&&&&&&&&&else:&&
&&&&&&&&&&&&&&&&&&&&&&&&print&str(garbage)&&
[dongsong@bogon&python_study]$&vpython&gc_test.py&&&
a&inited&&
b&inited&&
c&inited&&
c&deleted&&
automatic&collection&is&disabled&&
gc:&uncollectable&&MyObj&instance&at&0x7f3ebd455b48&&&
gc:&uncollectable&&MyObj&instance&at&0x7f3ebd455b90&&&
gc:&uncollectable&&dict&0x261c4b0&&&
gc:&uncollectable&&dict&0x261bdf0&&&
4&unreachable&&
4&garbages:&&
obj--&&__main__.MyObj&instance&at&0x7f3ebd455b48&&name--&a&attrrMyObj--&&__main__.MyObj&instance&at&0x7f3ebd455b90&&&
obj--&&__main__.MyObj&instance&at&0x7f3ebd455b90&&name--&b&attrrMyObj--&&__main__.MyObj&instance&at&0x7f3ebd455b48&&&
{'name':&'a',&'attr':&&__main__.MyObj&instance&at&0x7f3ebd455b90&}&&
{'name':&'b',&'attr':&&__main__.MyObj&instance&at&0x7f3ebd455b48&}&&
详细手册:
命令和gdb差不错(只是打印数据的时候不是必须加个p,而且调试界面和操作类似python交互模式)
h(elp) 帮助
c(ontinue)& 继续
n(ext) 下一个语句
s(tep)& 下一步(跟进函数内部)
b(reak) 设置断点
l(ist) 显示代码
回车 重复上一个命令
鸟人喜欢在需要调试的地方加入pdb.set_trace()然后进入状态....(其他还有好多方式备选)
五、django内存泄露
Django isn't known to leak memory. If you find your Django processes areallocating more and more memory, with no sign of releasing it, check to makesure your&setting is set toFalse. IfisTrue, then Django saves a copy of every SQL statement it has executed.
(The queries are saved in&django.db.connection.queries. See.)
To fix the problem, set&&toFalse.
If you need to clear the query list manually at any point in your functions,just callreset_queries(), like this:
from django import db
db.reset_queries()酷勤网 C 程序员的那点事!
当前位置: >
浏览次数:次
当运行一个复杂的 Python 程序,它需要很长时间来执行。你或许想提升它的执行时间。但如何做?
首先,你需要工具来查明你代码的瓶颈,比如,那部分执行花费的时间长。用这个方法,你可以首先专注于提升这部分的速度。
而且,你也应该控制内存和 CPU 使用率,因为它可以为你指出的代码可以改进的新的部分。
所以,在本文中,我将对 7 个不同的 Python 工具发表意见,给你一些关于你函数执行时间和内存以及 CPU 使用率的见解。
1. 使用一个装饰器来测量你的函数
测量一个函数最简单的方式就是定义一个装饰器来测量运行该函数的运行时间,并打印该结果:
import time
from functools import wraps
def fn_timer(function):
@wraps(function)
def function_timer(*args, **kwargs):
t0 = time.time()
result = function(*args, **kwargs)
t1 = time.time()
print (&Total time running %s: %s seconds& %
(function.func_name, str(t1-t0))
return result
return function_timer
这时,你已经在你想测量的函数之前添加了装饰器,像:
def myfunction(...):
例如,让我们测量下排序一个 2000000 个随机数的数组会花费多长时间:
def random_sort(n):
return sorted([random.random() for i in range(n)])
if __name__ == &__main__&:
random_sort(2000000)
如果你运行你的脚本,你将看到:
Total time running random_sort: 1. seconds
2. 使用 timeit 模块
另外一个选项是使用模块,它给你测量一个平均时间。
为了运行它,在你的终端执行以下命令:
$ python -m timeit -n 4 -r 5 -s &import timing_functions& &timing_functions.random_sort(2000000)&
timing_functions是你脚本的名字。
在输出的最后,你会看到一些像这样的东西:
4 loops, best of 5: 2.08 sec per loop
表明了运行这个测试 4 次(-n 4),并在每个测试中重复平均 5 次(-r 5),最佳的结果是 2.08 秒。
如果你没有指定测试或者重复,它默认是 10 次循环和 5 次重复。
3. 使用 Uinx 的 time 命令
尽管如此,装饰器和 timeit 模块都是基于 Python 的。这就是为什么 unix time 工具或许有用,因为它是一个外部的 Python 测量。
为了运行 time 工具类型:
$ time -p python timing_functions.py
将给出如下输出:
Total time running random_sort: 1. seconds
第一行来自于我们定义的装饰器,其他三行是:
real 表明了执行脚本花费的总时间
User 表明了执行脚本花费在的 CPU 时间
Sys 表明了执行脚本花费在内核函数的时间
因此, real time 和 user+sys 相加的不同或许表明了时间花费在等待 I/O 或者是系统在忙于执行其他任务。
4. 使用 cProfile 模块
如果你想知道花费在每个函数和方法上的时间,以及它们被调用了多少次,你可以使用模块。
$ python -m cProfile -s cumulative timing_functions.py
现在你将看到你的代码中每个函数被调用多少次的详细描述,并且它将通过累积花费在每个函数上面的时间来排序(感谢-s cumulative选项)
你将看到花费在运行你的脚本的总时间是比以前高的。这是我们测量每个函数执行时间的损失。
5. 使用 line_profiler 模块
给出了在你代码每一行花费的 CPU 时间。
这个模块首先应该被安装,使用命令:
$ pip install line_profiler
下一步,你需要指定你想使用装饰器@profile评估哪个函数(你不需要把它 import 到你的文件中)。
def random_sort2(n):
l = [random.random() for i in range(n)]
if __name__ == &__main__&:
random_sort2(2000000)
最后,你可以通过键入以下命令取得random_sort2函数逐行的描述:
$ kernprof -l -v timing_functions.py
-l标识表明了逐行和-v标识表明详细输出。使用这个方法,我们看到了数组结构花费了 44% 的计算时间,sort() 方法花费了剩余的 56%。
你也将看到,由于时间测量,这个脚本执行花费的或许更长。
6. 使用 memory_profiler 模块
模块被用于在逐行的基础上,测量你代码的内存使用率。尽管如此,它可能使得你的代码运行的更慢。
$ pip install memory_profiler
也建议安装包,使得memory_profile模块运行的更快:
$ pip install psutil
类似line_profiler的方式,使用装饰器@profile来标记哪个函数被跟踪。下一步,键入:
$ python -m memory_profiler timing_functions.py
是的,前面的脚本比之前的 1 或 2 秒需要更长的时间。并且,如果你不安装psutil模块,你将一直等待结果。
看上面的输出,注意内存使用率的单位是 MiB,这代表的是兆字节(1MiB = 1.05MB)。
7. 使用 guppy 包
最后,使用这个包,你可以跟踪每个类型在你代码中每个阶段(字符, 元组, 字典 等等)有多少对象被创建了。
$ pip install guppy
下一步,像这样添加到你的代码中:
from guppy import hpy
def random_sort3(n):
hp = hpy()
print &Heap at the beginning of the functionn&, hp.heap()
l = [random.random() for i in range(n)]
print &Heap at the end of the functionn&, hp.heap()
if __name__ == &__main__&:
random_sort3(2000000)
并且这样运行你的代码:
$ python timing_functions.py
你将看到一些像下面的输出:
通过配置 heap 在你的代码的不同地方,你可以在脚本中学到对象的创建和销毁。
如果你想学习更多提升你 Python 代码的知识,我建议你看看 2014 年 11 月出版的这本书。
& 相关主题:遇到个python进程占用内存太多的问题 | 盛大创新院团队博客 | 数据,更懂人心
我的图书馆
遇到个python进程占用内存太多的问题 | 盛大创新院团队博客 | 数据,更懂人心
在几个前辈的影响下,也开始用起了python,上手较快,并且觉得内存回收这些个烦心事自己也不用过问,自有解释器来完成。直到开始抓取某吧的帖子时才遇到问题。
抓取的思路很简单:获取某个分类下的所有的吧; 获取某个吧下的所有帖子;获取某个帖子的所有回复页。并且上面的每个步骤都是一个单独的接口实现。但是程序跑起来总是被莫名杀掉,并且dmesg查看会有“out of memory”的信息,应该是进程占用内存太大,被系统强制停掉。想着觉得不可思议,估算下,一个帖子只存储link和anchor,平均算上256byte,一个吧最多500w帖子,才用1G+多些,怎么会在一台16G的机器上down掉的呢(该机器虽然有其他程序,但每次进程退出时至少都占用了7G+).
于是开始找了些python的内存检测相关的工具和说明的页面看了看。
工具方面:
1、尝试valgrind检查,”确实看到很多xx are still reachable”的日志,而且这个xx数字越往后越大,囧。但是这些trace都是python解释器调用底层接口,也无法看懂(有个网页说到python2.x解释器可能存在内存泄露但还是先从自己程序找问题)。
2、cprofile分析:输出文件里有各个接口的调用次数和平均处理时间之类的,更像是解决性能的辅助工具。
3、尝试用gc模块的gc.collect接口进行强制的内存回收,没有效果。
4、尝试meliae:在上面的每次循环处理之后打印各个对象的占用情况,发现最多的对象都是beautifulsoup的Navigating占用了快80%的内存,于是在每次soup的find的object使用完后都强制del,仍然没有效果(感觉这个检测结果有点误导性)。
这个时候回头再仔细想了想之前的帖子里讲到的python的内存虽然会自动回收,但是回收完之后的内存并不是还给系统,而仍然是作为python的内存池。所以最根本的解决方法就是如何尽量少的让python从系统申请内存和复用自身的内存池资源。于是将上面的抓取流程稍微改动一下,每次并不是获取完一个吧的所有帖子,而且每次只获取一页的帖子,重新运行,便发现python的内存占用始终维持在0.2%左右了。
某种程序上来说,这种内存管理方式并没有能解释得通为何第一种写法会占用越来越大的内存;不过后来的解决方法却可以作为以后写python脚本时内存考虑的一个指导点。同时几种工具使用一遍,也是个不错的经历。更高追求的同学还可以关注下如何高效地释放内存的方式,我还没有尝试过。
TA的最新馆藏[转]&}

我要回帖

更多关于 怎么释放核显占用内存 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信