JVM

JVM垃圾回收

2021-05-27  本文已影响0人  肥兔子爱豆畜子

如何确定哪些内存需要回收

gc要做的三件事分别是:哪些内存需要回收?什么时候回收? 如何回收?那么首先来看一下如何确定垃圾的问题、也就是哪些对象已经可以回收了。

垃圾回收算法

  1. 复制算法,copying,将内存区域分为两个区,每次使用其中1个,满了之后将还存活的对象复制到另一个区域,然后将当前区域清空。显然当存活对象比较多的时候,会进行较多的复制操作而影响性能,且由于分区的关系,内存使用存在一定的浪费。
  2. 标记清除算法, mark-sweep,内存区域满了之后,将存活对象和可回收对象标记出来,然后把可回收对象的对应的内存区域清空。这个算法的问题在于会产生比较多的内存碎片、导致分配大对象找不到可用的连续的内存空间。此外,就算没有复制操作,但是由于需要逐个的去释放可回收对象、需要一个个的计算释放内存空间的起始位置,所以此算法的效率也是比较低的。
  3. 标记整理算法, mark-compack,先标记,然后把存活对象移动到一起组成连续空间,之后把剩余部分内存空间清空。减少了内存碎片化。但由于存在复制移动、付出了一定性能开销。

分代收集策略

目前大部分JVM采用的收集算法策略,简单来说就是根据对象存活生命周期的长短将它们分类存放到不同的区域,比如年轻代和老年代,然后在不同的区域使用适合的不同的垃圾回收算法。

综上,根据不同的区域,不同生命周期长短的对象类型,选用适合的不同的垃圾回收算法,对性能的影响还是比较大的。
除了分代收集策略之外,还有一种策略叫做分区收集算法策略,即将堆分为多个小区域,每个小区域可以独立回收。根据计算出来的预估的stop the world时间,每次垃圾回收选择若干个小区域分别执行回收,这样的好处是使得每次stw的时间变得可控。

垃圾收集器

上面讲的都是垃圾回收的算法层面。具体到JVM实现,则是通过各种垃圾收集器来体现的。通过选用不同的垃圾收集器,JVM传达了自己对垃圾回收算法思想的选择。

如果对于收集器运作原理不太了解,手工优化存在困难的时候,使用Parallel Scavenge收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成将是一个不错的选择。只需要把基本的内存数据设置好(如-Xmx设置最大堆),然后使用MaxGCPauseMillis参数(更关注最大停顿时间)或GCTimeRatio(更关注吞吐量)参数给虚拟机设立一个优化目标,那具体细节参数的调节工作就由虚拟机完成了。
对于清除调优方向的高级技术人员来说,清晰明确可控的gc收集策略要比JVM自动调节所带来的方便要重要的多,且在一些特定Java应用程序时候JVM所自动调节没有手工调节更有效、比如典型无状态应用的新生代可以适当调大、survior适当调大、减少进入老年代对象场景。这些时候,我们需要关闭这个参数,-XX:-UseAdaptiveSizePolicy

总结:由于上述过程中最耗时的并发标记和并发清除步骤都是gc线程和用户线程并发工作的没有stw,所以总体上来看,CMS看起来就是垃圾回收和用户线程一起并发执行的一样。
这里值得提一句的是,在并发清除阶段,由于用户线程的并发、清理的同时也会不断产生垃圾,一旦这时候产生的垃圾占满了预留空间,CMS会出现所谓current failed问题,退化为serial old收集器,老老实实的stw然后进行清除。可以说一旦出了这种情况,那CMS性能会直线下降、反而得不偿失了。

先到这里,东西一下太多记不住慢慢来,休息一下泡杯茶~
这个blog里边的关于JVM的文章不错 主要能看出来作者是自己一线心得写出来的,不是纸上谈兵。

上一篇下一篇

猜你喜欢

热点阅读