JVM通过GC来回收堆和方法区中的内存,GC的基本原理首先会找到程序中不再被使用的对象,然后回收这些对象所占用的内存。

1.收集器

通常采用收集器的方式实现GC,主要的收集器有引用计数收集器和跟踪收集器。

1.1引用计数收集器

引用计数收集器采用的是分散式的管理方式,通过计数器记录对象是否被引用。当计数器为零时,说明此对象已经不再被使用,于是可进行回收。引用计数需要在每次对象赋值时进行引用计数器的增减,它有一定的消耗。另外,引用计数器对于循环引用的场景没有办法实现回收。

1.2跟踪收集器

跟踪收集器采用的集中式的管理方式,全局记录数据的引用状态。基于一定条件的触发(例如定时、空间不足),执行时需要从根集合来扫描对象的引用关系,这可能会造成应用程序暂停,主要有复制(copying)、标记-清除(mark-sweep)、标记-压缩(mark-compact)三种实现算法。

1)复制

复制采用的方式为从根集合扫描出存活的对象,并将找到的存活对象复制到一块新的完全未使用的空间中。复制收集器方式仅需要从根集合扫描所有存活的对象,当要回收的空间中存活对象较少时,复制算法会比较高效,其带来的成本是要增加一块空的内存空间及进行对象的移动。

2)标记-清除

标记-清除采用的方式是从根集合开始扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未标记的对象,并进行回收。标记-清除动作不需要进行对象的移动,且仅对不存活的对象进行处理,在空间中存活对象较多的情况下较为高效。但由于标记-清除采用的是直接回收不存活对象所占用的内存,因此会造成内存碎片。

3)标记-压缩

标记-压缩采用和标记-清除一样的方式对存活的对象进行标记,但在清除时则不同。在回收不存活对象所占用的内存空间后,会将其他所有存活对象都往左端空闲的空间进行移动,并更新引用其他对象的指针。 标记-压缩在标记-清除的基础上还需要进行对象的移动,成本相对较高,好处则是不产生内存碎片。

2.根对象集合和引用

跟踪收集器中几种收集方式都是从根集合对象开始寻找存活对象,那么什么的根集合对象呢?

在Sun  JDK中,新生代中的根集合对象指的是这些对象:当前运行线程栈上的引用的对象、常量及静态变量、传到本地方法中,还没有被本地方法释放的对象引用。但如果只是从上面这些根集合对象中扫描新生代中的存活对象,则当旧生代中的对象也引用了新生代中的对象时就会出现问题,可能会把存活的对象当成非存活对象进行回收。如果扫描整个旧生代,由于旧生代通常比较大,JVM的性能会受到比较大的影响。为解决这个问题,Sun JDK采用了remember set方式来解决这个问题。

Sun JDK在进行对象赋值时,如果发现赋值的为一个对象引用,则产生write barrier,然后检查需要赋值的对象是否在旧生代及赋值的对象引用是否指向新生代;如果满足条件,则在remember set中做个标识。

因此,新生代在执行Minor GC时,完整的根集合为Sun JDK认为的根集合对象加上remember set 中标记的对象。

在确定根对象后,即可进行扫描后寻找存活的对象。为了避免在扫描过程对象间的引用关系发生变化,Sun JDK采取暂停应用的方式。Sun JDK在编译代码时为每段方法注入了SafePoint,通常SafePoint位于方法中循环的结束点和方法执行完毕的点,在暂停应用时需要等待所有的用户现场都进入到SafePoint,在用户线程进入SafePoint后,如果发现此时要进行Minor GC,则将其内存页设置为不可读状态,从而实现暂停用户现场的执行。

在扫描时,GC会首先判断所扫描的对象引用关系是否为Reference类型(SoftReference,WeakReference,PhantomReference),如果是Reference类型,且其所引用的对象无强引用,则认为该对象为相应的Reference类型,之后GC在进行回收这些对象则根据Reference类型的不同进行相应的处理。

3.新生代可用GC(Minor GC)

IBM研究:通常运行的程序有98%是临时对象,这些对象在新生代中存活时间较短,因而Sun JDK选择了基于Copying算法来实现对新生代对象的回收,根据上面对Copying算法的介绍,在执行复制时,需要一块未使用的空间来存放存活的对象,这是新生代被划分成Eden、S0、S1三块空间的原因。新生代分配内存采用空闲指针的方式,指针保持最后一个分配的对象在新生代内存区间的位置,当有新的对象要分配内存时,只须检查剩余的空间是否够存放新的对象,如果够,则直接创建对象并更新指针;否则就执行新生代GC。

根据扫描和复制的操作方式不同,新生代可以使用的GC共有三种:串行GC(-XX:+UseSerialGC)、并行回收GC(-XX:+UseParallelGC)、并行GC(-XX:+UseParNewGC).

这三种的详细情况在网上资料有很多,再次就不罗列,只进行下小结:

1)对象从新生代转入旧生代的条件

串行和并行采用的规则是相同的:只有经历几次Minor GC仍然存活的对象,才放入旧生代中,但也并不是说对象一定会存活MaxTenuringThreshold次才会晋升到旧生代。

并行回收的规则是:当需要给对象分配内存时,Edge Space空间不够的情况下,如果此对象的大小大于或等于Edge Space的一半时,直接在旧生代上分配。

2)Edge Space、S0、S1空间大小可变性

在并行回收和并行这两种方式进行gc时,可以根据Minor GC的频率、消耗时间来动态调整EdgeSpace、S0、S1的大小。通过参数-XX:(+/-)UseAdaptiveSizePolicy进行设置。

但串行方式无法动态调整空间大小。

3)与旧生代cms回收的关系

当旧生代采用CMS GC时,有些过程是并发进行的,如此时发生Minor GC,需要进行相应的处理,而并行回收GC是没有做这些处理的,此时新生代GC可以采用串行和并行回收,默认情况下新生代采用并行GC方式。

4)SurvivorRatio默认值

串行和并行GC的SurvivorRatio默认值都是8。

并行回收GC时采用的是InitialSurvivorRatio,这个值的默认值也是8。该值减去2就是SurvivorRatio的值,也就是说在并行回收时SurvivorRatio的默认值就是6.

4.旧生代可用GC(Major GC)

旧生代GC可用的回收方式有串行、并行、及并发三种。虽然名称和旧生代大体相同,但其实现方式却有很大不同。

1)串行(-XX:+UseSerialGC)

旧生代的串行基于Mark-Sweep-Compact实现,该方法结合了Mark-Sweep、Mark-Compact做了一些改进。串行执行的整个过程需暂停应用,且采用的为单线程方式,通常要耗费较长的时间。

2)并行(-XX:+UseParallelGC、-XX:+UseParallelOldGC)

并行GC采用Mark-Compact实现,首先将旧生代空间划分为并行线程个数的region,经过分析,大部分情况下经过多次GC后,通常旧生代空间左边存放的是有些活跃的对象。对这些对象进行压缩移动是不值得的,因此并行GC对此进行了优化,方式是从最左边开始向右扫描regions,直到找到第一个值得进行压缩移动的region,并将此region左边的region作为密度高区,对这些区域则不进行回收;然后继续向右扫描,对于一遍的regions根据存活的空间来决定压缩移动的源region和目标region,切换引用这些对象的指针,并在region上做标识,同时清除regions中其他不存活对象所占用的空间,目前此过程为单线程进行。基于regions上分析的信息,找到需要操作的目标region及完全没有存活对象的region,并行的进行对象移动和region的回收。

并行大部分操作是多线程同时进行操作的,在加上进行的密度高区(dense prefix)的优化,因此其对应用造成的暂停时间会缩短。但由于旧生代通常都比较大,扫描和标识对象上花费的时间还是比较长,也会造成应用暂停一定的时间。

指定并行GC可以用两个参数:-XX:+UseParallelGC制定使用Parallel Mark Sweep、-XX:+UseParallelOldGC制定Parallel Compacting

3)并发(-XX:+UseconcMarkSweepGC)

为满足对应用响应时间的高要求,Sun JDK提供了CMS GC,好处是GC的大部分动作均于应用并发进行,因此大大缩短了GC造成应用暂停的时间。但CMS GC需要执行三次标记,因此其完整的因此GC执行时间会比并行GC长,因而该中GC方式不适用于对吞吐量有高要求的应用。另外,CMS回收内存的方式使得其容易产生内存碎片,降低了内存空间的利用率,为了克服该弊端,CMS提供了一个整理碎片的功能,可以通过参数-XX: +UseCMSCompactAtFullCollection来启动此功能。

CMS GC触发的条件为旧生代已使用的空间达到总空间设定的百分比(-XX:CMSInitiatingOccupancyFraction=80)或者持久代已使用的空间达到设定的百分比

5.Full GC

除CMS GC外,当旧生代和持久化触发GC时,其实是对新生代、旧生代及持久代都进行GC,因此通常又称为Full GC。当Full GC被触发时,首先按照新生代配置的GC方式对新生代进行GC,然后按照旧生代的GC方式对旧生代、持久代进行GC。但其中有一个特殊情况,如在进行新生代GC前,JVM根据之前的统计信息估算到Minor GC后需要移动到旧生代的对象大小可能大于旧生代的剩余空间,这种情况下Minor GC就不会执行了,而是执行采用旧生代的GC方式对新生代、旧生代及持久代进行回收。

除直接调用System.gc()外,出发Full GC的情况有下面四种:

1)旧生代空间不足
2)持久代空间不足
3)统计得到Minor GC后移到旧生代的对象平均大小大于旧生代的空间大小
4) CMS GC 时出现promotion failed和Concurrent mode failure

promotion failed是在进行Minor GC时,S区间放不下、对象只能放到旧生代,而此时旧生代也放不下。

Concurrent mode failure是在执行CMS GC的过程中同时又有对象要放入到旧生代,而此时旧生代空间不足造成的。

6.GC选择基本策略

Sun JDK提供了两种简单的方式来帮助选择GC:吞吐量(GC所耗费的时间占应用运行总时间的百分比)优先和暂停时间(每次GC造成应用的停顿时间)优先。

默认情况下首先满足暂停时间优先策略,再满足吞吐量优先策略。大多数情况下只须配置JVM堆的大小及持久代的大小就可以让GC符合应用的要求运行,对于访问量、数据量较大的应用而言,如果确定GC对应用的运行状况造成了影响,则可根据应用状况来精确控制内存空间中每个代的大小、选择GC方式及调整GC参数,尽可能降低GC对应用运行的影响。

7.查看JVM参数常用命令

  • jps  查看java进程id
  • Jconsole,一个图形化工具,可以显示内存、线程、类和MBean等信息
  • Jmap –heap pid  查看JVM内存状况
  • Jmap  -histo pid 查看JVM堆中对象的详细占用情况
  • jstat –gcutil pid 1000 10 查看在执行Minor  gc时新生代旧生代的各项变化
  • jinfo –flag 参数名  pid  查看某一个参数的值
  • java   -XX:+PrintFlagsFinal  输出jvm所有参数的值

JVM之---垃圾回收的更多相关文章

  1. JVM的垃圾回收机制详解和调优

    JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都 ...

  2. jvm的垃圾回收算法

    一.对象存活判断判断对象是否存活一般有两种方式:1.引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收.此方法简单,无法解决对象相互循环引用的问题.2 ...

  3. 03 JVM的垃圾回收机制

    1.前言 理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序. 在学习GC前,你 ...

  4. JVM的垃圾回收机制 总结(垃圾收集、回收算法、垃圾回收器)

     相信和小编一样的程序猿们在日常工作或面试当中经常会遇到JVM的垃圾回收问题,有没有在夜深人静的时候详细捋一捋JVM垃圾回收机制中的知识点呢?没时间捋也没关系,因为小编接下来会给你捋一捋. 一. 技术 ...

  5. jvm详情——3、JVM基本垃圾回收算法回收策略

    JVM基本垃圾回收算法回收策略 引用计数(Reference Counting):比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的 ...

  6. 扒一扒JVM的垃圾回收机制,下次面试你准备好了吗

      相信和小编一样的程序猿们在日常工作或面试当中经常会遇到JVM的垃圾回收问题,有没有在夜深人静的时候详细捋一捋JVM垃圾回收机制中的知识点呢?没时间捋也没关系,因为小编接下来会给你捋一捋. 一. 技 ...

  7. JVM的垃圾回收机制

    JVM的垃圾回收机制:(GC通过确定对象是否被活动对象引用来确定是否收集该对象.) 1.触发GC(Garbage Collector)的条件. (1.GC在优先级最低的线程中运行,在未运行的线程中进行 ...

  8. 修改Tomcat的jvm的垃圾回收GC方式为CMS

    修改Tomcat的jvm的垃圾回收GC方式 cp $TOMCAT_HOME/bin/catalina.sh $TOMCAT_HOME/bin/catalina.sh.bak_20170815 vi $ ...

  9. JVM虚拟机—JVM的垃圾回收机制(转载)

    1.前言 理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序. 在学习GC前,你 ...

  10. JVM(九):垃圾回收算法

    JVM(九):垃圾回收算法 在本文中,我们将从概念模型的角度探讨 JVM 是如何回收对象,包括 JVM 是如何判断一个对象已经死亡,什么时候在哪里进行了垃圾回收,垃圾回收有几种核心算法,每个算法优劣是 ...

随机推荐

  1. 廖雪峰Python学习笔记——序列化

    序列化 定义:程序运行时所有变量都存在内存中,把变量从内存中变成可存储或可传输的过程称为序列化pickling,在其他语言中称为serialization,marshalling,flattening ...

  2. Java基础学习篇---------多态

    一.多态性的理解 1.向上转型:子类为父类对象实例化,调用的一定是子类覆写的方法,他们之间找的是共性 2.向下转型:子类扩充了父类的某些功能,而父类中没有该功能,他们之间找的是特性 案例: Numbe ...

  3. 根据已有的Jar包 一键生成对应的mavenpom.xml信息

    根据已有的jar包信息一键生成对应的maven坐标信息 .想一个问题 假如 我有一个SSH的项目, jar包是配置在lib中, 我现在想把它做成maven格式的SSH项目  ,那么这些jar包在mav ...

  4. 使用PhpSpreadsheet将Excel导入到MySQL数据库

    本文以导入学生成绩表为例,给大家讲解使用PhpSpreadsheet将Excel导入的MySQL数据库. 准备 首先我们需要准备一张MySQL表,表名t_student,表结构如下: CREATE T ...

  5. Nginx安装、配置和使用

    Nginx 1. 什么是nginx 是一个使用c语言开发的高性能的http服务器及反向代理服务器. Nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器. ...

  6. 【NOIP2017】列队 splay

    当年太菜了啊,连$60$分的暴力都没拿满,只打了一个$30$分的. 考虑到这题最多只会询问到$30W$个点,且整个矩阵会去到$30W\times 30W$,显然不能将所有的点存下来. 对于每一行(除最 ...

  7. 剑指offer二十七之字符串的排列

    一.题目 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 二.思路 我们 ...

  8. Java之Socket网络编程实践

    转自:http://my.oschina.net/leejun2005/blog/104955#comments 一.TCP/IP协议 既然是网络编程,涉及几个系统之间的交互,那么首先要考虑的是如何准 ...

  9. Mime、base64编码

    第一部分 在阮一峰老师的博客中,是这样介绍Mime的: MIME的全称是"Multipurpose Internet Mail Extensions",中译为"多用途互联 ...

  10. Linux驱动:LCD驱动框架分析

    一直想花时间来整理一下Linux内核LCD驱动,却一直都忙着做其他事情去了,这些天特意抽出时间来整理之前落下的笔记,故事就这样开始了.LCD驱动也是字符设备驱动的一种,框架上相对于字符设备驱动稍微复杂 ...