Overview

  • 垃圾收集考虑三件事:
    1. 哪些内存需要回收?
    2. 什么时候回收?
    3. 如何回收?
  • 重点考虑Java堆中动态分配和回收的内存。

Is Object alive?

引用计数法

  • 给对象添加一个引用计数器。
  • 该方法实现简单,判定效率高。但是它很难解决对象之间相互循环引用的问题,因此几乎很少有JVM选用该方法。eg:
    public class ReferenceCountingGC {
    public Object instance = null;
    // 占点内存,以便在GC日志中看清楚是否被回收过
    private static final int _1MB = 1024 * 1024;
    private byte[] bigSize = new byte[2 * _1MB]; public static void testGC() {
    ReferenceCountingGC objA = new ReferenceCountingGC();
    ReferenceCountingGC objB = new ReferenceCountingGC();
    objA.instance = objB;
    objB.instance = objA;
    objA = null;
    objB = null; System.gc();
    }
    }

可达性分析算法

  • 在主流商用程序语言(Java, C#, Lisp)的主流实现中,都是通过可达性分析(Reachability Analysis)来判断对象是否存活的。
  • 该算法的基本思路是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索。搜索的路径称为引用链(Reference Chain)。
  • 当一个对象到GC Roots没有任何引用链相连(即不可达)时,则该对象是不可用的。不可达的对象是可回收的。如下:
  • 在Java中,可作为GC Roots的对象包括下面几种:
    • 虚拟机栈(栈帧中的本地变量表)中的引用;
    • 方法区中类静态属性引用的对象;
    • 方法区中常量引用的对象;
    • 本地方法栈中JNI引用的对象。

再谈引用

  • 上面两种方法都是通过“引用”来判定对象是否alive。
  • 但是在JDK1.2之前,Java中的引用定义很传统:若reference类型的数据中存储的数值是另一块内存的起始地址,则称这块内存代表着一个引用。
  • 但是考虑这样的场景:我们希望描述这样的一类对象,当内存空间足够时,则能保留在内存中,否则可抛弃。 <-- 很多系统的缓存功能都符合这样的应用场景。
  • 于是在JDK1.2之后,Java扩充了引用的概念:将引用分为强引用(Strong Reference), 软引用(Soft Reference), 弱引用(Weak Reference), 虚引用(Phantom Reference) 4种,强度依次减弱。
    • 强引用:最常见的,类似Object obj = new Object()
    • 软引用:用以描述一些还有用但是非必须的对象。对于该类对象,系统在将要发生内存溢出异常之前,会回收这些对象。JDK1.2之后提供了SoftReference类。
    • 弱引用:也是用以描述非必须对象,但强度更弱于软引用。被弱引用关联的对象只能生存到下一次GC之前,而无论当前内存是否足够。
    • 虚引用:PhantomReference类。

生存还是死亡

  • 即使是在可达性分析算法中不可达的对象,也并非“非死不可”的。
  • 要真正宣告一个对象死亡,至少要经历两次标记过程:TBD...

回收方法区

  • 一般来说,在方法区进行垃圾收集的"性价比"比较低:在堆中,尤其是新生代中,常规应用一次垃圾收集一般可以回收70%~95%的空间,而永久代的垃圾收集效率远低于此。
  • 永久代的垃圾收集主要回收两部分:废弃常量和无用的类。
  • 回收废弃常量与回收Java堆中的对象非常相似。假设一个字符串"abc"已经进入常量池,但当前系统中没有任何一个String对象引用常量池中的"abc"常量,则"abc"常量就会被系统清理出常量池。
  • 相比之下判定一个类是否是“无用的类”的条件则相对苛刻很多。需同时满足:
    • 该类所有的实例已经被回收;
    • 加载该类的ClassLoader已经被回收;
    • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

垃圾收集算法

标记-清除算法

  • 算法分为“标记”和“清除”两个阶段。
  • 该算法是后续收集算法的基础。
  • 不足:
    • 效率问题:标记和清除两个过程的效率都不高;
    • 空间问题:标记清除后会产生大量不连续的内存碎片,空间碎片太多可能导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次GC。

  

复制算法

  • 它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。
  • 当使用的块用完时,就将还存活着的对象复制到另一块上面,然后再将已使用过的内存空间一次清理掉。
  • 这样使得每次都是对整个半区进行内存回收,内存分配时也不用考虑内存碎片等情况,只用移动堆顶指针,顺序分配即可。
  • 代价是内存缩小为原来的一半。
  • 现在的商业JVM都采用这种收集算法。IBM的专门研究表明,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1:1的比例划分内存空间。而是将内存分为一块较大的Eden空间和两块较小的Survivor空间。

标记-整理算法

  • 复制收集算法在对象存活率较高时就需要较多的复制操作,效率将会变低。更关键的是,若不想浪费一般的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以老年代一般不能采用这种算法。
  • 标记-整理算法:标记过程仍同于标记-清除算法。但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端外边的内存。

分代收集算法

  • 当前商业JVM的GC都采用“分代收集”(Generational Collection)算法。
  • 这种算法就是根据对象存货周期地不同将内存划分为几块。
  • 一般是把Java堆分为新声代和老生代,从而根据各个年代的特点采用最适当的收集算法。

Summary

  • 本篇主要覆盖了GC的两大问题:

    • 哪些对象需要回收:最基础的是引用计数法,简单但无法解决循环引用问题。在此基础上,更常用的是可达性分析算法。
    • 如何回收:最基础的是标记-清除法,即先标记出哪些对象需要回收,然后逐一清除,不足是会产生大量内存碎片,从而导致后续可能触发多次GC。此外更高效的是复制法,该方法确保了连续内存,但是将内存缩小至原内存一般,并且在对象存活率较高时会引入大量复制操作,效率降低。还有标记-整理算法。 另外,商业JVM都会采用分代收集算法,即将内存划分为几块,根据年代选取合适的收集算法。

[深入理解Java虚拟机]<垃圾收集器与内存分配策略>的更多相关文章

  1. 《深入理解Java虚拟机》虚拟机性能监控与故障处理工具

    上节学习回顾 从课本章节划分,<垃圾收集器>和<内存分配策略>这两篇随笔同属一章节,主要是从理论+实验的手段来讲解JVM的内存处理机制.好让我们对JVM运行机制有一个良好的概念 ...

  2. 《深入理解 java虚拟机》学习笔记

    java内存区域详解 以下内容参考自<深入理解 java虚拟机 JVM高级特性与最佳实践>,其中图片大多取自网络与本书,以供学习和参考.

  3. (1) 深入理解Java虚拟机到底是什么?

    好文转载:http://blog.csdn.net/zhangjg_blog/article/details/20380971 什么是Java虚拟机   作为一个Java程序员,我们每天都在写Java ...

  4. 深入理解java虚拟机(7)---线程安全 & 锁优化

    关于线程安全的话题,足可以使用一本书来讲解这些东西.<Java Concurrency in Practice> 就是讲解这些的,在这里 主要还是分析JVM中关于线程安全这块的内容. 1. ...

  5. 深入理解java虚拟机(6)---内存模型与线程 & Volatile

    其实关于线程的使用,之前已经写过博客讲解过这部分的内容: http://www.cnblogs.com/deman/category/621531.html JVM里面关于多线程的部分,主要是多线程是 ...

  6. 深入理解java虚拟机(5)---字节码执行引擎

    字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...

  7. 深入理解java虚拟机(4)---类加载机制

    类加载的过程包括: 加载class到内存,数据校验,转换和解析,初始化,使用using和卸载unloading过程. 除了解析阶段,其他过程的顺序是固定的.解析可以放在初始化之后,目的就是为了支持动态 ...

  8. 深入理解java虚拟机(1)------内存区域与内存溢出

    在C++领域,关于C++的内存存储,结构等等,有一本书:深度探索C++对象模型,讲解的非常透彻. 而Java确把这一工作交给了虚拟机来处理. 我们首先来看看关于内存的问题. 1.问题: 1)java ...

  9. 什么是HotSpot VM & 深入理解Java虚拟机

    参考 http://book.2cto.com/201306/25434.html 另外,这篇文章也是从一个系列中得出的: <深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)> ...

  10. 【Todo】深入理解Java虚拟机 读书笔记

    有一个在线系列地址 <深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)> http://book.2cto.com/201306/25426.html 已经下载了这本书(60多M ...

随机推荐

  1. ubuntu opencv

    sudo apt-get updatesudo apt-get upgrade sudo apt-get install build-essential libgtk2.0-dev libjpeg-d ...

  2. Find the duplicate Number (鸽巢原理) leetcode java

    问题描述: Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive ...

  3. Laravel中APP_KEY起什么作用

    框架中是这样描述的: This key is used by the Illuminate encrypter service and should be set to a random, 32 ch ...

  4. 先天性肾上腺增生症(ACH)

    先天性肾上腺增生症 类型 症状 并发症 治疗 情感支持 产前筛查 预防 什么是先天性肾上腺皮质增生症? 先天性肾上腺增生症(CAH)是一组影响肾上腺的遗传性疾病.肾上腺产生激素皮质醇和醛固酮.CAH是 ...

  5. 创建springboot的聚合工程(一)

    比起传统复杂的单体工程,使用Maven的多模块配置,可以帮助项目划分模块,鼓励重用,防止POM变得过于庞大,方便某个模块的构建,而不用每次都构建整个项目,并且使得针对某个模块的特殊控制更为方便.接下来 ...

  6. BCS SET EMAIL

    FUNCTION zcrm_send_email. *"------------------------------------------------------------------- ...

  7. Excel中如何匹配另外一个Excel中的数据

    场景: 我在Excel中想展示通过一列匹配到另外Excel中的数据.对于程序员来说,就是left join 出 B表的数据. 但是在Excel中怎么做呢,我又不想每次都在把数据导入到数据库中操作. 这 ...

  8. CP-ABE ToolKit 安装笔记(转载)

    博主论文狗,好久没有来贴博客,最近做实验需要用到属性加密,了解了下CP-ABE,前来记录一下: 网上相关的博文较多,博主看了大部分的,认为下面这两个看完了基本就可以成功安装. 可参见博文: http: ...

  9. Redis在CentOS7中的启动警告

    CentOS7安装Redis,启动时会出现如下图3个警告. 问题1:WARNING: The TCP backlog setting of 511 cannot be enforced because ...

  10. CAFFE 调试

    在Make.config 文件里将DEBUG=1的注释去掉,再make.可以用IDE如eclipse来import makefile工程.必要时按照IDE的提示将源文件cpp和对应的bin文件对应.