Java - GC

什么是GC

Garbage Collection,垃圾回收,就是通过某些算法,将不再使用,不需要的内存空间释放。

主要分为几个步骤:

在C语言中,GC是需要程序员手动进行的(malloc free),十分繁琐。而在Java,Python等语言中,GC是由程序自动执行的,减轻了程序员的负担。

为什么要做GC

什么是Garbage

在Java中,如果一个(或者一群)对象没有其它的对象引用到它(们),就是垃圾。

怎么找到Garbage

注:HotSpot采用的是Root Searching。

根可达算法进阶

程序运行后,它在内存中的状态可以看成是有向图,分为三种:

java-gc-state.png

可以作为 GC ROOTS 的对象包括下面几种:

GC的三种算法

JVM内存分代模型

内存分代模型是部分JVM(如Hotspot)使用的模型

新生代 + 老年代 + 永久代(1.7) PermGen space / 元数据区(1.8) Metaspace

java-gc-memory

注:MinorGC = YGC, MajorGC = FGC

新生代

组成:Eden + 2个suvivor区

老年代

GC流程

首先,关于一个概念:应用程序停止,stop-the-world:除了GC所需的线程以外,所有线程都处于等待状态,直到GC完成。它会发生在任何一个GC算法中,不同的算法,不同的优化策略,一定程度上就是减少系统等待时间。

java-gc-process

Escape Analysis

特别注意:在JDK1.6以后,当new一个新的object之后,JVM会做一个Escape Analysis,判断是否可以将这个obj分配到栈Stack上,如果不可以,再放在堆Heap中。 当然,不是所有obj都可以放到栈上的,是要满足特定条件才行。 这样带来的最大好处是:GC的效率特别高。

举例分析:每次调用Point::toString方法,就要创建一个StringBuilder对象,而当方法调用完了之后,这个StringBuilder对象就没用了。 把StringBuilder放到栈中,用完就弹出,无疑高效了许多。(E.A.能大大优化函数式编程Lambdas的执行效率)

public class Point {
    private final int x, y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder()
                .append("(")
                .append(x)
                .append(", ")
                .append(y)
                .append(")");
        return sb.toString();
    }
}

JVM垃圾回收器的类型

垃圾回收器的发展是随着内存不断扩大而不断演进的。 一开始,使用的是串行策略,慢慢地,变成了并行策略。

根据JVM内存分代模型,新生代和老年代采用了不同的垃圾回收器。具体如下:

Serial + SerialOld

ParallelScavenge + ParallelOld

ParNew + CMS(Concurrent Mark & Sweep) / G1 (Garbage First)

1.8默认的垃圾回收:PS + ParallelOld

java-gc-collector

可能发生内存泄露的情况

即使有了GC机制,还是可能发生内存泄露问题,如下:

JVM GC 调优

JVM参数分类

查看参数命令

-XX:+PrintCommandLineFlags
-XX:+PrintFlagsFinal 最终参数值
-XX:+PrintFlagsInitial 默认参数值

GC常用参数

了解生产环境下的垃圾回收器组合: java -XX:+PrintFlagsInitial | grep GC | grep true

开源工具:Arthas

产生了CPU爆增加的问题,如何定位?

注:JVM的命令行参数参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

Fork me on GitHub