一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術(shù)及教程分享平臺!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Python - 粗略分析Python中的內(nèi)存泄漏

粗略分析Python中的內(nèi)存泄漏

2020-06-11 10:44C Wong Python

這篇文章主要介紹了粗略分析Python中的內(nèi)存泄漏,分析了包括在垃圾回收時產(chǎn)生等的原因,需要的朋友可以參考下

引子

之前一直盲目的認(rèn)為 Python 不會存在內(nèi)存泄露, 但是眼看著上線的項目隨著運行時間的增長 而越來越大的內(nèi)存占用, 我意識到我寫的程序在發(fā)生內(nèi)存泄露, 之前 debug 過 logging 模塊導(dǎo)致的內(nèi)存泄露.

目前看來, 還有別的地方引起的內(nèi)存泄露. 經(jīng)過一天的奮戰(zhàn), 終于找到了內(nèi)存泄露的地方, 目前項目 跑了很長時間, 在業(yè)務(wù)量較小的時候內(nèi)存還是能回到剛啟動的時候的內(nèi)存占用.
什么情況下不用這么麻煩

如果你的程序只是跑一下就退出大可不必大費周章的去查找是否有內(nèi)存泄露, 因為 Python 在退出時 會釋放它所分配的所有內(nèi)存, 如果你的程序需要連續(xù)跑很長時間那么就要仔細(xì)的查找是否 產(chǎn)生了內(nèi)存泄露.
場景

如何產(chǎn)生的內(nèi)存泄露呢, 項目是一個 TCP server, 每當(dāng)有連接過來時都會創(chuàng)建一個連接實例來進行 管理, 每次斷開時連接實例還被占用并沒有釋放. 沒有被釋放的原因肯定是因為有某個地方對連接 實例的引用沒有釋放, 所以隨著時間的推移, 連接創(chuàng)建分配內(nèi)存, 連接斷開并沒有釋放掉內(nèi)存, 所以 就會產(chǎn)生內(nèi)存泄露.
調(diào)試方法

由于不知道具體是哪里引起的內(nèi)存泄露, 所以要耐心的一點點調(diào)試.

由于知道了斷開連接時沒有釋放, 所以我就不停的模擬創(chuàng)建連接然后發(fā)送一些包后斷開連接, 然后通過下面一行 shell 來觀察內(nèi)存占用情況:

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

如果在增長了一定的量后保持住就說明已經(jīng)沒有產(chǎn)生泄露.

同時可以在對象該釋放的時候查看對象的引用計數(shù), 通過 sys.getrefcount(obj). 如果引用計數(shù)變?yōu)榱?2 則說明該對象在跳出命名空間后就會被正確回收.
產(chǎn)生原因

項目中兩種情況導(dǎo)致對象沒有被正確回收:

  •     被退出才回收的對象引用
  •     交叉引用

被退出才回收的對象引用

為了追蹤連接所以把連接對象同時放在一個列表里, 而這個列表只有在程序退出時才會被回收, 如果不正確處理, 那么分配的對象將也會只在程序退出時才會被回收.

全局變量和類變量都只會在程序退出的時候才會被回收:

?
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 里, 如果在關(guān)閉的時候不從這個列表 里取出(減少引用)則 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

交叉引用

有時候我們?yōu)閷ο蠓峙湟粋€實例屬性時需要將自己本身賦值給實例屬性, 作為實例屬性的實例屬性, 說著很拗口, 看一下代碼:

?
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

上面的代碼就會產(chǎn)生交叉引用, 交叉引用會讓解釋器困惑, 從而之后只能靠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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美日韩精品一区二区三区视频在线 | 国内精品一区二区在线观看 | 国产一区二区在线免费观看 | 日韩毛片高清在线看 | 久久99热狠狠色AV蜜臀 | 亚洲成人三级 | 成人二区| 日韩v | 草榴色导航 | 大又大又黄又爽免费毛片 | 大乳女子一级毛片 | 国产精品免费 | 成人猫咪maomiav永久网址 | 极品丝袜小说全集 | 熟睡中的麻麻大白屁股小说 | 175m美女被网友灌醉啪啪玩脚 | 乌克兰xxxxx 我要色色网 | 日韩免费视频一区 | 黄 色 大 片 网站 | 久久精品国产亚洲AV蜜臀 | 国产成人精品视频一区二区不卡 | 女教师巨大乳孔中文字幕免费 | 日本妇人成熟免费观看18 | 人人福利| 青青草99久久精品国产综合 | 日本哺乳期网站xxxx | 亚洲 日韩 自拍 视频一区 | 波多野结衣在线免费观看 | 午夜神器老司机高清无码 | 国产综合亚洲欧美日韩一区二区 | 四虎免费永久观看 | 亚洲欧美色综合图小说 | 国产精品主播在线 | 视频亚洲一区 | 脱jk裙的美女露小内内无遮挡 | 四虎免费在线视频 | 成年人免费在线播放 | 国产haodiaose最新 | 91美女在线 | 男人的j进入女人的j免费 | 国产香蕉一区二区在线网站 |