本文由 ImportNew - 伍翀 翻译自 javapapers

目录

  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垃圾收集器

原文链接: javapapers 翻译: ImportNew.com伍翀
译文链接: http://www.importnew.com/13493.html

Java GC系列(2):Java垃圾回收是如何工作的?的更多相关文章

  1. Java GC系列(4):垃圾回收监视和分析

    本文由 ImportNew - lomoxy 翻译自 javapapers. 目录 垃圾回收介绍 垃圾回收是如何工作的? 垃圾回收的类别 垃圾回收监视和分析 在这个Java GC系列教程中,让我们学习 ...

  2. Java GC系列(1):Java垃圾回收简介

    本文由 ImportNew - 好好先生 翻译自 javapapers. Java的内存分配与回收全部由JVM垃圾回收进程自动完成.与C语言不同,Java开发者不需要自己编写代码实现垃圾回收.这是Ja ...

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

    本文由 ImportNew - 好好先生 翻译自 javapapers. 目录 垃圾回收介绍 垃圾回收是如何工作的? 垃圾回收的类别 垃圾回收监视和分析 在这篇教程中我们将学习几种现有的垃圾回收器.在 ...

  4. Java虚拟机内存模型及垃圾回收监控调优

    Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存 ...

  5. Java虚拟机学习笔记——JVM垃圾回收机制

    Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...

  6. Java中内存泄露及垃圾回收机制

    转自:http://blog.sina.com.cn/s/blog_538b279a0100098d.html 写的相当不错滴...................... 摘  要 Java语言中,内 ...

  7. Java垃圾回收机制的工作原理

    Java垃圾回收机制的工作原理 [博主]高瑞林 [博客地址]http://www.cnblogs.com/grl214 获取更多内容,请关注小编个人微信公众平台: 一.Java中引入垃圾回收机制的作用 ...

  8. [译]Java垃圾回收是如何工作的

    说明:这篇文章来翻译来自于Javapapers 的How Java Garbage Collection Works 这部分教程是为了理解Java垃圾回收的基础以及它是如何工作的.这是垃圾回收系列教程 ...

  9. [改善Java代码]不要主动进行垃圾回收

    建议51: 不要主动进行垃圾回收 很久很久以前,在Java 1.1的年代里,我们经常会看到System.gc这样的调用—主动对垃圾进行回收.不过,在Java知识深入人心后,这样的代码就逐渐销声匿迹了— ...

随机推荐

  1. cocos2dx 实现flappybird

    前两天在博客园看到网友实现的一个网页版的flappy bird,挂在360游戏平台,玩了一会儿得分超低,就很想自己做一个.刚好这两天炫舞的活都清了,就弄一下玩玩. 效果图 布局类GameScene.h ...

  2. 15款优雅的 WordPress 电子商务网站主题

    WordPress 电子商务网站主题今年非常流行,特别是对那些想要在几分钟内创建一个在线商店,但又没有掌握网络开发的很多知识的人来说.WordPress 是一个功能强大的 CMS,它的灵活性和可用性是 ...

  3. Mono addin 学习笔记 5 TypeExtensionPoint

    1. Attribute声明方式 定义扩展点: [TypeExtensionPoint]public interface ICommand{        void Run();} 定义扩展: [Ex ...

  4. css3 绘制优惠券

    今天偶然发现了一个css3制作动画的地方,发现css3的径向渐变好难理解,幸亏有这里的大神介绍http://www.daqianduan.com/5989.html,这是优惠券的介绍 还有这个http ...

  5. File Manager文件管理应用android源码

    这个刚刚在安卓教程网那里看到的,File Manager文件管理应用android源码,这个是File Manager文件管理应用源码,源码filemanager,一个开源的文件管理器完整源码,文件查 ...

  6. linux命令useradd添加用户详解

    1.作用 useradd或adduser命令用来建立用户帐号和创建用户的起始目录,使用权限是超级用户. 2.格式 useradd [-d home] [-s shell] [-c comment] [ ...

  7. JSP内置标签 JSP中JavaBean标签 JSP开发模式 EL和JSTL快速入门

    2 JSP内置标签(美化+业务逻辑)   1)为了取代<%%>脚本形式,使用JSP标签/JSP动作,目的:与JSP页面的美化,即JSP面页都是由标签组成,不再有其它的内容   2)JSP内 ...

  8. ASP.NET Web Forms 4.5的新特性

    作者:Parry出处:http://www.cnblogs.com/parry/ 一.强类型数据控件 在出现强类型数据控件前,我们绑定数据控件时,前台一般使用Eval或者DataBinder.Eval ...

  9. JavaScript设计模式接口

    JavaScript中实现接口的方法有三种: 第一种,使用注释的方法实现接口 特点:(1)最简单,但是功能最弱(2)利用 interface和 implement"文字"(3)把他 ...

  10. Linux系统Shell脚本编程

    1. shell脚本概念:C语言编写的.命令解释器.编程语言. 是用户使用linux的桥梁. shell脚本语言非常擅长处理文本类型的数据. 2. shell脚本作用:自动化管理.监控管理.日志数据处 ...