目錄
了解
循環(huán)引用造成的內(nèi)存泄露
大量創(chuàng)建對象造成的內(nèi)存泄漏
全局對象造成的內(nèi)存泄露
不適當(dāng)緩存造成的內(nèi)存泄露
內(nèi)存分析工具?
了解
### 以下為Python中可能會出現(xiàn)內(nèi)存泄露的情況:
? ? (1) 循環(huán)引用:當(dāng)兩個或多個對象相互引用,造成的循環(huán)引用進而導(dǎo)致內(nèi)存泄露
? ? (2) 大量創(chuàng)建對象:當(dāng)程序中頻繁創(chuàng)建大量的對象并沒有及時銷毀,也會導(dǎo)致內(nèi)存泄露。
? ? (3) 全局變量:當(dāng)全局變量被創(chuàng)建后一直存在,即使它們不再被使用,也會占用內(nèi)存空間,可能導(dǎo)致內(nèi)存泄露。
? ? (4) 不適當(dāng)?shù)木彺媸褂茫喝绻诰彺嬷写鎯α舜罅繑?shù)據(jù),而沒有適當(dāng)?shù)貏h除舊數(shù)據(jù),會導(dǎo)致內(nèi)存泄漏。
? ? (5) C擴展模塊:使用C擴展模塊編寫的代碼可能會出現(xiàn)內(nèi)存泄漏問題,因為C擴展代碼不受Python的垃圾回收機制控制。
? ??### 以下為避免這些情況導(dǎo)致的內(nèi)存泄漏的措施:
? ? (1) 盡可能避免循環(huán)引用,使用weakref庫避免對象之間相互引用。
? ? (2) 及時銷毀不需要的對象,特別是大量創(chuàng)建的對象。
? ? (3) 減少全局變量的使用,只在必要時使用。
? ? (4) 注意緩存的使用,需要定期清理緩存,避免緩存數(shù)據(jù)過多導(dǎo)致內(nèi)存泄漏。
? ? (5) 盡可能避免使用C擴展模塊,如果必須使用,需要仔細檢查代碼,確保它沒有導(dǎo)致內(nèi)存泄漏問題。
? ? (6) 使用生成器和迭代器:生成器和迭代器是Python中用于處理大型數(shù)據(jù)集合的高效工具。它們可以逐個返回數(shù)據(jù),而不是一次性返回整個數(shù)據(jù)集合。使用生成器和迭代器可以減少對內(nèi)存的需求,避免內(nèi)存泄漏。
? ? (7) 使用with語句:with語句是Python中用于管理資源的一種語法,它可以自動關(guān)閉文件和數(shù)據(jù)庫連接等資源,避免資源未被正確釋放導(dǎo)致的內(nèi)存泄漏。
? ? (8) 使用內(nèi)存分析工具:Python中有一些內(nèi)存分析工具可以幫助我們檢測和解決內(nèi)存泄漏問題,例如pympler和memory_profiler等工具。使用這些工具可以定位內(nèi)存泄漏的代碼,并進行優(yōu)化。
循環(huán)引用造成的內(nèi)存泄露
### 介紹:?? ?
?? ?在python中,都知道當(dāng)一個變量不被引用的時候(引用計數(shù)為0)就會觸發(fā)垃圾回收機制從而被從內(nèi)存中刪除,但如果在兩個對象間去互相引用(你引用我,我也引用你),這樣就會形成一個無限循環(huán)引用(你引用我,我引用你,你又引用我,我又引用你...),那么此時兩個對象一直處于被引用狀態(tài),意味著逃避了垃圾回收機制的回收,在內(nèi)存中一直循環(huán)引用占用著內(nèi)存資源,從而導(dǎo)致這些內(nèi)存資源將不能被再次使用。這就是循環(huán)引用造成的內(nèi)存泄露問題。
? ??
? ??
### 代碼問題闡述:class Person: def __init__(self, name): self.name = name self.cars = [] def add_car(self, car): self.cars.append(car) car.owner = self class Car: def __init__(self, kind): self.kind = kind self.owner = None person = Person("Joden") car = Car("Tesla") person.add_car(car) # car.owner = self # self.cars = [car]
### 代碼解決闡述:
(1) 以下為解決闡述:
?? ?使用弱引用,弱引用不會增加一個變量或?qū)ο蟮囊糜嫈?shù)
? ??
(2) 以下為解決代碼闡述:import weakref class Person: def __init__(self, name): self.name = name self.cars = [] def add_car(self, car): self.cars.append(weakref.ref(car)) car.owner = weakref.ref(self) class Car: def __init__(self, kind): self.kind = kind self.owner = None person = Person("Joden") car = Car("Tesla") person.add_car(car) # car.owner = weakref.ref(self) # self.cars = [weakref.ref(car)]
大量創(chuàng)建對象造成的內(nèi)存泄漏
### 介紹:
?? ?當(dāng)創(chuàng)建大量對象,并且不能及時被垃圾回收機制回收,從而導(dǎo)致的內(nèi)存泄露問題
? ??
? ??
### 代碼問題闡述:class MyClass: def __init__(self, data): self.data = date objs = [] for i in range(1000000): obj = MyClass(i) objs.append(obj)
? ? 闡述:在這個示例中,我們構(gòu)建了一個耗時的循環(huán)創(chuàng)建對象,并且不斷將對象添加到列表中,這樣在循環(huán)的過程中列表就無法被短時間內(nèi)釋放,就會在此期間一直占用內(nèi)存,從而造成內(nèi)存泄露問題
? ? ? ? ? ??
? ? ? ??
### 代碼解決闡述:class MyClass: def __init__(self, data): self.data = date def create_objs(num): for i in range(num): obj = MyClass(i) yield obj
? ? 闡述:在這個示例中,我們將創(chuàng)建對象過程放在了函數(shù)內(nèi)并且用生成器來返回我們創(chuàng)建的對象,這樣只有需要時才會去創(chuàng)建對象就避免了,大量創(chuàng)建對象造成的內(nèi)存泄露問題
全局對象造成的內(nèi)存泄露
### 介紹:
?? ?在了解這個全局變量造成的內(nèi)存泄露之前還需要了解兩個知識:深拷貝和淺拷貝、全局變量。
?? ?深拷貝和淺拷貝:在python中對基本數(shù)據(jù)類型的引用都是淺拷貝(實際上就是對內(nèi)存id的引用,本質(zhì)上為指向同一個內(nèi)存地址的引用),而深拷貝就不一樣了,比如對象、list、dict等等,它們被引用時就會開辟新的內(nèi)存空間來存儲這些數(shù)據(jù)。
?? ?全局變量:很簡單,創(chuàng)建一個py文件(模塊),直接在py文件內(nèi)定義的變量就是全局變量(而非在函數(shù)內(nèi)等局部作用域中定義的變量)。
?? ?那么,現(xiàn)在就很好理解全局變量造成的內(nèi)存泄露問題了。當(dāng)某些模塊中的全局變量被程序中多次引用,那么每次引用就會重新開辟內(nèi)存空間來存儲,這樣就會占用大量的內(nèi)存空間從而造成內(nèi)存泄露問題。
?? ?對于如何解決這個問題,方案是單例模式,單例模式可以確保類在內(nèi)存中只有一個實例然后在整個程序中都會對其引用,這樣就避免了全局變量被多次在內(nèi)存中創(chuàng)建從而導(dǎo)致的內(nèi)存泄露問題。、### 代碼問題闡述:
my_list = [...]
? ? 這個my_list在程序中被多次引用,而且倘若my_list占用較大空間### 代碼解決闡述:
class Singleton: __instance = None def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = super().__new__(cls) cls.__instance.my_list = [] return cls.__instance def add_data(new_data): obj = Singleton() obj.my_list.append(new_data) def get_data(): obj = Singleton() return obj.my_list add_data(1) add_data(2) add_data(3) get_list = get_data()
不適當(dāng)緩存造成的內(nèi)存泄露
### 了解:
?? ?對于這個“不適當(dāng)?shù)木彺嬖斐傻膬?nèi)存泄露”是我們使用緩存原理要注意的,如果我們的緩存中數(shù)據(jù)量過大時就有一定的風(fēng)險了,如果這個緩存在短時間內(nèi)沒被清除且一直變大就會占用越來越多的內(nèi)存就有可能造成內(nèi)存泄露,而且如果程序運行過程中突然崩潰強制關(guān)閉,此時內(nèi)存中的數(shù)據(jù)也可能無法清理總而造成內(nèi)存泄露。
? ? 那么,對于解決“不適當(dāng)?shù)木彺嬖斐傻膬?nèi)存泄露”問題呢?其實本質(zhì)上也是由長時間引用沒被釋放造成的,所以可以使用弱引用來解決。### 問題代碼闡述:
class Calculator: __cache = {} @classmethod def add(cls, a, b): key = f"add_{a}_" if key in cls.__cache: return cls.__cache[key] result = a + b cls.__cache[key] = result return result
? ? 在這個示例中我們使用字典作為加法計算的緩存,如果已經(jīng)計算過了就直接引用,否則將添加到緩存字典中,考慮假若它是我們python底層實現(xiàn)的“加法”,我們在一個大型項目程序中將會多少次調(diào)用它,所以如果不能及時釋放緩存,緩存反而會弄巧成拙
### 解決代碼闡述:文章來源:http://www.zghlxwxcb.cn/news/detail-407050.html
import weakref class Calculator: __cache = weakref.WeakValueDictionary() @classmethod def add(cls, a, b): key = f"add_{a}_" result = cls.__cache.get(key) if result is None: result = a + b cls.__cache[key] = result return result
內(nèi)存分析工具?
### 工具介紹(需要自己去了解) ?
? ? gc:python內(nèi)置的內(nèi)存分析模塊
? ? objgraph:用于生成 Python 對象引用圖,可以幫助我們找出引用計數(shù)不正確的對象。
? ? memory_profiler:用于逐行分析 Python 代碼的內(nèi)存使用情況,可以幫助我們找出內(nèi)存占用過高的代碼段。
? ? pympler:包含了多個子模塊,可以幫助我們分析 Python 中的內(nèi)存使用情況,包括對象分配、垃圾回收、內(nèi)存泄露等。
? ? heapy:基于 Guppy,可以用于分析 Python 中的內(nèi)存使用情況,包括內(nèi)存分配情況、對象大小分布、內(nèi)存泄露等。
? ? 我們可以在程序中使用這些工具,來查看內(nèi)存使用情況、對象數(shù)量等信息,以及跟蹤內(nèi)存泄露和內(nèi)存占用過高的問題。具體使用方法可以參考對應(yīng)工具的官方文檔。文章來源地址http://www.zghlxwxcb.cn/news/detail-407050.html
到了這里,關(guān)于python外篇(內(nèi)存泄露)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!