[Unity] A memory leak occurred when I wrote a process to update Texture2D every frame.

2 minute read

Overview

When I was writing code that updates the Texture of a GameObject every frame and draws it like a movie, an event that crashes abnormally in the native environment occurred. The method investigated at that time and the cause are summarized.

Survey method

I knew that it was memory related because I had issued the following error in advance. (WebGL)

Uncaught RangeError : Maximum Call Stack Size Exceeded

Next, check the memory usage from Profiler and confirm that the Unity memory is over 3GB. (The pace of increase was several tens of MB per second)

image.png

Furthermore, when checking what is consuming memory from Detailed in the memory view, it was found that Texture2D allocates 2.3MB of memory for each frame as shown in the image.

image.png

I knew only one place where Texture2D was generated so much, so I decided to check the corresponding code.

Cause

The corresponding code is as follows.

//frame is a unique class that holds texture information in binary
var texture2D = new Texture2D(frame.Width, frame.Height, TextureFormat.RGBA32, false);
texture2D.wrapMode = TextureWrapMode.Clamp;
texture2D.LoadRawTextureData(frame.Buffer);
texture2D.Apply();
material.mainTexture = texture2D; 

The problem here is the part of new Texture2D (). The memory allocated this time is referenced by material.mainTexture in the current frame, but material.mainTexture is referenced by another in the next frame. Holds. Therefore, the memory area of Texture2D allocated in the previous frame is no longer referenced from anywhere, and since it is Unity memory, GC is not performed and it leaks as it is.

solution

The solution is to manually release the memory of Texture2D used in the previous frame.
Memory release of assets used in Unity can be done with MonoBehaviour.Destroy ().
The actual modified code is as follows.

var texture2D = new Texture2D(frame.Width, frame.Height, TextureFormat.RGBA32, false);
texture2D.wrapMode = TextureWrapMode.Clamp;
texture2D.LoadRawTextureData(frame.Buffer);
texture2D.Apply();

//Added this
Destroy(material.mainTexture);

material.mainTexture = texture2D;

This successfully eliminated the memory leak and prevented the app from crashing in the native environment.

Summary

If you say “the app is heavy or crashes due to memory”, first check “Profiler”.
Even if you hit the dark clouds and fix it, it will only cost you and it will not necessarily improve.

Optimization using Profiler is summarized in Notes from watching the video of “[Unite 2017 Tokyo] Technologies to remember before optimization”. If you have no connection at all, you should read it.

  • We would appreciate it if you could comment if there is a more efficient method of investigation or solution.