垃圾回收基础

2017-04-11  本文已影响329人  miaoLoveCode

垃圾回收(Garbage Collection,GC)是虚拟机内存管理很重要的一个模块,它帮助虚拟机在技术层面上做到了内存动态分配后的回收。垃圾回收主要发生在线程公有的区域。GC主要完成这样三件事情:

接下来就以如何做这三件事情为切入点,详细介绍一下GC的相关基础。

注:为什么垃圾回收发生在线程公有区域?
私有区域会随着线程的消亡而消亡,但是共有区域不会,公有区域内存的回收需要GC来完成。了解Java内存模型的小伙伴都知道,堆和方法区是公有的,所以GC目标区域在堆和方法区。

如何确定对象已死?

堆中几乎存放着Java程序中所有的对象实例,对于回收堆上的内存来说,确定哪些内存需要回收实质就是确定哪些对象已经死了,而后续的垃圾回收也就是将这些已死对象的内存做回收。那么,如何确定对象已死?

引用计数算法

确定一个对象是否已经死了就是看这个对象是不是没有引用了。在很多资料上都看到了判断对象是不是有引用的算法是这样子的:给对象添加一个引用计数器,一旦有地方引用它时,计数器值+1,引用失效时,计数器值-1,当引用计数器值为0时,该对象不再被引用。客观的说,引用计数算法实现很简单,判断效率也很高,但是,假如存在对象之间互相引用,这个算法就解决不了了,所以Java的GC并没有选用该算法来管理内存。

举个简单的例子:对象A和B都有私有属性instance,赋值令A.instance = B,B.instance = A,A和B互相引用。


demo

GC日志:


GC日志

从GC日志可以看出,虚拟机并没有因为a和b互相引用就不回收它们,这也说明虚拟机并不是通过引用计数算法来判断对象是否存在引用的。

根搜索算法

以GC Roots对象作为起始点,从这些节点依次向下搜索,如果当前对象到GC Roots没有任何的路径相连(对象不可达)时,那么,当前对象没有引用。

引用链:对象到GC Roots的走过的路径

在Java中,以下对象可作为GC Roots:

  1. Java虚拟机栈(栈帧中的本地变量表)中引用的对象;

  2. 本地方法栈中引用的对象;

  3. 方法区中的常量引用的对象;

  4. 方法区中的静态属性引用的对象。

根搜索算法中不可达的对象,是不是就非死不可呢?其实并不是的,要真正宣告一个对象死亡,至少要经过两次标记。在这里必须要多嘴几句说说finalize。

finalize

有过面试经历的人,可能都被问到了final,finally和finalize的区别。前两个关键字比较常用,在这里就不再做介绍了。finalize是一个方法名,Java允许使用finalize方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作,所以很多文章中又称它为析构函数的替代者。那么finalize方法是何时何地被调用呢?每次GC都会调用它么?

注:看到这里大致已经了解了finalize的执行过程以及执行次数,千万别认为finalize方法会一直都被执行,其实它只会被执行一次,一旦它在上次GC已经被执行,以后就不会再执行,除非你重启系统。

如何回收内存?

在确定需要回收的对象之后,接下来就将这些内存做回收。先来看看垃圾回收算法吧,本文只简要介绍垃圾回收算法思想,至于实现后面文章会给出较详细的分析。

注:HotSpot默认Eden区和Survivor区大小比例为8:1,至于为什么设置这个比例是因为young区的对象98%都是朝生夕死的。当然,JVM也提供参数以供使用者来更改Eden区和Survivor区的大小比例。

垃圾回收算法的思想到这里就分析完毕了,要想完整的了解垃圾回收,还需要了解垃圾回收载体 - 垃圾收集器。考虑到垃圾收集器类型较多,分析篇幅较大,就不再本文做相关分析了,会在后续的文章就垃圾收集器做详细的分析。

到这里,垃圾回收的相关基础内容就介绍完毕了,欢迎大家继续关注后续的垃圾回收更深层次的介绍。

上一篇 下一篇

猜你喜欢

热点阅读