在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
...... 废话不多说了,本人是搞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(){ (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();//构造函数中运用
终结链表与终结可达队列 创建一个新对象时,如果对象的类型定义了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; } } 然后就是该类对象集合,实现遍历以及索引:
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() == 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代对象的主要操 |
请发表评论