.NET面试题系列(二)GC
序言
对象生存期
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相关的东西吧:
- cpu占用高,内存占用不高.
- cpu和内存占用都比较高
- cpu和内存占用都不高,但是网站响应很慢
14.手工进行回收
//对所有代进行垃圾回收。
GC.Collect();
//对指定的代进行垃圾回收。
GC.Collect(int generation);
//强制在 System.GCCollectionMode 值所指定的时间对零代到指定代进行垃圾回收。
GC.Collect(int generation, GCCollectionMode mode);
.NET面试题系列(二)GC的更多相关文章
- JVM系列二:GC策略&内存申请、对象衰老
JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...
- U3D面试题系列二
高频问题: 一.什么是渲染管道? 是指在显示器上为了显示出图像而经过的一系列必要操作. 渲染管道中的很多步骤,都要将几何物体从一个坐标系中变换到另一个坐标系中去. 主要步骤有: 本地坐标->视图 ...
- 【转载】JVM系列二:GC策略&内存申请、对象衰老
JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类 现在比较常用的是分代收集(generatio ...
- [转]JVM系列二:GC策略&内存申请、对象衰老
原文地址:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html JVM里的GC(Garbage Collection)的算法有 ...
- 很有用的PHP笔试题系列二
1.如何用php的环境变量得到一个网页地址的内容?ip地址又要怎样得到? Gethostbyname() echo $_SERVER ["PHP_SELF"];echo $_SER ...
- 面试必备【含答案】Java面试题系列(二
1.写clone()方法时,通常都有一行代码,是什么?答:super.clone(),他负责产生正确大小的空间,并逐位复制. 2.GC 是什么? 为什么要有GC?答:GC 是垃圾收集的意思(Gabag ...
- .NET技术面试题系列(1) 基础概念
这是.NET技术面试题系列第一篇,今天主要分享基础概念. 1.简述 private. protected. public.internal 修饰符的访问权限 private : 私有成员, 在类的内部 ...
- .NET面试题系列[0] - 写在前面
.NET面试题系列目录 .NET面试题系列[1] - .NET框架基础知识(1) .NET面试题系列[2] - .NET框架基础知识(2) .NET面试题系列[3] - C# 基础知识(1) .NET ...
- [知识库分享系列] 二、.NET(ASP.NET)
最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...
- 【转载】.NET面试题系列[0] - 写在前面
原文:.NET面试题系列[0] - 写在前面 索引: .NET框架基础知识[1] - .NET框架基础知识(1) http://www.cnblogs.com/haoyifei/p/5643689.h ...
随机推荐
- HDU 5203 Rikka with wood sticks 分类讨论
题目链接: hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5203 bc(chinese):http://bestcoder.hdu.edu.cn/con ...
- Python数据挖掘学习路程--起步
一.首先第一步我去了解了Python开发环境:Python(程序运行基础的解释器)+第三方类库(功能扩展)+编辑器(提高代码编辑效率) 编辑器有:Pycharm.Spyder.jupyter note ...
- python、Eclipse、pydev环境配置
转载来源:http://www.cnblogs.com/Bonker/p/3584707.html 编辑器: Eclipse + pydev插件: 1. Eclipse是写JAVA的IDE, 这样就可 ...
- 解决zabbix使用中文是出现乱码的问题
解决zabbix使用中文是出现乱码的问题 ①:上传windows的simhei.ttf字体到zabbix服务器的/usr/share/zabbix/fonts/目录下 ②:编辑/usr/sh ...
- 【第二周】scrum站立会议
1.站立会议:敏捷软件开发方法论Scrum的相关技术之一,是scrum的最佳实践 2.具体形式:每天的同一时间让团队成员面对面站立交流工作进展 3.功能: (1)让团队所有人都相互知道彼此的进展,了解 ...
- oracle package pragma SERIALLY_REUSABLE(编译指示 告诉PL/SQL 的运行时引擎,在数据引用之时不要保持包级数据。)
当包第一次被动调用时,将进行初始化:比如将包从硬盘上调到内存中来,放到系统全局工作区的共享缓冲池中,包的运行状态则被放到用户全局区的会话中存储区中,因此可以保证每个调用包的会话都拥有包的运行副本,当会 ...
- window redis php(必须版本>=5.4) 安装
1.下载redis的win版客户端 下载地址: http://code.google.com/p/servicestack/wiki/RedisWindowsDownload 2.选择32bit,64 ...
- Java web 强制301跳转
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); response.setHeader("Location" ...
- 第168天:json对象和字符串的相互转换
json对象和字符串的相互转换 1.json对象和字符串的转换 在Firefox,chrome,opera,safari,ie9,ie8等高级浏览器直接可以用JSON对象的stringify()和pa ...
- jquery 添加与删除的规律 当要添加时候要定位到自己的父元素 当要删除时候 通过事件函数传入的this找到自己的父元素进行删除
jquery 添加与删除的规律 当要添加时候要定位到自己的父元素 当要删除时候 通过事件函数传入的this找到自己的父元素进行删除