Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

9 years ago by quanghd

Hi guys!

I was developing a game on ImpactJS and build to native app by using CocoonJs, it works correct and a little smooth on both iOS and Android devices. But when i try it on iPad 2, it shows a critical problem about memory management.

You know, iPad 2 has 512MB RAM, and it'll kill any running application when the free memory is too low (maybe lower than 30MB). My game consume 200MB RAM and it's always killed. I did some memory profiling and saw that large images is the reason.

And the question is: When do garbage collector act? why the garbage collector don't force when I switch between ig.Game? So, it's reason why memory never free.

9 years ago by quanghd

For more detail:

I means when I switch between ig.Game screens, i really expect the images on memory is released. But I still see its on the memory log of CocoonJs. Can do I release its manually?

9 years ago by Joncom

Images are never freed from memory, I believe, because they remain cached in the object ig.Image.cache.

9 years ago by quanghd

Hey Joncom,

I think the cached images is stored on rom (or sdcard) instead RAM memory, like every browser caching feature, right? The image data is loaded onto RAM when the script have a reference to it and It should be freed when the reference is destroy.

9 years ago by Joncom

cached images is stored on rom (or sdcard) instead RAM memory, like every browser caching feature, right?
Maybe this is true sometimes, but not always.

Certain images are transferred over the internet in a compressed manner. These are decompressed for faster layout and hence are stored in a decompressed cache held in memory.
From FAQ of the Caching Mechanism (Mozilla)

Another reason I suspect that images are stored in RAM, is because in Chrome, having several tabs open, viewing high-res images, quickly eats up all my memory.

9 years ago by dominic

This is quite complicated. First of all, if you load your images with the pre-loader, they will be ALL loaded when the game starts. Instead, you could try to load images on instantiation. E.g.:

// Instead of this
EntityPlayer = ig.Entity.extend({
	animSheet:  new ig.AnimationSheet( 'media/player.png', 16, 16 ),
	someImage: new ig.Image('test.png'),
	// ...
});

// Do this
EntityPlayer = ig.Entity.extend({
	init: function(x, y, settings) {
		this.animSheet =  new ig.AnimationSheet( 'media/player.png', 16, 16 );
		this.someImage = new ig.Image('test.png');

		this.parent(x, y, settings);
	}
});

This will ensure that the image will only then be loaded when it's needed. Additionally, the image is now attached to the class instance, instead of to the class prototype itself. This means that when the instance will be garbage collected, so will the image. (The class prototype in turn is never garbage collected, because it will always be accessible at window.EntityPlayer.prototype ).

Well, that's almost true. As Joncom said, you still have to clear the cache by hand:

ig.Image.cache = {};

9 years ago by quanghd

Okay, I placed this code in init function of every ig.Game instances to ensure that the images is no more cached:

ig.Image.cache = {};

But it seems no changing, the internal cache of Impact is cleared, but I still see these images on CocoonJs memory log. I think it's a problem of CocoonJS caching feature.

http://www.ludei.com/forum#/discussion/5/cocoonjs-ios-launcher

The Ludei guy said that loaded images on memory will be deallocated by CocoonJs if no more reference point to its. So, do I forget any reference (except ig.Image.cache) to destroy?

9 years ago by dominic

Yes, please read my post again. You have to clear the cache and omit the pre-loading of images. I.e. don't load images in class prototypes, but only in their init() methods.

9 years ago by quanghd

Of course, I did it already!

All images was loaded in init() function instead in prototypes. But when I switch between ig.Game, the memory increase to a max value. After that, when all images were cached, I saw the memory stops spread.

Here is memory stats when I went through 3 main screen (ig.Game) of my game:

[Step | ig.Game | Textures | Memory]
1 | MainScreen | 66 | 124.15MB <= Used memory at first screen
2 | MapScreen | 82 | 125.89MB <= It's spreaded when go to next screen
3 | BattleScreen | 102 | 150MB <= Here is MAX value
4 | MapScreen | 102 | 150MB <= The memory is intact when return back
5 | MainScreen | 102 | 150MB <= Back to first screen

9 years ago by Joncom

Instead of clearing the cache like this:
ig.Image.cache = {};

Try doing it this way:
for(var path in ig.Image.cache) {
  delete ig.Image.cache[path];
}

Because even if you point ig.Image.cache to a new, empty object, the garbage collector would still think that the old object is using the images.

9 years ago by quanghd

No result. Anyway, thank you, Joncom and Dominic. I strong believe it's memory caching feature from CocoonJs, but It's very terrible for large games :(

9 years ago by quanghd

Hey guys,

I bring a good news for this topic. Ludei's just updated CocoonJs to new 1.4 version. This version comes with an option to resolve my problem.

http://blog.ludei.com/cocoonjs-1-4-available-now/

Images have a CocoonJS-only function called “dispose” that synchronously frees the video memory of the underlying texture so developers have the finest control over memory management


I've tested this function on Android and It works correctly. No more images in cache.

Cheers!

9 years ago by doobdargent

Very interesting topic. Just a quick question, when you load images in the init functions that means you should load the entities when the ig.Game start right?
Because if you use a new Entity with a new image in the middle of the game, it will load the image only at that moment and slow the experience.
Am I correct?

9 years ago by Xatruch

Yes it will load the image, but when you spawn the same entity later it won't load the image again, it will use the one loaded.

9 years ago by Joncom

Quote from dominic
You have to clear the cache and omit the pre-loading of images. I.e. don't load images in class prototypes, but only in their init() methods.
This technique seems to have no effect.

Here's a simple test I've put together ( also on Github ).

ig.module('game.main')
.requires('impact.game')
.defines(function(){

    MyGame = ig.Game.extend({
        load_images_into_memory: function() {
            var images = [
                new ig.Image('media/spider.jpg#01'),
                new ig.Image('media/spider.jpg#02'),
                new ig.Image('media/spider.jpg#03'),
                new ig.Image('media/spider.jpg#04'),
                new ig.Image('media/spider.jpg#05')
            ];
        },
        free_images_from_memory: function() {
            for(var path in ig.Image.cache) {
                delete ig.Image.cache[path];
            }
        }
    });

    ig.main( '#canvas', MyGame, 60, 320, 240, 2 );

});

Problem Test Case:

1. Run the game. (Should be using less than 100MB of memory).

2. Run ig.game.load_images_into_memory(); in console.

3. Watch memory usage climb to and stop around 470MB.
(If you wait long enough, it will drop to ~350MB on its own.)

4. Use ig.game.free_images_from_memory();

5. You will not observe any noticable difference in memory usage.

I suspect this is because there are some pointers which have not yet been broken which are keeping the images alive in memory. Where could they be?

Edit 1:

Managed to shave down the memory usage to about 300MB by breaking the pointer which each ig.Image uses for its canvas data:

free_images_from_memory: function() {
            for(var path in ig.Image.cache) {
                ig.Image.cache[path].data = null; <-- Added this line.
                delete ig.Image.cache[path];
            }
        }

Still feels a long way off... Any suggestions?

Edit 2:

So Firefox seems to handle this just fine actually:
- Loaded app in Firefox (~80MB used).
- Loaded images. (~880MB used).
- Freed memory. (~75 used).
So I suspect that the Chrome garbage collector perhaps doesn't free memory until it is needed for something else.

9 years ago by Joncom

Removed.
Page 1 of 1
« first « previous next › last »