프로그래밍/C# & .NET

CLR 메모리 구조 (노트)

조대협 2010. 5. 12. 00:01

CLR 메모리 참고 자료
에 있는데, JVM이 client모드와 server모드가 있듯이 workstation 모드와 server 모드 두개를 가지고 있다. (current and synchronous). 간략하게 정리하자면 concurrent mode는 JVM의 concurrent GC 모드와 유사하게 pause time을 최소화하는 것이고 주로 desk ap에서 응답 시간 위주로 튜닝하고자할때 사용되고, synchronous 모드는 JVM의 Thoughput collector와 비슷한 원리 같은데, pause를 주더라도 collection을 빨리하는 데 목적을 두고, 주로 Server ap에서(ASP.NET과 같은) 디폴트로 설정된다.
일단 GC 방법은 JVM과 유사한것 같고

The .NET collector has two main modes of operation: concurrent and synchronous (sometimes known as workstation and server). Concurrent garbage collection is used in desktop applications and synchronous is used in server applications such as ASP.NET by default.

In concurrent mode, .NET will try to avoid stopping the running program while a collection is in progress. This means that the total amount that the application can get done in a given period of time is less but the application won’t pause. It’s good for interactive applications where it’s important to give the impression to the user that the application is responding immediately.

In synchronous mode, .NET will suspend the running application while the garbage collector is running. This is actually more efficient overall than concurrent mode - garbage collection takes the same amount of time, but it doesn’t have to contend with the program continuing to run - but means that there can be noticeable pauses when a full collection has to be done.

메모리 Layout 역시 GC후에 Compaction 작업이 일어나면 Old Object를 Lower address로 옮긴다. 이렇게 해서 객체의 이동을 좀 줄이는 방식 같은데..  이 역시 Compaction을 사용하고 알고리즘은 JVM과 같게 Mark & Sweep을 사용한다.

위의 그림처럼 Heap 영역을 g0,g1,g2로 나누고 Old Object일수록 g2쪽에 놓는다. JVM의 New/Eden/Old 영역 사상과 좀 유사하다.어쨌거나  자세한 아키텍쳐는 뜯어봐야 겠고, Native Code에 의해서 Allocate되는등 특정 Address 위치에 Object가 Allocation되면 Pinned Object라고 해서 Fragmentation을 유발한다. 이건 Native Code도 같이 쓰니까는 어쩔 수 없는 것 같고.

GC 알고리즘이야 Root 객체에서 부터 Graph Traverse해가는 방식이라서 이건 유사한듯 보인다.
JVM에 Minor GC와 Full GC가 있는것 처럼 CLR에도 Partial Collection과  Full GC가 있다.
참고 : http://msdn.microsoft.com/en-us/library/ms973837.aspx
JVM에서 Minor GC가 New 영역을 Collection하는 거라면 CLR에서는 New한 Generation을 Collection 하는게 Partial Collection으로 이해된다. (g0 영역을 collection 하면 Partial)

Partial Collections

Unfortunately, the full garbage collection is simply too expensive to do every time, so now it's appropriate to discuss how having generations in the collection helps us out.

First let's consider an imaginary case where we are extraordinarily lucky. Let's suppose that there was a recent full collection and the heap is nicely compacted. Program execution resumes and some allocations happen. In fact, lots and lots of allocations happen and after enough allocations the memory management system decides it's time to collect.

Now here's where we get lucky. Suppose that in all of the time we were running since the last collection we didn't write on any of the older objects at all, only newly allocated, generation zero (gen0), objects have been written to. If this were to happen we would be in a great situation because we can simplify the garbage collection process massively.

Instead of our usual full collect we can just assume that all of the older objects (gen1, gen2) are still live—or at least enough of them are alive that it isn't worth looking at those objects. Furthermore, since none of them were written (remember how lucky we are?) there are no pointers from the older objects to the newer objects. So what we can do is look at all the roots like usual, and if any roots point to old objects just ignore those ones. For other roots (those pointing into gen0) we proceed as usual, following all the pointers. Whenever we find an internal pointer that goes back into the older objects, we ignore it.

When that process is done we will have visited every live object in gen0 without having visited any objects from the older generations. The gen0 objects can then be condemned as usual and we slide up just that region of memory, leaving the older objects undisturbed.

Now this is really a great situation for us because we know that most of the dead space is likely to be in younger objects where there is a great deal of churn. Many classes create temporary objects for their return values, temporary strings, and assorted other utility classes like enumerators and whatnot. Looking at just gen0 gives us an easy way to get back most of the dead space by looking at only very few of the objects.

Unfortunately, we're never lucky enough to use this approach, because at least some older objects are bound to change so that they point to new objects. If that happens it's not sufficient to just ignore them.

http://dotnetperls.com/garbage-collection 요건 .NET GC 내용을 그래프로 보는것 (HP JVM의 Jtune와 비슷)

조금 색다른것 중에 하나가 CLR 은 메모리 관리에 있어서 작은 객체영역과 85K가 넘는 대형 객체를 위한 영역을 나눠서 관리한다는 것이다. 일반적인 객체는 작은 객체 영역인 SOH (Small Object Heap)에 Allocation이 되고, 큰 객체(85K)이상은 LOH (Large Object Heap)에 Allocation이 된다. LOH는 G2 영역에 Allocation이 된다. Large Object는 오래 사용될것으로 예상되고 Collection상의 Overhead가 크기 때문에 Full GC 시에만 Collecting하기 위함이다.
LOH 영역은 Collection이 된후에도 Compaction이 되지 않고 Free Block으로 남아있고, LOH는 이 Free Block에 대한 List로 관리가 된다. 만약에 Free Block의 크기가 Allocation을 시도하고자 하는 Object 크기 보다 작을 경우 (Fragmentation으로 인해서) 추가로 메모리 Allocation을 요청할 수 있다. (이 메커니즘은 아직 이해안됨..). 모 하여간 이런 Large Object가 발생하는거는 주로 Buffer나 Array인 경우니까는 튜닝 가이드 보니까, 이런 Large Object에 의한 Fragmentation을 막기 위해서 Buffer는 되도록이면 재사용하라고 나와 있네 그랴.. 일단 LOH에 대한 상세한 자료는 http://msdn.microsoft.com/en-us/magazine/cc534993.aspx 여기

일단 호기심에서 한시간 정도 쭈욱 훝어보고 메모해놨는데, 실제 튜닝할려면 몇일 더 봐야 할듯.. 일단 다른것도 쭈욱 훝어 봐야 하니.. 링크만 걸어놓고..

참고 자료

'프로그래밍 > C# & .NET' 카테고리의 다른 글

Windows 7에서 IIS 설치하기  (1) 2010.05.13
.NET 튜닝 옵션  (0) 2010.05.12
CLR 메모리 구조 (노트)  (0) 2010.05.12
자바개발자가 본 .NET 프레임웍  (0) 2010.05.11
ApplicationDomain  (0) 2010.05.11
Microsoft Sync Framework (MSF)  (1) 2010.05.10