I’ve hit a wall because of massive memory leaks in Unity.
I’m creating texture atlases from Texture2Ds. My textures are imported as RGBA-32 uncompressed, I have 147 images which all told take around 550 MB in the library folder. While creating texture atlases from these source textures, Unity’s memory usage will grow from about 600 MB to 1.6GB on the first run, then bounce between about 1.8GB and 2.6GB on subsequent runs. After about 5-10 runs of the atlas generator, Unity will always crash with the error shown at the bottom of this post.
I am specifically taking these steps to AVOID memory leaks, but its obviously not working:
- The source images are stored in the ScriptableObject as GUID strings, not Texture2D Object references, so the textures are not loaded automatically when the data object is loaded.
- During atlas creation, textures are loaded one at a time through AssetDatabase.GUIDToAssetPath and AssetDatabase.LoadAssetAtPath. I specifically did this so garbage collection would be allowed to dump the source images from memory immediately.
- I never store any source images in variables that persist, only local variables.
- I never store any images in static variables. (I’m aware Unity doesn’t dump these.)
- When I create the atlas textures, I first create a new Texture2D() at the right size, load one source image at a time, write the pixels to the atlas using texture.SetPixels(), then convert to PNG with texture.EncodeToPNG(), Object.DestroyImmediate(atlasTexture), write to disk with BinaryWriter, then import it with AssetDatabase.ImportAsset().
- I am explicitly calling System.GC.Collect() after every run of the atlas generator, but it didn’t seem to change anything.
- Resources.UnloadUnusedAssets() also does not reduce the memory footprint.
It seems to me that garbage collection is simply not doing its job, otherwise the memory of these source images would be freed up. Should I be explicitly destroying arrays like the pixel Color arrays? These are always in local variables, so they should be automatically destroyed.
Here are some tests using Profiler.usedHeapSize to check before and after atlasing.
- 358 MB, 335 MB
- 361 MB, 334 MB
- 359 MB, 334 MB
- 358 MB, 334 MB
- 359 MB, 334 MB
- 358 MB, 334 MB
- CRASH
As you can see, Unity reports the heap size as fairly stable. However the memory footprint of Unity is not stable at all. During processing the heap peaks around 650 MB. Unity however gets up to 2.6GB starting at only 600MB before processing.
I did more testing using the Profiler. Now I can see it in concrete numbers. Column B, Mono Heap Size, grows with every run until it tops out around 2GB then crashes Unity. What could be causing this? Col C, Mono Used Size, also grows a bit. Is this just going to end up being a problem with Mono, meaning no solution? Oh boy. >_<
Run A B C D E F
1 Before 358 4 3 358 428 70
After 336 931 582 336 428 92
2 Before 359 931 353 359 428 69
After 335 1188 510 335 428 93
3 Before 361 1188 459 361 444 82
After 334 1444 458 334 444 109
4 Before 358 1444 345 358 444 85
After 334 1701 879 334 444 109
5 Before 326 1701 511 362 444 81
After 334 1957 586 334 444 109
6 Before 359 1957 380 359 444 85
After 334 1957 586 334 444 109
7 Before 360 1957 398 360 444 83
After 334 1957 586 334 444 109
8 Before 362 1957 535 362 444 81
After 334 1957 586 334 444 109
9 CRASH
All numbers in MB
Columns:
A = Used Heap Size
B = Mono Heap Size
C = Mono Used Size
D = Total Allocated Memory
E = Total Reserved Memory
F = Total Unused Reserve Memory
