diff --git a/Zend/tests/gc_011.phpt b/Zend/tests/gc_011.phpt index 3c6731c547e86..a8eb62c4f83ec 100644 --- a/Zend/tests/gc_011.phpt +++ b/Zend/tests/gc_011.phpt @@ -15,7 +15,6 @@ $a->a = $a; var_dump($a); unset($a); var_dump(gc_collect_cycles()); -var_dump(gc_collect_cycles()); echo "ok\n" ?> --EXPECTF-- @@ -24,6 +23,5 @@ object(Foo)#%d (1) { *RECURSION* } __destruct -int(0) int(1) ok diff --git a/Zend/tests/gc_016.phpt b/Zend/tests/gc_016.phpt index f28005e1bc5e2..daf2697d540d5 100644 --- a/Zend/tests/gc_016.phpt +++ b/Zend/tests/gc_016.phpt @@ -18,11 +18,9 @@ $a = new Foo(); $a->a = $a; unset($a); var_dump(gc_collect_cycles()); -var_dump(gc_collect_cycles()); echo "ok\n" ?> --EXPECT-- -> int(0) -int(0) int(2) ok diff --git a/Zend/tests/gc_017.phpt b/Zend/tests/gc_017.phpt index 118da83413336..95932bc04731d 100644 --- a/Zend/tests/gc_017.phpt +++ b/Zend/tests/gc_017.phpt @@ -32,13 +32,11 @@ unset($a); unset($b); unset($c); var_dump(gc_collect_cycles()); -var_dump(gc_collect_cycles()); echo "ok\n" ?> --EXPECTF-- string(1) "%s" string(1) "%s" string(1) "%s" -int(0) int(1) ok diff --git a/Zend/tests/gc_028.phpt b/Zend/tests/gc_028.phpt index aabff91a3d4b4..17f3645779621 100644 --- a/Zend/tests/gc_028.phpt +++ b/Zend/tests/gc_028.phpt @@ -28,8 +28,6 @@ $bar->foo = $foo; unset($foo); unset($bar); var_dump(gc_collect_cycles()); -var_dump(gc_collect_cycles()); ?> --EXPECT-- -int(0) int(1) diff --git a/Zend/tests/gc_029.phpt b/Zend/tests/gc_029.phpt index b5ecc8fff21bd..dae0f15a9a128 100644 --- a/Zend/tests/gc_029.phpt +++ b/Zend/tests/gc_029.phpt @@ -30,8 +30,6 @@ $bar->foo = $foo; unset($foo); unset($bar); var_dump(gc_collect_cycles()); -var_dump(gc_collect_cycles()); ?> --EXPECT-- -int(0) int(1) diff --git a/Zend/tests/gc_035.phpt b/Zend/tests/gc_035.phpt index 9118174ceb5ee..88aa72eb3f856 100644 --- a/Zend/tests/gc_035.phpt +++ b/Zend/tests/gc_035.phpt @@ -19,9 +19,7 @@ $a->x[] = $a; var_dump(gc_collect_cycles()); unset($a); var_dump(gc_collect_cycles()); -var_dump(gc_collect_cycles()); ?> --EXPECT-- int(0) -int(0) int(2) diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index f0c9a847b8c0b..1a49a98a2f89f 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -1430,7 +1430,10 @@ static void zend_get_gc_buffer_release(void); ZEND_API int zend_gc_collect_cycles(void) { int count = 0; + zend_bool should_rerun_gc = 0; + zend_bool did_rerun_gc = 0; +rerun_gc: if (GC_G(num_roots)) { gc_root_buffer *current, *last; zend_refcounted *p; @@ -1475,8 +1478,8 @@ ZEND_API int zend_gc_collect_cycles(void) * be introduced. These references can be introduced in a way that does not * modify any refcounts, so we have no real way to detect this situation * short of rerunning full GC tracing. What we do instead is to only run - * destructors at this point, and leave the actual freeing of the objects - * until the next GC run. */ + * destructors at this point and automatically re-run GC afterwards. */ + should_rerun_gc = 1; /* Mark all roots for which a dtor will be invoked as DTOR_GARBAGE. Additionally * color them purple. This serves a double purpose: First, they should be @@ -1609,6 +1612,15 @@ ZEND_API int zend_gc_collect_cycles(void) } gc_compact(); + + /* Objects with destructors were removed from this GC run. Rerun GC right away to clean them + * up. We do this only once: If we encounter more destructors on the second run, we'll not + * run GC another time. */ + if (should_rerun_gc && !did_rerun_gc) { + did_rerun_gc = 1; + goto rerun_gc; + } + zend_get_gc_buffer_release(); return count; }