Skip to content
Advertisement

Is there a way to check if an object is really released?

Per the doc, destroy() method

Destroys this Game Object removing it from the Display List and Update List and severing all ties to parent resources.

Also removes itself from the Input Manager and Physics Manager if previously enabled.

Use this to remove a Game Object from your game if you don’t ever plan to use it again. As long as no reference to it exists within your own code it should become free for garbage collection by the browser.

If you just want to temporarily disable an object then look at using the Game Object Pool instead of destroying it, as destroyed objects cannot be resurrected.

I wrote this code to check if an object is really released

class BootScene extends Phaser.Scene {
  constructor() {
    super({ key: 'BootScene' });
  }
  create() {
    this.bullet = this.add.circle(50, 50, 10, 0xff0000);
    this.physics.add.existing(this.bullet);
    this.bullet.body.setVelocity(150, 0);

    this.slow_delta = 0;

    this.bullet.body.setCollideWorldBounds(true);
    this.bullet.body.onWorldBounds = true;
    this.bullet.key = 'enemy'
    this.bullet.body.world.on('worldbounds', () => {
      console.log('destroy')
      this.bullet.destroy();
    })
  }

  update(time, delta) {
    // examine the t value every 100 ms
    this.slow_delta += delta;
    if (this.slow_delta > 1000 && time < 100000) {
      this.slow_delta = 0;
      console.log(time, this.bullet.x);
    }
  }
}

var config = {
  width: 800,
  height: 500,
  physics: {
    default: 'arcade',
    arcade: {
      gravity: { y: 0 },
    }
  },
  scene: [BootScene]
}

var game = new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>

and found after bullet goes out of world, its position stays at 790.

However, update() can still get its position rather than undefined which seems to mean the object is not actually released.

Is there a way to check if an object is really released?

with @winner_joiner’s reminder, I also tried this code

const cleanup = new FinalizationRegistry(key => {
});
this.bullet.body.world.on('worldbounds', () => {
  console.log('destroy')
  this.bullet.destroy();
  cleanup.register(this.bullet, 'werwer');
})

bullet still stays there.

Advertisement

Answer

I answer now in a answer, because I will have to go into some detail.

Well you code is almost 100% right, only have to delete the all references(variable/properties), that point to the object, in this case with: delete this.bullet.

Just in case: delete is a javascript operator https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete

Just keep in mind, that FinalizationRegistry just will only inform you, when the browser decides to “garbage collect” the object, this can take a while (and in rare occasions the object could even stay until the browser is closed).

Important: As mention on the mdn documentation: ‘…Note: Cleanup callbacks should not be used for essential program logic. …`. And you don’t really have to worry about correct destory/disposed objects, it’s the browsers job, to garbage collect them if it needs more space or so.

If you want to test, if it works, you would need to fill the memory, and to “force” the browser to start garbage collecting. On this page there is a nice example: https://www.javascripture.com/FinalizationRegistry illustrating how it could be done.

I adapted the example, from the link above, for your example, here you can see it in action:

Warning: this can take some seconds, 115s in my last run. The filling up of the memory with objects, should not be use in production, since it needlessly would slow/strain the computer/browser/application.

class BootScene extends Phaser.Scene {
  constructor() {
    super({ key: 'BootScene' });
  }
  create() {
    this.bullet = this.add.circle(50, 50, 10, 0xff0000);
    this.physics.add.existing(this.bullet);
    this.bullet.body.setVelocity(150, 0);

    this.slow_delta = 0;

    this.bullet.body.setCollideWorldBounds(true);
    this.bullet.body.onWorldBounds = true;
    this.bullet.key = 'enemy'
    
    // register object to watch
    registry.register(this.bullet, 42);
    
    this.bullet.body.world.on('worldbounds', async () => {
      console.log('destroy');
      this.bullet.destroy();
      
      // Remove the last reference to the bullet object
      delete this.bullet;

      // START -- THIS part should not be used for production
      const startTime = Date.now();
      console.log('Allocating a lot of objects to try to force garbage collection');
      while (waitingForCleanup) {
        for (let i = 0; i < 1000; i++) {
          const x = new Array(100);
        }
        await sleep(10);
      }
      console.log(` the bullet was reclaimed after ${((Date.now() - startTime) / 1000).toFixed(1)}s`);
      // END -- THIS part should not be used for production

    })
  }

  update(time, delta) {
    // examine the t value every 100 ms
    this.slow_delta += delta;
    if (this.slow_delta > 1000 && time < 100000) {
      this.slow_delta = 0;
      //console.log(time, this.bullet.x);
    }
  }
}

var config = {
  width: 800,
  height: 500,
  physics: {
    default: 'arcade',
    arcade: {
      gravity: { y: 0 },
    }
  },
  scene: [BootScene]
}

var game = new Phaser.Game(config);

const sleep = (ms) => new Promise(r => setTimeout(r, ms));

let waitingForCleanup = true;
const registry = new FinalizationRegistry((heldValue) => {
  console.log(`cleanup: ${heldValue}`);
  waitingForCleanup = false;
});
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement