1. 程式人生 > >【python測試開發棧】python記憶體管理機制(一)—引用計數

【python測試開發棧】python記憶體管理機制(一)—引用計數

什麼是記憶體

在開始進入正題之前,我們先來回憶下,計算機基礎原理的知識,為什麼需要記憶體。我們都知道計算機的CPU相當於人類的大腦,其運算速度非常的快,而我們平時寫的資料,比如:文件、程式碼等都是儲存在磁碟上的。磁碟的存取速度完全不能匹配cpu的運算速度,因此就需要一箇中間層來適配兩者的不對等,記憶體由此而來,記憶體的存取速率很快,但是儲存空間不大。

舉一個圖書館的例子,便於大家理解,我們圖書館的書架就相當於磁碟,存放了大量的圖書可以供我們閱讀,但是如果書放在書架上,我們沒辦法直接閱讀(效率低),只能將書取出來,放在書桌上看,那書桌就相當於記憶體。

記憶體回收

記憶體資源畢竟是有限的,所以在使用之後,必須被回收掉,否則系統執行一段時間後就會因無記憶體可用而癱瘓。我們軟體測試領域常用的兩種語言:java和python,全部都採用記憶體自動回收的方法,也就是我們只管申請記憶體,但是不管釋放記憶體,由jvm和python直譯器來定期觸發記憶體回收。作為對比,C語言和C++中,程式設計師需要使用malloc申請記憶體,使用free去釋放記憶體,malloc和free必須成對的出現,否則非常容易出現記憶體問題。

還拿上面圖書館的例子,假如圖書館的書看完之後放在書桌上就可以(因為圖書可自動回收),那麼很快的,就沒有位置給新進來的同學看書了。這時候就需要圖書館管理員(jvm或python直譯器)定期的回收圖書,清空書桌。不過正常情況下,我們離開圖書館時,要自己清空書桌,將書放回書架(類似C語言和C++的記憶體回收方式)。

python記憶體管理

引用計數

python通過引用計數來進行記憶體管理,每一個python物件,都維護了一個指向該物件的引用計數。python的sys庫提供了getrefcount()函式來獲取物件的引用計數。下面我們看個例子(注意:不同版本的python,執行結果不同,我這裡採用的是python3.7.4):

"""
    @author: xuanke
    @time: 2019/11/27
    @function: 測試python記憶體
"""
import sys

class RefClass(object):
    def __init__(self):
        print("this is init")

def ref_count_test():
    # 驗證普通字串
    str1 = "abc"
    print(sys.getrefcount(str1))
    # 驗證稍微複雜點的字串
    print(sys.getrefcount("xuankeTester"))
    # 驗證小的數字
    print(sys.getrefcount(12))
    # 驗證大的數字
    print(sys.getrefcount(257))
    # 驗證類
    a = RefClass()
    print(sys.getrefcount(a))
    # 驗證引用計數增加
    b = a
    print(sys.getrefcount(a))

    # 驗證引用計數減少
    b = None
    print(sys.getrefcount(a))

if __name__ == '__main__':
    ref_count_test()

大家先來思考下,最終的結果會是什麼?!我覺得應該很多人都會答錯,因為不同版本的python,對引用變數個數有影響(主要是可複用的物件)。我們先貼出來執行結果,再來分析產生結果的原因:

27
4
9
3
this is init
2
3
2

不過提前宣告一點:sys.getrefcount函式在使用時,因為將物件(比如上例中的str1)作為引數傳入,所以會額外增加一個變數(相當於getrefcount持有了str1的引用),因此實際每個物件的實際引用計數都得減1。下面分別介紹下上面的幾種情況:

  • 字串: str1='abc'的引用數是27-1=26,是因為字串'abc'比較簡單,在python直譯器(CPython)中確實可能存在26個引用。作為對比,在python2.7中,str1的引用變數個數是3-1=2。而字串'xuanketester',是我自定義的一個字串,所以不可能會有其他額外的引用,所以其引用變數個數是3-1=2(至於為什麼是2,理論應該是0,是因為python直譯器預設持有了所有字串的兩個引用)。
  • 數字: 數字12對應的引用計數個數是9-1=8,而257對應的引用計數個數是3-1=2,這主要是因為,在python初始化過程中,就建立了從-5到256的數字,快取起來,這樣做是為了頻繁的分配記憶體,提高效率。而對於不在這個區間的數字,則會重新分配記憶體空間。所以數字12因為被複用,其引用計數個數是8(在python2.7.14中,其引用計數個數是8)。
  • 類: 在上面例子中,建立一個RefClass物件,其引用計數就是2-1=1,因為其是一個我們自定義的類物件,在python直譯器(Cpython)中肯定不會被複用。

我們可以通過列印記憶體地址的方式來驗證上面這幾種情況:

    def memory_address_test():
    str1 = 'xuankeTester'
    str2 = 'xuankeTester'
    print(id(str1))
    print(id(str2))

    str3 = 'abc'
    str4 = 'abc'
    print(id(str3))
    print(id(str4))

    a = 12
    b = 12
    print(id(a))
    print(id(b))

    c = 257
    d = 257
    print(id(c))
    print(id(d))

按照我們上面的分析,c和d的地址應該是不一樣的,a和b的地址是一樣的,字串str1和str2、str3和str4記憶體地址都是一樣的。但是我在pycharm中,直接執行py檔案,結果卻和預想的不一致,結果如下:

2854496960176
2854496960176
2854496857840
2854496857840
140724423258720
140724423258720
2854498931120
2854498931120

所有情況的記憶體地址都是一樣的,這是為什麼呢?我考慮到是不是pycharm對py檔案做了優化,於是我又在命令列嘗試執行,結果還是一樣的。所以,我猜測可能是python直譯器在執行檔案時,為了提高py檔案的執行效率,對檔案的記憶體地址做了優化—相同內容的物件記憶體地址都一樣。

為了驗證這個想法,我直接在python互動模式下執行,果然得到了我想要的結果:

Python 3.7.4 (tags/v3.7.4:e09359112e, Jul  8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a=12
>>> b=12
>>> id(a)
140724423258720
>>> id(b)
140724423258720
>>> a=257
>>> b=257
>>> id(a)
2559155778384
>>> id(b)
2559155778192
>>> a='xuankeTester'
>>> b='xuankeTester'
>>> id(a)
2559155711280
>>> id(b)
2559155711280
>>>

從上面可以看到兩個257對應的地址確實是不一樣的,和我們最初判斷的是一致的。

總結

python通過物件的引用計數來管理記憶體,其實java的JVM也有用引用計數,所以理解了引用計數,為我們理解python的垃圾回收方法打下了基礎。本計劃這一篇文章就將python記憶體管理的機制講完的,但是發現一個記憶體引用計數就有很多東西得寫,所以索性就分兩篇文章來寫,之後再寫一篇文章來介紹python的垃圾回收方式。

相關推薦

python測試開發python記憶體管理機制引用計數

什麼是記憶體 在開始進入正題之前,我們先來回憶下,計算機基礎原理的知識,為什麼需要記憶體。我們都知道計算機的CPU相當於人類的大腦,其運算速度非常的快,而我們平時寫的資料,比如:文件、程式碼等都是儲存在磁碟上的。磁碟的存取速度完全不能匹配cpu的運算速度,因此就需要一箇中間層來適配兩者的不對等,記憶體由此而來

python測試開發python記憶體管理機制—垃圾回收

在上一篇文章中(python 記憶體管理機制—引用計數)中,我們介紹了python記憶體管理機制中的引用計數,python正是通過它來有效的管理記憶體。今天來介紹python的垃圾回收,其主要策略是引用計數為主,標記-清除和分代回收為輔助的策略(熟悉java的同學回回憶下,其實這和JVM的策略是有類似之處的)

python測試開發python基礎語法大盤點

周邊很多同學在用python,但是偶爾會發現有人對python的基礎語法還不是特別瞭解,所以幫大家梳理了python的基礎語法(文中的介紹以python3為例)。如果你已然是python大牛,可以跳過這篇文章。 編碼 python3的預設編碼格式是:UTF-8 ,換句話說也就是:如果不在檔案頭部宣告編碼格式,

java虛擬機器記憶體管理機制:JVM記憶體管理總結分享

近期看了看Java記憶體洩露的一些案例,跟原來的幾個哥們討論了一下,深入研究發現JVM裡面還是有不少以前不知道的細節,這裡稍微剖析一下。先看一看JVM的內部結構——如圖所示,JVM主要包括兩個子系統和兩個元件。兩個子系統分別是Class loader子系統和Execution

iOS開發中的ARC記憶體管理機制1——基礎概念

由於移動裝置的記憶體資源一般比較少,所以垃圾回收機制的操作會對裝置的效能造成比較明顯的影響,有可能在執行垃圾回收的時候讓移動裝置出現卡頓,這對於使用者來說是很難受的事。 由此蘋果公司提出了ARC方案。 0x01 自動引用計數 自動引用計數(Automatic Ref

java記憶體管理機制-執行時資料區

前言   本打算花一篇文章來聊聊JVM記憶體管理機制,結果發現越扯越多,於是分了三遍文章(文章講解JVM以Hotspot虛擬機器為例,jdk版本為1.8),本文為其中第一篇。from java記憶體管理機制(一)-執行時資料區    1、 java記憶體管理機制-執行時資料區

輕量級作業系統FreeRTOS的記憶體管理機制

 本文由嵌入式企鵝圈原創團隊成員朱衡德(Hunter_Zhu)近幾年來,FreeRTOS在嵌入式作業系統排行榜中一直位居前列,作為開源的嵌入式作業系統之一,它支援許多不同架構的處理器以及多種編譯工具鏈,

python測試開發帶你徹底搞明白python3編碼原理

在之前的文章中,我們介紹過編碼格式的發展史:[文章傳送門-todo]。今天我們通過幾個例子,來徹底搞清楚python3中的編碼格式原理,這樣你之後寫python指令碼時碰到編碼問題,才能有章可循。 我們先搞清楚幾個概念: 系統預設編碼:指python直譯器預設的編碼格式,在python檔案頭部沒有宣告其他編

python測試開發—理解python深拷貝與淺拷貝的區別

記憶體的淺拷貝和深拷貝是面試時經常被問到的問題,如果不能理解其本質原理,有可能會答非所問,給面試官留下不好的印象。另外,理解淺拷貝和深拷貝的原理,還可以幫助我們理解Python記憶體機制。這篇文章將會通過一些例子,來驗證記憶體拷貝的過程,幫助大家理解記憶體拷貝的原理。 Python3中的資料型別 我們首先得知

python測試開發—幫你總結Python os模組高頻使用的方法

Python中的os模組是主要和系統操作相關的模組,在平時的工作中會經常用到,花時間整理了os模組的高頻使用方法,同時整理出使用時需要注意的點。歸納來講,os模組的方法可以分為:目錄操作、檔案操作、路徑操作、系統操作等四大類,我們接下來依次進行介紹。 目錄操作相關 建立、刪除、重新命名目錄 # 當前目

python深入之python記憶體管理機制重點

關於python的儲存問題 (1)由於python中萬物皆物件,所以python的儲存問題是物件的儲存問題,並且對於每個物件,python會分配一塊記憶體空間去儲存它 (2)對於整數和短小的字元等,python會執行快取機制,即將這些物件進行快取,不會為相同的物件分配多個

Python記憶體管理機制記憶體塊的申請

哈哈哈,看的有點兒興奮了,筆記下:       在Python的內部同時維護著巨集和記憶體管理函式兩種記憶體管理機制,巨集定義可以節省一次函式呼叫的開銷, 提高執行效率,但同時,使用巨集是危險的,因為隨著Python的演進,記憶體管理機制的實現可能會發生改變,因為巨集

opencv入門之七形態學圖像處理:膨脹、腐蝕

tar struct show 函數 使用 運算 腐蝕和膨脹 依賴 版本 參考網站: http://blog.csdn.net/poem_qianmo/article/details/23710721 1、形態學(morphology)概述   數學形態學(Mathem

數學之美筆記自然語言處理部分.md

strip BE 模擬 ges arr 實驗 語句 次數 而不是 文字、數字、語言 、信息 數字、文字和自然語言一樣,都是信息的載體,他們的產生都是為了記錄和傳播信息。 但是貌似數學與語言學的關系不大,在很長一段時間內,數學主要用於天文學、力學。 本章,我們將回顧一下信息時

朝花夕拾Android效能篇之序言及JVM

序言        筆者從事Anroid開發有些年頭了,深知掌握Anroid效能優化方面的知識的必要性,這是一個程式設計師必須修煉的內功。在面試中,它是面試官的摯愛,在工作中,它是程式碼質量的攔路虎,其重要性可見一斑。在團隊中,效能優化的工作又往往由經驗豐富的老師傅來

自動記憶體管理機制5- 虛擬機器效能監控

自動記憶體管理機制(5)- 虛擬機器效能監控 0. 概述 在我們日常開發的專案中,有時經常會碰到以下問題: OOM(OutOfMemoryError),記憶體不足 記憶體洩漏 執行緒死鎖 Lock Contention,鎖爭用 Java程序消耗CP

自動記憶體管理機制4- 記憶體分配和回收策略

自動記憶體管理機制(4)- 記憶體分配和回收策略 Java所承諾的自動記憶體管理主要是針對物件記憶體的回收和物件記憶體的分配。 在Java虛擬機器的五塊記憶體空間中,程式計數器、Java虛擬機器棧、本地方法棧記憶體的分配和回收都具有確定性,一般在編譯階段就能確定需要分配的記憶體大小,

自動記憶體管理機制3-HotSpot垃圾收集器

自動記憶體管理機制(3)-HotSpot垃圾收集器 如果說收集演算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實現。 這裡討論的收集器都是JDK1.7(包含JDK1.7)以後的HotSpot虛擬機器: 上半部屬於新生代收集器,下半部屬於老年代收集器。如果兩個收集器

自動記憶體管理機制2- 記憶體回收和垃圾收集演算法

自動記憶體管理機制(2)- 記憶體回收和垃圾收集演算法 1. 概述 首先思考三個問題: 哪些記憶體需要回收 什麼時候回收 如何回收 程式計數器、虛擬機器棧、本地方法棧是執行緒私有的,因此這幾個區域的記憶體分配和回收都具有確定性(執行緒結束時執行垃圾回

自動記憶體管理機制1- java記憶體區域與虛擬機器物件

自動記憶體管理機制(1)- java記憶體區域與虛擬機器物件 1. 執行時資料區域 Java虛擬機器在執行Java程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。有的區域隨著虛擬機器進行的啟動而存在,有些區域則以來使用者執行緒的啟動和結束而建立和銷燬。 有以下幾個區域