Java内存运行时其中的程序计数器、虚拟机栈、本地方法栈会随着线程的的消亡而回收,这些区域都不用考虑垃圾回收的问题。堆和方法区是线程共享的,而垃圾回收就是针对这些内存进行回收处理。我将把个人认知记录下来,主要记录CMS垃圾回收的详情。希望大家指正。

一 确认哪些垃圾已经死了需要被回收?

1 引用计数法

引用计数法就是 给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。任何时刻计数值为0的对象就是不可能再被使用的。 这种算法的效率很高(python就是使用这种算法),但是处理不了循环引用的问题。比如对象A—>B,B—>A,这种情况下时A,、B对象都有引用导致一直回收不了。

2 可达性分析

目前主流的商用程序语言(Java、C#)都是使用这种算法。基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索

,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。

如下图,object5,object6,object7虽然有引用但是没有GC Roots对象引用即不可达标志为垃圾。

即使是可达性算法分析中不可达的对象,也不是非死不可,这个时候它们是缓刑阶段,至少要经历两次标记才能被宣告死亡

在java语言中可以作为GC Roots的对象(两栈两方法区)

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

  • 本地方法栈中JNI(即一般是说的native方法)引用的对象

  • 方法区中常量引用的对象

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

二 Java的四种引用

1.强引用:类似于"Object obj = new Object()",只要强引用存在,垃圾回收器就不会收掉被引用的对象实例。

2.软引用:对于软引用关联的对象,在系统将要发生内存溢出之前,会把这些对象列入回收范围之中进行第二次回收,如果这次回收还是没有足够的内存,才会抛出内存溢出异常。

3.弱引用:例如ThreadLocal中的key就是以弱引用的方式保存的。被弱引用的对象只能存活到下一次垃圾回收之前,当GC开始,无论当前内容是否够用,都会回收掉只被弱引用关联的对象。

4.虚引用:无法通过虚引用取得一个对象实例,因此一个对象是否有虚引用的存在不会对其生存时间构成影响。为一个对象设置虚引用的唯一目的就是能在对象回收前收到一个系统通知。

三 垃圾收集算法

可达性分析算法来是否是垃圾但至少找出哪些是垃圾,并没有清除。如何高效的标记+清除垃圾有不同的垃圾垃圾手机算法。由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确规定,各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。

1 标记-清除(Mark-Sweep)算法

这是最基础的垃圾收集算法(后续的算法都是基于此算法改进的),分为标记和清除两部分。首先标记所有需要回收的对象,标记完成之后统一回收标记的对象。标记清除算法的执行过程是如图。

标记清除算法的不足

  1. 效率不高,标记和清除的两个过程效率都不高

  2. 空间问题,标志清除之后会产生大量不连续的内存碎片,内存碎片太多可能导致程序后续运行大对象是分配不到内存看见触发垃圾回收

2 复制(Copying)算法

为解决标记清除算法的效率问题,推出了复制算法。将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题,只要移动堆顶指针,按顺序分配内存即可实现简单高效。具体过程如下图所示:

复制算法的不足:只能使用一半的内存空间浪费内存。复制效率跟存活对象的多少有关,当存活对象不管增多,复制算法的效率就会大大的降低。所以一般复制算法用带新生代做垃圾回收

3 标记整理算法

根据老年代的特性,提出了标志整理算法,该算法和标志清除算法一样,在标志完成之后,标志整理算法不是直接对垃圾对象进行清理的,而是让所有存活得对象向一端移动,然后清除这一端以外的内存。具体过程如图下图

标志整理算法的不足:虽然解决标记清除算法的内存碎片的问题,但是因为后期内存整理效率慢低。常用在老年代。

5 分代收集算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。目前大部分垃圾收集器对于新生代都采取复制算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

而由于老年代的特点是每次回收都只回收少量对象,一般使用的是标记-整理算法

四 垃圾收集器

标志清除算法、复制算法、标志整理算法是理论方法,垃圾回收器是这些收集算法的具体实现。Java虚拟机对垃圾回收器没有任务规定,不同的厂家实现不一样的。主要介绍一些常见的垃圾回收器。HotSpot垃圾回收器如下图。

新生代收集器:Serial、ParNew、Parallel Scavenge;

老年代收集器:Serial Old、Parallel Old、CMS;

整堆收集器:G1、ZGC

吞吐量优先:Parallel Scavenge收集器、Parallel Old 收集器。

停顿时间优先:CMS(Concurrent Mark-Sweep)收集器。G1收集器

1 Serial收集器

一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。Stop The World。特点:针对新生代、串行、复制算法、单线程一方面意味着它只会使用一个CPU或一条线程去完成垃圾收集工作。优点: 简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

2 ParNew收集器

ParNew收集器就是Serial收集器的多线程版本,它也是一个新生代收集器。除了使用多线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法(复制算法)、Stop The World、对象分配规则、回收策略等与Serial收集器完全相同,两者共用了相当多的代码。 ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器

3 Parallel Scavenge收集器

Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器。 停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

吞吐量:CPU用于运行用户代码的时间与CPU消耗的总时间的比值。

吞吐量 = (执行用户代码时间)/(执行用户代码时间+垃圾回收占用时间)

-XX:MaxGCPauseMillis 垃圾收集器最大停顿的时间,但最大停顿时间过短必然会导致新生代的内存大小变小,垃圾回收频率变高,效率可能降低。

-XX:CGTIMERatio 吞吐量大小(0-100),默认为99。

4 Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。如果在Server模式下,那么它主要还有两大用途:一种用途是在JDK 1.5以及之前的版本中与Parallel Scavenge收集器搭配使用,另一种用途就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

5 Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。 在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。

这个收集器是在JDK 1.6中才开始提供的,在此之前,新生代的Parallel Scavenge收集器一直处于比较尴尬的状态。原因是,如果新生代选择了Parallel Scavenge收集器,老年代除了Serial Old收集器外别无选择(Parallel Scavenge收集器无法与CMS收集器配合工作)。由于老年代Serial Old收集器在服务端应用性能上的“拖累”,使用了Parallel Scavenge收集器也未必能在整体应用上获得吞吐量最大化的效果,由于单线程的老年代收集中无法充分利用服务器多CPU的处理能力,在老年代很大而且硬件比较高级的环境中,这种组合的吞吐量甚至还不一定有ParNew加CMS的组合“给力”。直到Parallel Old收集器出现后,“吞吐量优先”收集器终于有了比较名副其实的应用组合。

特点:

Parallel Old是Parallel Scavenge的老年代版本

Parallel Old 老年代采用的是标记 - 整理算法,其他特点与Parallel Scavenge相同

6 CMS(Concurrent Mark Sweep)收集器

CMS收集器是一种以获取最短停顿时间为目标的老年代垃圾回收算法,采用标记清除算法,,应用于尤其重视服务的响应速度,希望系统停顿时间最短的系统。CMS分为4步:初始标记、并发标记、重新标记,并发清除

初始标记(stw):单线程执行,只是标记一下GC Roots对象。速度很快。

并发标志:gc线程和用户线程并发进行,时间较长。三色标记法+写屏障保证不会错标记,可能出现浮动垃圾。

重新标记(stw):修正并发标记过程中因为用户重新运行而导致标记产生变化的哪些对象的标记记录。停顿时间比初始标记长,但也很快。

并发清除:并发清除之前标记的垃圾

CMS优点:并发收集、停顿

缺点:1 CPU敏感,在并发标记阶段会占用一部分cpu资源导致程序变慢,吞吐量降低。

2 无法处理浮动垃圾,由于并发清理阶段程序还在运行产生的垃圾就只能等到下一次垃圾回收处理了。因为CMS的并发性,老年代垃圾回收不能像之前的回收器一样等老年代内存全部用完了才触发垃圾回收,CMS需要预留内存空间存放浮动垃圾。这个值是可以设置的。默认是68%。不能过高,当值设置的太高的话,CMS预留的空间不足时会触发Concurrent Mode fail导致来一次的FUll Gc(采用备用方案Serial Old收集器来回收老年代,这个效率很低)。

3 CMS采用的事标记清除算法,会产生内存碎片。但是CMS也提供了参数两个配置参数 1 开启内存碎片合并,在CMS顶不住要进行FUll gc时候进行内存碎片合并整理 2 设置合并整理的次数,当进行了多少次垃圾清除之后就来一次合并整理。

7 G1收集器

G1(Garbage-First)是一款面向服务端应用的垃圾收集器。HotSpot开发团队赋予它的使命是未来可以替换掉JDK 1.5中发布的CMS收集器。 G1算法将堆划分为若干个区域(Region),E.、S、O和之前的新生代老生代逻辑差不多,只是g1中新生代和老生代不是隔离的可能连在一块。Humongous区域是G1特殊区域。 一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象,默认直接会被分配在老年代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC。

与其他GC收集器相比,G1具备如下特点。

  1. 并行和并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。( G1 能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短“Stop The World”停顿时间)

  2. 分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果(收集包括新生代和老生代,能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭,能够采用不同方式处理不同时期的对象)

  3. 空间整合:与CMS的“标记—清理”算法不同,G1从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。(整体看是基于标记-整理算法; 局部(两个Region间)看,是基于复制算法;不会产生内存碎片,不会像CMS因为内存不足触发下一次GC)

  4. 可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。g1之所以可以建立可预测的停顿时间模型,是因为它可以有计划得避免在整个堆进行全区域的垃圾回收,g1会监控每块region里面的垃圾堆积价值,在后台维护一个优先表,然后每次根据允许的时间回收垃圾回收价值高的region。

G1分为4个步骤(与CMS类似)

  1. 初始标志(STW):仅去oopMap标记GC Roots 对象。速度很快

  2. 并发标记:根据Gc Roots对象一层一层的标记(三色标记+写屏障),耗时长期间用户线程可以运行

  3. 最终标记(STW): 修正并发标记过程中因为用户重新运行而导致标记产生变化的哪些对象的标记记录。停顿时间比初始标记长,但也很快。

  4. 筛选回收:排序各个Region的回收价值和成本,根据用户期待的gc停顿时间制定回收计划,优先回收价值高的region,回收时把一个或多个Region存活的对象复制到空的Region。 并且在此过程中压缩和释放内存。期间用户线程可以运行,降低停顿时间,提高吞吐量。

常用的参数

  1. "-XX:+UseG1GC":指定使用G1收集器;

  2. "-XX:InitiatingHeapOccupancyPercent":当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;

  3. "-XX:MaxGCPauseMillis":为G1设置暂停时间目标,默认值为200毫秒;

  4. "-XX:G1HeapRegionSize":设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个

7 ZGC垃圾

在JDK 11的ZGC。它的回收耗时平均不到2毫秒。它是一款低停顿高并发的收集器。将对堆内存分成多个大小不等的region(跟g1不同)。

ZGC几乎在所有地方并发执行的,除了初始标记的是STW的。所以停顿时间几乎就耗费在初始标记上,这部分的实际是非常少的。那么其他阶段是怎么做到可以并发执行的呢?

ZGC主要新增了两项技术,一个是着色指针Colored Pointer,另一个是读屏障Load Barrier。

ZGC 是一个并发、基于区域(region)、增量式压缩的收集器。Stop-The-World 阶段只会在根对象扫描(root scanning)阶段发生,GC 暂停时间并不会随着堆和存活对象的数量而增加。

ZGC设计的目标:TB 级别的堆内存管理;最大 GC Pause 不高于 10ms;最大的吞吐率(Throughput)损耗不高于 15%;

着色指针Colored Pointer

ZGC利用指针的64位中的几位表示Finalizable、Remapped、Marked1、Marked0(ZGC仅支持64位平台),以标记该指向内存的存储状态。相当于在对象的指针上标注了对象的信息。注意,这里的指针相当于Java术语当中的引用。 在这个被指向的内存发生变化的时候(内存在Compact被移动时),颜色就会发生变化。

读屏障Load Barrier

由于着色指针的存在,在程序运行时访问对象的时候,可以轻易知道对象在内存的存储状态(通过指针访问对象),若请求读的内存在被着色了,那么则会触发读屏障。读屏障会更新指针再返回结果,此过程有一定的耗费,从而达到与用户线程并发的效果。把这两项技术联合下理解,引用R大(RednaxelaFX)的话:与标记对象的传统算法相比,ZGC在指针上做标记,在访问指针时加入Load Barrier(读屏障),比如当对象正被GC移动,指针上的颜色就会不对,这个屏障就会先把指针更新为有效地址再返回,也就是,永远只有单个对象读取时有概率被减速,而不存在为了保持应用与GC一致而粗暴整体的Stop The World。

laji

Java垃圾回收笔记相关推荐

  1. java垃圾回收机制_笔记 | Java垃圾回收机制

    本文经授权转载自程序员杂货铺(ID:speakFramework) 垃圾回收 最近上海的小伙伴是不是要被强垃圾分类搞疯了???哈哈哈哈 上海是个走在前列的城市啊,不光骑自行车闯红灯要被罚钱,垃圾不分类 ...

  2. Java垃圾回收机制与垃圾收集器

    Java垃圾回收机制与垃圾收集器 前言 判定对象是否存活(标记) 引用计数法 可达性分析 算法思想 算法步骤 对象复活 引用概念的完善 垃圾回收算法 标记 - 清除法 标记 - 复制法 标记 - 整理 ...

  3. 假期三天,我肝了万字的Java垃圾回收,看完你还敢说不会?

    大家好,我是狂聊,上一篇已经把 Jvm 的运行区数据和类加载机制聊完了. 今天来说说 Java 垃圾回收,高频面试问题. 提纲附上,话不多说,直接干货 1.什么是垃圾回收? 垃圾回收(Garbage ...

  4. 细述 Java垃圾回收机制→Types of Java Garbage Collectors

    本文将会介绍各种不同类型的Java垃圾回收器.垃圾回收是Java用来将程序员从分配和释放内存的琐事中解放出来的自动过程. Java有四种类型的垃圾回收器, Serial Garbage Collect ...

  5. 细述 Java垃圾回收机制→Java Garbage Collection Monitoring and Analysis

    本文非原创,翻译自Java Garbage Collection Monitoring and Analysis 在Java中为对象分配和释放内存空间都是由垃圾回收线程自动执行完成的.和C语言不一样的 ...

  6. Java垃圾回收工作原理

    在C++中,在heap上分配对象比在stack上分配对象更加昂贵.程序需要找到合适的内存块,再返回内存的地址.但是在Java中垃圾回收器显著地提高了在heap上分配对象的速度.听起来会有些怪,但是这就 ...

  7. Java 垃圾回收机 GC Roots详解(Garbage Collection Roots)

    背景: 之前面试阿里支付宝,被问到常见的GC Root 是什么? 当时自己支支吾吾,明明自己看过深入理解Java 虚拟机这本书,但是就是回答不上来. 后来自己工作中,遇到内存泄漏问题.我百度,下载了M ...

  8. Java垃圾回收机制(Garbage Collection)

    引用博客地址:http://www.cnblogs.com/ywl925/p/3925637.html 以下两篇博客综合描述Java垃圾回收机制 第一篇:说的比较多,但是不详细 http://www. ...

  9. java垃圾回收机制串行_Java垃圾回收机制

    Java语言是一门自动内存管理的语言,不再需要的对象可以通过垃圾回收自动进行内存释放. Java运行时内存区域划分 JVM将Java程序运行时内存区域划分成以下几个部分: 程序计数器(Program ...

最新文章

  1. task ':app:compileDebugJavaWithJavac'.错误
  2. Python代码规范之简明概述
  3. 优先队列的数组实现(有序)
  4. 2019-2020-1 20175313 《信息安全系统设计基础》第二周学习总结
  5. day21-字节流和字符流
  6. 如何以管理员方式管理文件
  7. Atitit 图像金字塔原理与概率 attilax的理解总结qb23
  8. Oracle11g客户端client的下载与安装
  9. 解析Esri WebScene
  10. 美资软件公司JAVA工程师电话面试题目
  11. Lake Counting
  12. 信息隐藏 !!!!!!!!!!!!!
  13. 宝塔Linux面板问题QA汇总
  14. 请解析IP地址和对应的掩码,进行分类识别。要求按照A/B/C/D/E类地址归类,不合法的地址和掩码单独归类。
  15. 无标度网络/幂律分布、小世界网络
  16. 《达尔文自传》——达尔文最后的信仰作者:光在我心
  17. SATA学习笔记 8 ---SATA FIS类型与详细格式解析
  18. 如何提高运维团队的运维效率?
  19. 单片机红外线c语言,自己写的51单片机的红外线遥控接收程序(C语言)
  20. 766. 托普利茨矩阵(javascript)766. Toeplitz Matrix

热门文章

  1. 国内电子商务网站分析报告
  2. Matlab人工智能算法
  3. Tomcat优化学习
  4. 手机屏幕技术浅述(TFT、SLCD、AMOLED、NOVA、IPS、ASV)
  5. android studio 运行按钮为灰色的解决办法之一
  6. 经纬度和坐标之间怎么相互转换
  7. linux+磁带机检查,Redhat Enterprise Linux磁带机简单操作方法
  8. 字符串连接操作符“+”
  9. [渝粤教育] 西南科技大学 建筑工程定额与预算 在线考试复习资料2021版(2)
  10. 电信业服务流程设计--用例图