序言

对象生存期

Phone item=new Phone()

在C#中,创建对象使用的是new关键字。 要注意的是new操作返回的并不是对象本身,而是对象的一个引用(Reference)。

如果使用item=null;语句,将上面的item变量赋值为null,不过是切断了变量和对象之间的引用关系,对象并没有销毁,还停留在托管堆上。

并不是将item设为null才会将对象变为无法访问的。 如果方法结束后,再没有其他地方引用对象,那么它也会变成无法访问的。

垃圾回收机制

什么是垃圾?

  简单理解就是没有被引用的对象。

  GC是垃圾回收(Garbage Collect)的缩写,是.NET核心机制的重要部分。她的基本工作原理就是遍历托管堆中的对象,标记哪些被使用对象(那些没人使用的就是所谓的垃圾),然后把可达对象转移到一个连续的地址空间(也叫压缩),其余的所有没用的对象内存被回收掉。

托管资源:

  由CLR管理的存在于托管堆上的称为托管资源,注意这里有2个关键点,第一是由CLR管理,第二存在于托管堆上。托管资源的回收工作是不需要人工干预的,CLR会在合适的时候调用GC(垃圾回收器)进行回收。

  我们可以使用GC类提供的GC.Collect方法来使应用程序在一定程度上直接控制垃圾回收器,但是一般不要去手动干预GC。没有特殊理由,不要去调用GC.Collect(),让它自己决定什么时候去回收内存。还是人家的比较严谨。

非托管资源:

  非托管资源是不由CLR管理,例如:Image Socket, StreamWriter, Timer, Tooltip, 文件句柄, GDI资源, 数据库连接等等资源(这里仅仅列举出几个常用的)。这些资源GC是不会自动回收的,需要手动释放。

 垃圾回收算法 - 分代(Generation)算法

  CLR托管堆支持3代:第0代,第1代,第2代。第0代的空间约为256KB,第1代约为2M,第2代约为10M。新构造的对象会被分配到第0代,

  当第0代的空间满时,垃圾回收器启动回收,不可达对象(上图C、E)会被回收,存活的对象被归为第1代。

  当第0代空间已满,第1代也开始有很多不可达对象以至空间将满时,这时两代垃圾都将被回收。存活下来的对象(可达对象),第0代升为第1代,第1代升为第2代。

垃圾回收的基本流程包含以下三个关键步骤:

  ① 标记

  ② 清除

  ③ 压缩

关于代龄(Generation)

  当然,实际的垃圾回收过程可能比上面的要复杂,如果没次都扫描托管堆内的所有对象实例,这样做太耗费时间而且没有必要。分代(Generation)算法是CLR垃圾回收器采用的一种机制,它唯一的目的就是提升应用程序的性能。分代回收,速度显然快于回收整个堆。分代(Generation)算法的假设前提条件:

  1、大量新创建的对象生命周期都比较短,而较老的对象生命周期会更长

  2、对部分内存进行回收比基于全部内存的回收操作要快

  3、新创建的对象之间关联程度通常较强。heap分配的对象是连续的,关联度较强有利于提高CPU cache的命中率

.NET将托管堆分成3个代龄区域: Gen 0、Gen 1、Gen 2:

  第0代,新近分配在堆上的对象,从来没有被垃圾收集过。 任何一个新对象,当它第一次被分配在托管堆上时,就是第0代。

  第1代,经历过一次垃圾回收后,依然保留在堆上的对象。

  第2代,经历过两次或以上垃圾回收后,依然保留在堆上的对象。

  当进行垃圾回收时,垃圾回收器将会首先检查所有的第0代对象,并对其中可回收的对象进行清理。如果清理后获取到了足够的内存空间,经历过垃圾回收后的对象将提升为第1代对象。

  如果所有第0代对象都检查过,但是内存空间还不够用,那么将会检查第1代对象的可访问性,并进行垃圾回收。 此时,如果经历过垃圾回收的第1代对象仍保留在堆上,则会升级为第2代对象。 类似地,如果内存仍不够用,将会对第2代对象进行检查和垃圾回收。 如果第2代的部分对象在此次回收后仍保留在堆栈上,它依然是第2代对象,因为总共只定义了三代对象。 如果第2代对象在进行完垃圾回收后空间仍然不够用,则会抛出OutOfMemoryException异常。

大对象堆

  垃圾回收的过程中还有一个很影响性能的地方,就是在压缩的过程中,因为要批量地挪动对象,以填充腾出来的空间,如果对象很大,那么要挪动的数据量就会很大。 除此以外,如果将大对象直接分配在第0代,那么第0代的空间很快就会被占满,从而迫使CLR执行一次垃圾回收,这样执行垃圾回收的次数就会变得很频繁。因此,第二个优化策略就是采用大对象堆(LOH,Large Object Heap),当对象的大小超过指定数值(85000字节)时,就会被分配在大对象堆上。 大对象堆有几个特点:
  没有代级的概念,所有对象都被视为第2代。
  不进行对象移动和空间压缩,因为移动大对象是相对耗时的操作。 因此,需要一个链表来维护空闲区域的位置。
  对象不会被分配在末尾,而会在链表中寻找合适的位置,因此会存在碎片的问题。

对象析构

  非托管资源回收.NET中提供释放非托管资源的方式主要是:Finalize() 和 Dispose()。

  Dispose()常用的大多是Dispose模式,主要实现方式就是实现IDisposable接口

  Finalize()  终结器(析构函数)磁盘文件、 TCP连接、 通信端口、 数据库连接等, 当对象被垃圾回收时, 只是被简单地覆盖掉, 并不会释放这些资源。

  Dispose()和Finalize()区别Finalizer的执行时间是不确定的, 有时候, 我们期望客户端在对象使用完毕后立即释放资源,此时可以实现IDisposable()接口:

public interface IDisposable {
void Dispose();
}

using

using(A a = new A())
{
//使用A对象的方法
}

  在作用域结束的时候,会自动调用A对象的Dispose方法

  但是前提A对象必须实现了IDispose接口

  否则无法使用using关键字

性能优化建议

  尽量不要手动执行垃圾回收的方法:GC.Collect()

  垃圾回收的运行成本较高(涉及到了对象块的移动、遍历找到不再被使用的对象、很多状态变量的设置以及Finalize方法的调用等等),对性能影响也较大,因此我们在编写程序时,应该避免不必要的内存分配,也尽量减少或避免使用GC.Collect()来执行垃圾回收,一般GC会在最适合的时间进行垃圾回收。

  而且还需要注意的一点,在执行垃圾回收的时候,所有线程都是要被挂起的(如果回收的时候,代码还在执行,那对象状态就不稳定了,也没办法回收了)。

  推荐Dispose代替Finalize

  如果你了解GC内存管理以及Finalize的原理,可以同时使用Dispose和Finalize双保险,否则尽量使用Dispose。

  选择合适的垃圾回收机制:工作站模式、服务器模式

关于GC的面试题

1. 简述一下一个引用对象的生命周期?

  • new创建对象并分配内存
  • 对象初始化
  • 对象操作、使用
  • 资源清理(非托管资源)
  • GC垃圾回收

2. 创建下面对象实例,需要申请多少内存空间?

public class User
{
public int Age { get; set; }
public string Name { get; set; } public string _Name = "" + "abc";
public List<string> _Names;
}

40字节内存空间,详细分析文章中给出了。

3. 什么是垃圾?

  一个变量如果在其生存期内的某一时刻已经不再被引用,那么,这个对象就有可能成为垃圾。

4. GC是什么,简述一下GC的工作方式?

  GC是垃圾回收(Garbage Collect)的缩写,是.NET核心机制的重要部分。她的基本工作原理就是遍历托管堆中的对象,标记哪些被使用对象(哪些没人使用的就是所谓的垃圾),然后把可达对象转移到一个连续的地址空间(也叫压缩),其余的所有没用的对象内存被回收掉。

5. GC进行垃圾回收时的主要流程是?

  ① 标记:先假设所有对象都是垃圾,根据应用程序根Root遍历堆上的每一个引用对象,生成可达对象图,对于还在使用的对象(可达对象)进行标记(其实就是在对象同步索引块中开启一个标示位)。

  ② 清除:针对所有不可达对象进行清除操作,针对普通对象直接回收内存,而对于实现了终结器的对象(实现了析构函数的对象)需要单独回收处理。清除之后,内存就会变得不连续了,就是步骤3的工作了。

  ③ 压缩:把剩下的对象转移到一个连续的内存,因为这些对象地址变了,还需要把那些Root跟指针的地址修改为移动后的新地址。

6. GC在哪些情况下会进行回收工作?

  • 内存不足溢出时(0代对象充满时)
  • Windwos报告内存不足时,CLR会强制执行垃圾回收
  • CLR卸载AppDomian,GC回收所有
  • 调用GC.Collect
  • 其他情况,如主机拒绝分配内存,物理内存不足,超出短期存活代的存段门限

7. using() 语法是如何确保对象资源被释放的?如果内部出现异常依然会释放资源吗?

  using() 只是一种语法形式,其本质还是try…finally的结构,可以保证Dispose始终会被执行。

8. 解释一下C#里的析构函数?为什么有些编程建议里不推荐使用析构函数呢?

  C#里的析构函数其实就是终结器Finalize,因为长得像C++里的析构函数而已。

  有些编程建议里不推荐使用析构函数要原因在于:第一是Finalize本身性能并不好;其次很多人搞不清楚Finalize的原理,可能会滥用,导致内存泄露,因此就干脆别用了

9. Finalize() 和 Dispose() 之间的区别?

  Finalize() 和 Dispose()都是.NET中提供释放非托管资源的方式,他们的主要区别在于执行者和执行时间不同:

  • finalize由垃圾回收器调用;dispose由对象调用。
  • finalize无需担心因为没有调用finalize而使非托管资源得不到释放,而dispose必须手动调用。
  • finalize不能保证立即释放非托管资源,Finalizer被执行的时间是在对象不再被引用后的某个不确定的时间;而dispose一调用便释放非托管资源。
  • 只有class类型才能重写finalize,而结构不能;类和结构都能实现IDispose。

  另外一个重点区别就是终结器会导致对象复活一次,也就说会被GC回收两次才最终完成回收工作,这也是有些人不建议开发人员使用终结器的主要原因。

10. Dispose和Finalize方法在何时被调用?

  • Dispose一调用便释放非托管资源;
  • Finalize不能保证立即释放非托管资源,Finalizer被执行的时间是在对象不再被引用后的某个不确定的时间;

11. .NET中的托管堆中是否可能出现内存泄露的现象?

  是的,可能会。比如:

  • 不正确的使用静态字段,导致大量数据无法被GC释放;
  • 没有正确执行Dispose(),非托管资源没有得到释放;
  • 不正确的使用终结器Finalize(),导致无法正常释放资源;
  • 其他不正确的引用,导致大量托管对象无法被GC释放;

12. 在托管堆上创建新对象有哪几种常见方式?

  • new一个对象;
  • 字符串赋值,如string s1=”abc”;
  • 值类型装箱;

 13.与GC相关的性能计数器

  如果遇到了性能问题,在使用debug之前分析问题较为不错的一个工具就是perfmon。

  如果您的网站遇到下面的几种情形,那还是先看看perfmon里GC相关的东西吧:

  1. cpu占用高,内存占用不高.
  2. cpu和内存占用都比较高
  3. cpu和内存占用都不高,但是网站响应很慢

14.手工进行回收

//对所有代进行垃圾回收。
GC.Collect();
//对指定的代进行垃圾回收。
GC.Collect(int generation);
//强制在 System.GCCollectionMode 值所指定的时间对零代到指定代进行垃圾回收。
GC.Collect(int generation, GCCollectionMode mode);

.NET面试题系列(二)GC的更多相关文章

  1. JVM系列二:GC策略&内存申请、对象衰老

    JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...

  2. U3D面试题系列二

    高频问题: 一.什么是渲染管道? 是指在显示器上为了显示出图像而经过的一系列必要操作. 渲染管道中的很多步骤,都要将几何物体从一个坐标系中变换到另一个坐标系中去. 主要步骤有: 本地坐标->视图 ...

  3. 【转载】JVM系列二:GC策略&内存申请、对象衰老

    JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...

  4. [转]JVM系列二:GC策略&内存申请、对象衰老

    原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html JVM里的GC(Garbage Collection)的算法有 ...

  5. 很有用的PHP笔试题系列二

    1.如何用php的环境变量得到一个网页地址的内容?ip地址又要怎样得到? Gethostbyname() echo $_SERVER ["PHP_SELF"];echo $_SER ...

  6. 面试必备【含答案】Java面试题系列(二

    1.写clone()方法时,通常都有一行代码,是什么?答:super.clone(),他负责产生正确大小的空间,并逐位复制. 2.GC 是什么? 为什么要有GC?答:GC 是垃圾收集的意思(Gabag ...

  7. .NET技术面试题系列(1) 基础概念

    这是.NET技术面试题系列第一篇,今天主要分享基础概念. 1.简述 private. protected. public.internal 修饰符的访问权限 private : 私有成员, 在类的内部 ...

  8. .NET面试题系列[0] - 写在前面

    .NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] - .NET框架基础知识(2) .NET面试题系列[3] - C# 基础知识(1) .NET ...

  9. [知识库分享系列] 二、.NET(ASP.NET)

    最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...

  10. 【转载】.NET面试题系列[0] - 写在前面

    原文:.NET面试题系列[0] - 写在前面 索引: .NET框架基础知识[1] - .NET框架基础知识(1) http://www.cnblogs.com/haoyifei/p/5643689.h ...

随机推荐

  1. HDU 5203 Rikka with wood sticks 分类讨论

    题目链接: hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5203 bc(chinese):http://bestcoder.hdu.edu.cn/con ...

  2. Python数据挖掘学习路程--起步

    一.首先第一步我去了解了Python开发环境:Python(程序运行基础的解释器)+第三方类库(功能扩展)+编辑器(提高代码编辑效率) 编辑器有:Pycharm.Spyder.jupyter note ...

  3. python、Eclipse、pydev环境配置

    转载来源:http://www.cnblogs.com/Bonker/p/3584707.html 编辑器: Eclipse + pydev插件: 1. Eclipse是写JAVA的IDE, 这样就可 ...

  4. 解决zabbix使用中文是出现乱码的问题

       解决zabbix使用中文是出现乱码的问题 ①:上传windows的simhei.ttf字体到zabbix服务器的/usr/share/zabbix/fonts/目录下   ②:编辑/usr/sh ...

  5. 【第二周】scrum站立会议

    1.站立会议:敏捷软件开发方法论Scrum的相关技术之一,是scrum的最佳实践 2.具体形式:每天的同一时间让团队成员面对面站立交流工作进展 3.功能: (1)让团队所有人都相互知道彼此的进展,了解 ...

  6. oracle package pragma SERIALLY_REUSABLE(编译指示 告诉PL/SQL 的运行时引擎,在数据引用之时不要保持包级数据。)

    当包第一次被动调用时,将进行初始化:比如将包从硬盘上调到内存中来,放到系统全局工作区的共享缓冲池中,包的运行状态则被放到用户全局区的会话中存储区中,因此可以保证每个调用包的会话都拥有包的运行副本,当会 ...

  7. window redis php(必须版本>=5.4) 安装

    1.下载redis的win版客户端 下载地址: http://code.google.com/p/servicestack/wiki/RedisWindowsDownload 2.选择32bit,64 ...

  8. Java web 强制301跳转

    response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); response.setHeader("Location" ...

  9. 第168天:json对象和字符串的相互转换

    json对象和字符串的相互转换 1.json对象和字符串的转换 在Firefox,chrome,opera,safari,ie9,ie8等高级浏览器直接可以用JSON对象的stringify()和pa ...

  10. jquery 添加与删除的规律 当要添加时候要定位到自己的父元素 当要删除时候 通过事件函数传入的this找到自己的父元素进行删除

    jquery 添加与删除的规律 当要添加时候要定位到自己的父元素  当要删除时候 通过事件函数传入的this找到自己的父元素进行删除