序言

对象生存期

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. spring表单—乱码解决方案

    1.POST方式提交 A.jsp页面POST方式提交表单 B.web.xml配置过滤器 <!-- spring过滤器解决乱码问题 --> <filter> <filter ...

  2. Alpha阶段敏捷冲刺 DAY5

    一.举行站立式例会 1.今天我们利用晚上的时间开展了站立会议,总结了一下之前工作的问题,并且制定了明天的计划. 2.站立式会议照片 二.团队报告 1.昨日已完成的工作 (1)改进了程序算法 (2)优化 ...

  3. mac下面安装Navicat Premium 12.0.24 for mac已破解中文亲测可用

    链接:https://pan.baidu.com/s/17HQ7dsCun2cSJGRpdP9yXA 密码:hwq5 Navicat_Premium_mac  最新版 12.0.24(原版是英文的) ...

  4. webgl 初识1

    1.  webgl是什么?   WebGL其实是一个非常简单的API.好吧,“简单”可能是一个不恰当的描述. 它做的是一件简单的事,它仅仅运行用户提供的两个方法,一个顶点着色器和一个片断着色器, 去绘 ...

  5. mybatis 批量插入 返回主键id

    我们都知道Mybatis在插入单条数据的时候有两种方式返回自增主键: 1.对于支持生成自增主键的数据库:增加 useGenerateKeys和keyProperty ,<insert>标签 ...

  6. utuntu 安装python3.5

    如果想要升级Utuntu系统中的python版本,请不要卸载原先的版本. 桌面环境中的需要依赖于python相关,卸载之后会出现意想不到问题. (1)sudo add-apt-repository p ...

  7. 关于ADO一个容易被忽视的问题!UpdateBatch [问题点数:0分]

    这是一个常见但容易被忽视的问题,旧贴有问及但没答案,因此提高分数.相信大家常这样使用Cache模式:ADOConnection1.BeginTrans;Try ADODataSet1.UpdateBa ...

  8. Spring注解原理

    一.注解的基本概念和原理及其简单实用 注解(Annotation)提供了一种安全的类似注释的机制,为我们在代码中添加信息提供了一种形式化得方法,使我们可以在稍后某个时刻方便的使用这些数据(通过解析注解 ...

  9. fiddler启动报错Unable to bind to port [8888],ErrorCode:10106

    启动运行fiddler 报错,提示Unable to bind to port [8888],ErrorCode:10106 解决方式: 使用Fiddler或其他类似的监听工具出现这种错误时, Una ...

  10. C#使用 SharpSSH

    准备试一把监控Linux机器 . 附件如下 :http://files.cnblogs.com/files/lclblog/Tamir.SharpSsh.zip