python源码剖析-笔记2

  1. PyStringObject对象

    typedef struct {
        PyObject_VAR_HEAD
        long ob_shash;
        int ob_sstate;
        char ob_sval[1];
    } PyStringObject;
    

    头部保存一个ob_size,表示字符串在内存中的具体长度,字符串由ob_sval指针指向,但是,字符串的最后一位也一定是’\0’结束,由于有ob_size的标记,允许字符串中间也有’\0’字符的存在。

  2. 字符串的类型对象中,tp_itemsize设置为sizeof(char),这个值表示变长对象中每个元素单位的长度,每一个python中的变长对象(string、list等),都需要在对应的类型对象中设置tp_itemsize的值。tp_itemsize和ob_size共同决定了一个对象需要在内存中实际占用的空间大小。

  3. 对于空字符串,python会返回其内部的nullstring对象,避免每次都创建,这个对象相当于是共享的。

  4. Intern机制。为了避免每次为相同的字符串开创新对象,可以利用Intern机制来实现。相当于创建时会先查找是否有使用了Intern机制创建的对象,包含的字符串和待创建的一样,如果存在,则直接返回该对象的引用,而不用再次创建一个全新的对象。

  5. interned的实现,实际上是维护了一个map<PyObject*, PyObject*>的字典集合,key,value都是那个采用Interned机制的Py对象指针,如果在这个map中找到一个对象的字符串内容和待创建的一致,则直接把待创建的指针指向这个map的value。加入Map时,按照py的引用计数规则,对应的对象引用计数会先+1,相当于这个对象引用+2了,这样的话,这种对象实际不可能出现引用为0的情况,所有,对于加入到Interned中的对象,会在加入完毕后执行引用计数-2的操作。

  6. 对于单个字符的,也有一个静态缓冲区,类似PyIntObject对小整数的处理。

  7. PyListObject类似于c++中的vector

  8. 结构定义:

        typedef struct {
            PyObject_VAR_HEAD
            PyObject **ob_item;
            int allocated;
        } PyListObject;
    

    一次分配的内存是大于现实需要的内存,类似vector的内存分配策略。
    0 <= ob_size <= allocated;
    len(list) == ob_size

  9. append的元素会放在ob_size位置上,所以内部元素的内存空间不一定连续,但是逻辑上是连续的

  10. PyList的对象缓冲池。 在每个PyList被销毁的那会,会检测free_list这个缓冲池是否满了,如果没满,则会把当前这个待销毁的对象放入这个缓冲池中。当然,这个List内部的元素item都是要被free掉内存的,不然就是一堆野指针了。只是这个对象的内存空间会被缓存下来,避免下次的再次申请内存导致额外的消耗。

  11. PyDictObject底层采用hash_table来存储,hash冲突利用开放地址法来解决(lookdict方法来搜索元素)。

  12. 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在下次插入新元素时会使用到。

  13. 搜索时根据hash值查找,如果查到的key和待搜索的不同,则根据lookdict二次探测。

  14. dict每次的插入,会调用PyDict_SetItem,在这里面会先计算hash值,hash = PyObject_Hash(PyObject*)。在最后,会根据装载率来决定当前ma_table指向的内存是否需要扩容,因为装载率高了,hash冲突的概率就会加大。

  15. PyDict使用的缓冲池技术和Int这些类似,都是在一个Object销毁时加入到缓冲池中。

发布了47 篇原创文章 · 获赞 8 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/it_wjw/article/details/93861063