Reputation: 1717
I wrote a WPF application, targeting .NET 4.5.2 64-bit. It runs on Windows 8.1 Professional 64-bit.
The application is used to record multiple videos simultaneously. Because the video encoder's speed can vary greatly during operation, I make some very large arrays of byte arrays in memory (totalling 6 GiB), which is enough for me to buffer 40 seconds of video, and I let the encoding happen on a separate thread, so I don't risk getting blocked by the encoding and miss incoming frames from the cameras.
I have read somewhere that (byte) arrays are lazily allocated in .NET - i.e. just writing byte[] foo = new byte[1024*1024];
does not mean that 1 MiB is immediately allocated; the memory is not actually allocated until the first access. Though I can't find where I read that anymore. Anyhow, knowing that, I make sure to call Array.Clear()
on my arrays, which should force them to be immediately allocated. I also need to make sure the buffer stays in RAM at all times, so I have disabled the page file altogether.
But for some reason, it seems that either the .NET runtime or Windows is still doing something crazy: right after initializing my byte arrays and calling Array.Clear() on all of them, I could see in Task Manager that 7.4 GiB is in use. And it sat there until I pressed the "Record" button, at which point I started to have real data to fill the buffers with. And the strange thing is, the In Use memory started to drop to about 3.6 GiB, and then slowly climbed back to 7.4 GiB. At no point did my application discard and reallocate arrays. Also keep in mind that because I have disabled the page file, there is no place for Windows to page out to.
What is going on? How should I interpret this weirdness?
Upvotes: 2
Views: 109
Reputation: 149626
What is going on? How should I interpret this weirdness?
This isn't weird, this is simply how garbage collection works.
When you use Array.Clear
, you're only resetting those bytes to 0 (Thanks @Martin). This does not mean that a garbage collection will occur at that same moment. If the array is still referenced, this won't even make it eligable for collection. The running time of the GC is non-deterministic.
What you're seeing actually makes sense, since you see that once you press "Record", and the runtime needs to allocate more resources to use, it first invokes a collection, and then starts slowly increasing as those bytes are consumed by your application.
As others have said in the comments, if you need fine grained granularity over memory managed in your app, you should preferably be using a lower level language which enables you to control (as best as it can) the allocation and release of memory.
Upvotes: 2
Reputation: 63772
No, .NET arrays are not allocated lazily. However, the OS does a little trick - when you allocate a pre-zeroed page in memory (and .NET always allocates pre-zeroed pages), it will not actually give you physical RAM - instead, all of your pages point to the (single) zero-page. Until you actually write data to your huge empty array, all of the pages that store the array's data are that one page.
This means that while the virtual memory of the process is always what you'd expect (e.g. the megabyte of the 1024*1024 byte array), the physical RAM usage is not. This is similar to when the OS decides to page your memory out to the page file - you have no control over that, and you really shouldn't have.
In practice, this shouldn't make any difference whatsoever for your application. You still have your contiguous virtual memory space, and the OS will give you real memory as soon as you actually need it. Unless your system is under memory pressure, you get the real memory pretty much immediately (it may need to be zeroed).
Upvotes: 1