Egret engine memory optimization best practices

We are currently working on a mid-range game project in the clothing category that uses a lot of clothing graphics. During the game test, I found that the memory has been increasing, and some low-end models have flash back phenomenon. Here is the solution process.

There is no doubt that this is a matter of effective memory management. 1, check whether there is memory leak, to see whether there is deleted reference js object creation, whether there is removal of listener; 3. Optimize the size of picture material and reduce the loading of unnecessary pictures. After this change, the test found that the effect is not obvious, and the memory will continue to increase.

  • Is the image memory not freed?

Looking at such a large memory, the basic can be clear is the reason for the picture

  • How do I free image memory?

The inspection code has no place to reference the image, which should be collected by the garbage collector.

  • How to do?

Search the engine documentation and find a res.Destroyres API that destroys the cache data of a single resource file or group of resources. Re-read the source code and see that the Egret engine will reference the resource. If you do not call Res.DestroyRes to release the resource, the image memory will not be recycled! Conclusion: All images loaded by the engine will always be cached in memory unless objects are manually destroyed. The more images you load, the more memory you use until you flash back. The Egret engine itself does not have any memory management policies.

  • What happens next?

Knowing why, it’s easy to release image resources in the right place for each function module. Game materials generally include the following: 1. UI images, such as pop-ups, interfaces and buttons, which are generally packed into code packages; 2. Remote loaded images, such as large backgrounds, which are loaded and displayed according to user data; 3

We developed the following resource memory management strategies:

  1. Call destroyImage to release image resources for large UI materials, such as PNG and JPG, when the interface is closed for each function module.
  2. For remote HTTP images,

A. Use the reSetImage method instead of img.source for images used by non-list items, and call destroyImage when closing the interface to release image resources. B, the images used in the scroll list are managed by HttpImageCacheManage, which is released when the interface is closed. 3. Release mp3 sound files for each function module when closing the interface

Here is the code for several core methods:

/** * Release the eui.image resource. * Use example: destroyImage(this.bg); * Advice: Call this method to release * parameters for image objects larger than 1024: supports resource names, URLS, or image objects */ 
 public destroyImage(name:string | eui.Image){
     if(name instanceof eui.Image){
         if(name.source){
             let source = name.source;
             if(DEBUG) console.warn("Release:" + source)
             name.source = "";
             if(RES.getRes(<any>source))
                 return RES.destroyRes(<any>source);
         }
         return false;
     }
     if(DEBUG) console.warn("Release:" + name)
     if(RES.getRes(<any>name))
         return RES.destroyRes(<any>name);
     return false;
 }
 /* * Release the Image resource and set the Image to a new source * scene: replace img.source = XXXX with this method; * Example: reSetImage(this.bg, XXXX); * Usage suggestion: Call this method to release */ from images whose size is greater than 512, or which have been used once and are no longer used
 public reSetImage(name:eui.Image, newSource:string, nowDestroy:boolean=false){
     if(! name)return null;
     if(name.source){
         let source = <string>name.source;
         if(source ! = newSource){if(DEBUG) console.warn("Release:" + source)
             if(nowDestroy){
                 if(RES.getRes(source))
                     RES.destroyRes(source);
             }
             else{
                 name.once(egret.Event.COMPLETE, () = >{
                     if(RES.getRes(source))
                         RES.destroyRes(source);
                 }, this) name.source = newSource; }}return;
     }
     name.source = newSource;
 }
     
enum HttpImageTYPE { type1, type2, type3}; // Image type classification
/** * Image cache management: For some Http images that are not suitable to be released when changing the source, use the form of tag management, and release when the interface is closed.  HttpImageCacheManage.add(HttpImageTYPE.type1, url); * UI. Hide inside: HttpImageCacheManage. Destroy (HttpImageTYPE. Type1); * * /
class HttpImageCacheManage {

    private static pools = {};

	public constructor(){}// Add the material to the list by type for later release
    public static add(type:number, url:string){
        if(!this.pools[type]) this.pools[type] = {};

        if(!this.pools[type][url])
            this.pools[type][url] = 1;
    }

    // Release the corresponding material according to type type
    public static destroy(type:number){
        let list = this.pools[type];
        for(var key in list){
            let texture = RES.getRes(key);
            if(texture && texture.$bitmapData){
                let hashCode = texture.$bitmapData.hashCode;
                var tempList = egret.BitmapData["_displayList"][hashCode];
                if(tempList && tempList.length > 0) continue; // Indicates that it is in use
            }
            if(DEBUG) console.warn("Release:" + key)
            RES.destroyRes(key);
            delete list[key]
        }
        if(DEBUG){
            if(HttpImageCacheManage["dtime"]) egret.clearTimeout(HttpImageCacheManage["dtime"]);
            HttpImageCacheManage["dtime"] = egret.setTimeout(() = >{
                console.warn("Memory usage after resource release:");
                RES.profile();
            }, this.20); }}}Copy the code

Please pay special attention to: Egret.Texture is one of the resources that are shared in memory. Once released, the image will not be displayed in A.

The Egret engine does not provide the method Egret.Texture is using and requires us to read the private variable to determine.

public static checkIsUsing(resName:string){
	let texture = RES.getRes(resName);
    if(texture && texture.$bitmapData){
         let hashCode = texture.$bitmapData.hashCode;
         var tempList = egret.BitmapData["_displayList"][hashCode];
         if(tempList && tempList.length > 0)
				return true; // Indicates that it is in use
     }
	return false;
}
Copy the code

Through the use of the above strategy, the picture memory has been effectively managed, and then the heavy game, can run normally on the low-end machine. And code changes are cheap!

Finally, the above approach is based on the Egret engine RES component management material. Some developers use Egret. URLLoader or Egret. ImageLoader to load images instead of using RES. How do you free up image memory? A: Just call the dispose method on the unused image resource (egret.Texture) and remove the reference!

This article without permission, prohibit reprint ~~