概述
垃圾回收GC是Java非常重要的核心技术之一,Java开发程序员不需要关心对象的内存分配和资源释放,这些都由GC来完成,这使得Java开发者只需要将注意力集中再业务逻辑的处理上。
学习GC需要从以下4个方面入手:
1.如何判断某个对象是垃圾,需要被回收?
2.垃圾回收算法。
3.不同内存区域的回收方式
4.垃圾收集器的分类
如何判断对象是垃圾
Java对象被判定为垃圾的标准,没有被其他对象引用,判断方法有两种:
1.引用计数算法
通过判断对象的引用数量来决定是否要被回收,每一个对象实例都有一个计数器,被引用则+1,完成引用则-1.
什么是完成引用?
当改对象的引用超过了生命周期,或者引用指向了其他对象,在某方法中定义一个对象的引用变量,方法结束之后变量被虚拟机栈自动释放,则改对象的引用也就结束了,所以任何一个引用计数为0的对象是可以被当作垃圾回收的。
2.可达性分析算法
通过判断对象的引用链是否可达来决定对象是否要被回收,这个算法的基本思想就是通过一系列的称为GCRoot的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GCRoot没有任何引用链相连的话,则证明此对象是不可达的,即认为它不可用。如下图所示:
什么对象可以作为GCRoot?
1.虚拟机栈中的引用对象
2.方法区中的常量引用对象
3.方法区中的类静态属性引用对象
4.本地方法栈中的引用对象
5.活跃线程中的引用对象
垃圾回收算法
1.标记-清除算法
标记:从根集合进行扫描,对存活的对象进行标记
清除:对堆内存进行遍历,回收不可达对象内存
缺点:清除后会产生大量不连续的内存碎片,可能导致后续在创建较大对象是无法找到足够的连续内存而触发再一次的垃圾回收,如下图所示
2.复制算法
将可用内存分为对象面和空闲面,在对象,在对象面上创建对象,当对象面没有空间的时候,将还存活的对象复制到空闲面,将对象面所有对象清除。
解决了碎片化问题。顺序分配内存,简单高效,适用于对象存活率较低的场景,因为复制的内容少,所以效率高,如下图所示,
3.分析收集算法
是一种组合的回收机制,也是GC的主流回收算法,将不同的生命周期的对象分配到堆中的不同区域,采用不同的垃圾回收算法,提高JVM垃圾回收效率。
不同内存区域的回收方式
年轻代:
使用Minor GC进行回收,采用复制算法,年轻代分为Eden区和Survivor区。
Eden区:对象刚被创建的时候,存放在Eden区,如果Eden区放不下,则放在Survivor区,甚至老年代中。
Survivor区:Minor回收时使用,将Eden中存活的对象存入Survior中,再一次Minor时,将Survior From中的堆存入Survior To 中,清楚Survior From,下一次Minor时重复次步骤,
Survior From,依次循环,同时每次Minor,对象的年龄都+1,年龄增加到一定程度的对象,移动到老年代中。
老年代
存活生命周期较长的对象,使用标记-清除算法或者标记-整理算法进行回收。
垃圾收集器的分类
年轻代常见的垃圾收集器
1.Serial收集器(复制算法):单线程收集,进行垃圾收集时,必须暂停所有工作线程。
2.ParNow收集器(复制算法):多线程收集,垃圾收集和工作线程可同时执行
3.Parallel Scabenge收集器(复制算法):多线程收集,更关注系统的吞吐量。
老年代常见的垃圾收集器
1、Serial Old 收集器(标记-整理算法):单线程收集,进行垃圾收集时,必须暂停所有工作线程。
2、ParNew Old 收集器(标记-整理算法):多线程收集,垃圾收集和工作线程可同时执行,吞吐量优先。
3、CMS 收集器(标记-清除算法):垃圾回收线程和用户线程几乎可以同时工作。
4、Garbage First 收集器(复制+标记-整理算法):并发和并行,使用多个 CPU 来
缩短 Stop-the-World 的停顿时间,与用户线程并发执行,并且可采用不同的方式去处理新产生的对象。同时有利于空间整合,基于标记-整理算法,可以解决内存碎片的问题。
Stop-the-World:JVM 由于要执行 GC 而停止了应用程序的执行。
任何一种 GC 算法中都会发生,当 Stop-the-World 发生时,除了 GC 的线程以外, 所有线程都处于等待状态,直到 GC
任务完成,多数 GC 优化就是通过减少 Stop-the-World 发生的时间来提高程序性能。