C#垃圾回收之二次回收机制
来源:互联网 发布:c语言入门自学 编辑:程序博客网 时间:2024/04/30 03:29
MSDN2原文:
实现 Finalize 方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。用 Finalize方法回收对象使用的内存需要至少两次垃圾回收。当垃圾回收器执行回收时,它只回收没有终结器的不可访问对象的内存。这时,它不能回收具有终结器的不可访问对象。它改为将这些对象的项从终止队列中移除并将它们放置在标为准备终止的对象列表中。该列表中的项指向托管堆中准备被调用其终止代码的对象。垃圾回收器为此列表中的对象调用Finalize方法,然后,将这些项从列表中移除。后来的垃圾回收将确定终止的对象确实是垃圾,因为标为准备终止对象的列表中的项不再指向它们。在后来的垃圾回收中,实际上回收了对象的内存。
如果对象所在的类没有实现Finalize方法,或者对象所在的类没有定义析构函数(实际上析构函数隐式的转化为Finalize方法了,见之前的文章),则调用GC.Collect()时,如果该对象是不可访问的,则此对象一定就会被回收了。
如果对象是不可访问的,而且该对象所在的类定义了Finalize方法,则调用GC.Collect()时,会将该对象从待终结队列中移动到另外一个队列(指针指向),该队列中的对象都是等待调用Finalize方法的。
从上面的图片和论述中,我们可以很清楚没有实现Finalize方法的类的对象如何收回它的内存,以及什么时候可以手动收回,我们都清清楚楚。关键是实现了Finalize方法的类的对象呢?GC.Collect()把它放到待调用Finalize()方法队列后,又是何时去调用Finalize()方法的呢?
以下讨论的都是实现了Finalize()方法的情况,即二次回收的情况,不讨论没有实现Finalize()方法的情况。
一、如果对象是可访问的,就算显示调用GC.Collect(),并且紧接着就调用GC.WaitForPendingFinalizers(),该对象的Finalize方法也不会被调到。
上代码:
运行结果:
实际上,上面的代码只运行到了Main()里的Console.ReadLine();这一行还没有跑,如果我们在控制台再随便录入什么东西,然后回车,就会发现Beijing的析构函数被调用了,紧接着China的析构函数也被调用了,再接着World的Finalize()也被调用了,而不是在GC.WaitForPendingFinalizers()方法中就调用这些Finalize()方法,为什么会这样呢?
MSDN2上有说明:
在应用程序域(注意:不是作用域哦)的关闭过程中,对没有免除终结的对象将自动调用Finalize,即使那些对象仍是可访问的。
在Main方法关闭时,即应用程序域退出的时候,按照上面的说法bj这个对象的Finalize()方法被调用,它的finalize()的调用触发了它爸爸,以及它爷爷的finalize()方法的调用。
从结果中也可以看出来,在一个对象还可以访问时,调用GC.Collect()以及GC.WaitForPendingFinalizers()都不会收回该对象的内存(不会触发该对象的finalize()方法的执行)。
二、如果对象已不可访问,那此时调用GC.Collect()以及GC.WaitForPendingFinalizers(),该对象的内存会被回收(前提是它的finalize方法中不要再让它复活了)。
实现 Finalize 方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。用 Finalize方法回收对象使用的内存需要至少两次垃圾回收。当垃圾回收器执行回收时,它只回收没有终结器的不可访问对象的内存。这时,它不能回收具有终结器的不可访问对象。它改为将这些对象的项从终止队列中移除并将它们放置在标为准备终止的对象列表中。该列表中的项指向托管堆中准备被调用其终止代码的对象。垃圾回收器为此列表中的对象调用Finalize方法,然后,将这些项从列表中移除。后来的垃圾回收将确定终止的对象确实是垃圾,因为标为准备终止对象的列表中的项不再指向它们。在后来的垃圾回收中,实际上回收了对象的内存。
如果对象所在的类没有实现Finalize方法,或者对象所在的类没有定义析构函数(实际上析构函数隐式的转化为Finalize方法了,见之前的文章),则调用GC.Collect()时,如果该对象是不可访问的,则此对象一定就会被回收了。
如果对象是不可访问的,而且该对象所在的类定义了Finalize方法,则调用GC.Collect()时,会将该对象从待终结队列中移动到另外一个队列(指针指向),该队列中的对象都是等待调用Finalize方法的。
从上面的图片和论述中,我们可以很清楚没有实现Finalize方法的类的对象如何收回它的内存,以及什么时候可以手动收回,我们都清清楚楚。关键是实现了Finalize方法的类的对象呢?GC.Collect()把它放到待调用Finalize()方法队列后,又是何时去调用Finalize()方法的呢?
以下讨论的都是实现了Finalize()方法的情况,即二次回收的情况,不讨论没有实现Finalize()方法的情况。
一、如果对象是可访问的,就算显示调用GC.Collect(),并且紧接着就调用GC.WaitForPendingFinalizers(),该对象的Finalize方法也不会被调到。
上代码:
- using System;
- using System.Threading;
- using System.IO;
- using System.Data.SqlClient;
- using System.Net;
- namespace Lab
- {
- class Log
- {
- public static void Write(string s)
- {
- Console.WriteLine("{0}/tTotalMilliseconds:{1}/tTotalMemory:{2}",s,DateTime.Now.TimeOfDay.TotalMilliseconds,GC.GetTotalMemory(false));
- }
- }
- class World
- {
- protected SqlConnection conn = null;
- public World()
- {
- conn = new SqlConnection();
- }
- protected void Finalize()
- {
- conn.Dispose();
- Log.Write("World's destructor is called");
- }
- }
- class China : World
- {
- public China()
- : base()
- {
- }
- ~China()
- {
- Log.Write("China's destructor is called");
- }
- }
- class Beijing : China
- {
- public Beijing()
- : base()
- {
- }
- ~Beijing()
- {
- Log.Write("Beijing's destructor is called");
- }
- }
- }
- namespace Lab
- {
- class Program
- {
- static void Main(string[] args)
- {
- TestOne();
- Log.Write("进入Main/t/t");
- Console.ReadLine();
- }
- static void TestOne()
- {
- Log.Write("对象创建之前/t/t");
- Beijing bj = new Beijing();
- Log.Write("对象创建之后,垃圾回收之前/t/t");
- GC.Collect();
- GC.WaitForPendingFinalizers();//此方法相当于join终结器线程,等待执行完毕。
- Log.Write("垃圾回收之后/t/t");
- }
- }
- }
实际上,上面的代码只运行到了Main()里的Console.ReadLine();这一行还没有跑,如果我们在控制台再随便录入什么东西,然后回车,就会发现Beijing的析构函数被调用了,紧接着China的析构函数也被调用了,再接着World的Finalize()也被调用了,而不是在GC.WaitForPendingFinalizers()方法中就调用这些Finalize()方法,为什么会这样呢?
MSDN2上有说明:
在应用程序域(注意:不是作用域哦)的关闭过程中,对没有免除终结的对象将自动调用Finalize,即使那些对象仍是可访问的。
在Main方法关闭时,即应用程序域退出的时候,按照上面的说法bj这个对象的Finalize()方法被调用,它的finalize()的调用触发了它爸爸,以及它爷爷的finalize()方法的调用。
从结果中也可以看出来,在一个对象还可以访问时,调用GC.Collect()以及GC.WaitForPendingFinalizers()都不会收回该对象的内存(不会触发该对象的finalize()方法的执行)。
二、如果对象已不可访问,那此时调用GC.Collect()以及GC.WaitForPendingFinalizers(),该对象的内存会被回收(前提是它的finalize方法中不要再让它复活了)。
- using System;
- using System.Threading;
- using System.IO;
- using System.Data.SqlClient;
- using System.Net;
- namespace Lab
- {
- class Log
- {
- public static void Write(string s)
- {
- Console.WriteLine("{0}/tTotalMilliseconds:{1}/tTotalMemory:{2}",s,DateTime.Now.TimeOfDay.TotalMilliseconds,GC.GetTotalMemory(false));
- }
- }
- class World
- {
- protected SqlConnection conn = null;
- public World()
- {
- conn = new SqlConnection();
- }
- protected void Finalize()
- {
- conn.Dispose();
- Log.Write("World's destructor is called");
- }
- }
- class China : World
- {
- public China()
- : base()
- {
- }
- ~China()
- {
- Log.Write("China's destructor is called");
- }
- }
- class Beijing : China
- {
- public Beijing()
- : base()
- {
- }
- ~Beijing()
- {
- Log.Write("Beijing's destructor is called");
- }
- }
- }
- namespace Lab
- {
- class Program
- {
- static void Main(string[] args)
- {
- TestOne();
- Log.Write("进入Main/t/t");
- Console.ReadLine();
- }
- static void TestOne()
- {
- Log.Write("对象创建之前/t/t");
- Beijing bj = new Beijing();
- bj = null; //此时new Beijing()所创建的内存已不可访问
- Log.Write("对象创建之后,垃圾回收之前/t/t");
- GC.Collect();
- GC.WaitForPendingFinalizers();//此方法相当于join终结器线程,等待执行完毕。
- Log.Write("垃圾回收之后/t/t");
- }
- }
- }
结果如下图所示:
当bj已不可访问时,此时调用GC.Collection(),再紧接着调用GC.WaitForPendingFinalizers(),则bj的finalize()方法,以及它爸爸的finalize方法、它爷爷的finalize方法都被调用了。
三、有时候大家会发现对象的Finalize()方法被执行后,本来收集了垃圾,内存占用应该会减少,但紧接着调用
- C#垃圾回收之二次回收机制
- C# 垃圾回收机制
- c#垃圾回收机制
- C# 垃圾回收机制
- c#垃圾回收机制
- C# 垃圾回收机制
- c#垃圾回收机制
- C#学习笔记之垃圾回收机制
- C#基础知识梳理之垃圾回收机制
- 理解C#垃圾回收机制
- 理解C#垃圾回收机制
- 理解C#垃圾回收机制
- C#中的垃圾回收机制
- C#垃圾回收机制详解
- c#中垃圾回收机制
- C#垃圾回收机制详解
- C#垃圾回收机制详解
- PHP垃圾回收机制之回收周期
- [英语阅读]日本进入经济萧条期
- oracle没有创建dump目录的奇怪现象
- 11月1日到11月14日的成果
- 第五章 依赖倒转
- ifconfig 指令详细介绍
- C#垃圾回收之二次回收机制
- EJB3.0注释小结
- 关于Python的静态变量
- C#数据库(c#通过调用存储过程返回表)
- 用PXE安装WINDOWS(linux->windows)
- C#数据库操作类
- Java同步技术(九)
- 面试顺利的7个迹象
- linux 心得