垃圾回收要解决的问题:

  1. 哪些内存需要回收?

线程私有区域不需要回收,如PC、Stack、Native Stack;Java 堆和方法区需要

  1. 什么时候回收?

以后的文章解答

  1. 如何回收?

首先进行对象存活性的分析,然后利用GC回收算法进行回收,具体算法请看下文。

如何判断对象是否可以回收?

有两种方式:引用计数算法和可达性分析算法,目前主流商业JVM普遍采用可达性分析算法

引用计数算法

引用计数算法顾名思义,为对象的引用计数,每当有一地方引用它时,计数器加1,引用失效(离开作用域时)减1,当计数器值为0时,对象就可以被回收了。引用计数算法优点是判定效率高,缺点是无法解决对象互相引用的问题,使用此技术进行内存管理的技术有微软的COM、Flash、Python等

可达性分析算法

可达性分析算法,基本思路是以GC Roots为根、向下搜索,所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链可达时,就成为此对象不可用,即可以被回收。注意再注意:该算法的本质是通过找出所有活对象来把其余空间认定为“无用”,而不是找出所有死掉的对象并回收它们占用的空间。分代式GC是一种部分收集(partial collection)的做法。在执行部分收集时,从GC堆的非收集部分指向收集部分的引用,也必须作为GC roots的一部分。具体到分两代的分代式GC来说,如果第0代叫做young gen,第1代叫做old gen,那么如果有minor GC / young GC只收集young gen里的垃圾,则young gen属于“收集部分”,而old gen属于“非收集部分”,那么从old gen指向young gen的引用就必须作为minor GC / young GC的GC roots的一部分。

可以作为GC Roots的对象包括:

  1. 虚拟机栈 VM Stack 中局部变量表(Local Variables)中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 所有当前被加载的Java类
  5. Java类的运行时常量池里的引用类型常量(String或Class类型)
  6. String常量池(StringTable)里的引用
  7. VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。
  8. 本地方法栈中JNI(Native方法)引用的对象

对象引用类型的不同对存活周期的影响:

JDK1.2之后,引用概念得到了扩展,分为强引用Strong、软引用Soft、弱引用Weak、虚引用Phantom

  1. 强引用:一般未指定的,都是强引用,包括Object o = new Object(),只要满足存活算法,就不会被回收
  2. 软引用:表示有用但非必须的对象,这些对象在系统将要发生溢出异常之前,进行一次回收,如果回收到还没有足够的空间,再抛出异常。
  3. 弱引用:其关联的对象只能生存到下一次垃圾回收之前。
  4. 虚引用:不会影响对象的生存时间,也就是说生存时间与强引用一样,设置虚引用的目的是得到垃圾回收的通知

生存还是死亡?

存活算法之后,不可达的对象被标记,并进行一次筛选,筛选的条件是“是否有必要执行finalize”,当对象没有覆盖此方法或此方法已经被调用过,都将视为没有必要执行,如果有必要执行,该对象将被放在一个称为F--Queue的队列中,并被一个虚拟机建立的线程去执行finalize方法,如果在方法中该对象成功建立了与GC Roots的引用链,他就可以从待回收列表中移除,并继续存活。

回收方法区

方法区采用永久代实现,相比堆,尤其是新生代,方法区回收的性价比极低,永久代的垃圾回收主要回收废弃常量和无用的类,参数-verbose:class可以查看类加载和卸载情况

垃圾回收算法

标记-清除算法Mark Sweep

先标记,再删除,缺点是空间利用率低,清除之后会产生大量不连续的内存碎片,浪费内存空间。

复制算法

将内存分为使用和待用两部分,当使用部分快满时,将还存活的对象复制到另外待用部分,然后堆整个使用区域进行回收,优点是回收不会出现内存碎片、缺点是浪费了待用区域的内存空间。商业虚拟机以这种算法为基础进行了优化,以HotSpot为例,将内存分为较大的Eden空间和两个较小的Survivor空间,比例默认是8:1:1,可以通过参数调节,每次使用Eden和一块Survivor空间,回收时,将这两个空间存活的对象复制到剩余的一块Survivor空间中,并清理掉Eden和第一块Survivor空间,也就是说可用空间达到了90%,只浪费了10%,这里有个问题,如果存活对象的超过Survivor空间怎么办,这时要依赖一种被称为分配担保的机制(Handle Promotion),将多出的对象分配到老年代。

标记整理算法

适用于老年代这种存活率高的空间,相比标记清除算法,标记阶段一致,只不过在标记后,会对可回收对象进行整理,让存活对象向内存一个方向聚集,然后直接清理掉编辑额外的内存。

分代收集算法

根据存活周期的不同将内存划分为几块,一般把java堆分为新生代和老年代,然后根据不同年代的特点选择最适合的算法,比如新生代中的对象朝生夕灭,只有少量存活,比较适合复制算法,而老年代中对象存活率高,没有额外空间提供分配担保,必须使用标记-清除或标记-整理算法进行回收。

HotSpot算法实现

枚举根节点

程序的执行是动态的,与之相关的引用关系也是动态的,所以GC roots的枚举和分析必须在一个能保证一致性的快照下进行,JVM是依赖被称为GC停顿的机制实现,在这个时间点会停顿所有的Java执行线程,即俗称的”Stop The World“,GC Roots的枚举过程并不是在内存中便利所有对象和执行上下文,那样效率无疑会很低,其实在Class加载和解析的工作中,HotSpot已经使用一组称为OopMap的结构对GC Roots进行标记,这样在GC过程中即可直接获得Roots信息。

安全点 SafePoint

那么GC停顿是如何让线程暂停的呢?有一个关键的概念叫安全点,顾名思义是指一些特定的位置,当线程运行到这些位置时,线程的一些状态可以被确定(the thread's representation of it's Java machine state is well described),比如记录OopMap的状态,从而确定GC Root的信息,使JVM可以安全的进行一些操作,比如开始GC。
另一个问题是GC如何让线程在安全点上停顿下来,有两种方式可选,一种是抢先式中断,GC主动中断所有线程,发现有线程中断的地方不在安全点上,就恢复线程,让它继续跑到安全点上,一种是主动式中断,GC不主动中断线程,而是设置一个标志,线程执行时主动去轮询这个标志,发现标志为真就自动挂起,轮询标志和安全点是重合的。

safepoint指的特定位置主要有:

  1. 循环的末尾 (防止大循环的时候一直不进入safepoint,而其他线程在等待它进入safepoint)
  2. 方法返回前
  3. 调用方法的call之后
  4. 抛出异常的位置

安全区域 SafeRegion

安全点没有解决一个问题,就是当线程不执行的时候(如进入Sleep或Blocked状态),这时候线程无法响应JVM的中断请求,这就需要安全区域这种机制来解决。安全区域是指一段代码范围,这个范围内任何位置引用关系都不会发生变化,也就是说在这些位置进行GC是安全的,你可以把安全区域看成被扩展了的安全点。
线程在进入安全区域后会标识自己已经进入了安全区域,这样GC就会忽略这个线程,同样离开安全区域时,线程要检查GC是否完成了根节点枚举,如果完成,线程继续进行,否则继续等待。

参考资料

本文参考:《深入理解Java虚拟机》

JVM学习笔记二:垃圾收集算法的更多相关文章

  1. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

  2. JVM学习笔记(二):垃圾收集

    程序计数器. 虚拟机栈. 本地方法栈3个区域随线程而生,随线程而灭:栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作. 每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,因此这 ...

  3. 【JVM学习笔记二】垃圾收集器与内存分配策略

    1. 概述 1) GC的历史比Java久远 2) GC需要完成的三件事: | 哪些内存需要回收 | 什么时候回收 | 如何回收 3) Java内存运行时区域各个部分: | Java虚拟机栈.计数器.本 ...

  4. java jvm学习笔记二(类装载器的体系结构)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao                 在了解java虚拟机的类装载器之前,有一个概念我们是必须先知道的,就是java的沙箱,什 ...

  5. jvm学习笔记二(减少GC开销的建议)

    一:触发主GC(Garbage Collector)的条件 JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大.更值得关注的是主GC的触发条件,因为它对系统影响很明显.总 ...

  6. JVM学习笔记——GC垃圾收集器

    GC 垃圾收集器 Java 堆内存采用分代回收算法,因此 JVM 针对新生代和老年代提供了多种垃圾收集器. 1. Serial 收集器 Serial 收集器是单线程收集器,采用复制算法. 是最基本的垃 ...

  7. JVM学习笔记二:JVM参数

    所有线程共享的内存主要有两块:堆内存和方法区. 其中堆内存分为两块:新生代Young generation(Eden区.From Survivor区.To Survivor区).老年代Tenured ...

  8. JVM 学习笔记二 :JVM内存区域

    一.内存分配概述

  9. 【Java虚拟机】JVM学习笔记之GC

    JVM学习笔记二之GC GC即垃圾回收,在C++中垃圾回收由程序员自己来做,例如可以用free和delete来回收对象.而在Java中,JVM替程序员来执行垃圾回收的工作,下面看看GC的详细原理和执行 ...

随机推荐

  1. 匿名属性 anonymous property

    利用匿名属性可以用很简洁的语法来自动声明不可变(immutable)的元组(tuple)类型. 属性:在字段用来表示类型和对象的状态的前提下,希望状态不被随意的更改,字段一般应该设置为private, ...

  2. Ubuntu16.04更换漂亮绚丽flatabulous主题

    作者:tongqingliu 转载请注明出处: Ubuntu16.04更换漂亮绚丽flatabulous主题 更新 sudo apt-get update sudo apt-get upgrade 安 ...

  3. error: open of glibc-devel-2.12-1.132.el6.i686.rpm failed: 没有那个文件或目录

    在安装qt的时候出现了错误: error: open of glibc-devel-2.12-1.132.el6.i686.rpm failed: 没有那个文件或目录 错误原因:缺少glibc-dev ...

  4. 解决Socket粘包问题——C#代码

    解决Socket粘包问题——C#代码 前天晚上,曾经的一个同事问我socket发送消息如果太频繁接收方就会有消息重叠,因为当时在外面,没有多加思考 第一反应还以为是多线程导致的数据不同步导致的,让他加 ...

  5. [命令行] curl查询公网出口IP

    转载:http://blog.csdn.net/orangleliu/article/details/51994513 不管是在家里还是办公室,或者是公司的主机,很多时候都是在内网中,也就是说很多都是 ...

  6. 【Android Developers Training】 59. 管理图片存储

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  7. 遇到ANDROID “call to opengl es api with no current context”错误

    延迟线程执行 Timer timer=new Timer();//实例化Timer类 timer.schedule(new TimerTask(){ public void run(){ buyed( ...

  8. 【WPF】在新线程上打开窗口

    当WPF应用程序运行时,默认会创建一个UI主线程(因为至少需要一个),并在该UI线程上启动消息循环.直到消息循环结束,应用程序就随即退出.那么,问题就来了,能不能创建新线程,然后在新线程上打开一个新窗 ...

  9. pdf文件之itextpdf插入html内容以及中文解决方案

    简述 目前网上已经有很多种html文件直接转pdf的技术帖子,但是很少有直接将部分html作为段落插入到pdf中,而且也没有一个可以很好的解决中文显示的问题. 因此今天上午围绕这个问题进行了研究,把解 ...

  10. 简单svg动画实现

    一.将svg嵌入到html中 svg是指可伸缩矢量图形,它使用XML格式定义图像.在html中可以使用<svg>标签直接嵌入svg代码,例如: <svg version=" ...