21

The Setup

.NET allocates memory for each generation’s heap (0, 1, 2, LOH) in segments to get a continuous block of memory, on startup, and when it attempts to satisfy an allocation request, after a collection.

This memory allocated for each heap will likely level off as the application “warms up”, except potentially for generation 2, and large object heap. During a garbage collection, each heap (0, 1, 2) is swept and compacted, except for the large object heap (LOH), which is just swept.

I understand the ‘sweep’ part of a collection to mean that the GC identifies which objects are no longer rooted and are available for collection (or finalization) and that ‘compact’ means that the addresses that are still alive in a heap are reorganized so that the available remaining heap has more continuous memory available to it.

As the budget for each segment within the heap is exceeded, .NET will allocate another segment in order to fulfill allocations if it can.

The Question

My question comes down to what happens to that memory in each heap, that is not be used by the application (committed) any longer, but is still reserved by .NET? When is it released back to the OS?.

I believe this to be the scenario where a process might appear to be consuming a lot of memory (virtual size is quite large, but private bytes small), but when inspecting its heaps are mostly free space. As another caveat, the total size of the heaps may also be quite small, and not account for the memory being consumed by the process.

There is no blocked finalizer and all looks healthy for a process - it may have been running for weeks before it triggered a monitor alert (e.g.).

Trying for further clarification of the question, if you read Tess .NET Memory Management - A Restaurant Analogy, if the tables are heap segments, does the restaurant ever lose tables (e.g. free heap segments)?

Edit

  1. Removed confusing reference to working set and chickens
  2. Added reference to Tess restaurant analogy
Zach Bonham
  • 6,759
  • 36
  • 31
  • I don't think minimizing asks anybody for any reserved memory (if that is even something that does happen sometime). As far as I can tell, minimizing only results in Windows being more eager to swap that process's memory to disk. – R. Martinho Fernandes Mar 09 '11 at 11:22
  • @Martinho, Winform **may** hook the minimized message and call GC.Collect(), I don't know but it could. – Ian Ringrose Mar 09 '11 at 15:23
  • @Ian: that wouldn't be Windows asking for anything. That would be your own process deciding to compact the heap resulting in even more unused memory that is reserved for your process. Even though that memory is "free", it still belongs to your process, not the OS. The question is not about when a garbage collection occurs, but about when the memory the garbage collector "frees" is returned to the OS. – R. Martinho Fernandes Mar 09 '11 at 15:27
  • If Windows does indeed have a mechanism to ask a process to release some of its unused memory, I never heard of it and all applications I've written so far would just ignore any such request from Windows, based on the fact that I never wrote code to handle it. I bet a lot of apps out there would behave the same way. When I don't need memory I release it. I don't wait for Windows to come bugging "Hey, I need that memory, give it back!" If it comes asking for it, I don't have any to give back. It doesn't seem to make much sense. I can be wrong. – R. Martinho Fernandes Mar 09 '11 at 15:33
  • 1
    @Martinho - your right, what I was (mis)remembering was the processes 'working set' reported being effected by minimizing an application. – Zach Bonham Mar 13 '11 at 23:29
  • @Martinho I believe you are right. Once the .NET runtime allocates a new heap segment for a generation (0,1,2,LOH) I don't see the heap backing each generation ever being released. They may only be added. Still researching this (e.g. pestering SO and [Tess Ferrandez](http://blogs.msdn.com/b/tess/) – Zach Bonham Mar 13 '11 at 23:32
  • Check this site: http://www.informit.com/articles/article.aspx?p=100597 "This is called nondeterministic finalization—you can't say when an object's finalizer will be called. The result is that any memory or unmanaged resources that your object uses remain allocated for an indeterminate time after the object itself is no longer actually in use by your program." – Marek Kwiendacz Mar 09 '11 at 11:23

2 Answers2

10

My answer is - it doesn't matter. The OS gives the application (within which the .NET runtime runs) virtual memory. This is not 'real' memory. The OS can put each page of virtual memory wherever it likes - on the processor, in main memory, on the disk. So, it is possible for an app to use more memory than the amount of RAM on the system, and the OS will copy the bits that are needed to/from disk to ensures the app keeps running (modulo certain addressing & technical limitations).

The OS manages the virtual memory of all the processes on the system, and ensures that one program doesn't hog all the RAM on the system to the detriment of other programs. When the .NET runtime asks for memory from the system for use in the heaps, but then doesn't use it, this memory will (if the system is low on free RAM) be moved to disk, because it isn't being accessed.

Clarification via email from Tess: (emphasis mine)

The segment sizes stay the same throughout the course of the application, but there are two things to consider here.

  1. The allocations for a segment is a virtual allocation, meaning that while we reserve virtual memory, we only commit what we actually use, so the private bytes used for a segment is not the same as the segment size, this means that after a GC, your private bytes will go down, while your virtual bytes will stay the same.

  2. When a segment is no longer used, i.e. if you happen to GC everything in a segment so that it no longer contains any .net objects, the virtual alloc is returned back to the OS.

Taking that on faith, then heap segments (restaurant tables) will be returned back to the OS.

Zach Bonham
  • 6,759
  • 36
  • 31
thecoop
  • 45,220
  • 19
  • 132
  • 189
  • 2
    While I generally agree, especially about the "it doesn't matter" part, this behavior does affect something: how big the swap file is. – R. Martinho Fernandes Mar 09 '11 at 11:43
  • My hard disk at home is 1TB. A few extra MB, even 1GB in the swap file, isn't going to matter – thecoop Mar 09 '11 at 11:48
  • 1
    thecoop: swap file size doesn't matter with large disc, but disc access performance still could be a problem - so faster (and smarter) releasing memory and then smaller swap file is better for performance – Marek Kwiendacz Mar 09 '11 at 11:54
  • 1
    I happen to run a system with the swap file on a 5 GB partition. A 1GB increase in the swap file is going to matter. Just like RAM, swap space is not an inexhaustible resource. It rarely matters, and it's not something you should worry about in your app, but saying it "won't really affect anything" is not entirely correct. – R. Martinho Fernandes Mar 09 '11 at 12:30
  • I, too, generally don't like worry about memory management until I have to! :) Memory 'leaks' in .NET can still occur, so I've tried to understand .NET memory management the best I can. This is one area I would like to know better. – Zach Bonham Mar 13 '11 at 23:38
  • Just so I'm on the same 'page' (bada boom!) the swap file is a shared resource as all processes will utilize it. A 5GB, or [n]GB, swap file, doesn't mean my applications can use all that - i'll still run into addressable process limits based on OS architecture. [Windows Memory Limits)](http://msdn.microsoft.com/en-us/library/aa366778(v=vs.85).aspx#physical_memory_limits_windows_server_2008_r2). – Zach Bonham Mar 13 '11 at 23:43
  • @thecoop - ".NET runtime may, if it chooses, return some unused heap memory back to the OS" - this is exactly the point I'm trying to understand. When does this happen? I know that heap allocs (a heap 'segment') can occur to back each generation, but are those heap segments ever 'freed'? My experience is no, once a process has allocated a new heap segment for a generation memory may be reported as 'freed', but the backing heap will never be released. – Zach Bonham Mar 13 '11 at 23:47
  • 1
    @thecoop - I added comments from an out of band email from Tess to clarify your answer. Please let me know if that was inappropriate. – Zach Bonham Mar 14 '11 at 11:18
0

I don't know the answer to this, but I suspect that .NET doesn't release its heaps until the process exits (and possibly on unloading an AppDomain, at a guess). This is only based on my watching the perfmon .NET Memory counters. The GC total bytes counters shows, at least in my app, that the Reserved bytes go up and down a little over the life of my app, like about 30MB.

And, if the GC is running in the server mode, perhaps .NET releases memory more often.

Chris O
  • 5,017
  • 3
  • 35
  • 42