先来看如下的一段代码:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Threading;
public static class Program
{
public static void Main()
{
// Create a Timer object that knows to call our TimerCallback
// method once every 1000 milliseconds.
Timer t = new Timer(TimerCallback, null, 0, 1000);
// Wait for the user to hit <Enter>
Console.ReadLine();
}
private static void TimerCallback(Object o)
{
// Display the date/time when this method got called.
Console.WriteLine("In TimerCallback: " + DateTime.Now);
}
}

我们在main函数中生成了一个timer,然后这个timer会每隔一秒输出一条记录,显示当前的时间。

如果运行这个程序(release模式),我们可以得到如下的输出:

这个程序看起来是运行正常的,可是真的是没有问题的吗?

我们简单的修改一下TimerCallback函数,强制调用一下GC,如下所示:

1
2
3
4
5
6
7
    private static void TimerCallback(Object o)
{
// Display the date/time when this method got called.
Console.WriteLine("In TimerCallback: " + DateTime.Now);
// Force a garbage collection to occur.
GC.Collect();
}

我们再次运行一下这个函数,得到了如下的输出:

我们可以看到,这次timer只被调用了一次!!!

这个时候大家应该能猜到原因了,我们的timer被垃圾回收了!!!

C#的垃圾回收采用了reference tracking的算法,大概的意思是说在执行垃圾回收时,所有的对象都默认认为是可以被回收的,然后遍历所有的roots(指向reference type的对象,包括类成员变量,静态变量,函数参数,函数局部变量),把这个root指向的对象标记成不能被回收的。

回到我们的代码,当我们强制调用GC.Collect()时,这个时候我们的timer t已经是一个没有被指向的对象了,于是垃圾回收就把t给回收了。这和C++的对象析构不太一样,C++的对象需要在出了作用域之后析构函数才会被调用到。

所以,即使我们没有显示的在这里调用GC.Collect(),但是我们不能确定什么时候CLR会调用GC,那个时候timer也就被回收了,总之,不能实现我们的意图。

 

再来个有意思的,如果我们把上面的程序改成debug模式再运行,发现我们的timer还是能够正常工作的,就是说还是能看到每隔一秒就输出一条记录。这又是为什么呢?

因为Visual Studio为了让debug更方便在debug模式下编译时延长了局部变量的生命周期。比如说,假设你在一个局部变量最后一次被使用之后打了断点,但是这个时候你在watch窗口已经看不到那个局部变量的值了(被垃圾回收了),那是不是很抓狂。所以debug的编译器就做了这个小手脚。

 

那如果要实现我们的初衷,就需要在Console.ReadLine之后还能保持一个对timer的引用,所以我们写了如下的代码:

 1
2
3
4
5
6
7
8
9
10
    public static void Main()
{
// Create a Timer object that knows to call our TimerCallback
// method once every 1000 milliseconds.
Timer t = new Timer(TimerCallback, null, 0, 1000);
// Wait for the user to hit <Enter>
Console.ReadLine(); t = null;
}

很不幸,这在release下还是不行,因为编译器认为把一个对象置为null是没必要的,帮我们优化掉了!

所以正确的做法是:

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
    public static void Main()
{
// Create a Timer object that knows to call our TimerCallback
// method once every 1000 milliseconds.
Timer t = new Timer(TimerCallback, null, 0, 1000);
// Wait for the user to hit <Enter>
Console.ReadLine(); // This assignment will be removed by compiler optimization
//t = null; // This will survive the GC
t.Dispose();
}

这样就可以了。当然,我们也可以直接用using语句:

 1
2
3
4
5
6
7
8
9
10
    public static void Main()
{
// Create a Timer object that knows to call our TimerCallback
// method once every 1000 milliseconds.
using (new Timer(TimerCallback, null, 0, 1000))
{
// Wait for the user to hit <Enter>
Console.ReadLine();
}
}

嗯,现在我们有了一个不会被垃圾回收掉的timer。希望对大家理解C#的垃圾回收有些帮助。

谁动了我的timer?——C#的垃圾回收和调试的更多相关文章

  1. Clr Via C#读书笔记---垃圾回收机制

    #1 垃圾回收平台的基本工作原理: 访问一个资源所需的具体步骤: 1)调用IL指令newobj,为代表资源的类型分配内存.在C#中使用new操作符,编译器就会自动生成该指令.2)初始化内存,设置资源的 ...

  2. 重温CLR(十五) 托管堆和垃圾回收

    本章要讨论托管应用程序如何构造新对象,托管堆如何控制这些对象的生存期,以及如何回收这些对象的内存.简单地说,本章要解释clr中的垃圾回收期是如何工作的,还要解释相关的性能问题.另外,本章讨论了如何设计 ...

  3. cir from c# 托管堆和垃圾回收

    1,托管堆基础 调用IL的newobj 为资源分配内存 初始化内存,设置其初始状态并使资源可用.类型的实列构造器负责设置初始化状态 访问类型的成员来使用资源 摧毁状态进行清理 释放内存//垃圾回收期负 ...

  4. [CLR via C#]21. 自动内存管理(垃圾回收机制)

    目录 理解垃圾回收平台的基本工作原理 垃圾回收算法 垃圾回收与调试 使用终结操作来释放本地资源 对托管资源使用终结操作 是什么导致Finalize方法被调用 终结操作揭秘 Dispose模式:强制对象 ...

  5. C#技术漫谈之垃圾回收机制(GC)

    GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由鼎鼎大名的图林奖得主John McCarthy所实现的Lisp语言就已经提供了GC的功能,这是 ...

  6. C#技术漫谈之垃圾回收机制(GC)(转)

    GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由鼎鼎大名的图林奖得主John McCarthy所实现的Lisp语言就已经提供了GC的功能,这是 ...

  7. 【转】【C#】C# 垃圾回收机制

    摘要:今天我们漫谈C#中的垃圾回收机制,本文将从垃圾回收机制的原理讲起,希望对大家有所帮助. GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由 ...

  8. C#垃圾回收机制(GC)

    GC的前世与今生 虽然本文是以.net作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由鼎鼎大名的图林奖得主John McCarthy所实现的Lisp语言就已经提供了GC的功能,这是 ...

  9. C#技术------垃圾回收机制(GC)

    GC的前世与今生 虽然本文是以.NET作为目标来讲述GC,但是GC的概念并非才诞生不久.早在1958年,由鼎鼎大名的图林奖得主John McCarthy所实现的Lisp语言就已经提供了GC的功能,这是 ...

随机推荐

  1. Java线程(学习整理)--1--守护线程

    1.什么是守护线程? 今天老师讲解我才知道有守护线程这回事!原来守护线程经常存在于我们的身边,比如:一个免费的网页游戏,里面都会或多或少有些插入性的广告!! 一般情况下,我们不会去点击这些广告的,但是 ...

  2. maven mirror

    国内连接maven官方的仓库更新依赖库,网速一般很慢,收集一些国内快速的maven仓库镜像以备用. ====================国内OSChina提供的镜像,非常不错=========== ...

  3. imod报错:error while loading shared libraries: libjpeg.so.62的解决办法

    the file libjpeg.so.62(in /usr/lib/libjpeg.so.62)belongs to the package libjpeg62so try to reinstall ...

  4. (转载)无缝滚动图片的js和jquery两种写法

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. extjs中gridpanel动态显示/隐藏列

    在extjs3中,大家知道用 myGrid.getColumnModel().setHidden(i,true);但到了4.0后,已经没有getColumnModel这个方法了,我们在Ext.pane ...

  6. [前端笔记]第三篇:JavaScript

    JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理. 一.代码存放位置 J ...

  7. 《30天自制操作系统》读书笔记(5) GDT&IDT

    梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas    InitialProgramLo ...

  8. [BC Round#26] Card 【各种水】

    题目链接:HDOJ - 5159 这道题的做法太多了..BC的第二题也是可以非常水的.. 算法一 我在比赛的时候写的算法是这样的.. 预处理出所有的答案,然后对于每个询问直接输出. 询问 (a, b) ...

  9. JVM系列五:JVM监测&工具

    JVM系列五:JVM监测&工具[整理中]  http://www.cnblogs.com/redcreen/archive/2011/05/09/2040977.html 前几篇篇文章介绍了介 ...

  10. Jersey Politics

    poj2454:http://poj.org/problem?id=2454 题意:给你3*k个数,然后让你分成三堆,使得至少其中的两堆中的数字之和大于500*k.题解:这道题一开始我并不知道怎么做, ...