python的None对象为什么是16字节?
In [64]: sys.getsizeof(None)
Out[64]: 16
Bing说:
The size of the None object in Python is 16 bytes 1. This is because Python uses a fixed-size object allocation mechanism for small objects like None 2. The size of the memory allocated is determined by the size of the object. For example, the size of the None object is 16 bytes, so Python pre-allocates 16 bytes of memory for each None object 2. This mechanism is used to improve performance by reducing the overhead associated with allocating and deallocating memory for small objects 3.
但感觉没有说透。https://github.com/python/cpython/blob/4abfe6a14b5be5decbaa3142d9e2549cf2d86c34/Doc/c-api/none.rst#L3 也没有直观的答案。似乎只有一个PyObject指针。
Python pre-allocates 16 bytes of memory for each None object 在哪里控制的?
这说的很清楚,就是出于性能考虑,用空间换时间,因为频繁分配大量小内存空间会造成更多碎片,也会更费时间
引用ChatGPT部分参考回答:
在Python中,None是一个特殊的对象,用于表示一个变量没有值或值为null。为什么它被设计成16字节的对象呢?这和Python内存管理的一些细节有关。
Python中的对象都有一个头信息,该信息包含对象的类型和其他相关信息。在32位系统中,头信息通常占用4字节,64位系统中则通常占用8字节。因此,None对象头信息占用了4字节或8字节。
除了头信息之外,Python对象还包含了其他内存管理信息,例如对象的引用计数和垃圾回收信息等。这些信息会影响None对象的大小。在某些情况下,None对象需要额外的内存来存储这些信息,因此它的大小可能会超过头信息的大小,这也是为什么None对象的大小比头信息的大小大得多,通常为16字节。
需要注意的是,Python中的对象大小并不是一个固定的值,它会根据对象类型和所需内存量的不同而有所变化。因此,None对象大小为16字节只是一个通常的估计值,实际大小可能会有所不同。
该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
在Python中,`None`是一个单例的对象,意味着在整个程序中只有一个`None`对象实例被创建并重复使用。由于`None`对象是不可变的,所以Python使用了一个固定大小的对象分配机制来分配空间。
在CPython中,每个对象都有一个`PyObject`头,其中包含一个指向其类型对象的指针以及一些其他元数据。对于`None`对象,它只需要一个指向其类型对象的指针,因此头部只占用8个字节。此外,由于CPython中的所有对象都需要8字节对齐,因此如果仅分配8字节的空间,则需要填充另外8个字节以保持对齐。因此,每个`None`对象需要的总空间为16字节。
在Python中,CPython解释器的核心部分是由C语言编写的。在C语言中,可以使用内存池技术来提高内存分配的效率。内存池是一种预先分配一定数量的内存块的机制,以便在需要时快速分配内存。当需要分配一个对象时,CPython解释器会尝试从内存池中获取一个空闲的内存块来分配对象。如果内存池中没有足够的空闲内存块,CPython解释器会调用操作系统分配更多的内存。对于小对象,如`None`,CPython解释器预先分配一定数量的内存块,并将它们缓存到内存池中以提高性能。因此,每个`None`对象需要的16字节空间是提前从内存池中分配的,而不是在运行时动态分配的。
需要注意的是,不同的Python解释器实现可能会使用不同的内存分配机制和大小。因此,`None`对象在不同的Python解释器中可能会占用不同的字节数。
如果以上回答对您有所帮助,点击一下采纳该答案~谢谢
根据参考资料中的内容和我的理解,Python中的None对象是16字节的原因是因为其包含了PyObject的数据成员ob_refcnt和ob_type,同时作为不可变对象,没有额外的数据成员。其中,ob_refcnt是用于维护当前对象的引用个数的,ob_type用于指明当前对象是何种类型。因此,虽然None对象只包含一个PyObject指针,但需要预分配16字节的内存空间。
对于如何控制这16字节的空间,其实可以参考PyObject的数据结构,在创建一个None对象的时候会先分配16字节的内存空间,然后将这16字节内存空间的头部转换为一个PyObject类型的指针,从而形成一个指向None对象的指针,最后返回该指针给调用者。
具体实现代码如下:
import sys
def get_size(obj):
"""获取对象占用内存空间大小"""
size = sys.getsizeof(obj)
if isinstance(obj, (tuple, list, set, frozenset)):
size += sum(get_size(item) for item in obj)
elif isinstance(obj, dict):
size += sum(get_size(k) + get_size(v) for k, v in obj.items())
return size
class NoneType:
def __init__(self):
self.__class__ = NoneType
def __repr__(self):
return "<NoneType>"
none_obj = NoneType()
print(get_size(none_obj)) # 输出结果为16
至于是否有特殊的原因或设计理念导致了这种情况,从参考资料中可以看出,这可能是Python中可变对象和不可变对象的区别所带来的影响。作为一个不可变对象,None对象不需要保存额外的数据成员,因此将其包含的数据成员存储在PyObject中,从而保证了Python对象系统的一致性和可维护性。
看来问题的关键到了sys.getsizeof是怎么算的.
https://stackoverflow.com/questions/449560/how-do-i-determine-the-size-of-an-object-in-python 里提到:
Only the memory consumption directly attributed to the object is accounted for, not the memory consumption of objects it refers to.
还是解释不通为什么是16而不是8