JVM系列-02-GC-扫盲

2016-12-18  本文已影响36人  hylexus

[TOC]

声明

本篇文章是本人阅读《深入理解JVM》和《java虚拟机规范》时的笔记。
记录的都是一些概念性的东西。
JVM是HotSpot,jdk1.7。
大神绕路,不喜勿喷。

1 GC算法

先来走马观花般地浏览一些著名的GC算法。
这里也仅仅是说一下大致过程,具体细节的介绍对于我一个Java程序员来说表示无能为力,因为底层实现要牵扯到具体的实现语言了,而且不同的JVM实现商肯定有不同的实现细节。

1.1 标记/清除算法

这种算法的大概过程是:

这种算法很直观,但他的缺点如下:

1.2 复制算法

上面说的标记/清除算法不太好的主要原因就是其回收粒度太过细微了。
签于此,复制算法的主要做法是:

这种算法相比于标记/清除算法的最大特点是:

1.3 标记/整理算法

上面说的复制算法的最大缺点就是对象的复制操作。尤其是在有效的对象很多的情况下。

这里的标记/整理算法的大致过程是:

1.4 分代收集算法(Generational Collection)

既然上面说集中算法都各有优劣,那么根据他们各自的优点,在不同的情况下使用最优的算法会不会更好呢?

分代收集的大致思路就是这样的:

2 GC的代价——Stop The World

上面说了一大堆GC的理论。但是忽略了一点:

怎么确定哪些对象或内存区域是可以被回收的呢???

在java中对于对象是否还“活着”,采用的不是像Python或者其他语言中的"引用计数"的方法。
java中采用的是"可达性分析"。
至于可达性分析的细节没必要去深究,但是由"判断对象是否还存活?"引出的另一个问题却不得不考虑,看下文。

无论采用什么方法去区分哪些对象还活着,不得不做的一个让步就是:这个判定过程中必须暂时让其他所有的线程都暂时停顿,这个现象对于JVM中的各个对象来说就相当于整个世界停止了。也就是所谓的Stop The World

这个停顿当然是有必要的,比如你开始分析对象的存活状态时一个对象是无用的,当你分析完成后那个对象却让其他线程操作了变成有效对象了。

所以,在整个判断过程中,要能够确保一致性。也就免不了Stop The World

当然,应用的规模越大,Stop The World带来的影响越大。
所以,频繁的GC也不见得是好事。

3 垃圾收集器

上面说的都是GC的大致理论知识,现在看看GC的实现:垃圾收集器。

3.1 Serial收集器

Serial收集器是众多垃圾收集器中的元老。是一个单线程的收集器。在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop The World)。
虽然它的出现非常早,但是它依然是虚拟机运行在Client模式下的默认新生代收集器,也有其独特的优点:

3.2 ParNew收集器

这个ParNew的介绍是来自《深入理解JVM》的作者说的,与本人没任何关系 _ .. _

ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、 -XX:PretenureSizeThreshold、 -XX:HandlePromotionFailure等)、 收集算法、 Stop The World、 对象分配规则、 回收策略等都与Serial收集器完全一样,在实现上,这两种收集器也共用了相当多的代码。

3.3 Parallel Scavenge收集器

3.3.1 简介

他的特点如下:

3.3.2 参数

3.4 Serial Old收集器

3.5 Parallel Old收集器

3.6 CMS收集器

他的缺点如下:

3.7 G1收集器

《深入理解JVM》一书是这么说的:

在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,而G1不再是这样。 使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。

4 GC日志

GC日志的格式乍看起来乱七八糟,乌漆嘛黑的。当然他肯定是有格式的。就拿《深入理解JVM》中的这段代码来说吧:

public class ReferenceCountingGC {
    public Object instance = null;
    private static final int _1MB = 1024 * 1024;
    /**
     * 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
     */
    byte[] bigSize = new byte[2 * _1MB];

    public static void main(String[] args) {
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new ReferenceCountingGC();
        objA.instance = objB;
        objB.instance = objA;
        objA = null;
        objB = null;
        // 假设在这行发生GC,objA和objB是否能被回收?
        System.gc();
    }
}

虚拟机参数:

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps

在我的机器(jdk1.7)上输出如下:

2016-12-17T16:11:19.650+0800: 0.093: [GC [PSYoungGen: 5427K->568K(38400K)] 5427K->568K(124416K), 0.0016819 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2016-12-17T16:11:19.652+0800: 0.095: [Full GC [PSYoungGen: 568K->0K(38400K)] [ParOldGen: 0K->463K(86016K)] 568K->463K(124416K) [PSPermGen: 2514K->2513K(21504K)], 0.0109008 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 38400K, used 998K [0x00000007d5c80000, 0x00000007d8700000, 0x0000000800000000)
  eden space 33280K, 3% used [0x00000007d5c80000,0x00000007d5d79a60,0x00000007d7d00000)
  from space 5120K, 0% used [0x00000007d7d00000,0x00000007d7d00000,0x00000007d8200000)
  to   space 5120K, 0% used [0x00000007d8200000,0x00000007d8200000,0x00000007d8700000)
 ParOldGen       total 86016K, used 463K [0x0000000781600000, 0x0000000786a00000, 0x00000007d5c80000)
  object space 86016K, 0% used [0x0000000781600000,0x0000000781673eb0,0x0000000786a00000)
 PSPermGen       total 21504K, used 2520K [0x000000077c400000, 0x000000077d900000, 0x0000000781600000)
  object space 21504K, 11% used [0x000000077c400000,0x000000077c676178,0x000000077d900000)

解释如下:

2016-12-17T16:11:19.650+0800
    -XX:+PrintGCDateStamps的作用,就是GC的时间了
0.093:表示的从JVM启动以来经过的秒数
GC [PSYoungGen:....
    GC发生的区域
        PSYoungGen表示采用的收集器为Parallel Scavenge
        如果使用的是Serial收集器,新生代名为“Default New Generation”,显示就是“[DefNew”
        如果使用的是ParNew收集器,新生代名称为“[ParNew”,意为“Parallel New Generation”
        如果采用的是Parallel Scavenge收集器,新生代名称就是“PSYoungGen”
“Full”,说明这次GC是发生了Stop-The-World

GC日志,暂时就先写这么多吧,在后续的文章中再详细介绍GC日志。

5 和GC相关的JVM参数

注:以下参数总结来自《深入理解JVM》一书

6 Minor GC 和 Full GC/Major GC

参考文章

上一篇下一篇

猜你喜欢

热点阅读