JVM学习系列(二) 垃圾回收

如何判断对象是否可回收

引用计数法

1、概念:给对象中添加一个引用计数器,每当有一个地方引用他时,计数器的值+1,当引用失效的时候,计数器-1,任何时刻计数器为0的对象就是不可以在被使用的对象。

2、缺点:无法解决对象循环引用的问题(如下图)

可达性分析法

1、概念:垃圾回收根节点(GCRoot)向下搜索,搜索所走过的路径称为引用链,当一个对象对GCRoot没有任何的引用链时,代表当前对象不可用。

2、GCRoot包含的对象:

  • 虚拟机栈(帧栈中的本地变量表)中的引用的对象
  • 方法区类静态属性 所引用的对象
  • 方法区常量所引用的对象
  • 本地方法栈中锁引用的对象

3、引用的概念

  • 强引用:垃圾收集器永远不会回收掉被引用的对象;Object obj = new Object();
  • 软引用:系统将要发生内存溢出之前,会把这类对象归类到回收范围中并进行第二次回收;SoftReference
  • 弱引用:强度比软引用更弱,被弱引用关联的对象只能生存到下一次垃圾收集发生之前;WeakReference
  • 虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象实例,为一个对象设置虚引用关联的目的就是系统能在这个对象被收集器回收时得到一个系统通知;PhantomReference

回收算法

标记清除算法

1、概念:标记出所有需要回收的对象,在标记完成后统一回收掉被标记的对象。
2、缺点:

  • 效率问题:标记和清理的效率不高
  • 空间问题:会产生大量的不连续的内存碎片,空间碎片太多可能会导致在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作

复制算法

1、概念:将可用的内存按容量分为大小相等的两块,每次只使用其中的一块,当这一块内存用完时,将存活的对象复制到另外一个上面,然后将已经使用过的内存空间一次清理掉;

2、优点:解决标记清除算法中存在的内存碎片问题

3、缺点:

  • 空间浪费:将内存缩小为原来的一半,内存使用效率低。
  • 对象存活率较高时就要执行多次的复制算法,效率变低。
  • 只能用于新生代,无法用户老年代这种对象存活率较高的情况
    3、复制算法优化:
    用该算法回收新生代,将内存分为一块较大的Eden和两块Survivor空间(HotSpot默认比例是8:1),每次使用Eden和其中的一块Survivor空间,当回收时,将Eden和Survivor中还存活的对象一次性拷贝到另外一个Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间,这样只会浪费10%的空间;如果另外一个Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。

标记整理算法

1、概念
过程与标记清除算法一致,但后续在清理的时候不是直接对可回收的对象进行清理,而是让所有存活的对象都向另外一端移动,然后直接清理掉端边界以外的内存

2、优点:老年代可以用,可以解决标记复制算法在对象存活较高时效率低下的问题

分代收集算法

1、概念:根据对象的存活周期将内存划分为几块,JAVA堆分为新生代和老年代,根据各个年代的特点采用适当的收集算法,新生代收集时大量对象被收集,小亮对象存活,所有采用复制算法,老年代存活率高,没有额外的空间对它进行分配担保,可以用标记清理或者标记整理算法。

垃圾回收器

Serial

1、概念:最基本收集器,新生代收集的唯一选择,单线程收集器

2、优点:相对于其他收集器的单线程比,没有线程交互的开销,简单切高校

3、缺点:在进行垃圾收集时,会暂停其他的工作线程(Stop The World)

Parnew

1、概念:Serial收集器的多线程版本

2、优点:除了Serial收集器外,只有它能与CMS收集器配合使用

3、缺点:还是存在Stop The World的问题,只不过比Serial稍微好点

4、相关参数:

  • -XX:+UseParNewGc 使用parnew收集器
  • -UseConcMarkSweepGc 使用该选项时,会默认将该收集器做为新生代的收集器
    • 并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
    • 并发:用户线程和垃圾收集线程同时执行;

Parallel Scavenge 收集器

1、概念:新生代收集器,使用复制算法的并行多线程收集器;目标是达到一个可控制的吞吐量,适用于后台运算不需要太多交互的任务;该收集器可以与Serial Old配合使用

2、缺点:无法和CMS收集器一起使用

3、相关参数:

  • -XX:MaxGcPauseMillis 控制最大垃圾收集停顿时间(大于0的毫秒数),该值越小吞吐量越低,相对的
  • -XX:GCTimeRatio 吞吐量大小 参数值(垃圾收集时间占总时间的比率)大于0小与100的整数,正常是吞吐量的倒数
  • -XX:+UseAdaptiveSizePolicy 打开后,不需要手动指定新生代的大小(-Xmn)、Eden和Survivor的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等参数,虚拟机回根据当前系统的运行情况收集戏能健康信息,动态调整来提供合适的停顿时间或者是吞吐量

    吞吐量:CPU用户运行代码的时间与CPU总消耗时间的比值,吞吐量=运行代码时间/(运行用户代码时间+垃圾收集时间)

Serial Old 收集器

1、概念:Serial收集器的老年代版本,单线程,采用标记整理算法

Parallel Old收集器

1、概念:Parallel Scavenge收集器的老年代版本,多线程,采用标记整理算法;在注重吞吐量和CPU资源敏感的情况下,可以与Parallel Scavenge 配合使用

CMS(Concurrent Mark Sweep)收集器

1、概念:一种以获取最短回收停顿时间为目标的收集器,基于标记清除算法实现的;默认开启回收线程数是(CPU+3)/4
2、运作过程:

  • 初始标记:
  • 并发标记:
  • 重新标记:
  • 并发清除:

3、优点:

  • 并发收集
  • 低停顿

4、缺点:

  • 对CPU资源敏感:虽然不会Stop the world,但是会占用部分线程导致用户程序变慢,吞吐量变低。当CPU在四个以上时,收集线程占用不会超过25%,低于4个时,影响就很大了;

    • 优化方式:增量式并发收集器:i-CMS,在并发表基和并发清理的时候让GC线程和用户线程交替运行,减少GC线程独占资源的时间,这样可能会导致收集时间边长,但是对程序的影响较低
  • 浮动垃圾:因为浮动垃圾的存在,可能出现Concurrent Mode Failure的情况。由于CMS的特性,在垃圾收集阶段用户线程继续运行,必须预留足够的内存空间给用户线程使用,所以CMS无法和其他收集器一样,等老年代完全满了才开始收集,需要预留一部分空间提供并发收集时的程序运作使用。默认情况下,老年代占用68%就会被激活收集;这个可以通过-XX:CMSInitiatingOccupancyFraction来做调整;
  • 空间碎片问题:因为基于标记清除算法,所以会导致碎片的问题
    • 优化方式:-XX:+UseCMSCompackAtFullCollection fullGC完以后自动进行碎片的压缩(导致停顿时间变长);-XX:CMSFullGCsBrforeCompaction 设置多少次不压缩的fullGC后,执行一次压缩的FullGC

      • 并发清理的同时因为不Stop The world导致会同时产生新的垃圾,这部分垃圾叫做浮动垃圾;
      • ConcurrentMode Failure:预留内存无法满足程序运行的需要时,会发生Concurrent Mode Failture,发生后会临时采用会临时启动Serial Old来进行收集,这会导致停顿时间更长

G1收集器

1、概念:基于标记整理的算法实现
2、优点:

  • 不会产生碎片
  • 可以精确控制停顿

原文地址:https://www.cnblogs.com/jakaBlog/p/11767885.html

时间: 2024-10-25 22:08:03

JVM学习系列(二) 垃圾回收的相关文章

JVM调优(二)垃圾回收算法

原文出处: pengjiaheng 可以从不同的的角度去划分垃圾回收算法: 按照基本回收策略分 引用计数(Reference Counting): 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此算法最致命的是无法处理循环引用的问题. 标记-清除(Mark-Sweep): 此算法执行分两阶段.第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除.此算法需要暂停整个应用,同时,会产生内存碎

【JVM】JVM系列之垃圾回收(二)

一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二.哪些内存需要进行垃圾回收 对于虚拟机中线程私有的区域,如程序计数器.虚拟机栈.本地方法栈都不需要进行垃圾回收,因为它们是自动进行的,随着线程的消亡而消亡,不需要我们去回收,比如栈的栈帧结构,当进入一个方法时,就会产生一个栈帧,栈帧大小也可以借助类信息确定,然后栈帧入栈,执行方法体,退出方法时,栈帧出

JVM系列之垃圾回收(二)

一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二.哪些内存需要进行垃圾回收 对于虚拟机中线程私有的区域,如程序计数器.虚拟机栈.本地方法栈都不需要进行垃圾回收,因为它们是自动进行的,随着线程的消亡而消亡,不需要我们去回收,比如栈的栈帧结构,当进入一个方法时,就会产生一个栈帧,栈帧大小也可以借助类信息确定,然后栈帧入栈,执行方法体,退出方法时,栈帧出

Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收

很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确实很低,一方面,Java语言采用面向对象思想,这也决定了其必然是开发效率高,执行效率低.另一方面,Java语言对程序员做了一个美好的承诺:程序员无需去管理内存,因为JVM有垃圾回收(GC),会去自动进行垃圾回收. 其实不然: 1.垃圾回收并不会按照程序员的要求,随时进行GC. 2.垃圾回收并不会及时

JVM实用参数——新生代垃圾回收

JVM实用参数目录 JVM实用参数——新生代垃圾回收 概述 第1部分  新生代垃圾回收介绍 第2部分 参数介绍 参考 第1部分  新生代垃圾回收介绍 本部分,我们将关注堆(heap) 中一个主要区域,新生代(young generation).首先我们会讨论为什么调整新生代的参数会对应用的性能如此重要,接着我们将学习新生代相关的JVM参数. 单纯从JVM的功能考虑,并不需要新生代,完全可以针对整个堆进行操作.新生代存在的唯一理由是优化垃圾回收(GC)的性能.更具体说,把堆划分为新生代和老年代有2

ASP.NET MVC学习系列(二)-WebAPI请求

继续接着上文 ASP.NET MVC学习系列(一)-WebAPI初探 来看看对于一般前台页面发起的get和post请求,我们在Web API中要如何来处理. 这里我使用Jquery 来发起异步请求实现数据调用. 继续使用上一文章中的示例,添加一个index.html页面,添加对jquery的引用. 一.无参数Get请求 一般的get请求我们可以使用jquery提供的$.get() 或者$.ajax({type:"get"}) 来实现: 请求的后台Action方法仍为上篇文章中的GetU

JVM内存管理和垃圾回收机制介绍

http://backend.blog.163.com/blog/static/20229412620128233285220/ 内存管理和垃圾回收机制是JVM最核心的两个组成部分,对其内部实现的掌握是Java开发人员开发出高质量的Java系统的必备条件.最近整理了一些关于JVM内存管理和垃圾回收方面的知识,这里梳理一下,分享给大家,希望能够对Java虚拟机有更深入的了解. 1. JVM内存管理 首先,JVM将内存组织为主内存和工作内存两个部分.主内存中主要包括本地方法区和堆.每个线程都有一个工

JVM内存模型以及垃圾回收

原文:http://www.blogjava.net/freeman1984/archive/2011/03/08/345929.html JVM内存模型以及垃圾回收内存由 Perm 和 Heap 组成. 其中 Heap = {Old + NEW = { Eden , from, to } }具体可查看javavisualvm JVM内存模型中分两大块,一块是 NEW Generation, 另一块是Old Generation. 在New Generation中,有一个叫Eden的空间,主要是

jvm的几种垃圾回收策略

jvm的垃圾回收主要是针对java堆这块内存空间,因为java的虚拟栈是随着线程的销毁而自动回收的,而方法区的垃圾回收条件极其苛刻. java堆中存放着运行期间的对象实例,随着程序的运行实例越来越多,不回收垃圾就会产生OOM异常,而怎么判断一个对象是否是垃圾呢,下面是几种常用的垃圾判别法. 1引用计数法 基本想法就是 一个对象,如果有地方在引用它,那么它就不是垃圾,因此呢,我给这个对象创建一个引用计数器,对象 被引用一次,计数器就+1,引用失效,计数器-1,垃圾回收的时候一看这个对象计数器值为0