python源码剖析-笔记2
PyStringObject對象
typedef struct {PyObject_VAR_HEADlong ob_shash;int ob_sstate;char ob_sval[1]; } PyStringObject;頭部保存一個ob_size,表示字符串在內存中的具體長度,字符串由ob_sval指針指向,但是,字符串的最后一位也一定是’\0’結束,由于有ob_size的標記,允許字符串中間也有’\0’字符的存在。
字符串的類型對象中,tp_itemsize設置為sizeof(char),這個值表示變長對象中每個元素單位的長度,每一個python中的變長對象(string、list等),都需要在對應的類型對象中設置tp_itemsize的值。tp_itemsize和ob_size共同決定了一個對象需要在內存中實際占用的空間大小。
對于空字符串,python會返回其內部的nullstring對象,避免每次都創建,這個對象相當于是共享的。
Intern機制。為了避免每次為相同的字符串開創新對象,可以利用Intern機制來實現。相當于創建時會先查找是否有使用了Intern機制創建的對象,包含的字符串和待創建的一樣,如果存在,則直接返回該對象的引用,而不用再次創建一個全新的對象。
interned的實現,實際上是維護了一個map<PyObject*, PyObject*>的字典集合,key,value都是那個采用Interned機制的Py對象指針,如果在這個map中找到一個對象的字符串內容和待創建的一致,則直接把待創建的指針指向這個map的value。加入Map時,按照py的引用計數規則,對應的對象引用計數會先+1,相當于這個對象引用+2了,這樣的話,這種對象實際不可能出現引用為0的情況,所有,對于加入到Interned中的對象,會在加入完畢后執行引用計數-2的操作。
對于單個字符的,也有一個靜態緩沖區,類似PyIntObject對小整數的處理。
PyListObject類似于c++中的vector
結構定義:
typedef struct {PyObject_VAR_HEADPyObject **ob_item;int allocated;} PyListObject;一次分配的內存是大于現實需要的內存,類似vector的內存分配策略。
0 <= ob_size <= allocated;
len(list) == ob_size
append的元素會放在ob_size位置上,所以內部元素的內存空間不一定連續,但是邏輯上是連續的
PyList的對象緩沖池。 在每個PyList被銷毀的那會,會檢測free_list這個緩沖池是否滿了,如果沒滿,則會把當前這個待銷毀的對象放入這個緩沖池中。當然,這個List內部的元素item都是要被free掉內存的,不然就是一堆野指針了。只是這個對象的內存空間會被緩存下來,避免下次的再次申請內存導致額外的消耗。
PyDictObject底層采用hash_table來存儲,hash沖突利用開放地址法來解決(lookdict方法來搜索元素)。
dict里面每個pair對都是一個PyDictEntry
typedef struct {long me_hash; /* cached hash code of me_key */PyObject *me_key;PyObject *me_value;} PyDictEntry;每個dict內部都有一個PyDictEntry的小規模數組(默認8),當dict的size小于8時,內部標識的ma_table指針就指向這個小數組,如果大于8,則申請一塊大內存,ma_table指向那塊內存。me_key有三種狀態,dummy、unused和實際有效使用的active。刪除元素后會變成dummy狀態,freeslot指針會指向該位置,freeslot在下次插入新元素時會使用到。
搜索時根據hash值查找,如果查到的key和待搜索的不同,則根據lookdict二次探測。
dict每次的插入,會調用PyDict_SetItem,在這里面會先計算hash值,hash = PyObject_Hash(PyObject*)。在最后,會根據裝載率來決定當前ma_table指向的內存是否需要擴容,因為裝載率高了,hash沖突的概率就會加大。
PyDict使用的緩沖池技術和Int這些類似,都是在一個Object銷毀時加入到緩沖池中。
總結
以上是生活随笔為你收集整理的python源码剖析-笔记2的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++学习书籍推荐《Advanced C
- 下一篇: python各种类型转换-int,str