废话不多说了,本人是搞Web方向的,C/S不太熟悉,先看界面图(比较粗糙),这里仅仅是从一个视觉的效果来初步显示GC相对应的操作(简单的效果显示,并不是真正的GC内幕,那个我也不懂)

基本概念
对象的生成过程(newobj指令)
1:计算类型(包括基类)所有字段的字节总数
2: 字节总数再加上对象开销字段字节数(相加为:对象所需的字节数)。每个对象包含2个开销字段:类型对象指针以及同步块索引。WIN32中,各占32位,WIN64中,各占64位。
3:CLR检测托管堆中是否有足够的空间满足对象所需的字节数。如果满足,对象将被分配在NextObjPtr指针指示的地方,实例构造器被调用,(new操作)返回对象的内存地址。指针NextObjPtr越过对象所在的区域,指示下一个新建对象在托管堆中的地址。如果不满足,进行垃圾收集。
根
每一个应用程序都有一组根Root。一个根是一个存储地址,包含一个指向类型对象的指针。
该指针有2种形式:(1)指向托管堆中的一个对象。(2)设为null。
根包括静态字段,方法参数,局部变量,CPU寄存器。
对象的代
托管堆中,对象的代大概为0代,1代,2代,相应的内存容量为256K,2M,10M。当然,垃圾收集器也会自动调整预算容量。
终结操作和释放模式
终结操作(Finalize()方法)可以确保托管对象在释放内存的同时不会泄露本地资源,但是不能确定它在何时被调用。
释放模式(Dispose()方法):当对象不再被使用的时候显示的释放掉它所占有的资源。 (更多控制)注:可以用来控制在对象生命周期内资源的重复利用,例如connection资源不一定每次操作都要关闭。
下序的代码显示了GC.Collect()方法将使Finalize()方法被调用:
public
static
class
Program
{
static
void
Main(
string
[] args)
{
new
GCNotice();
Timer timer
=
new
Timer(TimerCallBack,
null
,
0
,
2000
);
Console.ReadLine();
timer.Dispose();
}
private
static
void
TimerCallBack(
object
obj)
{
Console.WriteLine(
"
GC START Time:
"
+
DateTime.Now.ToString());
GC.Collect();
Console.WriteLine(
"
GC END Time:
"
+
DateTime.Now.ToString());
}
}
sealed
class
GCNotice
{
~
GCNotice(){
Console.Beep();
Console.WriteLine(
"
*********GCNotice FINALIZE():
"
+
DateTime.Now.ToString());
if
(
!
AppDomain.CurrentDomain.IsFinalizingForUnload())
{
new
GCNotice();
}
}
}
~GCNotice(){
} 析构函数(C++)就是我们所说的终结操作(与C++不同),也就是Finalize()方法。在下列事件中将触发:
(1):第0代对象充满时(垃圾收集)。
(2):代码显示调用System.GC.Collect()。
(3):Windoms报告内存不足。
(4):CLR卸载应用程序域。
(5):CLR关闭。
一般情况下,如果一个类型中本地资源需求比较大,建议使用HandleCollector来促进GC.Collect()执行(释放资源)。
代码
public HandleCollector(string name, int initialThreshold); //组合到类型中,实例化
public void Add();//构造函数中运用
public void Remove();//析构函数中运用
终结链表与终结可达队列
创建一个新对象时,如果对象的类型定义了Finalize()方法,那么指向该对象的指针将被放到终结链表中。终结链表上的每一个条目都引用着一个对象,指示GC在回收这些对象之前调用它们的Finalize()方法。
主要过程如下
好了,概念的东西不再介绍了,本人思路如下:
(一)准备工作:创建一个DataObject类型(模拟一个对象实体),DataObjects(对象集合),DataObjectManager(对象集合管理)。
(二)初始化一个属性值为随机值的 DataObject对象
(三)判断托管堆0代内存是否充足,如果满足则分配对象内存(模拟)(如果有终结方法,则添加引用到终结链表中)。如果不满足,进行垃圾收集。
(四)垃圾收集操作:细分为0,1,2代的比较判断与操作
(五)收集后内容的显示,调用面板panel的refresh()方法。
(六)随机修改原对象集合中的对象的值 HasRoot为false(后来添加的),标识无根。
(一) 准备工作
先自创建一个类,主要是以该对象来作为操作的。
public
class
DataObject : Jasen.GCShow.IDataObject
{
public
Boolean HasFinalizeMethod {
get
;
set
; }
public
Boolean HasRoot {
get
;
set
; }
public
Int32 Number {
get
;
set
; }
public
System.Drawing.Color Color {
get
;
set
; }
public
String OXString{
get
{
//
return (HasRoot ? "R" : "0") + (HasFinalizeMethod ? "F" : "");
return
(HasFinalizeMethod
?
"
[F]
"
:
"
[!F]
"
);
}
}
public
String Name {
get
;
set
; }
public
String NiceName {
get
;
set
; }
public
Int32 Generation {
get
;
set
; }
}
然后就是该
类对象集合,实现遍历以及索引:
其次就是该对象集合的管理类,负责所有对象的ItemCollection,以及0,1,2代对象集合,以及终结链表,终结可达队列
1
public
class
DataObjectManager : Jasen.GCShow.IDataObjectManager
2
{
3
DataObjects items
=
new
DataObjects();
4
Queue
<
DataObject
>
freachableQueue
=
new
Queue
<
DataObject
>
();
5
DataObjects finalizeTable
=
new
DataObjects();
6
7
public
DataObjects ItemsCollection
8
{
9
get
{
return
items; }
10
set
{ items
=
value; }
11
}
12
public
DataObjects ZeroGenerationCollection
13
{
14
get
{
return
GetCollection(
0
); }
15
}
16
17
public
DataObjects GetCollection(
int
generation)
18
{
19
if
(ItemsCollection.Count()
==
0
)
return
null
;
20
DataObjects generationObjects
=
new
DataObjects();
21
foreach
(DataObject obj
in
ItemsCollection){
22
if
(obj.Generation
==
generation){
23
generationObjects.Add(obj);
24
}
25
}
26
return
generationObjects;
27
}
28
public
DataObjects OneGenerationCollection
29
{
30
get
{
return
GetCollection(
1
); }
31
}
32
public
DataObjects TwoGenerationCollection
33
{
34
get
{
return
GetCollection(
2
); }
35
}
36
public
DataObjects FinalizeTable
37
{
38
get
{
return
finalizeTable; }
39
set
{ finalizeTable
=
value; }
40
}
41
public
Queue
<
DataObject
>
FreachableQueue
42
{
43
get
{
return
freachableQueue; }
44
set
{ freachableQueue
=
value; }
45
}
46
}
(二)初始化一个属性值为随机值的 DataObject对象
通过随机设置类的值来实例化一个对象
DataObject item
=
new DataObject()
{
HasFinalizeMethod
=
Randoms.GetRandomBoolen(
0.3),
HasRoot
=
Randoms.GetRandomBoolen(
0.6),
Color
= Randoms.GetRandomColor(),
Number
=
Randoms.RandomNum(
1
,
3),
Name
= Guid.NewGuid().ToString(),
NiceName
= Randoms.AddNum().ToString(),
Generation
=
0
//
默认为0代
};
以上的值大部分是随机的,不确定的,比如下面的方法----->返回随机比例为rate(比如0.3)的true值,它等价于有30%的概率返回true,70%概率返回false,
///
<summary>
///
返回随机比例为rate的 true值
///
</summary>
///
<param name="rate"></param>
///
<returns></returns>
public
static
Boolean GetRandomBoolen(
double rate) {
if
(rate
<
0
||
rate
>
1
)
throw
new
ArgumentOutOfRangeException(
"
rate must be between 0 to 1
");
Random random
=
new
Random((
int)DateTime.Now.Ticks);
System.Threading.Thread.Sleep(
100);
if
(random.Next(
0
,
10000
)
>=
10000
*
(
1
-rate)){
return
true;
}
return
false;
}
随机颜色如下
public
static
Color GetRandomColor()
{
Random randomFirst
=
new
Random((
int
)DateTime.Now.Ticks);
System.Threading.Thread.Sleep(
300
);
Random randomSencond
=
new
Random((
int
)DateTime.Now.Ticks);
System.Threading.Thread.Sleep(
300
);
Random randomThird
=
new
Random((
int
)DateTime.Now.Ticks);
int
intRed
=
randomFirst.Next(
256
);
int
intGreen
=
randomSencond.Next(
256
);
int
intBlue
=
randomThird.Next(
256
);
return
Color.FromArgb(intRed, intGreen, intBlue);
}
(三)判断托管堆0代内存是否充足
判断的大概过程如下:
#region
newobject指令过程
private
Int32 CHARTOTALNUMBER
=
0
;
private
void
NewObjectOperationProcess(DataObject item){
//
计算类型所有字段的字节总数
CHARTOTALNUMBER
=
CountTypeCharTotalNumber(item);
//
计算2个开销字段:类型对象指针,同步块索引 WIN32--32位×2=64位=8字节
CountObjectExtraCharNumber();
//
判断0代对象内存(256K)是否含有所需的字节数 (长度)
Boolean isEnough
=
CheckZeroGenerationHasEnoughChars();
//
计算新建对象在托管堆中的地址 (长度)
if
(isEnough)
{
RefreshZeroGenenrationAndFinalizeTable(item);
}
else
{
//
回收垃圾
GCCollect(item);
}
}
如果托管堆0代内存充足,那么显示如下:

上面显示的是对象含有根,没有终结方法。我们来看一张含有终结方法的图,含有终结方法的对象会被添加引用到终结链表中,如下:

(四)垃圾收集操作:细分为0,1,2代的比较判断与操作
(1)处理托管堆0代对象的主要操作如下:
private
void
GCSystemOperation()
{
ClearFreachableQueue();
DataObjects temps
=
new
DataObjects();
//
清理没根的没终结方法的0代对象 0代对象 +1 (清除)
DataObjects list
=
manager.ZeroGenerationCollection;
if
(list
==
null
)
return
;
foreach
(DataObject obj
in
list)
{
//
如果对象没有根 并且没有终结方法
if
(obj.HasRoot
==
false
&&
obj.HasFinalizeMethod
==
false
){
manager.ItemsCollection.Remove(obj);
}
else
{
temps.Add(obj);
//
obj.Generation++;
}
}
if
(temps.Count()
>
0
){
int
tempsLength
=
CountSize(temps);
int
oneGenerationCurrentLength
=
CountSize(manager.OneGenerationCollection);
Boolean isOneGenerationEnough
=
(SystemConst.OneGenerationLength
-
oneGenerationCurrentLength
>
tempsLength)
?
true
:
false
;
if
(isOneGenerationEnough)
{
GenerationAddOne(temps);
}
else
{
//
处理托管堆1代对象
MessageBox.Show(
"
处理托管堆1代对象!
"
);
HandleOneGeneration(temps);
}
}
}
当一直添加对象时,达到如下情况:

我们不知道下一个对象的内存大小,很有下一次就会可能发生垃圾收集。如下图所示,当托管堆0代对象内存容量不足时,会触发垃圾收集:

其中先清理可达队列中的数据对象,(含有Finalize()终结方法并且无根,一般情况为在第1次收集时将终结链表中的指针移动至终结可达队列中,这样可达队列中才有指针。第2次收集就会将可达队列中的指针清理)

执行下列代码:
1
private
void
ClearFreachableQueue()
2
{
3
//
清理终结可达队列中的对象 没根 有终结方法 (清除) 一般为清理上次收集数据
4
while
(manager.FreachableQueue.Count
>
0
){
5
DataObject obj
=
manager.FreachableQueue.Dequeue();
6
manager.ItemsCollection.Remove(obj);
7
}
8
MessageBox.Show(
"
清理可达队列对象
"
);
9
//
终结链表中的数据 --》可达队列
10
foreach
(DataObject item
in
manager.FinalizeTable){
11
if
(item.HasRoot
==
false
){
12
manager.FreachableQueue.Enqueue(item);
13
}
14
}
15
MessageBox.Show(
"
将终结链表中的可达对象移动至可达队列
"
);
16
foreach
(DataObject obj
in
manager.FreachableQueue){
17
manager.FinalizeTable.Remove(obj);
18
}
19
MessageBox.Show(
"
移除终结链表中包含的可达队列对象
"
);
20
}
显然,将终结链表的数据移动到可达队列后,然后再移除终结链表包含的可达队列的指针,操作后如下:

(2)处理托管堆1代对象的主要操作如下:
private
void
HandleOneGeneration(DataObjects temps)
{
DataObjects currentTempObjects
=
new
DataObjects();
foreach
(DataObject obj
in
manager.OneGenerationCollection){
if
(obj.HasRoot
==
false
&&
obj.HasFinalizeMethod
==
false
){
manager.ItemsCollection.Remove(obj);
}
else
{
currentTempObjects.Add(obj);
}
}
if
(currentTempObjects.Count()
>
0
)
{
Boolean enough
=
CheckTwoGenerationEnough(currentTempObjects);
if
(enough)
{
MessageBox.Show(
"
托管堆2代内存充足----》托管堆1代对象 对象代+1
"
);
GenerationAddOne(currentTempObjects);
}
else
{
MessageBox.Show(
"
托管堆2代内存不足----》处理
"
);
HandleTwoGeneration(currentTempObjects);
}
}
MessageBox.Show(
"
托管堆0代对象 对象代+1
"
);
GenerationAddOne(temps);
}
继续创建新的对象:
发现越来越多的对象在托管堆1代中存在。

一直增加,当托管堆0代对象内存不足,并且托管堆1代对象内存也不足时候,将导致1代对象的代+1;其中也包括1代对象的清理工作,移除无根的对象。

(3)处理托管堆2代对象的主要操作如下:
1
private
void
HandleTwoGeneration(DataObjects currentTempObjects)
2
{
3
Boolean enough
=
CheckTwoGenerationEnough(currentTempObjects);
4
if
(enough){
5
GenerationAddOne(currentTempObjects);
6
}
7
else
{
8
MessageBox.Show(
"
托管堆2代对象内存满了,清理托管堆2代无根对象
"
);
9
ClearGenerationUnusefulObject(manager.TwoGenerationCollection);
10
if
(CheckGenerationEnough(currentTempObjects, manager.TwoGenerationCollection, SystemConst.TwoGenerationLength)){
11
MessageBox.Show(
"
托管堆1代对象 对象代+1
"
);
12
GenerationAddOne(currentTempObjects);
13
}
14
else
{
15
ClearToEmpty();
//
托管堆对象全部清理
16
}
17
}
18
}
(五)垃圾收集后内容的显示,调用面板panel的refresh()方法。
例如托管堆0代的面板刷新
private
void
panelZeroGenenration_Paint(
object
sender, PaintEventArgs e)
{
if
(manager.ItemsCollection.Count()
==
0
) {
return
; }
DataObjects list
=
manager.ZeroGenerationCollection;
if
(list
==
null
)
return
;
Graphics graphics
=
e.Graphics;
FillRectangle(graphics, list,
true
,
true
);
}
相应的面板绘制如下,采用累加来计算绘制的坐标(left)
1
private
void
FillRectangle(Graphics graphic, DataObjects list,Boolean markArrows,Boolean markOX)
2
{
3
float
left
=
0
,width
=
0
,top
=
0
,height
=
20
,rams
=-
15
;
4
for
(
int
i
=
0
; i
<
list.Count(); i
++
){
5
int
sum
=
0
;
6
if
(i
!=
0
) {
7
for
(
int
z
=
0
; z
<
i; z
++
){
8
sum
+=
list[z].Number;
//
i-1次
9
}
10
}
11
left
=
sum
*
SystemConst.GraphicLength;
12
width
=
SystemConst.GraphicLength
*
list[i].Number;
13
graphic.FillRectangle(
new
SolidBrush(list[i].Color), left, top, width, height);
14
graphic.FillRectangle(
new
SolidBrush(Color.Red), left, top,
2
, height);
15
graphic.DrawString((
"
[
"
+
list[i].NiceName
+
"
]
"
+
(list[i].HasRoot
?
"
R
"
:
""
)), smallFont,
new
SolidBrush(Color.Red), left, top);
16
if
(markOX){
17
graphic.DrawString(list[i].OXString, smallFont,
new
SolidBrush(Color.Red), left, top
+
40
);
18
}
19
}
20
if
(markArrows){
21
graphic.DrawString(
"
↑
"
, bigFont,
new
SolidBrush(Color.Red), left
+
width
+
rams, top
+
20
);
22
}
23
}
(六)随机修改原对象集合中的对象的值 HasRoot为false(后来添加的),标识无根。
1
///
<summary>
2
///
随机修改对象的根为 false
3
///
</summary>
4
private
void
RandomChangeItemsRootValue()
5
{
6
DataObjects list
=
manager.ItemsCollection;
7
if
(list
==
null
)
return
;
8
foreach
(DataObject item
in
list){
9
if
(item.HasRoot
==
true
){
10
item.HasRoot
=
Randoms.GetRandomBoolen(
0.9
);
11
}
12
}
13
}
最后显示如下(托管堆0代对象+1,1代对象+1):

同时我们应该注意到:在第6步中的方法随机的修改了集合中对象的HasRoot属性,再看下下一张图:

将上面图对照,发现用紫色框标识的 [36]R [39]R转变成了[36] [39],从这里发现从 有根 ---->无根 转变了。这和GC中无根对象才会被回收是一个道理。
当托管堆对象2代满了时会自动清理0,1,2代的垃圾。有一个特殊情况,当1代中对象代+1后,转变为2代,与原来2代的对象总共的内存超过了容量,就有可能使应用程序中断。(不过本人这里也不太清楚,本人将所有的对象之空,设置为null)
最后一点:本来想用Timer来定时触发对象的生成操作,代码如下:
1
private
void
btnAutoAdd_Click(
object
sender, EventArgs e)
2
{
3
timer
=
new
System.Timers.Timer(
3000
);
4
timer.Elapsed
+=
new
System.Timers.ElapsedEventHandler(timer_Tick);
5
timer.AutoReset
=
true
;
6
timer.Enabled
=
true
;
7
btnAutoAdd.Enabled
=
false
;
8
btnAutoStop.Enabled
=
true
;
9
}
10
public
void
timer_Tick(
object
sender, ElapsedEventArgs e) {
11
NewOneObjectOperation();
12
}
13
private
void
btnAutoStop_Click(
object
sender, EventArgs e)
14
{
15
timer.Stop();
16
btnAutoAdd.Enabled
=
true
;
17
btnAutoStop.Enabled
=
false
;
18
}
但是对于Panel的refresh()操作也是线程的,这样的话将触发异常:

本示例的目的是用一种视觉的效果来看我们.NET平台下的垃圾收集过程,本人水平有限,难免有N多BUG以及不太清楚的地方,还请各位多多指教。
本GC初步模拟程序代码下载地址:Jasen.GCShow.rar [GC初步模拟效果程序 ]
http://www.cnblogs.com/jasenkin/archive/2010/10/03/clr_gc_show.html