Skip to content

Instantly share code, notes, and snippets.

@shalvah
Last active February 18, 2023 12:53
Show Gist options
  • Save shalvah/e50345ef0e1a7ef4ede974d84bf362d3 to your computer and use it in GitHub Desktop.
Save shalvah/e50345ef0e1a7ef4ede974d84bf362d3 to your computer and use it in GitHub Desktop.
Testing garbage collection of circular references in PHP and JS. Thread: https://twitter.com/theshalvah/status/1308797556827267075
// Make sure to run with node --expose-gc
// Control script for the circular references test
// We set circular references, but we remove them explicitly before removing the parent object
// On my machine, memory usage increases by around +1kB at first, then "stabilises" and increases more slowly
(async () => {
while(1) {
let a = {};
let b = {};
a.f = b;
b.f = a;
let mem = process.memoryUsage();
console.log("Total used memory: " + new Intl.NumberFormat().format(mem.heapUsed) + " bytes");
console.log("Total allocated memory: " + new Intl.NumberFormat().format(mem.heapTotal) + " bytes");
// console.log("Total used memory: " + new Intl.NumberFormat().format(window.performance.memory.usedJSHeapSize) + " bytes");
// console.log("Total allocated memory: " + new Intl.NumberFormat().format(window.performance.memory.totalJSHeapSize) + " bytes");
await new Promise((res) => setTimeout(res, 1000)); // Sleep for 1s
delete a.f;
delete b.f;
delete a;
delete b;
gc();
}
})();
<?php
// Control script for the circular references test
// We set circular references, but we remove them explicitly before removing the parent object
// In my tests, memory usage increases for a bit at first (by around 90B),
// then "stabilises" after a few seconds and increases only slowly.
while(1) {
$a = new \stdClass;
$b = new \stdClass;
$a->f = $b;
$b->f = $a;
print "Total used memory: " . number_format(memory_get_usage()) . " bytes\n";
print "Total allocated memory: " . number_format(memory_get_usage(true)) . " bytes\n";
sleep(1);
unset($a->f);
unset($b->f);
unset($a);
unset($b);
}
// Make sure to run with node --expose-gc
// Test script for the circular references test
// Set circular references, and remove the parent objects without removing the references to each other
// On my machine, memory usage increases by around +1kB at first, then "stabilises" and increases more slowly
(async () => {
while(1) {
let a = {};
let b = {};
a.f = b;
b.f = a;
let mem = process.memoryUsage();
console.log("Total used memory: " + new Intl.NumberFormat().format(mem.heapUsed) + " bytes");
console.log("Total allocated memory: " + new Intl.NumberFormat().format(mem.heapTotal) + " bytes");
// console.log("Total used memory: " + new Intl.NumberFormat().format(window.performance.memory.usedJSHeapSize) + " bytes");
// console.log("Total allocated memory: " + new Intl.NumberFormat().format(window.performance.memory.totalJSHeapSize) + " bytes");
await new Promise((res) => setTimeout(res, 1000)); // Sleep for 1s
delete a;
delete b;
gc();
}
})();
<?php
// Test script to see whether PHP properly GCs circular references
// Set circular references, and remove the parent objects without removing the references to each other
// Memory usage increases dramatically. On my machine, it's around +1kB each time.
while(1) {
$a = new \stdClass;
$b = new \stdClass;
$a->f = $b;
$b->f = $a;
print "Total used memory: " . number_format(memory_get_usage()) . " bytes\n";
print "Total allocated memory: " . number_format(memory_get_usage(true)) . " bytes\n";
sleep(1);
unset($a);
unset($b);
}
// Make sure to run with node --expose-gc
// Control script for the circular references (memory island) test
// We set circular references, but we remove them explicitly before removing the parent object
// On my machine, memory usage increases by around +1kB at first, then "stabilises" and increases more slowly
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman
}
}
(async () => {
while (1) {
let family = marry({}, {});
let mem = process.memoryUsage();
console.log("Total used memory: " + new Intl.NumberFormat().format(mem.heapUsed) + " bytes");
console.log("Total allocated memory: " + new Intl.NumberFormat().format(mem.heapTotal) + " bytes");
// console.log("Total used memory: " + new Intl.NumberFormat().format(window.performance.memory.usedJSHeapSize) + " bytes");
// console.log("Total allocated memory: " + new Intl.NumberFormat().format(window.performance.memory.totalJSHeapSize) + " bytes");
await new Promise((res) => setTimeout(res, 1000)); // Sleep for 1s
delete family.father.wife;
delete family.mother.husband;
delete family.mother;
delete family.father;
delete family;
gc();
}
})();
<?php
// Control script for the circular references (memory island) test
// We set circular references, but we remove them explicitly before removing the parent object
// In my tests, memory usage increases for a bit at first (by around 70B),
// then "stabilises" after a few seconds and increases only slowly.
function marry($man, $woman) {
$woman->husband = $man;
$man->wife = $woman;
$result = new \stdClass;
$result->father = $man;
$result->mother = $woman;
return $result;
}
while(1) {
$family = marry(new \stdClass, new \stdClass);
print "Total used memory: " . number_format(memory_get_usage()) . " bytes\n";
print "Total allocated memory: " . number_format(memory_get_usage(true)) . " bytes\n";
unset($family->father->wife);
unset($family->mother->husband);
unset($family->mother);
unset($family->father);
unset($family);
sleep(1);
}
// Make sure to run with node --expose-gc
// Test script for the circular references (memory island) test
// Set circular references, and remove the parent objects without removing the references to each other
// On my machine, memory usage increases by around +1kB at first, then "stabilises" and increases more slowly
function marry(man, woman) {
woman.husband = man;
man.wife = woman;
return {
father: man,
mother: woman
}
}
(async () => {
while (1) {
let family = marry({}, {});
let mem = process.memoryUsage();
console.log("Total used memory: " + new Intl.NumberFormat().format(mem.heapUsed) + " bytes");
console.log("Total allocated memory: " + new Intl.NumberFormat().format(mem.heapTotal) + " bytes");
// console.log("Total used memory: " + new Intl.NumberFormat().format(window.performance.memory.usedJSHeapSize) + " bytes");
// console.log("Total allocated memory: " + new Intl.NumberFormat().format(window.performance.memory.totalJSHeapSize) + " bytes");
await new Promise((res) => setTimeout(res, 1000)); // Sleep for 1s
delete family;
gc();
}
})();
<?php
// Test script for the circular references (memory island) test
// Unset the parent reference without getting rid of the child circular references, leading to an unreachable "memory island"
// Memory usage increases dramatically. On my machine, it's around +800B each time.
function marry($man, $woman) {
$woman->husband = $man;
$man->wife = $woman;
$result = new \stdClass;
$result->father = $man;
$result->mother = $woman;
return $result;
}
while(1) {
$family = marry(new \stdClass, new \stdClass);
print "Total used memory: " . number_format(memory_get_usage()) . " bytes\n";
print "Total allocated memory: " . number_format(memory_get_usage(true)) . " bytes\n";
unset($family);
sleep(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment