文章字数:1111,阅读全文大约需要4分钟
GC
(Garbage Collection
)垃圾回收机制,对死亡的对象侵占的空间释放。
垃圾回收场所
JVM
的垃圾回收主要在堆,其次是方法区。
具体实现
- 分区
JVM
将堆内存分成三部分
- 新生代(Young Generation)
新生代分为伊甸园Eden Region
和幸存者区Survivor Region
幸存者区又分为Form Survivor Region
和To Survivor Region
幸存者区的两个部分是from
还是to
并不固定,有对象的就是from
另一个是to
。
所以新生代总共是三部分,协同工作。 - 老年代(Old Generation)
- 永久代(Perm Generation)
java8后变成元数据区
- 回收条件
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
- 加载该类的ClassLoader已经被回收
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
- 回收过程
新生代
- 新对象首先进入
Eden
区 - 再来一个对象,加入
Eden
区剩余内存不够。此时会触发一次Minor GC
,把Eden
区存活的对象转移到From
区 - 给新对象分配内存,放到
Eden
区
老年代
- 在进行Minor GC时发现,存活的对象在To区中存不下,那么把存活的对象存入老年代
- 大对象直接进入老年代:若新创建的对象很大,即使Eden区有足够的空间,也不会放在Eden区,而是直接存入老年代。(PretenureSizeThreshold设置,默认3M)
- 长期存活的对象将进入老年代:如果对象在Eden出生且经过1次Minor GC后仍存活,并能被To区容纳,将被移动到To区,并把对象年龄设置为1,对象过1次Minor GC,年龄就+1,当它的年龄增加到一定程度(默认15岁,配置参数-XX:MaxTenuringThreshold),就会被晋升到老年代中。
- 动态对象年龄判定:如果在From中,相同年龄所有对象的大小总和大于From和To空间总和的一半,那么年龄大于等于该年龄的对象就会被移动到老年代,而不用等到15岁(默认)
GC运行方式
串行GC:client模式下默认GC方式,使用-XX:+UseSerialGC指定
并行GC:server模式下默认GC方式,可用-XX:+UseParallelGC指定
并发GC:CMS GC时默认采用,可用-XX:+UseParNewGC,-XX:+UseConcMarkSweepGC指定;
吞吐量收集器使用并行版本的新生代垃圾收集器,用于中大规模数据的应用程序。
串行收集器对大多数的小应用(在处理器上需要大概100M左右的内存)就足够了。
- 存活判断
引用计数算法
给对象添加一个引用计数器,当有地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;计数器为0的对象就是不被使用的。可达性分析算法
通过GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
- 垃圾清除算法
标记-清楚算法(Mark-Sweep)
用在老生代中, 先对对象进行标记,然后清除。标记过程就是第五部分提到的标记过程。值得注意的是,使用该算法清楚过后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。复制算法(Copying)
用在新生代中,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另外一块上,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可。
(内存缩小,代价太高)