DotNet面试题解析07-GC与内存管理

面试题解析

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

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

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

1
2
3
4
5
6
7
8
public class User
{
public int Age { get; set; }
public string Name { get; set; }

public string _Name = "123" + "abc";
public List<string> _Names;
}

44字节内存空间。

详细信息参考:DotNet基础-GC与内存管理

3. 什么是垃圾?

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

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

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

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

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

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

(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”;
  • 值类型装箱;

参考:

.NET面试题解析(06)-GC与内存管理