mirror of
https://github.com/python/cpython.git
synced 2025-12-04 16:43:27 +00:00
GH-125174: Make immortality "sticky" (GH-131355)
* Use a higher threshold for immortality for INCREF than DECREF
This commit is contained in:
parent
aabab76c8a
commit
9881fc5483
2 changed files with 32 additions and 3 deletions
|
|
@ -41,6 +41,10 @@ having all the lower 32 bits set, which will avoid the reference count to go
|
|||
beyond the refcount limit. Immortality checks for reference count decreases will
|
||||
be done by checking the bit sign flag in the lower 32 bits.
|
||||
|
||||
To ensure that once an object becomes immortal, it remains immortal, the threshold
|
||||
for omitting increfs is much higher than for omitting decrefs. Consequently, once
|
||||
the refcount for an object exceeds _Py_IMMORTAL_MINIMUM_REFCNT it will gradually
|
||||
increase over time until it reaches _Py_IMMORTAL_INITIAL_REFCNT.
|
||||
*/
|
||||
#define _Py_IMMORTAL_INITIAL_REFCNT (3ULL << 30)
|
||||
#define _Py_IMMORTAL_MINIMUM_REFCNT (1ULL << 31)
|
||||
|
|
@ -288,7 +292,7 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
|
|||
}
|
||||
#elif SIZEOF_VOID_P > 4
|
||||
PY_UINT32_T cur_refcnt = op->ob_refcnt;
|
||||
if (((int32_t)cur_refcnt) < 0) {
|
||||
if (cur_refcnt >= _Py_IMMORTAL_INITIAL_REFCNT) {
|
||||
// the object is immortal
|
||||
_Py_INCREF_IMMORTAL_STAT_INC();
|
||||
return;
|
||||
|
|
@ -303,7 +307,10 @@ static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
|
|||
#endif
|
||||
_Py_INCREF_STAT_INC();
|
||||
#ifdef Py_REF_DEBUG
|
||||
// Don't count the incref if the object is immortal.
|
||||
if (!_Py_IsImmortal(op)) {
|
||||
_Py_INCREF_IncRefTotal();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ high memory limit to regrtest, with the -M option.
|
|||
"""
|
||||
|
||||
from test import support
|
||||
from test.support import bigmemtest, _1G, _2G, _4G
|
||||
from test.support import bigmemtest, _1G, _2G, _4G, import_helper
|
||||
_testcapi = import_helper.import_module('_testcapi')
|
||||
|
||||
import unittest
|
||||
import operator
|
||||
|
|
@ -1257,6 +1258,27 @@ class DictTest(unittest.TestCase):
|
|||
d[size] = 1
|
||||
|
||||
|
||||
class ImmortalityTest(unittest.TestCase):
|
||||
|
||||
@bigmemtest(size=_2G, memuse=pointer_size * 9/8)
|
||||
def test_stickiness(self, size):
|
||||
"""Check that immortality is "sticky", so that
|
||||
once an object is immortal it remains so."""
|
||||
if size < _2G:
|
||||
# Not enough memory to cause immortality on overflow
|
||||
return
|
||||
o1 = o2 = o3 = o4 = o5 = o6 = o7 = o8 = object()
|
||||
l = [o1] * (size-20)
|
||||
self.assertFalse(_testcapi.is_immortal(o1))
|
||||
for _ in range(30):
|
||||
l.append(l[0])
|
||||
self.assertTrue(_testcapi.is_immortal(o1))
|
||||
del o2, o3, o4, o5, o6, o7, o8
|
||||
self.assertTrue(_testcapi.is_immortal(o1))
|
||||
del l
|
||||
self.assertTrue(_testcapi.is_immortal(o1))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
support.set_memlimit(sys.argv[1])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue