前言
什么是垃圾
java中,内存运行时区域中的程序计数器、虚拟机栈、本地方法栈3个区域生命周期随着线程的生存而生存,而堆和方法区被各线程共享,这些占用空间而不被任何对象引用的对象,我们称之为垃圾(Garbage),而垃圾收集器(Garbage Collector)的工作即是通过一些列算法对这些垃圾进行清理。
分代收集算法
分代收集算法是基于JVM内存分代模型的一种算法,是目前大部分垃圾收集器都采用的算法,它的核心思想是根据对象存活的生命周期将内存划分为新生代和老年代,老年代的特点是每次垃圾回收时都只有少量的对象被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,可以根据不同的特点采取适合的回收算法。
一个对象产生到灭亡的过程
- 新产生的对象优先分配在Eden区。
- 当Eden区满了或放不下了进行GC,这时候其中存活的对象会复制到from区,如果from区放不下则会全部进入老年代,然后Eden内存全部清除。
- 之后产生的对象继续分配在Eden区,当Eden区又满了或放不下了,这时候将会把Eden区和from区存活下来的对象复制到to区(同理,如果存活下来的对象to区都放不下,则这些存活下来的对象全部进入年老代),之后回收掉Eden区和from区的所有内存。
- 如上这样,会有很多对象会被复制很多次(每复制一次,对象的年龄就+1),默认情况下,当对象被复制了15次(这个次数可以通过:-XX:MaxTenuringThreshold来配置),就会进入年老代了。
当年老代满了或者存放不下将要进入年老代的存活对象的时候,就会发生一次Full GC(这个是我们最需要减少的,因为耗时很严重)。
如何确定垃圾
哪些是垃圾?垃圾回收器进行垃圾回收前的第一个步骤就是确定哪些对象是存活的,哪些对象是被抛弃的,通过引用计数、根可达分析两种算法来确定。
引用计数
给对象添加一个引用计数器,每当一个对象引用时计数器+1,当引用失效时就-1,计数器为0时该对象就说明该对象不可用。
- 优点:实现简单、效率高
- 缺点:无法处理循环引用的对象,比如:a->b->c->a
根可达分析
以GC Roots为起点向下扫描,扫描所经过的路径称之为引用链,当一个对象不在引用链上,说明该对象不可用。
GC Roots定义:在JAVA语言中,存在虚拟机栈、本地方法栈、方法区、常量池等引用的对象。
- 优点:精准、严谨,可解决循环引用的对象
- 缺点:实现复杂,效率低。
如何回收垃圾
垃圾回收是在垃圾定位后的操作行为,常见的垃圾回收算法有:标记-清除、复制算法、标记压缩。
标记-清除
通过根可达算法标记被引用的对象即存活对象,未被标记的则为垃圾对象,然后对其清除。
其特点为:算法相对简单,需要扫描两遍空间(第一次 标记,第二次清除),清理后容易产生碎片。适用于存活对象多的情况(标记多,回收少),多为老年代。
复制算法
通过根可达算法标记所有存活的对象并将这些对象复制到另一块内存中,然后将之前的内存全部回收。
其特点为:需要一块空的内存去移动复制对象,调整对象的引用,只需扫描一次,清理后不会产生碎片,适用于存活对象少的情况(标记少,回收多),多为新生代。
标记压缩
与标记-清除算法相似,不同的是在清除后再将存活的对象整理压缩在一起。
其特点优化了标记清除算法的碎片,因为需要移动对象,效率较低。
如何使用垃圾回收器
如图所示,图中展示了7种不同分代的收集器,在实际虚拟机中都是搭配使用,以达到最佳效果。
收集器 | 描述 | 算法 |
---|---|---|
Serial | 新生代单线程收集器,标记和清理都是单线程。 | 复制算法 |
Serial Old | 老年代单线程收集器,Serial收集器的老年代版本。 | 标记-整理算法 |
ParNew | 新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现。 | 复制算法 |
Parallel Scavenge | 并行收集器,多个收集器线程同时工作,追求高吞吐量,高效利用CPU。 | 复制算法 |
Parallel Old | Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量优先 | 复制算法 |
CMS | 并发收集器,收集器线程与用户线程同时工作。高并发、低停顿,追求最短GC回收停顿时间,cpu占用比较高,响应时间快,停顿时间短,多核cpu 追求高响应时间的选择 | 三色标记+Incremental Update |
G1 | 并发收集器,不存在分代,适用于不需要实现很高的吞吐量的场景 | 三色标记 +SATB |
常用组合参数
- -XX:+UseSerialGC =Serial New +Serial Old
- -XX:+UseParNewGC =ParNew+Serial Old
- -XX:+UseConcMarkSweepGC =ParNew+CMS+Serial Old
- -XX:+UseParallelGC =Parallel Scavenge+Parallel Old
GC常用参数
- -Xmn(年轻代) -Xms(最小堆) -Xmx(最大堆) -Xss(栈空间)
- -XX:+UseTLAB
- -XX:+PrintTLAB
- -XX:+TLABSize
- -XX:+PrintGC
- -XX:+PrintGCDetails
- -XX:+PrintGCTimeStamps
- -verbose:class (类加载详细过程)
- -XX:+PrintFlagsFinal (最终参数). java -XX:+PrintFlagsFinal -version
- -XX:+PrintCommandLineFlags (默认参数) java -XX:+PrintCommandLineFlags -version
- -Xloggc:/path (GC日志路径)
- -XX:+MaxTenuringThreshold (升代年龄 最大15)
- -XX:+UseParallelGC
- -XX:SurvivorRatio
- -XX:+ParallelGCThreads (并行收集器的线程数,一般设为和CPU核数相同)
- -XX:+UseAdaptiveSizePoliy (自动选择各区大小)
- -XX:+UseConcMarkSweepGC
- -XX:ParallelCMSThreads
- -XX:CMSInitiatingOccupancyFraction (使用多少比例开始CMS收集,默认68%)
- -XX:+UseG1GC
- -XX:+MaxGCPauseMillis
- G1NewSizePercent (新生代最小比例,默认为5%)
- G1MaxNewSizePercent (新生代最大比例,默认为60%)