什么是垃圾回收
跟踪所有仍在使用的对象并将其余对象标记为垃圾的这一过程就叫做垃圾回收。
手动内存管理
在开始介绍现代Garbage Collection之前,快速回顾一下以前不得不手动和显式分配和释放数据存储空间的日子。如果你忘记释放它,则将无法重用它,但这块内存已经被声明了只是没有被使用,这种情况称为内存泄漏。
下面是一个用C语言编写的,使用手动内存管理的简单示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int send_request() { size_t n = read_size(); int *elements = malloc(n * sizeof(int));
if(read_elements(n, elements) < n) { return -1; }
free(elements) return 0; }
|
如你所见,忘记释放内存是很容易的。内存泄漏曾经是比今天更常见的问题,你只能通过修复代码来真正打败“它们”,那有没有更优雅的解决方案呢?答案是肯定的,更好的方法是采取自动回收未使用的内存的策略,从而完全消除人为错误的可能性。这种自动化的过程称为垃圾收集(或简称GC)。
自动内存管理
在上面的C++代码中必须明确地知道何时需要进行内存管理,把这个工作给程序员,就意味着肯定有系统性风险,即人为忽略。如果把内存管理交给程序自己处理呢?这将非常方便,因为开发人员不再需要考虑自己清理。在程序运行时将自动了解不再使用某些内存并将其释放。换句话说,它会自动** 收集垃圾**。第一个垃圾收集器是在1959年为Lisp创建的,此后技术才有所发展。
引用计数(Reference Counting)
上面用C++的共享指针示例可以适用于所有对象,并且许多语言(例如Perl,Python或PHP)都采用这种方法。下面用图片说明:
![]()
绿云(GC ROOTS)表示程序员指向的对象仍在使用中。从技术上讲,这些可能是当前正在执行的方法中的局部变量或静态变量之类的东西。
蓝色圆圈是内存中的活动对象,其中的数字表示其引用计数。灰色圆圈表示没有被哪个仍在显式使用的对象中引用。因此灰色表示的是垃圾,可以由垃圾收集器清理。
这一切看起来真的很好,不是吗?但是这个策略有一个巨大的缺点。如果原先的引用不存在了,但可能它们之间仍然有相互引用,这种循环引用的问题,会导致它们的引用计数永远不为零。下面是一个例子:
![]()
红色圆圈表示的对象实际上是应用程序不使用的垃圾,可是由于引用计数的限制,仍然会存在内存泄漏问题。
有一些方法可以解决此问题,例如使用特殊的“弱”引用或应用单独的循环收集算法。上述语言(Perl、Python和PHP)都以某种方式处理循环,不过本方主要介绍JVM采取的方法。