您好,登錄后才能下訂單哦!
本篇文章為大家展示了在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項目中哪些情況下會出現內存泄漏,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。