Hey all… I’ve found what appears to be a significant memory leak when looping over collections (e.g. ArrayList). By significant we’re talking around 4096 bytes every few times the profiler spits out its stats (which is every 30 frames if I recall correctly). I’ve submitted a bug report to Unity but I figure I’d open this up to community discussion for possible workarounds. This is with Unity iPhone v1.0.1.
using UnityEngine;
using System.Collections;
public class LoopLeakTest : MonoBehaviour {
ArrayList myArray;
void Awake()
{
if (myArray == null)
{
myArray = new ArrayList();
}
else
{
myArray.Clear();
}
myArray.Add(1);
myArray.Add(2);
myArray.Add(3);
myArray.Add(4);
myArray.Add(5);
}
// Update is called once per frame
void Update ()
{
test();
}
void test()
{
foreach(int val in myArray)
{
//Debug.Log(val);
}
}
}
To test this out, make a new project in Unity iPhone and delete everything from it. Make a new, empty GameObject and attach that script to it. Build for iPhone and enable the internal profiler. Run from XCode and you can then watch the heap go up and up and up via the console.
I’ve also tried using an IEnumerator and a while loop. It seems linked to collections because I’ve tested this with a primitive array and it doesn’t happen there.
The used heap is going up? That’s perfectly normal. Garbage collection will run eventually.
“Used heap represents the amount of the allocated heap which is currently used up by objects. Every time you create a new class instance (not a struct) this number will grow until collection phase is invoked.”
If I manually invoke the garbage collector it doesn’t clean it up. I’m not allocating any classes in that loop example code shown above.
In my regular project I have an outer loop and and inner loop iterating over about 10 objects, in the FixedUpdate function. That causes the used heap to increase by 4096 bytes every 30 frames.
At that rate I expect I’ll run out of memory within an hour or so.
Yes I have run that, but oddly it doesn’t report any Leaks. The ObjectAlloc for GeneralBlock-16 keeps increasing at an alarming rate though. I think that would indicate that something is still interested in that allocated memory? Either that or I’m not using the Leaks instrument correctly.
that 16byte error sadly is a physx memory leak which has to be fixed in the tech itself.
So you don’t have a leak, the GC just refuses to run at the current memory usage because the overhead of cleaning the memory is larger than the gained benefit out of its view.
If this is Physx, why does the rate of the leak increase the more I loop? Physics code shouldn’t be invoked at all in this example. Also, what do you mean in the tech itself? There’s an OS or hardware problem?
I don’t know if the garbage collector is actually -executing- when I invoke it via the Application class, but it certainly slows everything down quite a lot.
GeneralBlock-16 - shows only construction of blocks it always increases
GeneralBlock–16 - shows only destruction of blocks and it always decreases (always negative).
If Leaks doesn’t report anything, that means there is no memory leaked.
That is correct. You can only hint garbage collector. It is up to garbage collector to decide if complete cleanup is required.
Nevertheless you want to avoid such functions/code patterns which allocate a lot per frame since that will put more stress on GC - it will require more time to scan larger heap.
The GC does not see a reason to clean it yet.
Test it longtime, commonly GCs will raise the heap up to a given point and then it will remain constant unless not freed objects are created
GCs do not clean immediately, that would be totally counterproductive. The first generation of managed languages did that, the result is a seriously bad performance as allocating / freeing memory against the OS is a very slow operation so the GC tries to keep a “practical and likely needed base of allocated memory” for its own object generation and cleaning.
I’m curious and can’t resist asking–why are you worrying about this instead of actually making your game? The quote Keli uses as his sig comes to mind:
The foreach structure creates enumerator objects – even if the loop is empty. If you used a standard for-loop over each index of the ArrayList then it shouldn’t do any heap allocations (AFAIK).
Of course, Unity is probably doing some heap allocations behind the scenes, so you’ll probably see some increase anyway.
Ordinarily I would agree - but in this case, at 26 megs it’s gg.
Before we started refactoring code around the leaks and somewhat annoying internal allocations we had about an 8 minute crash timer - and poorly timed GC calls.
The most memory you will consume with script allocations is about 4mb. After 4mb, GC runs and cleans it out. As long as your app has about 4mb of breathing room, you’re fine.
If its any consolation, the first couple versions of Zombieville were a complete mess, and my scripts allocated memory like crazy. When a lot of stuff was on screen at a time it was at its worst, and I would consume 4mb with allocated garbage in the span of only 3-5 minutes of play. This was sloppy coding on my part and I’ve since learned a lot more about optimizing my scripts, but the game was still stable and didn’t crash. The GC always kicked in after exactly 4mb of junk built up, and my apps total memory usage was capped. The only adverse side effect of my clumsy work was that when the GC ran, there was a noticeable performance hit that lasted a couple seconds. A couple seconds of poor performance every few minutes - not the end of the world, and it didn’t stop the game from being quite popular.
In the most recent version I’ve removed the majority of allocations from my scripts, but it still has a lot left which would have been very difficult to remove due to how I built my systems. Now it runs GC maybe once every 15 minutes, and is barely noticeable, but it still allocates memory pretty often.
I just wanted to dispell the myth that (A) memory growth from scripts can go on INFINITELY and (B) Garbage Collection is evil.