1.內(nèi)存分析與處理
程序的運(yùn)行離不開對(duì)內(nèi)存的操作,一個(gè)軟件要運(yùn)行,需要將數(shù)據(jù)加載到內(nèi)存中,
通過(guò)cpu進(jìn)行內(nèi)存數(shù)據(jù)的讀寫,完成數(shù)據(jù)的運(yùn)算。
2.程序內(nèi)存淺析
軟件程序在計(jì)算機(jī)中執(zhí)行,主要通過(guò)數(shù)據(jù)單元,控制單元,執(zhí)行單元共同協(xié)作,完成數(shù)據(jù)的交互。
python程序運(yùn)行,主要是解釋器從系統(tǒng)中申請(qǐng)內(nèi)存空間來(lái)運(yùn)行
python軟件解釋器將申請(qǐng)的內(nèi)存主要分為以下幾部分來(lái)處理執(zhí)行程序軟件:
棧內(nèi)存(stack) 用于直接分配數(shù)據(jù),存取速度快,數(shù)據(jù)存儲(chǔ)不穩(wěn)定,適用小數(shù)據(jù)塊的快速存取,一般在程序中存儲(chǔ)變量數(shù)據(jù)
方法區(qū)(data):加載程序使用的代碼數(shù)據(jù),二進(jìn)制數(shù)據(jù),方法數(shù)據(jù)等程序運(yùn)行需要的預(yù)加載數(shù)據(jù)
靜態(tài)區(qū)(static):用于加載存儲(chǔ)程序中一些靜態(tài)數(shù)據(jù),常量數(shù)據(jù)等,在python中不可變數(shù)據(jù)也會(huì)存儲(chǔ)在靜態(tài)常量中
堆內(nèi)存(heap):存儲(chǔ)數(shù)據(jù)穩(wěn)定,一般用于存儲(chǔ)加載比較重量級(jí)數(shù)據(jù),例如程序運(yùn)行過(guò)程中的對(duì)象
3.不可變數(shù)據(jù)類型和可變數(shù)據(jù)類型
一般基本數(shù)據(jù)類型都是不可變數(shù)據(jù)樂行。
一般組合數(shù)據(jù)類型或者自定義數(shù)據(jù)類型都是可變數(shù)據(jù)類型。
3.1可變或者不可變,為什么有這樣的規(guī)則?
python中一切都是對(duì)象,可以通過(guò)id()函數(shù)查詢對(duì)象在內(nèi)存中的地址數(shù)據(jù),可變數(shù)據(jù)類型是在定義數(shù)據(jù)后,修改變量數(shù)據(jù),內(nèi)存地址不會(huì)發(fā)生變化。 不可變數(shù)據(jù)類型是定義數(shù)據(jù)后,修改變量數(shù)據(jù),變量不會(huì)改變?cè)瓉?lái)內(nèi)存地址的數(shù)據(jù) 而是會(huì)指向新的地址,原有數(shù)據(jù)保留,方便基本數(shù)據(jù)的利用率。
4.代碼塊
python中最小的運(yùn)行單元是代碼塊,代碼塊最小單元是一行代碼。
實(shí)際開發(fā)過(guò)程中,需要注意python兩種操作方式: (1):交互模式
(2):ide開發(fā)模式
在交互模式下,每行命令是一個(gè)獨(dú)立代碼塊,每個(gè)代碼塊運(yùn)行會(huì)獨(dú)立申請(qǐng)一次內(nèi)存,在操作過(guò)程中交互模式?jīng)]有退出情況下遵循python官方操作標(biāo)準(zhǔn)。
例如,在整數(shù)-5到256之間的數(shù)據(jù)自動(dòng)緩存,字符串自動(dòng)緩存,他們的地址一樣,超出范圍的數(shù)據(jù)需要重新申請(qǐng)內(nèi)存,地址數(shù)據(jù)不一樣。
在IDE開發(fā)模式下,代碼封裝在模塊中,通過(guò)python命令運(yùn)行模塊時(shí),模塊整體作為一個(gè)代碼塊向系統(tǒng)申請(qǐng)內(nèi)存并執(zhí)行程序,地址數(shù)據(jù)沒有改變。
3.2 程序內(nèi)存代碼檢測(cè)
python對(duì)于內(nèi)存操作,社區(qū)開發(fā)一款強(qiáng)大的專門用于檢測(cè)代碼的,用于
代碼調(diào)優(yōu)的模塊memory_profile
是一個(gè)比較簡(jiǎn)單,并且可視化工具,通過(guò)pip install memory_profile安裝即可。
通過(guò)在測(cè)試的函數(shù)或者類型前面添加@profile注解,讓內(nèi)存分析模塊直接可以進(jìn)行代碼運(yùn)行監(jiān)測(cè)
3.3 操作符號(hào) is和==的使用
用于判斷對(duì)象和對(duì)象中的值的情況
a is b : 判斷對(duì)象a 和對(duì)象b 是否同一個(gè)地址,即同一個(gè)對(duì)象
a==b: 判斷a中內(nèi)容是否和b中內(nèi)容一致
無(wú)論基本類型數(shù)據(jù) 還是內(nèi)容復(fù)雜對(duì)象 都可以通過(guò)對(duì)象判斷is 和內(nèi)容判斷操作符號(hào)==來(lái)確定
引入數(shù)據(jù)類型的操作:自定義數(shù)據(jù)類型,變量中存放的是對(duì)象在內(nèi)存中的地址
自定義類型的對(duì)象,每次創(chuàng)建同樣也是在堆內(nèi)存中單獨(dú)創(chuàng)建的對(duì)象,所以 is 判斷 False
3.4 引用,淺拷貝,深拷貝
(1)對(duì)象的內(nèi)存分配
對(duì)象的創(chuàng)建,依賴于申請(qǐng)的內(nèi)存空間中數(shù)據(jù)的加載,對(duì)象在內(nèi)存中的創(chuàng)建過(guò)程依賴于3部分
內(nèi)存處理:對(duì)象分配內(nèi)存地址,引用變量分配內(nèi)存地址,對(duì)象和引用變量之間的關(guān)聯(lián)。
對(duì)象的創(chuàng)建是將堆內(nèi)存中創(chuàng)建的對(duì)象地址臨時(shí)存儲(chǔ)在棧內(nèi)存變量中,如果程序在多個(gè)地方使用
一個(gè)對(duì)象數(shù)據(jù)應(yīng)該怎么辦? 復(fù)制
python對(duì)于這種情況,有三種不同的操作方式:
1.如果程序中多個(gè)不同地方使用同一個(gè)對(duì)象,通過(guò)對(duì)象的引用賦值,將同一個(gè)對(duì)象賦值給
多個(gè)變量
2.如果程序中多個(gè)不同地方都使用相同對(duì)象數(shù)據(jù),通過(guò)對(duì)象拷貝完成數(shù)據(jù)的簡(jiǎn)單復(fù)制就可以
對(duì)象中包含數(shù)據(jù)要求必須一致。
3.如果程序中多個(gè)不同地方使用相同而且獨(dú)立的對(duì)象數(shù)據(jù),通過(guò)對(duì)象深層復(fù)制將對(duì)象數(shù)據(jù)
完整復(fù)制成獨(dú)立一份即可。
(2)對(duì)象的引用賦值
可以將對(duì)象的內(nèi)存地址同時(shí)賦值給多個(gè)變量,這多個(gè)變量存放的都是同一個(gè)對(duì)象引用地址
如果通過(guò)一個(gè)變量修改對(duì)象內(nèi)容,那么其他變量也指向的對(duì)象內(nèi)容同步改變。
對(duì)象的引用變量賦值 a=[1,2,3] b=a
被賦值的變量b 變量a 指向同一個(gè)列表對(duì)象 a=[1,2,3] b=[1,2,3]
通過(guò)引用變量a修改對(duì)象數(shù)據(jù) 通過(guò)引用變量b查看到也是修改后數(shù)據(jù)
a.append(5)
a=[1,2,3,5] b=[1,2,3,5]
對(duì)象的引用變量賦值 并不是對(duì)象的復(fù)制或者備份 只是將對(duì)象的地址存儲(chǔ)在多個(gè)變量中
方便程序操作
注意: python中對(duì)象的引用賦值,針對(duì)可變類型 組合數(shù)據(jù)類型或者自定義class類型都具備
引用賦值操作,不過(guò)不適合不可變類型,一旦通過(guò)變量重新賦值會(huì)指向新的引入對(duì)象。
(3)對(duì)象的淺拷貝
核心機(jī)制是主要賦值對(duì)象內(nèi)部數(shù)據(jù)的引用
python內(nèi)建標(biāo)準(zhǔn)模塊copy提供一個(gè)copy函數(shù)完成
(4)對(duì)象的深拷貝
對(duì)象的深拷貝是對(duì)象數(shù)據(jù)的直接拷貝,而不是簡(jiǎn)單的引用拷貝,通過(guò)python內(nèi)建模塊copy
提供的deepcopy函數(shù)完成
3.5 垃圾回收機(jī)制
垃圾回收機(jī)制(Garbage Collection:GC)基本是所有高級(jí)語(yǔ)言的標(biāo)準(zhǔn)配置之一
一定程度上可以優(yōu)化編程語(yǔ)言數(shù)據(jù)處理效率 提高編程開發(fā)軟件安全性能
python中 垃圾回收機(jī)制 主要以 引用計(jì)數(shù) 為主要手段
以標(biāo)記清除和隔代回收機(jī)制為輔助操作手段
完成對(duì)內(nèi)存中無(wú)效數(shù)據(jù)的自動(dòng)管理操作
3.5.1 引用計(jì)數(shù)
引用計(jì)數(shù)(Reference Counting:RC)是python垃圾回收機(jī)制的核心操作算法
(1)引用計(jì)數(shù)的概念
核心思想:當(dāng)一個(gè)對(duì)象被創(chuàng)建或者拷貝時(shí),引用計(jì)數(shù)+1,當(dāng)這個(gè)對(duì)象多個(gè)引用變量,被銷毀
一個(gè)時(shí)該對(duì)象引用計(jì)數(shù)-1,如果一個(gè)對(duì)象的引用計(jì)數(shù)為0表示該對(duì)象不被引用,可以讓垃圾
回收機(jī)制清除并釋放該對(duì)象占有的內(nèi)存空間。
引用計(jì)數(shù) 優(yōu)點(diǎn): 操作簡(jiǎn)單,實(shí)時(shí)性能優(yōu)秀,能在短時(shí)間獲得并運(yùn)算對(duì)象引用計(jì)數(shù)
缺點(diǎn): 為了維護(hù)每個(gè)對(duì)象引用計(jì)數(shù)算法,python必須提供和對(duì)象對(duì)等的內(nèi)存消耗來(lái)維護(hù)引用
計(jì)數(shù),無(wú)形增加內(nèi)存負(fù)擔(dān)。同時(shí)引用計(jì)數(shù)對(duì)于循環(huán)應(yīng)用 對(duì)象之間互相引用,是無(wú)法進(jìn)行引入
計(jì)數(shù)操作的,會(huì)造成常駐內(nèi)存。
(2)python中的引用計(jì)數(shù)
python是一個(gè)面向?qū)ο蟮娜躅愋驼Z(yǔ)言,所有的對(duì)象都是直接或者間接繼承object類型,
object類型核心其實(shí)是一個(gè)結(jié)構(gòu)對(duì)象。
3.5.2 標(biāo)記清除
python中的標(biāo)記 清除機(jī)制 主要是針對(duì)可能產(chǎn)生循環(huán)引用的對(duì)象進(jìn)行的檢測(cè)機(jī)制
一般情況下循環(huán)引用總是發(fā)生在其他可變對(duì)象的內(nèi)部屬性中,如list,dict,class等
使得該方法消耗的資源和程序中可變對(duì)象的數(shù)量有關(guān)。
標(biāo)記清除核心思想:首先找到 PYTHON 中的一批根節(jié)點(diǎn)對(duì)象,如 object 對(duì)象,通過(guò)根
節(jié)點(diǎn)對(duì)象可以找到他們指向的子節(jié)點(diǎn)對(duì)象,如果搜索過(guò)程中有這個(gè)指向是從上往下的指向,
表示這個(gè)對(duì)象是可達(dá)的,否則該對(duì)象是不可達(dá)的,可達(dá)部分的對(duì)象在程序中需要保留下來(lái),
不可達(dá)部分的對(duì)象在程序中是不需要保留的
3.5.3 分代回收
python中分代回收機(jī)制,是一種通過(guò)空間換取時(shí)間效率的做法,PYTHON 內(nèi)部處理機(jī)制
定義了三個(gè)不同的鏈表數(shù)據(jù)結(jié)構(gòu)[第零代(年輕代),第 1 代(中年代),第 2 代(老年代)]
PYTHON 為了提高程序執(zhí)行效率,將垃圾回收機(jī)制進(jìn)行了閾值限定,0 代鏈表中的垃圾
回收機(jī)制執(zhí)行最為密集,其次是 1 代,最后是 2 代;
備注:弱代假說(shuō):程序中年輕的對(duì)象往往死的更快,年老的對(duì)象往往存活更久
3.5.4 垃圾回收處理
PYTHON 中的 gc 模塊提供了垃圾回收處理的各項(xiàng)功能機(jī)制,必須 import gc 才能使用
gc.set_debug(flags):設(shè)置gc的debug日志,一般為gc.DEBUG_LEAK
gc.collect([generation]):顯式進(jìn)行垃圾回收處理,可以輸入?yún)?shù)~參數(shù)表示回收的對(duì)
象代數(shù),0 表示只檢查第 0 代對(duì)象,1 表示檢查第 0、1 代對(duì)象,2 表示檢查 0、1、2 代獨(dú)
對(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ò)程中,需要注意:
項(xiàng)目代碼盡量避免循環(huán)引用
引入gc模塊,啟動(dòng)gc模塊自動(dòng)清理循環(huán)引用對(duì)象的機(jī)制
將需要長(zhǎng)期使用的對(duì)象集中管理,減少 GC 資源消耗
gc 模塊處理不了重寫del方法導(dǎo)致的循環(huán)引用,如果一定要添加該方法,需要顯式
調(diào)用 gc 模塊的 garbage 中對(duì)象的del方法進(jìn)行處理