Java 进行GC的时,会从GC root进行可达性判断,常见的GC Root有:

  1. 通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root
  2. 处于激活状态的线程
  3. 栈中的对象
  4. JNI栈中的对象
  5. JNI中的全局对象
  6. 正在被用于同步的各种锁对象
  7. JVM自身持有的对象,比如系统类加载器等。

在调查OOM的时候可以根据GC Root来推导

对此需要了解常用的GC算法

  1. 标记回收算法
    从GC root进行遍历,把可达对象都标记,剩下那些不可达的进行回收,这种方式需要中断其他线程,并且可能产生内存碎片

  2. 复制算法
    把内存区域分为两块,每次使用一块,GC的时候把一块中的内容移动到另一块中,原始内存中的对象就可以被回收了。

  3. 标记压缩算法
    和标记回收差不多,但是在回收的时候会对可达对象进行整理,将其压缩到内存的一段,避免内存碎片

  4. 分代算法
    将内存区域分代,对不同的代使用不同的回收算法,通常分为新生代,老年代,和永久带。
    新生代一般包含三个区域,Eden区和两个Survivor区,新生代一般采用复制算法

老年代一般采用标记压缩算法

永久带一般是方法区,JVM规范并没有强制要求说必须要回收这个区域。

在Dalvik虚拟机中的堆其实是分为了两个,一个是Zygote堆,一个是Active堆,Zygote堆中主要是预加载的各种资源和对象,这个堆很少被写。我们分配的堆内存一般都是在Activie堆进行分配的。

在说到Java堆内存的时候,我们必须要对这几个指标很了解:

  1. Xms: starting size,在Dalvik虚拟机启动的时候会预先分配的一块大小的堆内存
  2. Xmx: 最大堆内存,超过这个值就会OOM
  3. XX: 不受控情况下最大的堆内存,就是我们在AndroidManifest中申请largeHeap属性的时候所允许的最大堆内存。

在GC中还有一个非常重要的指标,叫做堆目标利用率U,就是已分配的堆大小和堆总共的大小的比值,在GC的时候,垃圾回收器会尽可能把利用率往目标利用率靠近。

如果我们手动生成一些几百K的对象,来增大堆内存的大小,然后想着这样不用每次都需要对堆扩容,可以减少GC。但是这样是不存在的,反而会引起频繁的CONCURRENT GC,因为在分配这些对象的空间的时候会引发GC,然后这些对象的很快被使用完销毁,仍然会导致GC,因为JVM会把利用率往目标利用率上靠近,堆大小仍然会缩小到之前的大小。

看一下GC的类型

  1. GC_FOR_MALLOC:在堆上为对象分配内存的时候空间不足
  2. GC_CONCURRENT:JVM自动触发的gc,一般是在堆内存快要耗尽的时候触发
  3. GC_EXPLICIT:程序猿主动调用System.gc触发
  4. GC_BEFORE_OOM: 在OOM之前最后的努力了

看一下分配内存情况下GC的三次流程:

  1. 首先不扩容堆大小的情况下分配内存,如果成功则成功(这是轻量级操作),如果失败,则进行第一次GC
  2. 第一次GC会打印出GC_FOR_MALLOC,这次GC不会回收软引用对象
  3. 第一次GC完成之后会再次尝试分配内存,如果还是分配失败,则进行第二次GC
  4. 第二次GC会扩容堆内存,然后在尝试分配内存。
  5. 再次尝试分配内存,如果分配失败就要进行第三次,也就是最后一次GC了
  6. 最后一次GC会回收软引用所指向的对象,如果还不行的话,这次会打印出GC_BEFORE_OOM,如果还是失败,则会抛出OOM异常了。

当通过GC成功为一个对象分配了内存之后,JVM会判断一下当前的剩余内存是否达到了GC_CONCURRENT的阈值,如果达到了,JVM会自动执行一下并行GC,打印出GC_CONCURRENT. 那这个阈值是怎么来的呢? 会根据我们设置的 堆目标利用率来计算。

Android的Dalvik虚拟机大部分是采用的 标记清理和拷贝算法,几乎没有采用分代算法的。我们知道 标记清理算法 很容易产生内存碎片。 所以我们在写代码的时候要尽可能避免产生大量的小对象。 小对象能复用的时候尽量去复用。

从5.0以后,Android的虚拟机从Dalvik切换到了ART,我们需要了解一下ART中对于GC的处理。

ART将堆内存分为了四个部分(Dalvik分为了两个部分):Image Space, Zygote Space, Allocation Space, Large Object Space。

Image Space(主要是Android自己的一些需要预加载的类)和Zygote Space合起来相当于Dalvik中的Zygote堆。

Allocation Space相当于Dalvik的Activie堆。

Large Object Space是一些离散地址的集合,主要是用来分配大对象。Large Object Space中分配的都是大于12KB的数组对象。

ART GC和Dalvik GC的区别

ART GC和Dalvik GC在分配内存时候的触发逻辑是一样的。他们主要的区别在算法的选择上。

Dalvik GC一般只会有一种GC算法,但是ART GC会有多种GC算法,并且会在不同的时机选择不同的算法。

下面就来讲一下ART GC选择不同算法的时机:

  1. 非并发GC流程:
    1.1 通过InitializePhase初始化GC
    1.2 挂起所有ART运行时线程
    1.3 通过MarkingPhase方法执行标记
    1.4 通过ReclaimPhase方法执行清理
    1.5 恢复所有被挂起的线程
    1.6 调用FinishPhase结束GC

  2. 并行GC流程(实际上并非真正的并行)
    2.1 通过InitializePhase初始化GC
    2.2 获取堆上的锁
    2.3 调用MarkingPhase方法并行标记(此处是真正并行的
    2.4 释放堆上的锁。

2.5 挂其所有的ART运行时线程
2.6 调用HandleDirtyObjectPhase方法处理并行标记阶段被修改的对象(此刻并没有真正回收)
2.7 恢复所有ART进程

2.8 不断重复上面三部,直到所有被标记的对象都被处理完成

2.9 接下来就是调用ReclaimPhase进行回收,然后调用FinishPhase进行结束。

所以无论是否并发,都存在挂起所有ART进程的情况,不同的是并发的时候,单次挂起ART进程的时间会更短(如果并发清理只进行了一次循环,那么就只需要挂起单次,时间消耗会更少。如果并发清理执行了多次的循环,那么时间消耗可能会比非并发要多)。

从上面可以看出,ART并行回收在Mark阶段,Reclaim阶段和Finish阶段是不需要挂起线程的,只有在HandleDirtyObjectPhase阶段才需要挂起线程。

ART GC相对于Dalvik GC不仅是在算法(包括引入更多算法和在同一个算法上做优化)上进行了优化,减少了暂停时间,而且单独开辟了Large Object Space对大对象进行处理。而且ART GC在处理后台应用的时候会对后台应用的内存进行 标记压缩GC,从而减少内存碎片。

下面在稍微说一下分代收集,这是大部分JVM上采用的垃圾收集机制(Android并没有采用)。

分代收集的概念是指在不同的代上使用不同的垃圾回收算法,一般来讲 在新生代上使用复制算法,在老年代上使用标记清理或者标记压缩算法。在永久代上使用标记压缩算法(JVM规范并没有要求要对永久代进行回收)。

垃圾收集器是GC的具体实现,不同的垃圾收集器针对的代也是不一样的,简单介绍一下。

Serial收集器:主要针对针对新生代,什么都不配置的话JVM默认的收集器,采用复制算法
ParNew收集器:主要 针对新生代, Serial的多线程版本。

Parallel Scanvenge: 主要针对新生代,主要用在服务器上,这种垃圾收集器也是采用复制算法,但是关注CPU吞吐量,强调CPU运行垃圾回收的时间和CPU执行用户代码的时间的比例。

Serial Old收集器:主要针对老年代,采用标记清理和标记压缩算法,单线程版本

Parallel Old收集器:主要针对老年代,多线程版本。

CMS(Concurrent Mark Sweep):大名鼎鼎的并行标记清理收集器,主要针对老年代,多线程,主要关注停顿时间,这个在嵌入式设备上非常有名,因为用户对于停顿时间很敏感。

CMS的原理是三次标记一次清除。
第一次标记先找到应用中所有的GC root,这次标记需要暂停用户线程。但是时间非常非常短。
第二次标记不需要暂停用户线程,根据第一次标记的结果去寻找不可达对象。
第三次标记也需要暂停用户线程,因为在第二次标记的过程中,GC root可能发生了变化,这个时候就要在把变化的重新标记一下。

三次标记完成之后就会执行清理过程。

由于在第二次并行标记的时候用户线程仍然在执行,所以需要预留足够的内存给用户线程使用,所以CMS并不会在老年代满了之后才执行Full GC, 一般是在老年代使用了一大半的时候就会执行一次Full GC.

同样的,CMS由于采用标记清理算法,也会导致内存碎片的产生。

并发清理是指:垃圾清理的过程中用户线程还可以继续工作,所以CMS是并发收集器。

并行清理是指:有多个垃圾回收线程在执行清理,所以Parallel收集器是并行收集器。

常见Java GC Root相关推荐

  1. java常见的gc回收器_一篇文章让你了解GC垃圾回收器

    简单了解GC垃圾回收器 了解GC之前我们首先要了解GC是要做什么的?顾名思义回收垃圾,什么是垃圾呢? GC回收的垃圾主要指的是回收堆内存中的垃圾对象. 从根对象出发,所有被引用的对象,都是存活对象 其 ...

  2. Java垃圾回收(GC)、找垃圾的方式、GC Root、GC停顿、引用、垃圾收集算法、收集器、GC日志、安全点、安全区域

    1.垃圾回收 1.1概念 在Java语言中,垃圾回收(Garbage Collection,GC)是一个非常重要的概念. 它的主要作用是回收程序中不再被使用的内存,Java提供的GC功能可以自动监测对 ...

  3. java gc日志乱码_Java中9种常见的CMS GC问题分析与解决(四)

    目前,互联网上 Java 的 GC 资料要么是主要讲解理论,要么就是针对单一场景的 GC 问题进行了剖析,对整个体系总结的资料少之又少.前车之鉴,后事之师,美团的几位工程师历时一年多的时间,搜集了内部 ...

  4. 【Java 虚拟机原理】垃圾回收算法 ( 可达性分析算法 | GC Root 示例 | GC 回收前的两次标记 | finalize 方法示例 )

    文章目录 一.可达性分析算法 二.GC Root 示例 三.GC 回收前的两次标记 四.finalize 方法示例 一.可达性分析算法 在 堆内存 中 , 存在一个 根对象 GC Root , GC ...

  5. java 弱引用定位_手把手教你定位常见Java性能问题

    概述 性能优化一向是后端服务优化的重点,但是线上性能故障问题不是经常出现,或者受限于业务产品,根本就没办法出现性能问题,包括笔者自己遇到的性能问题也不多,所以为了提前储备知识,当出现问题的时候不会手忙 ...

  6. java gc回收算法_Java GC回收算法-判定一个对象是否可以回收

    开源推荐 推荐一款一站式性能监控工具(开源项目) Pepper-Metrics是跟一位同事一起开发的开源组件,主要功能是通过比较轻量的方式与常用开源组件(jedis/mybatis/motan/dub ...

  7. java gc回收机制种类_JAVA的垃圾回收机制(GC)

    1.什么是垃圾回收? 垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制. 2.什么时候 ...

  8. JAVA GC(Garbage Collection)及OOM那些事

    2019独角兽企业重金招聘Python工程师标准>>> JAVA运行时内存区域 程序计数器 一块很小的内存空间 当前线程所执行的字节码的行号指示器 当前线程私有 不会出现OutOfM ...

  9. 常见的GC算法(GC的背景与原理)

    常见的GC算法(GC的背景与原理) GC 是英文词汇Garbage Collection的缩写,中文一般直译为 "垃圾收集".当然也会说 "垃圾回收". 三种垃 ...

最新文章

  1. 39岁程序员住进养老院,早6晚9的作息,网友羡慕哭了
  2. ncurses鼠标事件:mousemask(),ALL_MOUSE_EVENTS,KEY_MOUSE,getmouse(),mouse_grafo(),wmouse_trafo()
  3. 3d激光雷达开发(生成RangeImage)
  4. 【Statistics】均值
  5. 监控三剑客<cacti、nagios、zabbix>
  6. 【转载】聪明说话35招
  7. 保研后,你们都怎么样了?
  8. ZZULIOJ 1190: 按出生日期排序(结构体专题)
  9. CVE-2022-28512 Fantastic Blog CMS 1.0 版本存在SQL注入漏洞
  10. 帝国cms php循环,帝国CMS listshowclass循环栏目标签
  11. 2018 “百度之星”程序设计大赛 - 初赛(A)
  12. [bowtie2, libtbb.so.2]error while loading shared libraries: libtbb.so.2: cannot open shared object
  13. 安装calico网络插件后K8s集群节点间通信找不到主机路由(no route to host)
  14. TLM通信(transaction level modle)
  15. c语言如何找到进程基址,从0开始学模拟挂(一)--找内存基址,包含原理 _ 脚本
  16. 5G移动性增强技术分析
  17. mysql 1326_SQL Server 出现Error: 1326错误)问题解决方案
  18. while循环语句 while-do-done
  19. c语言基础学习12_项目实战:快译通字典
  20. 【成长之路】应届生的毕设经验

热门文章

  1. log4j,ConversionPattern的英文全拼说明
  2. 安卓开发问题一:在虚拟机上运行APP不能运行,即闪退现象
  3. xml java jaxb_【Java】JAXB操作XML用法详解
  4. C/C++内存问题检查利器—Purify (五)
  5. 活力长者社区(C语言)
  6. 元素垂直居中的五种方式
  7. Cesium|xt3d gltf标绘绘制编辑
  8. Downloading https://ultralytics.com/assets/Arial.ttf下载过于缓慢问题
  9. 接facebook广告_Facebook广告抵制并没有削弱Facebook。 但它发送了一条消息
  10. mysql data文件恢复_mysql 通过data文件下来恢复数据