• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

[原创]CLR GC垃圾收集过程模拟(用C#来显示垃圾收集过程的视觉效果) ...

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

......

废话不多说了,本人是搞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()执行(释放资源)。

代码
namespace System.Runtime.InteropServices
{
    
//
 摘要:
    
//     跟踪未处理的句柄,并在达到指定阈值时强制执行垃圾回收。

    public sealed class HandleCollector
    {
        
//
 摘要:
        
//
     使用一个名称以及一个阈值(在达到该值时开始执行句柄回收)初始化 System.Runtime.InteropServices.HandleCollector
        
//
     类的新实例。
        
//

        
// 参数:
        
//
   name:
        
//
     回收器的名称。此参数允许您为跟踪句柄类型的回收器分别命名。
        
//

        
//   initialThreshold:
        
//
     指定何时开始执行回收的值。
        
//

        [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
        
public HandleCollector(string name, int
 initialThreshold);
        
//
 摘要:  
        
//     增加当前句柄计数。

      
        public void Add();
        
//     减少当前句柄计数。
        
//

      
        public void Remove();
  
    }
}

 

     

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 { getset; }
        
public Boolean HasRoot { getset; }
        
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 { getset; }
        
public String NiceName { getset; }
        
public Int32 Generation { getset; }
    }

 

然后就是该类对象集合,实现遍历以及索引:
  public class DataObjects : IEnumerable, Jasen.GCShow.IDataObjects
    {
        
private List<DataObject> objectList=null;
        
public DataObjects(List<DataObject> objects) {
            
this.objectList = objects;
        }
        
public DataObjects(){
        
this.objectList=new  List<DataObject>();
        }
        
public void Add(DataObject item)
        {
            
if(!objectList.Contains(item)){
            objectList.Add(item);
            }
        }
        
public void Remove(DataObject item)
        {
            
if (objectList.Contains(item)){
                objectList.Remove(item);
            }
        }
        
public int Count()
        {
            
if(objectList==null){
                
return 0;
            }
            
return objectList.Count;
        }
        
public DataObject this[int i]{
            
get{
                
if (objectList != null && objectList.Contains(objectList[i])){
                    
return objectList[i];
                }
                
else{
                    
return default(DataObject);
                }
            }
            
set{
                objectList[i] 
= value;
            }
        }
        
#region IEnumerable 成员

        IEnumerator IEnumerable.GetEnumerator()
        {
             
for (int i = 0; i < objectList.Count(); i++)
             {
                
yield return this[i];
             }
        }
        
#endregion
    }

 

其次就是该对象集合的管理类,负责所有对象的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() == 0return 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(13),
                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 > 1throw 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 == nullreturn;
            
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代对象的主要操


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
用于验证码图片识别的类(C#源码)发布时间:2022-07-10
下一篇:
C#输出毫秒发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap