1、如何判断垃圾对象
垃圾收集的第一步就是先需要算法来标记哪些是垃圾,然后再对垃圾进行处理。通常的编程语言都会用以下算法之一进行判断:
- 引用计数(ReferenceCounting)算法
这种方法比较简单直观,核心思路是,给每个对象添加一个被引用计数器,被引用时+1,引用失效-1,等于0时就表示该对象没有被引用,可以被回收。
FlashPlayer/Python使用该算法,简单高效。但是,Java/C#并不采用该算法,因为该算法没有解决对象相互引用的问题,即:当两个对象相互引用且不被其它对象引用时,各自的引用计数为1,虽不为0,但仍然是可被回收的垃圾对象。
- 根搜索(GC Roots Tracing)算法
基本原理是:GCRoot对象作为起始点(根)。如果从根到某个对象是可达的,则该对象称为可达对象(存活对象,不可回收对象)。否则就是不可达对象,可以被回收。
2、新生代如何清理垃圾
垃圾清理算法
新生代的垃圾收集器通常会假设大部分的对象的存活时间都非常短,只有少数对象的存活时间比较长。根据这个假设,新生代清理垃圾的算法主要是复制算法(Copying)。通过复制算法,可以将没有被引用的对象清理掉,并且可以将经过若干次(可配置)清理仍然存活的对象放入老生代。
了解堆内存看这里:java堆内存是什么样的
了解堆的Copying算法看这里:[什么是新生代的Copying算法][3]
[3]:
垃圾清理触发方式
新生代采用“空闲指针”的方式来控制GC触发,指针保持最后一个在新生代分配的对象位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。新生代的GC通常叫做young GC,有时候也叫minor GC。
在连续分配对象过程中,对象会按照复制算法逐渐从Eden区到Survivor区,最后到老生代。
常用配置
- 新生代的大小:-Xmn
- Eden区和Survivor区的比值:-XX:SurvivorRatio
3、老生代如何清理垃圾
垃圾清理算法
老生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记/整理(也叫标记-紧凑,Mark-Compact)算法。
了解堆的 Mark算法看这里:[什么是老生代的Mark算法][4]
[4]:
垃圾清理触发方式
老生代的GC,通常叫做full GC,也叫major GC。老生代有多情况会触发GC,不过一般来说发生频率不高:
- 旧生代空间不足
调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在老生代创建对象。
- Pemanet Generation空间不足
增大Perm Gen空间,避免太多静态对象。
- GC后晋升到老生代的平均大小大于老生代剩余空间
控制好新生代和旧生代的比例。
- 手动调用System.gc()
垃圾回收不要手动触发,尽量依靠JVM自身的机制。
常用配置
- 堆的初始空间:-Xms,可以推算出老生代的大小为-Xms减去-Xmn
- 堆的最大空间:-Xmx
- 最大年龄阈值:-XX:MaxTenuringThreshold,即新生代转入老生代的存活次数
- 老生代和新生代的比值:-XX:NewRatio,例如该值为3,则表示新生代与老生代比值为1:3
4、垃圾回收方式有哪些
以上是java垃圾回收机制的基础,JVM为我们提供了若干可供选择的回收方式,即我们俗称的垃圾回收器。主要有4种:
- 串行垃圾回收器(Serial Garbage Collector)
- 并行垃圾回收器(Parallel Garbage Collector)
- 并发标记扫描垃圾回收器(CMS Garbage Collector)
- G1垃圾回收器(G1 Garbage Collector)
这里我们将 详细介绍说明各类垃圾回收器:[java垃圾回收都有哪些方式][5]
[5]: