本篇文章主要介绍了"JAVA GC垃圾收集器的分析",主要涉及到JAVA GC垃圾收集器的分析方面的内容,对于JAVA GC垃圾收集器的分析感兴趣的同学可以参考一下。

 
 
 

在 很多人看来,java中内存的动态分配与内存回收已经不用用户担心了,因为它给我们提供了GC自动回收 ,感觉一切都进入了自动化了,但是对于各种内存溢出,内存泄漏问题的出现,我们还是很有必要学习GC的。地球人都知道,Java有个东西叫垃圾收集器,它 让创建的对象不需要像C/C++那样delete、free掉,但你能不能谈谈,GC是在什么时候,对什么东西,做了什么事情?”  如果还不是很了解那我们一起来学习吧。有目的地去学习GC你就不会觉得枯燥,从头到尾,带着“When” "What" "How" 这个疑问去学习,你的思路会很清晰。

一:要想知道GC到底在什么时候开始工作,必须要先了解垃圾回收器是作用在哪一个区域,采用什么样的内存回收的方式,这种方式设计的GC中采用哪些算法,因此这些问题都要先一一详细说明。

1、 先来说GC工作在哪块区域呢?根据JVM运行的内存数据区域下画分,如下图。其实GC是工作在方法区和堆区。程序计数器,虚拟机栈(也就平时所说的栈), 本地方法栈这三区域随着线程而生,随着线程而灭,出栈入栈的操作,在栈中分配配的多少内存都具有确定性,在这几个区域就不用考虑回收问题了,因为方法结束 或线程结束,内存自然就回收了。而堆区和方法区都是线程共享,堆区主要存放对象实例及数组对象,方法区存储已加载的类信息(每个类都有唯一个 Class<?>类对应着)、常量、静态变量等,所以只有在程序运行的时候我们才能知道要创建哪些对象,这部分内存的分配和回收都是动态的, 所以这是就GC关注的区域也是它工作的区域。

2、 就拿现在为java已经实现的JVM中的GC是采用哪种内存回收方式呢?实际有很多种内存回收方式,实现垃圾回收时总会综合使用多种设计方式, 会针对不同的情况采用不同的垃圾回收实现。不同方式设计基础也就是基于不同的算法,垃圾收集又有哪些的算法呢?下面分别说明:

(1)复制算法:将可用内存容量划分为大小相等的两块,每次只使用其中的一块,当这块内存用完了,就要将活着的对象(从根(GC Roots)开始访问每一个关联可到达的对象)复制到另外一块空间上,然后再把已使用的内存空间一次清理掉。如下图:

(2)标记-清除算法:首先标记出所有需要回收的对象,在标记完成后统一回收所有地被标记的对象。如下图:

(3)标记-整理算法:和标记-清除算差不多,首先标记出所有需要回收的对象,先不直接回收不可达对象,而是让所有地存活的对象都向一端移动  然后再清理掉边界以外的内存空间,如下图:

(4)分代收集算法:这个很简单,根据对象的存活周期的不同将内存划分为几块。

3、现行的垃圾回收器用分代的方式来采用不同的回收设计。根据对象生存时间的长短,把堆内存分 成3个代。Young(年轻代),Old(老年代),Permanent(永久代)。

Young:主要是用来存放新生的对象。
Old:主要存放应用程序中生命周期长的内存对象。
Permanent:是指内存的永久保存区域,.它和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理。

其实在JVM中对于垃圾回收时不论采用哪种算法机制来设计垃圾回收器,都会利弊参半,因此实际上实现垃圾回收时总会综合使用多种设计方式也就是基于多种算法的一个综合应用。java现行JVM中基于不同代的特点之上采用不同的回收算法,从而充分利用种回收算法的优点。

首先提前了解下(Minor GC),(Major GC  / Full GC)

GC(Minor GC):指发生在新生代的垃圾收集动作,它不会导致老年代里进行垃圾收集动作发生,因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。
GC(Major GC  / Full GC):指发生在老年代的 GC,出现了 Major GC,经常会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10 倍以上。

再看上图:划分了不同代后,由于GC主要是发生在对象经常消亡的新生代,如有IBM 的专门研究表明,新生代中的对象98%是朝生夕死的,复制算法实现简单,运行高效,对于复制量不是很大的情况下使用比较合理。所以现在商业虚拟机都采用复 制算法来回收新生代。再看新生代内划分为三个小块。因为新对象经过一次GC后存活下来的就会很少了,不需要按照1:1的比例来划分内存空间,而是设计成将 内存分为一块较大的Eden空间各两块较小的Survivor空间(这两个Survivor空间就是Survivor space:From Survivor或To Survivor。这两个Survivor空间是一样大小的。例如,新生代大小是10M(Xmn10M),那么缺省情况下 (-XX:SurvivorRatio=8),Eden Space 是8M,From和To都是1M。),每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地 拷贝到另一块Survivor空间上(如果此时Survivor空间不足怎么办,请看下面),最后清理掉Eden各刚才用过的Survivor的空间。

当 new一个对象时,先在Eden Space上分配,如果Eden Space没有足够的内存空间了,此时就要做一次Minor GC。Minor GC后,要把Eden和From中仍然活着的对象们复制到To空间中去。一般,虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。

说到这可以说明(如果此时Survivor空间不足怎么办) 这个问题了,不用担心在发生 Minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,改为直接进行一次Full GC  ,如果小于,说明老年代空间还有足够的空间,则要查看设置参数HandlePromotionFailure是否为true,如果是那只会完成这一次 Minor GC,GC后的新生代中活下来并符合老年代条件的对象就被promote到老年代空间,还有To Survivor空间不能容纳下Minor GC后活着的某个对象也会被promote到老年代空间,还有另一种情况就是如果新进来的一个大对象,Eden也没有足够空间存放的时也会直接把它 promote到老年代空间 ;如果false 则也要改为直接进行一次Fulll GC。对于参数HandlePromotionFailure它是指在Survivor轮换备份的过程中,Survivor无法容纳的对象是否直接进入老 年代。谈到怎么进入老年代,顺便说下另一种情况,并不是所有要进入老年代区域的对象年龄必须达到:MaxTenuringThreshold设定的值的, 如果Survivor空间中相同age(例如,age=5)对象的所占内存空间总和大于等于Survivor空间的一半,那么age>=5的对象在 下一次Minor GC后就可以直接promote到老年代,而不用等到age增长到阈值。

说到这里,再在已经能对GC到底在什么时候开始工作这个问题做个总结了:当在new 对象时,首先考虑在Eden Space上分配内存,如果Eden满了则 进行minor gc,晋升到老年代的对象大于老年代剩余空间full gc,或者小于时被HandlePromotionFailure参数强制full gc;

二,对什么东西进行GC呢?

想知道对什么东西,则就要了解java是如何去判定一个对象是否还存活的。这里它的实现要用到根搜索算法(GC Roots Tracing) 基本意思就是通过一系列名为“ GC Roots”的对象作为起点,从这些节点向下开始进行搜索,搜索所经过的路径称为引用链,当一个对象到GC  Roots 没有任何引用链时相连(说白了就是从GC Roots到这个对象不可达时),则证明此对象是不可用的。
下图中,对象Object6、Object7、Object8虽然互相引用,但他们的GC Roots是不可到达的,所以它们将会被判定为是可回收的对象。

       

可作为GC Roots 的对象包括:

1、虚拟机栈(栈帧中的本地变量表)中的引用对象。

2、 方法区中的类静态属性引用的对象

      3、  方法区中的常量引用的对象

      4、  本地方法栈中JNI的引用对象。

在根搜索算法中不可到达的对象,也并非 是“真正的死”,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它将被第一次标记,并且进行一次筛选,筛选条件是此对象是否有必要执行finalize()方法、当对象没有重写 finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行 finalize()”,finalize()方法是对象逃脱死亡命运的最后一次机会了,如果finalize()方法中没有重新与引用链上的任何一个对 象建立关联,说明这个对象不可能复活了,则进行第二次标记,则这个对象真正要被GC清理掉了。

分析到这里,对于什么东西这个问题也能回答了:从root搜索不可到达的对象,而且经过第一次标记、清理后,仍然没有复活的对象。

四.做了什么事情,就剩下这个问题了,回答这个问题的空间非常大。

也非常有必要了解不同厂商,不同版本的虚拟机提供的垃圾收集器的实现,了解串行回收器,并行回收器,并行整理回收器,并发标志-清理(CMS)回收器,它们的具体实现,其实这些回收器的实现也是基于前面说过那几种收集算法。

可以去查看相关的书集,如《深入理解JVM》,这里不一一分析了。

所 以问及GC做了什么事情,答的空间很大,可以说清楚新生代做的是复制清理、from survivor、to survivor是干啥用的、老年代做的是标记清理、标记清理后碎片要不要整理、复制清理和标记清理有有什么优劣势,串行、并行(整理/不整理碎片)、 CMS等搜集器可作用的年代、特点、优劣势等。

JAVA GC垃圾收集器的分析的更多相关文章

  1. java - GC垃圾收集器详解(一)

    概要 该图标记了在jdk体系中所使用到的垃圾收集器及对应的关系图.图片上方为年轻代的垃圾收集器而图片下方是老年代的垃圾收集器.当选择某一个区域的垃圾收集器时会自动选择另外一个区域的另一个垃圾收集器.例 ...

  2. java - GC垃圾收集器详解(三)

    以前收集器的特点 年轻代和老年代是各自独立且连续的内存块 年轻代收集必须使用单个eden+S0+S1进行复制算法 老年代收集扫描整个老年代区域 都是以尽可能少而快速地执行GC为设计原则 G1是什么 G ...

  3. java - GC垃圾收集器详解(二)

    CMS收集器 CMS收集器(ConcurrentMarkSweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器. 适合应用在互联网站或者B/S系统的服务器上,这类应用尤其重视服务器的响应 ...

  4. Java虚拟机垃圾收集器与内存分配策略

    Java虚拟机垃圾收集器与内存分配策略 概述 那些内存须要回收,什么时候回收.怎样回收是GC须要完毕的3件事情. 程序计数器.虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性 ...

  5. [转] 深入理解Java G1垃圾收集器

    [From] https://www.cnblogs.com/ASPNET2008/p/6496481.html 深入理解Java G1垃圾收集器 本文首先简单介绍了垃圾收集的常见方式,然后再分析了G ...

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

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

  7. 深入理解 Java G1 垃圾收集器--转

    原文地址:http://blog.jobbole.com/109170/?utm_source=hao.jobbole.com&utm_medium=relatedArticle 本文首先简单 ...

  8. 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)

    1.  前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保  2.  垃圾 ...

  9. Java虚拟机-垃圾收集器

    垃圾收集器(Garbage Collection, GC)的诞生引导出了三个问题: 哪些内存需要回收? 什么时候回收? 如何回收? 对于线程独占的三个区域(程序计数器.虚拟机栈.本地方法栈)不用过多的 ...

随机推荐

  1. First:安装配置JDK and 部署Tomcat

    (一)准备 百度云地址(win,64Bit): 1.tomcat(7.0):链接:https://pan.baidu.com/s/1f60DOGO5Hnj9bq-987FNrw 密码:6q55 2.j ...

  2. spring5——Aop的实现原理(动态代理)

    spring框架的核心之一AOP,面向切面编程是一种编程思想.我对于面向切面编程的理解是:可以让我们动态的控制程序的执行流程及执行结果.spring框架对AOP的实现是为了使业务逻辑之间实现分离,分离 ...

  3. JSON(一)——JSON与JavaScript的关系

    JSON是一种轻量级的数据交换格式,全称--JavaScript 对象表示法(JavaScript Object Notation). 类比XML,你可以把JSON看作是一种存储数据的格式类型,一种数 ...

  4. express实践(一)

    涉及以下这些内容: 主体. cookie.session 数据 模板引擎 服务器基本结构: const express=require('express'); const static=require ...

  5. 判断ssh远程命令是否执行结束

    注:这是一个没什么鸟用的功能.不过也算是一种拓展. 通常在那些"一键化部署"的shell脚本中,可能需要使用ssh执行远程命令来实现一些简单的自动化,这些远程命令可能需要执行一段时 ...

  6. pthon/零起点(一、集合)

    pthon/零起点(一.集合) set( )集合,集合是无序的,集合是可变的,集合是可迭代的 set()强型转成集合数据类型 set()集合本身就是去掉重复的元素 集合更新操作案列: j={1,2,3 ...

  7. Hibernate(十二):HQL查询(一)

    概述 Hibernate提供了以下几种检索对象的方式 1)导航对象图检索方式:根据已经加载的对象导航到其他对象: 2)OID检索方式:按照对象的OID来检索对象: 3)HQL检索方式:使用面向对象的H ...

  8. scrapy批量下载图片

    # -*- coding: utf-8 -*- import scrapy from rihan.items import RihanItem class RihanspiderSpider(scra ...

  9. python列表很聪明,支持负数索引

    python列表很聪明,支持负数索引

  10. SQL to Java code for Elasticsearch

    Elasticsearch虽然定位为Search Engine,但是因其可以持久化数据,很多时候,我们把Elasticsearch当成Database用,但是Elasticsearch不支持SQL,就 ...