侧边栏壁纸
博主头像
再见理想博主等级

只争朝夕,不负韶华

  • 累计撰写 112 篇文章
  • 累计创建 64 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

JVM-垃圾收集算法

再见理想
2022-05-25 / 0 评论 / 0 点赞 / 320 阅读 / 1,792 字

一,哪些对象可以回收

包括两种算法,可达性分析算法和引用计数法,下面作详细介绍:

1.1 可达性分析算法

这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GCRoots到这个对象不可达)时,则证明此对象是不可用的。(Java虚拟机默认)


1.2 引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。

但主流的Java虚拟机里面没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间相互循环引用的问题。比如A对象引用B对象,B对象引用A对象,此时它们的引用计数器都不为0,但它们俩实际上已经没有意义因为没有任何地方指向它们。

二,四种引用

2.1 强引用

强引用就是指在程序代码之中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

2.2 软引用 SoftReference

软引用是用来描述一些还有用但并非必需的对象。

仅有软引用引用该对象时,在垃圾回收后,内存仍不足,会再次发起垃圾回收,回收软引用对象。可配合引用对象来释放软引用自身。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。

2.3 弱引用 WeakReference

弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。

2.4 虚引用 PhantomReference

必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存。
引用队列中有虚引用,Reference Handler线程调用Unsafe.freeMemory(),释放直接内存。

2.5 终结器引用

无需手动编码,其内部配合引用队列使用,在垃圾回收时,终结器引用入队,再由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize方法。
第二次GC时才能回收被引用对象。

三,垃圾收集算法

3.1 标记清除算法

标记—清除算法是最基础的收集算法,它分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实就是前面的可达性分析算法中判定垃圾对象的标记过程。

有如下缺点:
1,标记和清除过程的效率都不高;
2,标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不触发另一次垃圾收集动作。


3.2 复制算法

Java堆中新生代的垃圾回收算法
复制算法是针对标记—清除算法的缺点,在其基础上进行改进而得到的,它将可用内存按容量分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活着的对象复制到另外一块内存上面,然后再把已使用过的内存空间一次清理掉。

优点:每次只对一块内存进行回收,运行高效;只需移动栈顶指针,按顺序分配内存即可,实现简单;内存回收时不用考虑内存碎片的出现。
缺点:可一次性分配的最大内存缩小了一半

现在的商业虚拟机都采用这种收集算法来回收新生代,IBM 公司的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1∶1的比例来划分内存空间,而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor 。当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8∶1,也就是每次新生代中可用内存空间为整个新生代容量的 90% (80%+10%),只有 10% 的内存会被“浪费”。当然,98% 的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(HandlePromotion)。

参考文章:Java虚拟机2:jvm堆分代


3.3 标记整理算法

Java堆中老年代的垃圾回收算法
对于新生代,大部分对象都不会存活,所以在新生代中使用复制算法较为高效,而对于老年代来讲,大部分对象可能会继续存活下去,如果此时还是利用复制算法,效率则会降低。标记整理算法首先还是“标记”,标记过后,将不用回收的内存对象压缩到内存一端,此时即可直接清除边界处的内存,这样就能避免复制算法带来的效率问题,同时也能避免内存碎片化的问题。老年代的垃圾回收称为“Major GC”。

0

评论区