垃圾收集和终结

问题描述:

大家好。



昨天我公司部门的一名初级开发人员为我提供了代码,并问我一个问题。

我知道代码片段有很多违规行为,我已经将它们指向了大三学生:



  • Dispose模式的不正确实现
  • 在.NET终结器中使用托管对象,等等。
public class FileHandler
	{
		private StreamReader sr;
		private string _fname;
 
		public FileHandler(string filename)
		{
			sr = new StreamReader(filename);
		}
 
		public void PerformRead()
		{
			string line = null;
			while ((line = sr.ReadLine()) != null)
			{
				if (line.Contains("declare"))
					line = line.Trim();
			}
		}
 
		public void Close()
		{
			sr.Close();
		}
	}
 
	public sealed class FileReader
	{
		private FileHandler fileHandler;
 
		public FileReader(string fileName)
		{
			fileHandler = new FileHandler(fileName);
		}
 
		public void Read()
		{
			FileTools.ReadFile(fileHandler);
		}
 
		~FileReader()
		{
			Console.WriteLine("Closing vie finalization");
			fileHandler.Close();
		}
 
		public void Dispose()
		{
			fileHandler.Close();
			GC.SuppressFinalize(this);
		}
	}
 
	public static class FileTools
	{
		public static void ReadFile(FileHandler fileHandler)
		{
			if (fileHandler != null)
				fileHandler.PerformRead();
		}
	}
	
	class Program
	{
		static void Main(string[] args)
		{
			FileReader fr = new FileReader("myfile.txt");
			fr.Read();
		}
	}



注意:问题仅在发布模式下重现(当我们运行独立应用程序时,不在VS下) * .exe)这里的下一个关键障碍是我们正在处理的文件必须超过3 Mb。

我的问题是:

为什么在完成PerformRead方法之前,FileReader被垃圾收集(达到最终化,移动到Freachable队列并在那里倾斜)?

因此,当处理大文件时,我们在PerformRead之前收到NullReferenceException已完成。

在VS下我们无法观察到上述行为。

请参阅例外链接错误打印屏幕 [ ^ ]

所以在 @manchanx

的帮助下,我们达到了下一步:

1)m已经发布FileReader对象并将其置于最终阶段的罪魁祸首是在Release期间打开优化并且不发出调试符号。

这就是为什么在PerformRead方法的活动工作流中和乍一看保留所有引用活动,GC处理FileReader fr对象作为垃圾

并将其提交到完成阶段。



所以消除这样的问题有3个步骤:

1)将[MethodImplAttribute(MethodImplOptions.NoInlining)]应用于Main和Read方法。

2)尽可能多地操作fr引用尽可能:例如添加下一行:

So with help of @manchanx
we reached next steps:
1) The main culprit who has been released FileReader object and put it to the finalization stage was optimization that is turned on during Release and debug symbols are not emitted.
That is why during the active workflow in the PerformRead method and at first glance preserving all references alive , the GC treated FileReader fr object as a garbage
and submit it to the finalization stage.

So to eliminate such issue there are 3 steps:
1) Apply [MethodImplAttribute(MethodImplOptions.NoInlining)] to both Main and Read methods.
2) Operate fr reference as much as possible: for example add next lines:
class Program
	{
		static void Main(string[] args)
		{
			FileReader fr = new FileReader("myfile.txt");
			fr.Read();
                        Console.WriteLine(fr.GetType());
		}
	}





3)添加下一个设置文件[assemblyname] .ini

使用这些设置,您的应用程序可以全速运行。当您想通过打开调试跟踪并可能关闭(CIL)代码优化来调试应用程序时,只需使用以下设置:

下一个文件的内容:



[.NET Framework调试控制]

GenerateTrackingInfo = 1

AllowOptimize = 0



3) Add next setting file [assemblyname].ini
With these settings, your app runs at full speed. When you want to debug your app by turning on debug tracking and possibly turning off (CIL) code optimization, just use the following settings:
The content of a file is next:

[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0