Allocating Resources from the Managed Heap

The CLR requires that all objects be allocated from the managed heap. When a process is initialized, the CLR allocates a region of address space for the managed heap. The CLR also maintains a pointer, which I’ll call NextObjPtr. This pointer indicates where the next object is to be allocated within the heap. Initially, NextObjPtr is set to the base address of the address space region.

C#’s new operator causes the CLR to perform the following steps:

  1. Calculate the number of bytes required for the type’s fields (and all the fields it inherits from its base types).
  2. Add the bytes required for an object’s overhead. Each object has two overhead fields: a type object pointer and a sync block index. For a 32-bit application, each of these fields requires CHAPTER 21 The Managed Heap and Garbage Collection 507 32 bits, adding 8 bytes to each object. For a 64-bit application, each field is 64 bits, adding 16 bytes to each object.
  3. The CLR then checks that the bytes required to allocate the object are available in the region. If there is enough free space in the managed heap, the object will fit, starting at the address pointed to by NextObjPtr, and these bytes are zeroed out. The type’s constructor is called (passing NextObjPtr for the this parameter), and the new operator returns a reference to the object. Just before the reference is returned, NextObjPtr is advanced past the object and now points to the address where the next object will be placed in the heap.

The Garbage Collection Algorithm

the CLR uses a referencing tracking algorithm. The reference tracking algorithm cares only about reference type variables, because only these variables can refer to an object on the heap; value type variables contain the value type instance directly. Reference type variables can be used in many contexts: static and instance fields within a class or a method’s arguments or local variables. We refer to all reference type variables as roots.

When the CLR starts a GC, the CLR first suspends all threads in the process. This prevents threads from accessing objects and changing their state while the CLR examines them. Then, the CLR performs what is called the marking phase of the GC. First, it walks through all the objects in the heap setting a bit (contained in the sync block index field) to 0. This indicates that all objects should be deleted. Then, the CLR looks at all active roots to see which objects they refer to. This is what makes the CLR’s GC a reference tracking GC. If a root contains null, the CLR ignores the root and moves on to examine the next root.

Any root referring to an object on the heap causes the CLR to mark that object. Marking an object means that the CLR sets the bit in the object’s sync block index to 1. When an object is marked, the CLR examines the roots inside that object and marks the objects they refer to. If the CLR is about to mark an already-marked object, then it does not examine the object’s fields again. This prevents an infinite loop from occurring in the case where you have a circular reference.

The marking phase continues until all the application roots have been examined

Once complete, the heap contains some marked and some unmarked objects. The marked objects must survive the collection because there is at least one root that refers to the object; we say that the object is reachable because application code can reach (or access) the object by way of the variable that still refers to it. Unmarked objects are unreachable because there is no root existing in the application that would allow for the object to ever be accessed again.

Now that the CLR knows which objects must survive and which objects can be deleted, it begins the GC’s compacting phase. During the compacting phase, the CLR shifts the memory consumed by the marked objects down in the heap, compacting all the surviving objects together so that they are contiguous in memory. This serves many benefits. First, all the surviving objects will be next to each other in memory; this restores locality of reference reducing your application’s working set size, thereby improving the performance of accessing these objects in the future. Second, the free space is all contiguous as well, so this region of address space can be freed, allowing other things to use it. Finally, compaction means that there are no address space fragmentation issues with the managed heap as is known to happen with native heaps.

When compacting memory, the CLR is moving objects around in memory. This is a problem because any root that referred to a surviving object now refers to where that object was in memory; not where the object has been relocated to. When the application’s threads eventually get resumed, they would access the old memory locations and corrupt memory. Clearly, this can’t be allowed and so, as part of the compacting phase, the CLR subtracts from each root the number of bytes that the object it referred to was shifted down in memory. This ensures that every root refers to the same object it did before; it’s just that the object is at a different location in memory.

After the heap memory is compacted, the managed heap’s NextObjPtr pointer is set to point to a location just after the last surviving object. This is where the next allocated object will be placed in memory.

Generations: Improving Performance

A generational GC makes the following assumptions about your code:

  • The newer an object is, the shorter its lifetime will be.
  • The older an object is, the longer its lifetime will be.
  • Collecting a portion of the heap is faster than collecting the whole heap.

When initialized, the managed heap contains no objects. Objects added to the heap are said to be in generation 0. Stated simply, objects in generation 0 are newly constructed objects that the garbage collector has never examined.

When the CLR initializes, it selects a budget size (in kilobytes) for generation 0. So if allocating a new object causes generation 0 to surpass its budget, a garbage collection must start.

The objects that survive the garbage collection are said to be in generation 1.

After a garbage collection, generation 0 contains no objects. As always, new objects will be allocated in generation 0.

when the CLR initializes, it selects a budget for generation 0. Well, it also selects a budget for generation 1.

When starting a garbage collection, the garbage collector also sees how much memory is occupied by generation 1. In this case, generation 1 occupies much less than its budget, so the garbage collector examines only the objects in generation 0. Look again at the assumptions that the generational garbage collector makes. The first assumption is that newly created objects have a short lifetime. So generation 0 is likely to have a lot of garbage in it, and collecting generation 0 will therefore reclaim a lot of memory. The garbage collector will just ignore the objects in generation 1, which will speed up the garbage collection process.

the garbage collector sees that the objects in generation 1 are occupying so much memory that generation 1’s budget has been reached. Over the several generation 0 collections, it’s likely that a number of objects in generation 1 have become unreachable (as in our example). So this time, the garbage collector decides to examine all of the objects in generation 1 and generation 0.

After four collections: generation 1 survivors are promoted to generation 2, generation 0 survivors are promoted to generation 1, and generation 0 is empty.

As before, any objects that were in generation 0 that survived the garbage collection are now in generation 1; any objects that were in generation 1 that survived the collection are now in generation 2. As always, generation 0 is empty immediately after a garbage collection and is where new objects will be allocated. Objects in generation 2 are objects that the garbage collector has examined two or more times. There might have been several collections, but the objects in generation 1 are examined only when generation 1 reaches its budget, which usually requires several garbage collections of generation 0.

The managed heap supports only three generations: generation 0, generation 1, and generation 2; there is no generation 3.3

Garbage Collection Triggers

  • Code explicitly calls System.GC’s static Collect method
  • Windows is reporting low memory conditions
  • The CLR is unloading an AppDomain
  • The CLR is shutting down

Large Objects

There is one more performance improvement you might want to be aware of. The CLR considers each single object to be either a small object or a large object. Today, a large object is 85,000 bytes or more in size.4 The CLR treats large objects slightly differently than how it treats small objects:

  • Large objects are not allocated within the same address space as small objects; they are allocated elsewhere within the process’ address space.
  • Today, the GC doesn’t compact large objects because of the time it would require to move them in memory. For this reason, address space fragmentation can occur between large objects within the process leading to an OutOfMemoryException being thrown. In a future version of the CLR, large objects may participate in compaction.
  • Large objects are immediately considered to be part of generation 2; they are never in generation 0 or 1. So, you should create large objects only for resources that you need to keep alive for a long time. Allocating short-lived large objects will cause generation 2 to be collected more frequently, hurting performance. Usually large objects are large strings (like XML or JSON) or byte arrays that you use for I/O operations, such as reading bytes from a file or network into a buffer so you can process it.

.NET:CLR via C#The Managed Heap and Garbage Collection的更多相关文章

  1. Advanced .NET Debugging: Managed Heap and Garbage Collection(转载,托管堆查内存碎片问题解决思路)

    原文地址:http://www.informit.com/articles/article.aspx?p=1409801&seqNum=4 Debugging Managed Heap Fra ...

  2. Understanding the managed heap

    Understanding the managed heap   Another common problem faced by many Unity developers is the unexpe ...

  3. Windbg Extension NetExt 使用指南 【3】 ---- 挖掘你想要的数据 Managed Heap

    摘要 : NetExt中有两个比较常用的命令可以用来分析heap上面的对象. 一个是!wheap, 另外一个是!windex. !wheap 这个命令可以用于打印出heap structure信息. ...

  4. .NET:CLR via C#:CLR Hosting And AppDomains

    AppDomain Unloading To unload an AppDomain, you call AppDomain’s Unload static method.This call caus ...

  5. 从ASP.NET Core 3.0 preview 特性,了解CLR的Garbage Collection

    前言 在阅读这篇文章:Announcing Net Core 3 Preview3的时候,我看到了这样一个特性: Docker and cgroup memory Limits We conclude ...

  6. JVM学习二:垃圾收集(Garbage Collection,GC)机制

    JVM的GC分为两个主要部分,第一部分是判断对象是否已死(堆内存的垃圾回收占主要部分,方法区(metaspace)的内存回收在最新的官方文档中未给出详细解释,暂时不做讨论范围),第二部分是对内存区进行 ...

  7. [转] 分代垃圾回收的 新旧代引用问题(原标题:Back To Basics: Generational Garbage Collection)

    原文链接: https://blogs.msdn.microsoft.com/abhinaba/2009/03/02/back-to-basics-generational-garbage-colle ...

  8. ssh框架错误:org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role。

    在做ssh项目练习的时候出现问题: org.hibernate.LazyInitializationException: failed to lazily initialize a collectio ...

  9. CLR via C#读书笔记一:CLR的执行模型

    CLR(Common Language Runtime)公共语言进行时是一个可由多种编程语言使用的“进行时”. 将源代码编译成托管模块 可用支持CLR的任何语言创建源代码文件,然后用对应的编译器检查语 ...

随机推荐

  1. 红黑树:个人理解与Python实现

    红黑树:个人理解与Python实现 [基本事实1] 红黑树是一种平衡的二叉查找树,无论插入还是删除操作都可以在O(lg n)内实现,而一般的二叉查找树则在极端情况下会退化为线性结构.红黑树之所以是平衡 ...

  2. day6 xml文件格式的处理

        XML处理模块 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公 ...

  3. 浅谈jvm

    1 .说起jvm,很多人感觉jvm离我们开发实际很远.但是,我们开发缺每时每刻都离不开jvm. a: java源码 编译后成.class字节码文件, b:根据classpath找到这个字节码文件, c ...

  4. 7-13 Power Calculus 快速幂计算 uva1374

    想到快速幂  但是这题用不上 用迭代加深搜索 注意启发函数为  当前最大数<<(maxx-d)  如果大于n则剪枝 注意跳出语句的两种写法   一种170ms  一种390ms !!! d ...

  5. hdoj1102 Constructing Roads(Prime || Kruskal)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1102 题意 有n个村庄(编号1~n),给出n个村庄之间的距离,开始时n个村庄之间已经有了q条路,现在需 ...

  6. Scala入门4(_的用法)

    从网上找了一篇博客,详细讲解了Scala下划线的用法,这里做保留 博客链接

  7. Could not open JDBC Connection for transaction; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Could not create connection to database server.

    报错信息:Could not open JDBC Connection for transaction; nested exception is org.apache.commons.dbcp.SQL ...

  8. 在qemu环境中用gdb调试Linux内核

    简介 对用户态进程,利用gdb调试代码是很方便的手段.而对于内核态的问题,可以利用crash等工具基于coredump文件进行调试.其实我们也可以利用一些手段对Linux内核代码进行gdb调试,qem ...

  9. 【基础知识】ASP.NET[基础一(ashx)]

    一.ASP.NET介绍 1.ASP.NET包括: 一般处理程序(ashx):WebForm ( aspx ):MVC(Model view con~~): 2.ASP.NET的常用文件(重点): 1& ...

  10. “通用类型系统”(CTS)

    一.什么是“通用类型系统”(CTS) 描述类型的定义和行为 二.CTS规范 一个类型可以包含零个或者多个成员1,成员①字段(Field)作为对象状态一部分的数据变量.字段根据名称和类型来区分②方法(M ...