概要
不要以为 Python 有自动垃圾回收就不会内存泄漏,本着它有“垃圾回收”我有“垃圾代码”的精神,现在总结一下三种常见的内存泄漏场景。
无穷大导致内存泄漏
如果把内存泄漏定义成只申请不释放,那么借着 Python 中整数可以无穷大的这个特点,我们一行代码就可以完成内存泄漏了。
i = 1024 ** 1024 ** 1024
循环引用导致内存泄漏
引用记数器 是 Python 垃圾回收机制的基础,如果一个对象的引用数量不为 0 那么是不会被垃圾回收的,我们可以通过 sys.getrefcount 来得到给定对象的引用数量。
In [1]: import sys In [2]: a = {'name':'tom','age':16} In [3]: sys.getrefcount(a) # 由于 getrefcount 内部也会临时的引用 a 所以,使得计数器的值变成了 2 。 Out[3]: 2 In [4]: b = a In [5]: sys.getrefcount(a) Out[5]: 3
先来看一个循环引用的场景。
#!/usr/bin/evn python3 import sys import time import threading class Person(object): free_lock = threading.Condition() def __init__(self, name: str = ""): """ Parameters ---------- name: str 姓名 best_friend: str 最要好的朋友名 """ self._name = name self._best_friend = None @property def best_friend(self, person: "Person"): return self._best_friend @best_friend.setter def best_friend(self, friend: "Person"): self._best_friend = friend def __str__(self): """ """ return self._name def __del__(self): """ """ self.free_lock.acquire() print(f"{self._name} 要 GG 了,现在释放它的内存空间。") sys.stderr.flush() self.free_lock.release() def mem_leak(): """ 循环引用导致内存泄漏 """ zhang_san = Person(name='张三') li_si = Person("李四") # 构造出循环引用 # 李四的好友是张三 li_si.best_friend = zhang_san # 张三的好友是李四 zhang_san.best_friend = li_si if __name__ == "__main__": for i in range(3): time.sleep(0.01) print(f"{i}") mem_leak() print("mem_leak 执行完成了.") time.sleep(5)
运行效果。
python3 main.py
0
1
2
mem_leak 执行完成了.
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间
由于循环引用的存在,使得 mem_leak 函数就行执行完了其内部的局部变量引用计数器也不为 0 ,所以内存得不到及时的释放。释放这部分内存有两个途径 1、 被 Python 内部的循环检测机制发现了; 2、进程退出前的集中释放。
tracemalloc 可以在一定程序上帮我们发现问题,在此就不讲怎么用了,我们直接上解决方案。Python 为程序员提供了弱引用,通过这种方式可以不增加对象引用计数器的数值,这成为了我们打破循环引用的一种手段。
In [1]: import sys In [2]: import weakref In [3]: from main import Person In [4]: tom = Person('tom') In [5]: sys.getrefcount(tom) Out[5]: 2 In [6]: p = weakref.ref(tom) In [7]: sys.getrefcount(tom) # 弱引用不会增加计数器的值 Out[7]: 2
现在使用 weakref 技术来改造我们的代码。
#!/usr/bin/evn python3 import sys import time import weakref import threading class Person(object): free_lock = threading.Condition() def __init__(self, name: str = ""): """ Parameters ---------- name: str 姓名 best_friend: str 最要好的朋友名 """ self._name = name self._best_friend = None @property def best_friend(self, person: "Person"): return self._best_friend @best_friend.setter def best_friend(self, friend: "Person"): self._best_friend = weakref.ref(friend) def __str__(self): """ """ return self._name def __del__(self): """ """ self.free_lock.acquire() print(f"{self._name} 要 GG 了,现在释放它的内存空间。") sys.stderr.flush() self.free_lock.release() def mem_leak(): """ 循环引用导致内存泄漏 """ zhang_san = Person(name='张三') li_si = Person("李四") # 构造出循环引用 # 李四的好友是张三 li_si.best_friend = zhang_san # 张三的好友是李四 zhang_san.best_friend = li_si if __name__ == "__main__": for i in range(3): time.sleep(0.01) print(f"{i}") mem_leak() print("mem_leak 执行完成了.") time.sleep(5)
运行效果。
python3 main.py
0
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
1
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
2
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
mem_leak 执行完成了.
可以看到现在一旦函数执行完成,其内部的局部变量的内存就会得到释放,非常的及时。
外面库导致内存泄漏
这种情况我也只遇到过一次,之前 mysql-connector-python 的内存泄漏,导致我的程序跑着跑着占用的内存就越来越大;最后我们返的 C 语言扩展禁用之后就没有问题了。
以上就是总结python 三种常见的内存泄漏场景的详细内容,更多关于python 内存泄漏的资料请关注其它相关文章!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]