I need help for cocos2d-html5 game memory problem

Hello @pandamicro, @slackmoehrle
Hello cocos2d-x developers.
I have problem with cocos2d-html5 v3.14
I am using plist for graphic assets and using google chrome browser.
During start game, I load 35MB plist files(including png).
But it’s memory usage is over 400MB.

My code is below:

var s_preLoad ={
    "plist0" : "/res/texture/***.plist",
    "plist1" : "/res/texture/***.plist",
    "plist2" : "/res/texture/***.plist",
    "plist3" : "/res/texture/***.plist",
    "plist4" : "/res/texture/***.plist",
    "plist5" : "/res/texture/***.plist",
    "plist8" : "/res/texture/***.plist",
    "plist9" : "/res/texture/***.plist",
    "pic1" : "res/****.png",
    "pic2" : "res/****.png",
    "pic3" : "res/****.png",
    "pic4" : "res/****.png",
    "pic5" : "res/****.png",
    "pic6" : "res/****.png",
    "pic8" : "res/****.png",
    "pic9" : "res/****.png",
    .......
}
loadPreLoad:function () {
        var self = GameLoader.loadScene
        for(var i in s_preLoad){
            var startsStr = i.substr(0, 3)
            if(startsStr == "pic"){ //if png file
                cc.textureCache.addImageAsync(s_preLoad[i], function(data, tex){
                    if(self.hasProgress){
                        self.progress.setPercent(self.loaded / self.count * 100)
                    }
                    var rect = cc.rect(0, 0, tex.getPixelsWide(), tex.getPixelsHigh());
                    var frame = cc.SpriteFrame.createWithTexture(tex, rect)
                    cc.spriteFrameCache.addSpriteFrame(frame, s_preLoad[data])
                    self.loaded = self.loaded + 1;
                    if (self.loaded == self.count){
                        self.runAction(cc.sequence(cc.delayTime(0.2), cc.callFunc(function () {
                            self.loadEnd()
                        }, self)))
                    }
                }.bind(self, i), self)
            }else if(startsStr == "pli"){ //if plist file
                    cc.spriteFrameCache.addSpriteFrames(s_preLoad[i] + ".plist")
                    self.loaded = self.loaded + 1;
                    if (self.loaded == self.count){
                        self.runAction(cc.sequence(cc.delayTime(0.1), cc.callFunc(function () {
                            self.loadEnd()
                        }, self)))
                    }
            }
        }
        return true
 },

s_preLoad defined assets file size is about 35MB, but when after loadPreLoad() functions are proceed, my game memory usage is over 400MB, sometimes it is over 500MB.

Is it normal in cocos2d-html5 game or my code has any problem?

Please let me know your precious idea.
Thank you
Best Regards

I am not sure. I need to yield to @pandamicro

Ok. Thank you for your checking @slackmoehrle
Could you please ask @pandamicro to check my issue?
Hi @pandamicro, could you please help me for this problem?
Sorry for interrupt you.
Thank you

I checked my above code again, and big memory usage is not because of above code.
Above code is just add Sprite frames from already downloaded images.

As I check my game again, main memory usage is because of game assets download.

load:function () {
        this.errResources = []
        cc.loader.load(this.resources,
            function (result, count, loadedCount, err, value) {
                if(err == null){
                    this.loaded++
                    var percent = (this.loaded / this.count * 100);
                    percent = Math.min(percent, 100);
                    this.progress.setPercent(percent);
                }else {
                    this.errResources.push(value)
                }
            }.bind(this), function () {
                if(this.unloadCount < 5){
                    if(this.errResources.length > 0){
                        this.unloadCount++
                        this.resources = []
                        for(var i = 0; i < this.errResources.length; i++){
                            this.resources[i] = this.errResources[i]
                        }
                        this.load()
                    }else if(this.jslist.length == 0){
                        this.loadPreLoad()
                    }else {
                        this.loadJs(this.jslist)
                    }
                }else {
                    this.loadJs(this.jslist)
                    return
                }

            }.bind(this));
        return true
    },

We are using cc.loader.load() for download assets.

Below is load() function of CCBoot.js

    load: function (resources, option, loadCallback) {
        var self = this;
        var len = arguments.length;
        if (len === 0)
            throw new Error("arguments error!");

        if (len === 3) {
        if (typeof option === "function") {
            if (typeof loadCallback === "function")
                option = {trigger: option, cb: loadCallback};
            else
                option = {cb: option, cbTarget: loadCallback};
        }
    } else if (len === 2) {
        if (typeof option === "function")
            option = {cb: option};
    } else if (len === 1) {
        option = {};
    }

    if (!(resources instanceof Array))
        resources = [resources];
    var asyncPool = new cc.AsyncPool(
        resources, cc.CONCURRENCY_HTTP_REQUEST_COUNT,
        function (value, index, AsyncPoolCallback, aPool) {
            self._loadResIterator(value, index, function (value, err) {
                var arr =Array.prototype.slice.call(arguments, 2);
                if (option.trigger)
                    option.trigger.call(option.triggerTarget, arr[0], aPool.size, aPool.finishedSize, err, value);   //call trigger
                AsyncPoolCallback(err, arr[0]);
            }.bind(this, value));
        },
        option.cb, option.cbTarget);
    asyncPool.flow();
    return asyncPool;
},

We are using chrome browser and going to make PC web game.
Memory usage during download assets is over 400MB.
Is it normal or my mistaken in any coding?

Thank you

Hi @pandamicro
I just checked again
We are using cocos-html5->CCBoot.js ->load() function for preload assets.
We are just loading 35MB assets files(plist, mp3, png, fnt)
But game memory usage is over 400MB ~ 500MB.
Is it normal in cocos2d-html5 game?

Thank you
Best Regards

@Jang_Wei Thank you for the update. This is an interesting issue. I am going to ping @pandamicro and ask him to take a look at this. I know that it is preventing you from deploying your game.

memory usage will not be the same size as how much assets you downloaded, downloaded data will be stored in different form than just files which can not be accessed directly by CPU or GPU. For example, a png file could have only 100 kb file size for 1024 * 1024 pixels, but in GPU memory its gl texture costs 4 * 1024 * 1024 = 4 mb. Audio files also have different representations in memory which takes more memory than the original file size.

To understand what’s consuming memory, you can use Chrome task manager, refer to this doc. You should activate JS Heap Memory, GPU Memory and Image Cache.

1 Like

Thank you for your kind answer.
I understand about GPU memory and audio files memory usage.
So even my assets is only 35MB, memory usage will be much bigger than real assets size.

Our game has many scenes, and users will be replace scene frequently.
When users are replace scene, I am trying to release memory for current scene.
So I am using this function cc.director.purgeCachedData()

When I check cocos2d-x lua project, when access this function cc.director.purgeCachedData(), it is access below 2 functions internally in C++ engine code:

SpriteFrameCache::getInstance()->removeUnusedSpriteFrames();
_textureCache->removeUnusedTextures();

But i cocos2d-js project, when I use cc.director.purgeCachedData(), it’s declaration is below:

purgeCachedData: function () {
    cc.animationCache._clear();
    cc.spriteFrameCache._clear();
    cc.textureCache._clear();
},

_clear: function () {
    this._spriteFrames = {};
    this._spriteFramesAliases = {};
    this._frameConfigCache = {};
}

so it means purgeCachedData() is just initializing variables to {};

so when I replace scene, I am release memory by this functions

        cc.spriteFrameCache.removeSpriteFramesFromFile(s_preLoad[i] + ".plist");
        cc.textureCache.removeTextureForKey(s_preLoad[i] + ".png");

But purgeCachedData() already initialize variables, so removeSpriteFrame is just blank function.

removeSpriteFramesFromFile: function (url) {
.....
        if (!cfg) return;  
        //THIS CFG is always undefined, because purgeCachedData is already initialize variables. 
....
    },

And I can’t find removeUnusedTextures(), removeUnusedTextures() functions in cocos2d-js.

I think I am still don’t know how to load and remove sprites&frames properly in cocos2d-js game.
@pandamicro, Could you give me any good example of assets load->use->remove flow codes?

Because memory is 500MB in users are in scene1, and he replace to scene2 and return back to scene1, then memory is 600MB, I think I didn’t release resources properly.

It will much helpful for me.

Thank you
Best Regards.

I can give you some hint, but as it’s been a long time after I look into cocos2d-js code last time, I can’t guarantee it’s the best practice.

  1. Do not use cc.director.purgeCachedData
  2. Use cc.spriteFrameCache.removeSpriteFramesFromFile or cc.spriteFrameCache.removeSpriteFramesFromTexture then use cc.textureCache.removeTexture[ForKey]
  3. You do need to manage the texture and sprite frame memory manually, and Textures consumes almost always most memories, so focus on that.
1 Like

Many Thank you for your hint, @pandamicro
I will to focus on that and try again.

Hi @pandamicro
Thank you again for your precious tips.

I tried with your tips, and it is working well for me.
I used below 2 functions instead of purgeCachedData().

        for(var i in loadedData){
            var extension = cc.path.extname(loadedData[i]).toLowerCase();
            var filename = cc.path.mainFileName(loadedData[i]);
            if(extension == ".png" || extension == ".jpg"){
                cc.spriteFrameCache.removeSpriteFrameByName(loadedData[i]);
                cc.textureCache.removeTextureForKey(loadedData[i]);
            }
            else if(extension == ".plist"){
                cc.spriteFrameCache.removeSpriteFramesFromFile(filename + ".plist");
                cc.textureCache.removeTextureForKey(filename + ".png");
            }
        }

I think it is remove memory usage when scene is exchanged…
But I have another problem:

It is our game structure, so when player enter main scene, he can see menus, each menu is for 1 scene.
And every scene (A ~ H Scene) is not related with each other.
So when entering every scene, it will load it’s own assets which needs in scene. and release it when go back to main scene.

So when I changed my code with your tips, and I enter A scene and return back to main scene, it seems memory is not increased.

But when I enter A scene and return back to main scene, and enter B, return back to main scene, enter C
It is increasing memory, memory is increasing if I enter more scenes…

Is it because of my code problem?
or is it normal because every scene download assets from server?

And in unity3d, when plist&png files, if png size is 2048X2048, it’s memory usage is 16MB.
But 2048X1095, this size of image memory usage is over 20MB. So plist&png is good for memory when use size of power of 2.
Is it same in cocos2d-html5?

Thank you
Best Regards.

@Jang_Wei I am asking the engineering team to look at this.

1 Like

Many Thank you @slackmoehrle

Hello,send me your test project,we need analyze the problem.

1 Like

our game is need to communicate with server.
do you need running game or only source code?
If you also wants to running, I will configure servers.
please let me know your email address to send you project

How can I send my project to you?

I hope you can provide a lite offline test project , we need only a simple test of fast analyzing .
e-mail: xianyin.chen@cocos.com

1 Like

just sent to your email google drive link

Did you get project source code?
if any problems, please let me know.
Thank you

You can send private message directly to him.
In general it’s not good to expose mail or skype id to all public.

1 Like