垃圾回收的概要

查找不再使用的对象

垃圾回收指的是在程序运行过程中,检查是否有不再使用的对象,并自动释放它们所占用的内存。内存的检查和回收都是由垃圾收集器完成的。
Mac OS X中垃圾回收的目标是不再使用的实例对象。C风格的变量、C风格的结构体以及C风格申请的内存都不属于垃圾回收的范围。id类型、以类名作为类型的实例对象是垃圾回收的目标。
在Objective-C 2.0中,垃圾回收首先进行的工作就是识别不允许被回收的对象。首先,全局变量和静态变量引用的对象不允许被回收。另外,栈内临时变量引用的对象也不允许被回收。这些对象称为根集合。如果一个对象的实例变量引用了不允许被回收的对象或根集合中的对象,那么这个对象也不允许被回收。
也就是说,通过全局变量、静态变量或者栈内变量的引用而查找到的对象都不可以回收,反之,只要是从根集合出发无法到达的对象都属于垃圾回收的目标。
在垃圾回收有效的情况下,不需要再对obj和helper执行保持和释放操作,垃圾回收机制会自动判断变量是否可以被回收。
手动内存管理时有可能会出现多个对象循环引用的情况,而使用垃圾回收管理内存时,就算有循环引用也不影响内存的释放。相会引用的对象集合也是如此,只要不能被根集合中的变量引用到,就属于垃圾回收的目标。
虽然使用垃圾回收的情况不需要手动释放每个对象,但如果不再使用的对象属于根集合,这个对象就不会被回收。对于不再使用的对象,我们可以通过为其赋值nil来表示其已不再被使用,据此通知来及收集器回收该对象。

垃圾收集器

垃圾收集器的运行是自适应的,既不以固定的频率运行,也不能通过程序调用运行。当程序运行过程中分配的内存超过一定量时,垃圾收集器就会被自动触发运行。垃圾收集器通常作为一个单独的线程运行,并对已经不再被使用的对象进行回收。整个回收过程完全是根据程序占用内存的多少自动完成的。

一个程序只有一个垃圾收集器,通常在mainthread中运行。每个线程都有自己的栈空间,这些栈空间中的变量不属于垃圾回收的范畴。垃圾收集器运行的时候有可能会暂停其他线程来玩层呢垃圾收集,但不会暂停所有线程的运行。
虽然垃圾收集器会根据内存的情况自动运行,但我们可以通过给类NSGarbageCollector发送collectIfNeeded消息来主动启动垃圾收集器。
垃圾收集器会首先找到所有不再使用的对象,然后再给这些对象发送finalize消息。等所有对象响应了finalize之后,才释放这些对象,并且对象的dealloc方法不会被执行。

内存管理方式的比较

引用计数和垃圾回收

首先我们与手动内存管理相比,ARC和垃圾回收有哪些优点:

  • 不需要在意对象的所有权
  • 可以删除程序中内存管理的大部分代码,使代码结构更精简
  • 可以避免手动管理内存时出现的如内存泄漏等错误
  • 不需要再有引用计数内存管理中的一些特殊用法。例如访问方法的定义和临时对象的使用等
  • 可以使多线程环境下的编程更加简单。不用担心不同线程之间可能出现的所有权冲突问题

但是垃圾回收本身存在一定的缺点,所以导致后续又被废弃了:

  • 垃圾收集器运行时会影响程序的速度
  • 需要不停的监视内存的使用,同引用计数的方式相比,程序的速度会变慢
  • 会影响程序的效率。不经常使用的内存也会被垃圾收集器不时的访问,实际上有可能会占用更多的内存
  • 需要使用一些技巧来让对象被回收或不被回收
  • 无法使用引用计数管理方式下的一些设计方针。例如:事先准备好一些管理文件或其他资源类的对象,在对象被释放的同时关闭资源。

相比而言ARC没有垃圾回收那么多缺点,但也有一些需要注意的地方。

  • 循环引用一旦形成不会自己消失
  • 理论上可被复制的对象都可以被自动释放,但在处理结构体、数组、二重指针等类型的变量时有一些限制。