sys.getsizeof()
来计算内存,但是用这个方法计算时,可能会出现意料不到的问题。__sizeof__()
魔术方法,对于内置对象来说,这个方法是通过 CPython 解释器实现的。/*longobject.c*/
static Py_ssize_t
int___sizeof___impl(PyObject *self)
{
Py_ssize_t res;
res = offsetof(PyLongObject, ob_digit) + Py_ABS(Py_SIZE(self))*sizeof(digit);
return res;
}
pympler
和 pysize
:第一个项目已发布在 Pypi 上,可以“pip install pympler”安装;第二个项目烂尾了,作者也没发布到 Pypi 上(注:Pypi 上已有个 pysize 库,是用来做格式转化的,不要混淆),但是可以在 Github 上获取到其源码。64
118
190
206
300281
30281
def get_size(obj, seen=None):
"""Recursively finds size of objects in bytes"""
size = sys.getsizeof(obj)
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
# Important mark as seen *before* entering recursion to gracefully handle
# self-referential objects
seen.add(obj_id)
if hasattr(obj, '__dict__'):
for cls in obj.__class__.__mro__:
if '__dict__' in cls.__dict__:
d = cls.__dict__['__dict__']
if inspect.isgetsetdescriptor(d) or inspect.ismemberdescriptor(d):
size += get_size(obj.__dict__, seen)
break
if isinstance(obj, dict):
size += sum((get_size(v, seen) for v in obj.values()))
size += sum((get_size(k, seen) for k in obj.keys()))
elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
size += sum((get_size(i, seen) for i in obj))
if hasattr(obj, '__slots__'): # can have __slots__ with __dict__
size += sum(get_size(getattr(obj, s), seen) for s in obj.__slots__ if hasattr(obj, s))
return size
__dict__
和 __slots__
属性的部分(针对类对象),它主要是对字典类型及可迭代对象(除字符串、bytes、bytearray)作递归的计算,逻辑并不复杂。 def asizeof(self, *objs, **opts):
'''Return the combined size of the given objects
(with modified options, see method **set**).
'''
if opts:
self.set(**opts)
self.exclude_refs(*objs) # skip refs to objs
return sum(self._sizer(o, 0, 0, None) for o in objs)
def _sizer(self, obj, pid, deep, sized): # MCCABE 19
'''Size an object, recursively.
'''
s, f, i = 0, 0, id(obj)
if i not in self._seen:
self._seen[i] = 1
elif deep or self._seen[i]:
# skip obj if seen before
# or if ref of a given obj
self._seen.again(i)
if sized:
s = sized(s, f, name=self._nameof(obj))
self.exclude_objs(s)
return s # zero
else: # deep == seen[i] == 0
self._seen.again(i)
try:
k, rs = _objkey(obj), []
if k in self._excl_d:
self._excl_d[k] += 1
else:
v = _typedefs.get(k, None)
if not v: # new typedef
_typedefs[k] = v = _typedef(obj, derive=self._derive_,
frames=self._frames_,
infer=self._infer_)
if (v.both or self._code_) and v.kind is not self._ign_d:
# 猫注:这里计算 flat size
s = f = v.flat(obj, self._mask) # flat size
if self._profile:
# profile based on *flat* size
self._prof(k).update(obj, s)
# recurse, but not for nested modules
if v.refs and deep < self._limit_ \
and not (deep and ismodule(obj)):
# add sizes of referents
z, d = self._sizer, deep + 1
if sized and deep < self._detail_:
# use named referents
self.exclude_objs(rs)
for o in v.refs(obj, True):
if isinstance(o, _NamedRef):
r = z(o.ref, i, d, sized)
r.name = o.name
else:
r = z(o, i, d, sized)
r.name = self._nameof(o)
rs.append(r)
s += r.size
else: # just size and accumulate
for o in v.refs(obj, False):
# 猫注:这里递归计算 item size
s += z(o, i, d, None)
# deepest recursion reached
if self._depth < d:
self._depth = d
if self._stats_ and s > self._above_ > 0:
# rank based on *total* size
self._rank(k, obj, s, deep, pid)
except RuntimeError: # XXX RecursionLimitExceeded:
self._missed += 1
if not deep:
self._total += s # accumulate
if sized:
s = sized(s, f, name=self._nameof(obj), refs=rs)
self.exclude_objs(s)
return s
def flat(self, obj, mask=0):
'''Return the aligned flat size.
'''
s = self.base
if self.leng and self.item > 0: # include items
s += self.leng(obj) * self.item
# workaround sys.getsizeof (and numpy?) bug ... some
# types are incorrectly sized in some Python versions
# (note, isinstance(obj, ()) == False)
# 猫注:不可 sys.getsizeof 的,则用上面逻辑,可以的,则用下面逻辑
if not isinstance(obj, _getsizeof_excls):
s = _getsizeof(obj, s)
if mask: # align
s = (s + mask) & ~mask
return s