测试环境 Python 3.6.4,现象:
>>> id(1)
4489990816
>>> id(2)
4489990848
我的想法:相差的 32 应该是 32 bits(存疑)。id()
在 CPython 中返回的是 1 和 2 在内存中的地址,由于 Python 每个对象都有标准对象头,类型指针和引用计数等信息,所以不能简单地用 -5 ~ 255 这个范围内的整数所占的最小空间去存储,4 bytes 的空间内(感觉有点小?)存储了一些其他信息。因为没有去了解 CPython 具体的实现,不知道自己想的对不对。然后就又有个一个问题:
>>> import sys
>>> sys.getsizeof(1)
28
文档中说 sys.getsizeof()
"Return the size of an object in bytes." 并且 "Only the memory consumption directly attributed to the object is accounted for, not the memory consumption of objects it refers to." 请问这句话中的 directly attributed to 和 refers to 应该怎么理解?(听起来像英语阅读理解…_(:3 」∠)_)这个 size 真的是 bytes 么?
1
wwqgtxx 2018-06-17 21:58:34 +08:00 1
实际上 Python 中的 int 并不是定长的。。。
>>> import sys >>> sys.getsizeof(1) 28 >>> sys.getsizeof(100000000000000000000000) 36 >>> sys.getsizeof(1000000000000000000000000000000000000000000000000) 48 >>> sys.getsizeof(1000000000000000000000000000000000000000000000000000000000000000) 52 |
2
mimzy OP |
3
geelaw 2018-06-17 22:42:05 +08:00 1
你不应该假设连续制造的两个对象的地址也是“接近”的。
后面 Only ... to 这句话是想告诉你如下事实:考虑 A 对象具有 B 字段,B 字段的值是(指向) C 对象(的引用),A 对象的 size 不非要大于 C 对象的 size —— C 对象的 size 不会算入 A 对象的 size。类比到 C 的话 typedef struct { void *member1, *member2, *member3, *member4; } c_t; typedef struct { c_t *b; } a_t; 则 sizeof(a_t) 是 sizeof(void *) 而不是 sizeof(void *) + sizeof(c_t)。 |
4
future0906 2018-06-17 22:56:31 +08:00 1
CPython 里面所有的小整数都是被预初始化,做成一个池的。
|
5
jmc891205 2018-06-17 23:06:29 +08:00 1
id(1)和 id(2)相差的是 32bytes 不是 32bits
在 CPython 的实现里小整数缓存是这样定义的: static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; 在我的 64 位机器上 sizeof(PyLongObject)是 32bytes 因此 id(1)和 id(2)相差了 32bytes 在 CPython 的实现里也可以找到 PyLongObject 的定义: typedef struct _longobject PyLongObject; /* Revealed in longintrepr.h */ struct _longobject { ----PyObject_VAR_HEAD ----digit ob_digit[1]; }; 在我的机器上 PyObject_VAR_HEAD 占 24bytes, digit ob_digit[1]占 4bytes。 可能由于内存对齐的缘故, PyLongObject 实际占用了 32bytes 而不是 28bytes。 |
6
mimzy OP 首先更正小整数对象池 small_ints 的区间应该是 [-5, 257),我正文里写错了。
@geelaw #3 因为不熟悉 C,我目前只能大概理解你后面的意思,之后我会再好好想想。关于假设连续对象的地址接近这个问题,在 [-5, 257) 内的各个数都是差 32,应该是因为先开辟出一整块空间( PyIntBlock ),然后构建的链表,所以是连续的(我刚刚找到的链接里有提到)。然而验证又涉及到阅读具体实现的 C 源代码,看来还是绕不开… @future0906 #4 「小整数」这个关键词找到了有用的信息,我之前知道是被优化了的,但好像搜的关键词不太对 现在看到了这个 https://foofish.net/python_int_implement.html |
9
copie 2018-06-18 00:04:23 +08:00 1
由于我写的比较多,而且回复没有 md 所以我开辟了一个主题
https://www.v2ex.com/t/463799#reply0 |
10
wwqgtxx 2018-06-18 00:07:42 +08:00
其实探讨一下这个问题会更有趣
>>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000002)-id(100000000000000000000000) -40 >>> id(100000000000000000000001)-id(100000000000000000000000) -40 |
11
zhouheyang0919 2018-06-18 00:23:54 +08:00
@wwqgtxx
>>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -120 >>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -40 >>> id(100000000000000000000003)-id(100000000000000000000000) -80 -40 似乎是内存分配器正好分配了连续的内存空间而产生的巧合。 |
12
wwqgtxx 2018-06-18 00:35:20 +08:00
@zhouheyang0919 其实并没有那么简单,你再看看下面的几行输出
>>> id(100000000000000000000000) 1932290411216 >>> id(100000000000000000000001) 1932290411176 >>> id(100000000000000000000002) 1932290411136 >>> id(100000000000000000000003) 1932290411096 |
13
zhouheyang0919 2018-06-18 00:50:13 +08:00
@wwqgtxx
>>> id(100000000000000000000000) 139793637212488 >>> id(100000000000000000000000) 139793637212448 >>> id(100000000000000000000000) 139793637212408 >>> id(100000000000000000000000) 139793637212368 所以呢 |
14
wwqgtxx 2018-06-18 01:03:30 +08:00
@zhouheyang0919 只是说这个问题就很有趣了,涉及到表达式执行顺序,内存分配器策略和垃圾回收时机了,只要频率够高还能跑出来更加诡异的数字
>>> id(100000000000000000000001)-id(100000000000000000000000) -1160 >>> id(100000000000000000000001)-id(100000000000000000000000) -160 >>> id(100000000000000000000001)-id(100000000000000000000000) -1000 >>> id(100000000000000000000001)-id(100000000000000000000000) 1160 >>> id(100000000000000000000001)-id(100000000000000000000000) 1120 >>> id(100000000000000000000001)-id(100000000000000000000000) 1200 |