Unity UI memory leak optimization

Once the project is running, more and more memory is occupied and will not be released, causing GC to become more frequent and slower. Why are these? Let’s talk about the UI today.

First, let's talk about what is a memory leak?

Generally speaking, a memory leak means that our application applies for an address in the memory, and then all the related references to this address are lost. This memory can no longer be allocated. In the eyes of the computer, it is lost and cannot be retrieved. , unless restarted. . .

However, if we want to understand memory leaks in Unity, we must first understand Unity’s memory allocation mechanism and GC mechanism. Wow, but to be honest, if we really want to explain these two points in detail, it will take us several days to talk about them. It’s not over yet, let’s forget it, haha, let’s talk about it for a while,

When the program is running, it will first apply for a piece of memory from the computer. At this time, if we need to apply for an address, Unity will first find an address block of appropriate size from the heap memory for us. But at this time, if the heap When the memory is used up, the GC will take action. It will first clean up the useless data in the current memory and then allocate the memory blocks we need. What should we do if the GC still does not find enough memory for us to use? , Unity can only go to the memory that was sized before applying for a piece of memory 2.

Now let’s think about it, if we keep repeating the above steps in our project, does it mean that there is a memory leak? . . Now let us start to find out from the actual situation! ! !

At the beginning, we could only see through Unity's Profiler tool that our UI has been closed and destroyed, but the atlas used in the UI still exists in the memory. It shouldn't be. If the atlas is not released, doesn't it mean that we If many UIs are opened, these atlas resources will occupy a lot of memory. How to check the current atlas situation in the memory, you can refer to the figure below. First select the Memory module, then select Detailed, click Take Sample Playmode, and then the memory will be The atlas appears below. Refer to position 5. Here is the option of position 4. If it is not checked, the memory sampling speed will be much faster. If it is checked, it will be much slower, but the corresponding resource current will be sampled at the same time. citation status.

At this time, by sampling the memory of different nodes in the game, we can analyze which of our atlases have not been destroyed with the preset destruction.

The problem has been found, so how to solve it, how to start, now I don’t know what to do, it’s terrible! ! !

 But life has to go on, and problems have to be solved. Then we started problem analysis and countless demo tests, from AB package loading and unloading, to Unity memory allocation management, from the working method of GC to the underlying implementation principle of GC. , finally discovered these problems.

First of all, if our project is loaded through AssetBundle, then when we switch scenes or make stage changes, we need to deal with the release of useless resources and call the following interface.

Resources.UnloadUnusedAssets();

 Uninstall unused resources

 At this time, when we perform memory comparison analysis, we will find that some memory will be released, but the problem of the atlas not being destroyed is still there. I thought it was quite simple, but now it seems that the problem is more complicated. . .

At this time, another tool, Memory Profiler, was used. This tool is a function launched in versions after Unity 2020. It takes a snapshot of the current memory, displays the size of the current memory allocation in a visual form, and lists the type and value of each managed object. , occupied size, address, referenced chain and other information, you can also compare snapshots and analyze the new, deleted and unchanged memory objects in the two memory snapshots, so as to locate the project memory usage more conveniently and quickly.

By taking a snapshot of the memory, analyzing the reference chain of the atlas, blocking the code, re-snapshot testing, and testing again and again, we slowly narrowed the scope of the code and located the reason why the atlas was not destroyed. Finally, we found that our UI used static instances. To achieve the singleton effect, call it in other places, but when our UI does not need it, we do not set this static singleton to null. As a result, the relevant references to the entire UI resource always exist and cannot be released, and we are dealing with the button. When registering an event, the project-encapsulated interface is used. However, after the project-encapsulated interface obtains the delegated event object, it does not clear the delegated event object when the event is removed. As a result, the reference always exists and the related resources are lost. Cannot be released.

I believe that after the above steps, most of the problem of our atlas not being destroyed has been solved. We will add more details later if necessary, haha.

Here is another issue about pictures not being destroyed. In projects, we often dynamically replace certain pictures to realize our functions. At this time, it is very convenient to have a unified interface, but the problem of pictures not being destroyed is also related to this dynamic replacement interface. , since our unified interface will save a reference to the loaded image, when the corresponding preset is destroyed, since the image reference always exists, the image cannot be processed by the GC. At this time, we can consider using our dynamically loaded image. Carry out scene management and clear the reference list at the appropriate time. Since our dynamic image loading is to manage the loading resources ourselves, we must call the unloading resource interface of the corresponding interface once when clearing the list. Otherwise, the resources will still not be able to be loaded from the memory. released in.

So far, most of the problem of not destroying pictures in the atlas has been solved. As for whether there are other specific problems caused by the project, it remains to be studied in the future. To summarize:

  • The static class method is used to implement the singleton UI. After using it, be sure to set the corresponding singleton to null so that the GC can release the corresponding memory.
  • When using delegates or other times, you must remember to release the reference after getting a reference to the class object.
  • Remember to unload loaded resources when they are no longer applicable, such as AssetBundle.Load() and AssetBundle.Unload()
  • Call the Resource.UnloadUnusedAssets() interface at the appropriate time to release useless resources.

In short, to put it simply, memory optimization has always been the highlight of project development, but there is still a long way to go. . .

 With dreams in mind, run towards the distance 

Guess you like

Origin blog.csdn.net/a1191835397/article/details/132304558