Java内存管理——垃圾收集概念及特点

Posted by 李小武 on February 3, 2012

碎碎念,介绍Java内存管理基础知识

1.自动内存管理

内存管理就是查找出不再使用的对象,释放这些对象占用的内存空间,为其他对象的分配提供足够的内存空间。在一些编程语言(C,C++)中,内存管理是程序员的职责。内存管理使开发变得复杂,而且会造成许多潜在的bug和不可知的崩溃。导致程序猿花费大量时间debug来修复这些很隐蔽的问题。

在精确的内存管理中经常发生一个问题是悬摆指针 (dangling references)。程序猿可能释放一个对象占用的内存空间,即使这个对象还在被其他对象引用。如果一个对象引用另一个被不小心 释放的对象,这个释放对象的内存空间可能被重新分配新的对象,这个时候,不可预期的结果就可能发生了,而且很可能是致命的。

另一个在精确内存管理中经常发生的问题是内存泄露 (menory leaks)。泄露经常是对象占用的内存空间不再被任何其他对象引用,但是这个对象没有被回收。例如,如果你打算回收一个列表的空间,但是不小心只释放了列表的第一个元素,这个列表的其他元素将一直持有内存空间,因为这个列表引用已经不存在了,其他元素占用的空间得不到释放,造成了泄露。久而久之,类似的泄露累计,将会填满内存空间,导致没有足够的内存分配给其他的新对象。

鉴于上面的原因,Java做出一个明智但艰难的抉择,自动内存管理。程序猿不再管理负责的内存分配和回收,把更多的精力放在业务开发上。承担次任务的是Java中的垃圾收集器,它能解决悬摆指针,内存泄露问题。对象的引用关系会被垃圾收集器检索到,当某个对象不再被任何对象引用的时候,垃圾收集器会自动回收。

2.垃圾收的集概念

一个垃圾回收器的职责:

  • 分配内存
  • 确保被引用的对象留在内存中
  • 释放那些不再被任何其他对象引用的对象占用的内存空间

如果一个对象在以根对象 (root objects)为首的引用链中可达,那么这个对象是存活的;否则,这个对象已经死亡(将要被回收)。查找并释放不可达对象占用内存空间的过程,成为垃圾回收。

垃圾回收能解决大部分问题,但不是所有问题。你可以不断创建新的对象,并保持这写对象的引用,直到没有足够的内存位置(操作系统可能会用到交换空间,虚拟内存等保证系统正常运行,但是效率会下降)。

对于垃圾收集器分配和回收内存的算法,对程序员来说是透明的,通常对象内存空间是从(heap)中分配的。

垃圾收集器控制着垃圾回收的时间。通常,垃圾回收发生在整个堆或者堆一部分已经被填满或者堆的使用情况达到某个阈值。对象内存分配需要垃圾收集器在堆中找到特定大小的连续空间,而垃圾回收又会产生碎片 ,导致内存空间不连续。所以为了避免碎片,垃圾回收过程会有一些动作保证内存连续。

3.垃圾回收器特点

垃圾收集器应该是安全并且全面的。存活对象不能被错误的回收,并且在一次垃圾回收周期中,垃圾对象应该被全部清除,不能有残留。

垃圾回收器还应该是高效的,不能给正在运行的程序造成太大的停顿(STW —— stop the word)。在一个计算密集的系统中,在耗时,空间,频率通常会有一个权衡。如果堆的空间很小,垃圾收集很快就会完成,但是堆很快就会被填满,这时候垃圾收集器活动就会更频繁。相反,对于一个较大的堆,填满这个堆需要相对较长时间,但是收集器执行一次垃圾回收耗时也相对较长。

垃圾收集器的另一个特点是减少碎片空间。当回收对象占用的内存空间的时候,内存随便会遍布整个堆,新的对象分配没有足够大的连续空间导致分配失败。为了解决这个问题,在回收对象时,会对内存进行整理压缩

可扩展对垃圾收集器也很重要。垃圾收收集器不应该在单线程和多线程执行效率是一样的,也就是说,多cpu不应该成为垃圾收集器的瓶颈。