Python內(nèi)存分析

1. 內(nèi)存分析

1.1 程序運(yùn)行方式

Python執(zhí)行一個(gè)程序:程序就從解釋器申請(qǐng)內(nèi)存

Python解釋器:預(yù)加載->demo.py->demo01.pyc文件->內(nèi)存->運(yùn)行demo01.py程序代碼

1.2 內(nèi)存分配

1.2.1棧內(nèi)存

棧內(nèi)存(stack): 讀取、加載。數(shù)據(jù)速度快但不是很穩(wěn)定,適合存放經(jīng)常臨時(shí)分配、經(jīng)?;厥盏臄?shù)據(jù)變量。

1.2.2 堆內(nèi)存

堆內(nèi)存:讀取、加載數(shù)據(jù)較慢;比較消耗資源,但是一旦數(shù)據(jù)存在,數(shù)據(jù)操作比較穩(wěn)定適合存放的數(shù)據(jù):對(duì)象

1.2.3 數(shù)據(jù)區(qū)

方法區(qū)|數(shù)據(jù)區(qū)[data]:專門加載程序運(yùn)行的代碼字節(jié)數(shù)據(jù),方法數(shù)據(jù)、函數(shù)數(shù)據(jù)等等

1.2.4 靜態(tài)存儲(chǔ)區(qū)

靜態(tài)存儲(chǔ)區(qū)|常量區(qū)[static]:專門存放程序中的公共數(shù)據(jù)、靜態(tài)數(shù)據(jù)的內(nèi)存區(qū)域。

image.png

1.3不可變數(shù)據(jù)類型|可便數(shù)據(jù)類型

1.3.1 不可變數(shù)據(jù)類型

一般數(shù)據(jù)類型都是不可變數(shù)據(jù)類型,不可變數(shù)據(jù)類型是在定義了數(shù)據(jù)之后,修改變量的數(shù)據(jù),變量不會(huì)修改原來(lái)內(nèi)存的地址的數(shù)據(jù),而是會(huì)指向新的地址,原有的數(shù)據(jù)保留,這樣方便程序中基本數(shù)據(jù)的利用率。

例如:整數(shù)類型:-5->256,在解釋器加載時(shí),已經(jīng)自動(dòng)分配了這些數(shù)字的內(nèi)存,超出-5->256范圍的整數(shù),在一個(gè)代碼塊中申請(qǐng)一次內(nèi)存。

交互模式:一行命令就是一個(gè)代碼塊

IDE模式-開發(fā)工具:一個(gè)模塊就是一個(gè)代碼塊

b = 12
print(id(b))
b = 13
print(id(b))
輸出:
1796173168
1796173200

1.3.3 可變數(shù)據(jù)類型

對(duì)象在內(nèi)存地址中存儲(chǔ)的數(shù)據(jù)可變

a = list() # 堆內(nèi)存中:存在一個(gè)對(duì)象 list(),一個(gè)變量a指向這個(gè)對(duì)象
print(id(a)) # 查看對(duì)象a的內(nèi)存
print(a)
a.append("hello")
print(id(a))
print(a)    
輸出:
1357340489992
[]
1357340489992
['hello']

可變類型|不可變類型 思考題:


nums = [12, 13, 14,"python",["hello","world"]]
a = 12
print(id(a),id(nums[0]))
b = "python"
print(id(b),id(nums[3]))
c = ["hello","world"]
print(id(c),id(nums[4]))
輸出:
1796173168 1796173168
1324994797896 1324994797896
1324995705736 1324994820616

1.3 代碼和代碼塊

Python中的最小運(yùn)行單元是代碼塊,代碼塊的最小單元時(shí)一行代碼

思考:a = ‘hello’在內(nèi)存中會(huì)創(chuàng)建幾塊內(nèi)存空間 =======>2

image.png

p = Person(‘tom’,18)在內(nèi)存中,會(huì)創(chuàng)建幾塊內(nèi)存空間 ===========>4

image.png

1.4 程序內(nèi)存代碼檢測(cè)

為了便于檢測(cè)代碼內(nèi)存使用率,社區(qū)開發(fā)了一個(gè)模塊memory_profile

通過(guò) pip install memory_profiler

使用方法:通過(guò)測(cè)試的函數(shù)或者類型前面添加@profile注解,讓內(nèi)存分析模塊可以直接進(jìn)行代碼進(jìn)行檢測(cè)

from memory_profiler import profile

class Person:
    '''自定義類型'''
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
        
@profile(precision=10)
def main():
    '''入口函數(shù)'''
    p = Person('tom',18,'男')
    print(p)
    p2 = Person('tom',18,'男')
    print(p)
if __name__ == "__main__":
    main

2 操作符號(hào)

2.1 is 和 == 和 isinstance 的使用

1. a is b :判斷兩個(gè)變量a/b,他們指向的對(duì)象是否時(shí)同一個(gè)對(duì)象

2. a == b :判斷兩個(gè)變量a/b,他們指向的對(duì)象的數(shù)據(jù)內(nèi)容是否一致

3.isinstance(a,b)判斷a是否屬于b類型

3 引用|深拷貝|淺拷貝

3.1 引用

如果程序中多個(gè)不同的地方都要使用同一個(gè)對(duì)象,通過(guò)對(duì)象的引用賦值,將同一個(gè)對(duì)象賦值給多個(gè)變量。

應(yīng)用賦值并不會(huì)產(chǎn)生新的對(duì)象,而是讓多個(gè)變量可以共同指向一個(gè)對(duì)象,通過(guò)多個(gè)變量都可以操作同一個(gè)對(duì)象的數(shù)據(jù)。

class Person:
    def __init__(self,name,fav):
        self.name = name
        self.fav = ["籃球","足球"]
a = Person("tom",["羽毛球"])
b = c = a
print(id(a))
print(id(b))
print(id(c))
輸出:
2207567775728
2207567775728
2207567775728

3.2 淺拷貝

復(fù)制一個(gè)對(duì)象,復(fù)制對(duì)象中的屬性數(shù)據(jù)的引用

方法:導(dǎo)入模塊 copy

X = copy.copy(a) #拷貝了a對(duì)象,產(chǎn)生了一個(gè)對(duì)象x

class Person:
    def __init__(self,name,fav):
        self.name = name
        self.fav = ["籃球","足球"]
# a = Person("tom",["羽毛球"])
# b = c = a
# print(id(a))
# print(id(b))
# print(id(c))
import copy
a = Person("tom","lol")
x = copy.copy(a)
x.fav.append('aa')
print(id(a)) #2519924135976
print(id(x)) #2519924136872
print(a.fav) #['籃球', '足球', 'aa']
print(x.fav) #['籃球', '足球', 'aa']

3.3 深拷貝

和對(duì)象的淺拷貝不同,對(duì)象的深拷貝,是對(duì)象數(shù)據(jù)的直接拷貝,而不是簡(jiǎn)單的引用拷貝,主要是通過(guò)python內(nèi)建標(biāo)準(zhǔn)模塊copy提供的deepcopy函數(shù)可以完成對(duì)象深拷貝。

4 垃圾回收機(jī)制

自動(dòng)回收無(wú)效對(duì)象數(shù)據(jù),通過(guò)垃圾回收算法進(jìn)行操作

垃圾回收:Garbage Collection : GC

Python中,以引用計(jì)數(shù)垃圾回收算法為主要回收機(jī)制

以標(biāo)記-清除 和 分代回收為輔助回收機(jī)制

4.1 引用計(jì)數(shù)

1.查詢指定對(duì)象的引用數(shù)量

Import sys

s = sys.getrefcount(p) #查詢p的引用數(shù)量為s

  1. python是一個(gè)面向?qū)ο蟮娜躅愋驼Z(yǔ)言,所有的對(duì)象都是直接或者間接繼承自object類型,object類型的核心其實(shí)就是一個(gè)結(jié)構(gòu)體對(duì)象
typedef struct_object { 
    int ob_refcnt; 
    struct_typeobject *ob_type; 
} PyObject;

在這個(gè)結(jié)構(gòu)體中,ob_refcnt就是對(duì)象的引用計(jì)數(shù),當(dāng)對(duì)象被創(chuàng)建或者拷貝時(shí)該計(jì)數(shù)器就會(huì)增加1,當(dāng)對(duì)象的引用變量被刪除時(shí)就會(huì)減少1,當(dāng)引用計(jì)數(shù)為0時(shí),對(duì)象數(shù)據(jù)就會(huì)被釋放。

4.2 標(biāo)記清除

標(biāo)記清除思想: 首先找到python中的一批根節(jié)點(diǎn)對(duì)象,通過(guò)根節(jié)點(diǎn)對(duì)象可以找到他們指向的子節(jié)點(diǎn)對(duì)象,如果搜索過(guò)程中有這個(gè)指向是從上往下的指向,表示這個(gè)對(duì)象是可達(dá)的,否則該對(duì)象是不可達(dá)的,可達(dá)部分的對(duì)象在程序中需要保留下來(lái),不可達(dá)部分的對(duì)象在程序中時(shí)不需要保留的。

4.3 分代回收

在python內(nèi)部處理機(jī)制中,定義了三個(gè)不同的鏈表數(shù)據(jù)結(jié)構(gòu)[第0代,第一代,第二代]。Python為了提高程序執(zhí)行效率,將垃圾回收機(jī)制進(jìn)行了閾值限定,0代回收最為密集,其次是1代,最后是2代

4.4 垃圾回收處理

Python中的gc模塊提供了垃圾回收處理的各項(xiàng)功能機(jī)制,必須import gc才能使用

gc.set_debug(flags):設(shè)置gc的debug日志,一般為gc.DEBUG_LAKE

gc.collect([generation]):顯示進(jìn)行垃圾回收處理,可以輸入?yún)?shù)~參數(shù)表示回收的對(duì)象代數(shù),0表示只檢查第0 代對(duì)象,1表示檢查第0、1代對(duì)象,2表示檢查0,1,2代對(duì)象,如果不傳遞參數(shù),執(zhí)行FULL COLLECT,也就是默認(rèn)傳遞2

gc.set_threshold(threshold0[,threshold2[,threshold3]]):設(shè)置執(zhí)行垃圾回收機(jī)制的頻率

gc.get_count():獲取程序?qū)ο笠玫挠?jì)數(shù)器

gc.get_threshold():獲取程序自動(dòng)執(zhí)行GC的引用計(jì)數(shù)閾值

在程序開發(fā)過(guò)程中需要注意:

l 項(xiàng)目代碼中盡量避免循環(huán)引用

l 引入gc模塊,啟用gc模塊自動(dòng)清理循環(huán)引用對(duì)象的機(jī)制

l 將需要長(zhǎng)期使用的對(duì)象集中管理,減少GC資源消耗

l gc模塊處理不了重寫del方法導(dǎo)致的循環(huán)引用,如果一定要添加該方法,需要顯式調(diào)用gc模塊的garbage中對(duì)象的del方法進(jìn)行處理
`

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容