How many is a lot of arrays?
I actually recently had a discussion on here about this similar situation, I don’t remember where it is though.
Anyways… first and foremost, like @Kiwasi said, check your actual memory usage in the unity profiler to see where the memory is coming from.
But with that said, you will notice that the memory used by the application in the task manager is still very large. Taking memory away from other programs. Well, there’s a reason for that.
Unity runs all your program code in a virtual runtime called ‘Mono’, which is just an open-source version of Microsoft .Net. One of its features is that it performs all the memory management for you. Here’s the thing though, memory allocation is an annoying complicated task that compilers/languages have been trying to hide from programmers for years.
When you create a new object (like an array) memory has to be allocated. A program when it first starts will make a guess at how much memory it’ll need at startup and ask the OS for a chunk of memory at start to use. In our case, this is called the ‘Heap Memory’. As the program runs we toss objects into the heap in the first open spot, it doesn’t want to waste time calculating the best packing every allocation, so it will just search for the first open space with enough contiguous area to fit your object. In the case of an array this is BIG.
So, if we’ve filled up this memory, and we go to create an object that’s really large, and it can’t find a contiguous block of memory that it had allocated from the OS already… well it doesn’t have enough memory. So it must go back to the OS and ask for another chunk of memory to use for the heap. This is expensive, so our virtual runtime Mono/.Net will assign a larger chunk of data than what we need in anticipation of future objects (usually, depends on how smart the runtime is, us developers don’t control that, it’s all on the version of Mono/.Net being used).
This causes the task in Task Manager to look like more memory is being used. Because it is, we’ve allocated more memory from the OS. That’s what that number reflects… it doesn’t know what part of that allocated memory is used for anything of substance by the program… it’s just been reserved by our program for use whenever it needs it.
For it to be returned, our program has to return it. Regular GC doesn’t necessarily do this, allocating memory from the OS is expensive, so usually it just unassigns the memory, but keeps it reserved for reuse later on. The mono/.net runtime assumes that if you needed this memory at some point in your program, you’ll probably need it again.
It may return it after some time, if it sees you haven’t been using it for a while. But I’m willing to bet the old outdated version of Mono that we use doesn’t do that. It probably just keeps it and keeps on trucking along.
This isn’t even unique to managed languages either. Unmanaged C++ code is also susceptible to it. Default compiler settings often have the free/delete functions just make it available for reuse, yet still reserved from the OS. This is why programs like your browser if left running for days on end will chew up more and more memory despite the resources it allocated memory for are no longer in use (images and what not). It’s not actually using the hundreds of megs of memory it seems to have in the ‘task manageer’, yet it still has them reserved.
Your best bet in general is to reuse large data structures as much as possible. You have several arrays that are 2048x2048 in size? If those were just integers… that’s 2048 * 2048 * 4 bytes == 16 megabytes of memory, if it were Vector3’s for verts, we’re now up to 48 MBs.
Well, if you have several of them, and you create multiple of these arrays, that’s 16 megs each time. But if they all are 2048x2048, and I highly doubt you need each array to exist simultaneously. I’m willing to bet you’re building some structure, manipulating it, and than dumping it back out again (since you force GC when all done with), create 1 array, do your job, and then fill that same array with the values for the next job. You can process 1000 whatevers (image data maybe?), one after the other, while consistantly using only 16 megs of ram for the entire process.