Java GC系列(3):垃圾回收器种类

Java的内存分配与回收所有由JVM垃圾回收进程自动完成。与C语言不一样,Java开发者不须要本身编写代码实现垃圾回收。这是Java深受你们欢迎的众多特性之一,可以帮助程序员更好地编写Java程序。html

下面四篇教程是了解Java 垃圾回收(GC)的基础:java

  1. 垃圾回收简介
  2. 圾回收是如何工做的?
  3. 垃圾回收的类别
  4. 垃圾回收监视和分析

这篇教程是系列第一部分。首先会解释基本的术语,好比JDK、JVM、JRE和HotSpotVM。接着会介绍JVM结构和Java 堆内存结构。理解这些基础对于理解后面的垃圾回收知识很重要。程序员

Java关键术语

  • JavaAPI:一系列帮助开发者建立Java应用程序的封装好的库。
  • Java 开发工具包 (JDK):一系列工具帮助开发者建立Java应用程序。JDK包含工具编译、运行、打包、分发和监视Java应用程序。
  • Java 虚拟机(JVM):JVM是一个抽象的计算机结构。Java程序根据JVM的特性编写。JVM针对特定于操做系统而且能够将Java指令翻译成底层系统的指令并执行。JVM确保了Java的平台无关性。
  • Java 运行环境(JRE):JRE包含JVM实现和Java API。

Java HotSpot 虚拟机

每种JVM实现可能采用不一样的方法实现垃圾回收机制。在收购SUN以前,Oracle使用的是JRockit JVM,收购以后使用HotSpot JVM。目前Oracle拥有两种JVM实现而且一段时间后两个JVM实现会合二为一。算法

HotSpot JVM是目前Oracle SE平台标准核心组件的一部分。在这篇垃圾回收教程中,咱们将会了解基于HotSpot虚拟机的垃圾回收原则。shell

JVM体系结构

下面图片总结了JVM的关键组件。在JVM体系结构中,与垃圾回收相关的两个主要组件是堆内存和垃圾回收器。堆内存是内存数据区,用来保存运行时的对象实例。垃圾回收器也会在这里操做。如今咱们知道这些组件是如何在框架中工做的。编程

Java堆内存

咱们有必要了解堆内存在JVM内存模型的角色。在运行时,Java的实例被存放在堆内存区域。当一个对象再也不被引用时,知足条件就会从堆内存移除。在垃圾回收进程中,这些对象将会从堆内存移除而且内存空间被回收。堆内存如下三个主要区域:安全

  1. 新生代(Young Generation)
    • Eden空间(Eden space,任何实例都经过Eden空间进入运行时内存区域)
    • S0 Survivor空间(S0 Survivor space,存在时间长的实例将会从Eden空间移动到S0 Survivor空间)
    • S1 Survivor空间 (存在时间更长的实例将会从S0 Survivor空间移动到S1 Survivor空间)
  2. 老年代(Old Generation)实例将从S1提高到Tenured(终身代)
  3. 永久代(Permanent Generation)包含类、方法等细节的元信息

永久代空间在Java SE8特性中已经被移除。服务器

 

 

 

目录

  1. 垃圾回收介绍
  2. 垃圾回收是如何工做的?
  3. 垃圾回收的类别
  4. 垃圾回收监视和分析

本教程是为了理解基本的Java垃圾回收以及它是如何工做的。这是垃圾回收教程系列的第二部分。但愿你已经读过了第一部分:《Java 垃圾回收介绍》多线程

Java 垃圾回收是一项自动化的过程,用来管理程序所使用的运行时内存。经过这一自动化过程,JVM 解除了程序员在程序中分配和释放内存资源的开销。架构

启动Java垃圾回收

做为一个自动的过程,程序员不须要在代码中显示地启动垃圾回收过程。System.gc()Runtime.gc()用来请求JVM启动垃圾回收。

虽然这个请求机制提供给程序员一个启动 GC 过程的机会,可是启动由 JVM负责。JVM能够拒绝这个请求,因此并不保证这些调用都将执行垃圾回收。启动时机的选择由JVM决定,而且取决于堆内存中Eden区是否可用。JVM将这个选择留给了Java规范的实现,不一样实现具体使用的算法不尽相同。

毋庸置疑,咱们知道垃圾回收过程是不能被强制执行的。我刚刚发现了一个调用System.gc()有意义的场景。经过这篇文章了解一下适合调用System.gc() 这种极端状况。

Java垃圾回收过程

垃圾回收是一种回收无用内存空间并使其对将来实例可用的过程。

Eden 区:当一个实例被建立了,首先会被存储在堆内存年轻代的 Eden 区中。

注意:若是你不能理解这些词汇,我建议你阅读这篇 垃圾回收介绍 ,这篇教程详细地介绍了内存模型、JVM 架构以及这些术语。

Survivor 区(S0 和 S1):做为年轻代 GC(Minor GC)周期的一部分,存活的对象(仍然被引用的)从 Eden 区被移动到 Survivor 区的 S0 中。相似的,垃圾回收器会扫描 S0 而后将存活的实例移动到 S1 中。

(译注:此处不该该是Eden和S0中存活的都移到S1么,为何会先移到S0再从S0移到S1?)

死亡的实例(再也不被引用)被标记为垃圾回收。根据垃圾回收器(有四种经常使用的垃圾回收器,将在下一教程中介绍它们)选择的不一样,要么被标记的实例都会不停地从内存中移除,要么回收过程会在一个单独的进程中完成。

老年代: 老年代(Old or tenured generation)是堆内存中的第二块逻辑区。当垃圾回收器执行 Minor GC 周期时,在 S1 Survivor 区中的存活实例将会被晋升到老年代,而未被引用的对象被标记为回收。

老年代 GC(Major GC):相对于 Java 垃圾回收过程,老年代是实例生命周期的最后阶段。Major GC 扫描老年代的垃圾回收过程。若是实例再也不被引用,那么它们会被标记为回收,不然它们会继续留在老年代中。

内存碎片:一旦实例从堆内存中被删除,其位置就会变空而且可用于将来实例的分配。这些空出的空间将会使整个内存区域碎片化。为了实例的快速分配,须要进行碎片整理。基于垃圾回收器的不一样选择,回收的内存区域要么被不停地被整理,要么在一个单独的GC进程中完成。

垃圾回收中实例的终结

在释放一个实例和回收内存空间以前,Java 垃圾回收器会调用实例各自的 finalize() 方法,从而该实例有机会释放所持有的资源。虽然能够保证 finalize() 会在回收内存空间以前被调用,可是没有指定的顺序和时间。多个实例间的顺序是没法被预知,甚至可能会并行发生。程序不该该预先调整实例之间的顺序并使用 finalize() 方法回收资源。

  • 任何在 finalize过程当中未被捕获的异常会自动被忽略,而后该实例的 finalize 过程被取消。
  • JVM 规范中并无讨论关于弱引用的垃圾回收机制,也没有很明确的要求。具体的实现都由实现方决定。
  • 垃圾回收是由一个守护线程完成的。

对象何时符合垃圾回收的条件?

  • 全部实例都没有活动线程访问。
  • 没有被其余任何实例访问的循环引用实例。

Java 中有不一样的引用类型。判断实例是否符合垃圾收集的条件都依赖于它的引用类型。

引用类型 垃圾收集
强引用(Strong Reference) 不符合垃圾收集
软引用(Soft Reference) 垃圾收集可能会执行,但会做为最后的选择
弱引用(Weak Reference) 符合垃圾收集
虚引用(Phantom Reference) 符合垃圾收集

在编译过程当中做为一种优化技术,Java 编译器能选择给实例赋 null 值,从而标记实例为可回收。

1
2
3
4
5
6
7
8
9
10
class Animal {
     public static void main(String[] args) {
         Animal lion = new Animal();
         System.out.println( "Main is completed." );
     }
  
     protected void finalize() {
         System.out.println( "Rest in Peace!" );
     }
}

在上面的类中,lion 对象在实例化行后从未被使用过。所以 Java 编译器做为一种优化措施能够直接在实例化行后赋值lion = null。所以,即便在 SOP 输出以前, finalize 函数也可以打印出 'Rest in Peace!'。咱们不能证实这肯定会发生,由于它依赖JVM的实现方式和运行时使用的内存。然而,咱们还能学习到一点:若是编译器看到该实例在将来不再会被引用,可以选择并提前释放实例空间。

  • 关于对象何时符合垃圾回收有一个更好的例子。实例的全部属性能被存储在寄存器中,随后寄存器将被访问并读取内容。无一例外,这些值将被写回到实例中。虽然这些值在未来能被使用,这个实例仍然能被标记为符合垃圾回收。这是一个很经典的例子,不是吗?
  • 当被赋值为null时,这是很简单的一个符合垃圾回收的示例。固然,复杂的状况能够像上面的几点。这是由 JVM 实现者所作的选择。目的是留下尽量小的内存占用,加快响应速度,提升吞吐量。为了实现这一目标, JVM 的实现者能够选择一个更好的方案或算法在垃圾回收过程当中回收内存空间。
  • 当 finalize() 方法被调用时,JVM 会释放该线程上的全部同步锁。

GC Scope 示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Class GCScope {
     GCScope t;
     static int i = 1 ;
  
     public static void main(String args[]) {
         GCScope t1 = new GCScope();
         GCScope t2 = new GCScope();
         GCScope t3 = new GCScope();
  
         // No Object Is Eligible for GC
  
         t1.t = t2; // No Object Is Eligible for GC
         t2.t = t3; // No Object Is Eligible for GC
         t3.t = t1; // No Object Is Eligible for GC
  
         t1 = null ;
         // No Object Is Eligible for GC (t3.t still has a reference to t1)
  
         t2 = null ;
         // No Object Is Eligible for GC (t3.t.t still has a reference to t2)
  
         t3 = null ;
         // All the 3 Object Is Eligible for GC (None of them have a reference.
         // only the variable t of the objects are referring each other in a
         // rounded fashion forming the Island of objects with out any external
         // reference)
     }
  
     protected void finalize() {
         System.out.println( "Garbage collected from object" + i);
         i++;
     }
  
class GCScope {
     GCScope t;
     static int i = 1 ;
  
     public static void main(String args[]) {
         GCScope t1 = new GCScope();
         GCScope t2 = new GCScope();
         GCScope t3 = new GCScope();
  
         // 没有对象符合GC
         t1.t = t2; // 没有对象符合GC
         t2.t = t3; // 没有对象符合GC
         t3.t = t1; // 没有对象符合GC
  
         t1 = null ;
         // 没有对象符合GC (t3.t 仍然有一个到 t1 的引用)
  
         t2 = null ;
         // 没有对象符合GC (t3.t.t 仍然有一个到 t2 的引用)
  
         t3 = null ;
         // 全部三个对象都符合GC (它们中没有一个拥有引用。
         // 只有各对象的变量 t 还指向了彼此,
         // 造成了一个由对象组成的环形的岛,而没有任何外部的引用。)
     }
  
     protected void finalize() {
         System.out.println( "Garbage collected from object" + i);
         i++;
     }

GC OutOfMemoryError 的示例程序

GC并不保证内存溢出问题的安全性,粗心写下的代码会致使 OutOfMemoryError

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.LinkedList;
import java.util.List;
  
public class GC {
     public static void main(String[] main) {
         List l = new LinkedList();
         // Enter infinite loop which will add a String to the list: l on each
         // iteration.
         do {
             l.add( new String( "Hello, World" ));
         } while ( true );
     }
}

输出:

1
2
3
4
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
     at java.util.LinkedList.linkLast(LinkedList.java:142)
     at java.util.LinkedList.add(LinkedList.java:338)
     at com.javapapers.java.GCScope.main(GCScope.java:12)

接下来是垃圾收集系列教程的第三部分,咱们将会看到经常使用的 不一样 的Java垃圾收集器

 

 

 

目录
  1. 垃圾回收介绍
  2. 垃圾回收是如何工做的?
  3. 垃圾回收的类别
  4. 垃圾回收监视和分析

在这篇教程中咱们将学习几种现有的垃圾回收器。在Java中,垃圾回收是一个自动的进程能够替代程序员进行内存的分配与回收这些复杂的工做。这篇是垃圾回 收教程系列的第三篇,在前面的第2部分咱们看到了在Java中垃圾回收是如何工做的,那是篇有意思的文章,我推荐你去看一下。第一部分介绍了Java的垃 圾回收,主要有JVM体系结构,堆内存模型和一些Java术语。

Java有四种类型的垃圾回收器:

  1. 串行垃圾回收器(Serial Garbage Collector)
  2. 并行垃圾回收器(Parallel Garbage Collector)
  3. 并发标记扫描垃圾回收器(CMS Garbage Collector)
  4. G1垃圾回收器(G1 Garbage Collector)

每种类型都有本身的优点与劣势。重要的是,咱们编程的时候能够经过JVM选择垃圾回收器类型。咱们经过向JVM传递参数进行选择。每种类型在很大程度上有 所不一样而且能够为咱们提供彻底不一样的应用程序性能。理解每种类型的垃圾回收器而且根据应用程序选择进行正确的选择是很是重要的。

一、串行垃圾回收器

串行垃圾回收器经过持有应用程序全部的线程进行工做。它为单线程环境设计,只使用一个单独的线程进行垃圾回收,经过冻结全部应用程序线程进行工做,因此可能不适合服务器环境。它最适合的是简单的命令行程序。

经过JVM参数-XX:+UseSerialGC可使用串行垃圾回收器。

二、并行垃圾回收器

并行垃圾回收器也叫作 throughput collector 。它是JVM的默认垃圾回收器。与串行垃圾回收器不一样,它使用多线程进行垃圾回收。类似的是,它也会冻结全部的应用程序线程当执行垃圾回收的时候

三、并发标记扫描垃圾回收器

并发标记垃圾回收使用多线程扫描堆内存,标记须要清理的实例而且清理被标记过的实例。并发标记垃圾回收器只会在下面两种状况持有应用程序全部线程。

  1. 当标记的引用对象在tenured区域;
  2. 在进行垃圾回收的时候,堆内存的数据被并发的改变。

相比并行垃圾回收器,并发标记扫描垃圾回收器使用更多的CPU来确保程序的吞吐量。若是咱们能够为了更好的程序性能分配更多的CPU,那么并发标记上扫描垃圾回收器是更好的选择相比并发垃圾回收器。

经过JVM参数 XX:+USeParNewGC 打开并发标记扫描垃圾回收器。

四、G1垃圾回收器

G1垃圾回收器适用于堆内存很大的状况,他将堆内存分割成不一样的区域,而且并发的对其进行垃圾回收。G1也能够在回收内存以后对剩余的堆内存空间进行压缩。并发扫描标记垃圾回收器在STW状况下压缩内存。G1垃圾回收会优先选择第一块垃圾最多的区域

经过JVM参数 –XX:+UseG1GC 使用G1垃圾回收器

Java 8 的新特性

在使用G1垃圾回收器的时候,经过 JVM参数 -XX:+UseStringDeduplication 。 咱们能够经过删除重复的字符串,只保留一个char[]来优化堆内存。这个选择在Java 8 u 20被引入。

咱们给出了所有的四种Java垃圾回收器,须要根据应用场景,硬件性能和吞吐量需求来决定使用哪种。

垃圾回收的JVM配置

下面的JVM关键配置都与Java垃圾回收有关。

运行的垃圾回收器类型

配置 描述
-XX:+UseSerialGC 串行垃圾回收器
-XX:+UseParallelGC 并行垃圾回收器
-XX:+UseConcMarkSweepGC 并发标记扫描垃圾回收器
-XX:ParallelCMSThreads= 并发标记扫描垃圾回收器 =为使用的线程数量
-XX:+UseG1GC G1垃圾回收器

GC的优化配置

配置 描述
-Xms 初始化堆内存大小
-Xmx 堆内存最大值
-Xmn 新生代大小
-XX:PermSize 初始化永久代大小
-XX:MaxPermSize 永久代最大容量

使用JVM GC参数的例子

1
java -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar java-application.jar

在Java垃圾回收教程的下一部分,咱们将会用一个Java程序演示如何监视和分析垃圾回收。

 

 

 

目录

  1. 垃圾回收介绍
  2. 垃圾回收是如何工做的?
  3. 垃圾回收的类别
  4. 垃圾回收监视和分析

在这个Java GC系列教程中,让咱们学习用于垃圾回收监视和分析的工具。而后,选用一种工具来监视一个Java示例程序的垃圾回收过程。若是你是一名初学者,你最好仔细阅读该系列教程。你能够从这里(垃圾回收介绍)开始。

Java GC监视和分析工具

下面是一些可用的工具,每一个都有本身的优点和缺点。咱们能够经过选择正确的工具并分析,来提高应用程序的性能。这篇教程中,咱们选用Java VisualVM。

  • Java VisualVM
  • Naarad
  • GCViewer
  • IBM Pattern Modeling and Analysis Tool for Java Garbage Collector
  • HPjmeter
  • IBM Monitoring and Diagnostic Tools for Java-Garbage Collection and Memory
  • Visualizer
  • Verbose GC Analyzer

Java VisualVM

Java VisualVM使用是免费的,其须要安装Java SE SDK。看一下Java JDK的bin文件夹中(路径:\Java\jdk1.8.0\bin),这里面有不少javac和java工具,jvisualvm就是其中之一。

Java VisualVM可以被用于:

  •   生成并分析堆的内存转储;
  •   在MBeans上观察并操做;
  •   监视垃圾回收;
  •   内存和CPU性能分析;

一、启动VisualVM

jvisualvm位于JDK bin文件夹下,直接点击就能够。

二、安装可视化GC插件

咱们须要安装可视化GC插件,以便在Java GC过程当中有良好的视觉感觉。

三、监视GC

如今,是时候监视垃圾回收进程了,开启你的Java程序,它将自动被检测到并显示到Java VisualVM界面,左侧“Application”(应用程序)窗口下,“Local”(本地节点)下,全部本地运行的Java程序都会被列出。

Java VisualVM是一个Java应用程序,所以它也会被列在其中,教程的意图在于使用VisualVM来监视它本身的GC进程。

双击“Local”(本地)下的VisualVM图标。

如今,程序监控窗口在右侧打开,这有许多不一样关于应用程序性能的相关监视指数的tab页,目前为止,咱们最感兴趣的是“Visual GC”,点击它。

上面图片显示在Old、Eden、S0和S1上空间利用状况,下图显示了每部分空间的分配和释放状况。它按照指定的刷新率保持持续刷新。

上面图片所展现的是正常运行程序的状况,当出现内存泄露或者反常的行为时,它会在图表中明确的显示出来。最少咱们能理解他是与对象内存分配和垃圾回收相关的事情。随后,经过其余tab页(像“Threads”)和Thread Dump的帮助,咱们可以减小这个问题。

在“Monitor”tab页中,咱们可以监控并定时展现全部堆内存使用状况图。经过“Perform GC”按钮能够启动垃圾回收进程。

在“Sampler”tab页中,咱们可以启动内存和CPU性能分析,它将显示详细每一个实例使用的实时报告,它将帮助咱们明确性能问题。

这篇教程是咱们四篇Java垃圾回收系列教程的最后一篇。

http://www.importnew.com/13838.html

相关文章
相关标签/搜索