文章目录

  • 垃圾收集器和内存分配策略
    • 一、对象存活算法?
      • 1.1 引用计数法
      • 1.2 可达性分析算法
    • 二、引用类型?
      • 2.1 强引用
      • 2.2 软引用
      • 2.3 弱引用
      • 2.4 虚引用
    • 三、可达性和对象死亡?
      • 3.1 堆区
      • 3.2 方法区
    • 四、垃圾回收算法
      • 4.1 复制算法
      • 4.2 标记算法
      • 4.3 分代收集
      • 4.4 对比
    • 五、垃圾收集器
      • 5.1 Serial
      • 5.2 ParNew
      • 5.3 Parallel Scavenge
      • 5.4 Serial Old
      • 5.5 Parallel Old
      • 5.6 CMS(Concurrent Mark Sweep)
      • 5.7 G1
    • 六、内存分配与回收策略
      • 6.1 优先Eden分配
      • 6.2 大对象进入老年代
      • 6.3 老对象进入老年代
      • 6.4 动态年龄判断
      • 6.5 空间分配担保
    • 七、收集器选择?
      • 7.1 默认垃圾收集器
      • 7.2 不同场景GC策略选择?
    • 八、参考

垃圾收集器和内存分配策略

JVM系列文章是基于:《深入理解Java虚拟机:JVM高级特性与最佳实践》-周志明第二版

一、对象存活算法?

  • 对象存活算法是判断对象是否存活,或者说判断对象是否为垃圾的算法,这个是垃圾回收的前提。

1.1 引用计数法

  • 为对象添加一个引用计数器,如果对象被引用则计数器加1,引用失效则减1,计数器为0则表示该对象为垃圾。不过主流JVM并没有采用该方法。
  • 优缺点:
实现简单效率高
无法解决循环引用

1.2 可达性分析算法

  • 通过一种GC ROOTS作为起点,从节点开始通过引用链进行搜索,如果能够达到一个对象说明该对象不是垃圾,反之所有的GC ROOTS都无法到达该对象,说明它是垃圾。
  • GC ROOTS的选择。如果能够作为GC ROOTS,那么说明它自身肯定不是垃圾,主要包括全局性引用和执行上下文,可以作为GC ROOTS的对象如下:
1.虚拟机栈和本地方法栈中引用的对象;栈中的对象肯定不是垃圾,可以作为GC ROOTS(执行上下文)
2.方法区中的静态属性或者常量引用的对象;静态属性和常量引用的对象不是垃圾,可以作为GC ROOTS(全局性引用)

二、引用类型?

2.1 强引用

  • 常用的赋值就是强引用,存在一个对象的强引用的话,该对象永远不会被垃圾回收器回收。

2.2 软引用

  • OOM之前,垃圾回收器会尝试将软引用关联的对象回收,简而言之内存即将不足的时候才会回收,可以适合做缓存。

2.3 弱引用

  • 弱引用关联的对象,最多活不过下一次垃圾回收。不管内存够不够用,下一次垃圾回收丢会被干掉。(ThreadLocal)

2.4 虚引用

  • 虚引用对一个对象的生命周期没有影响,也无法通过虚引用来获得一个对象实例,作用仅仅只是在对象被回收的时候收到一个系统通知。

三、可达性和对象死亡?

3.1 堆区

  • 如果可达性分析中,从GC ROOTS开始对象A不可达,那么不并代表A一定会被回收。如下图:

  • 由上图可以看到在finalize()方法中可以完成对象的自我拯救,避免被回收,但是不推荐使用该方法来完成一些操作,比如关于资源回收的操作(使用try…catch…finally)。

3.2 方法区

  • 方法区的回收条件较为严苛,因此回收的效率也比堆区要低。比如对一个类对象的回收,需要至少满足下面的条件(还至少必要条件):
1.类的实例全部被回收
2.加载类的ClassLoader被回收
3.类的Class对象没有在任何地方被引用,无法通过反射构造对象
  • 相关参数:
-XX:TraceClassLoaing: 查看类加载信息
-XX:TraceClassUnLoaing: :查看类卸载信息

四、垃圾回收算法

4.1 复制算法

  • 将存活的对象集中复制到一块区域,再清除之前的一块区域。

4.2 标记算法

  • 标记清除:标记出全部的垃圾对象,再清除这些对象
  • 标记压缩(整理):标记垃圾对象,然后将存活的对象移动到内存的一端

4.3 分代收集

  • 堆包括新生代和老年代,新生代包含一个eden和两个survior,默认二者比例是8比1;在空间和性能做一个折中,这样指损失10%的内存
  • 默认的新生代老年代比例为1:2(可通过–XX:NewRatio指定),即新生代=1/3的堆空间
  • 针对新生代和老年代采用不同的垃圾回收算法

4.4 对比

回收算法 优点 缺点 应用区域
复制算法 简单、高效 浪费空间、存活对象多时效率低 新生代
标记清除 思想简单 存在碎片,效率不高 老年代 (CMS)
标记压缩 不存在碎片 老年代(G1)

五、垃圾收集器

  • 下面是7种垃圾收集器,收集器所在区域代表该收集器锁应用的区域,连线代表收集器可以搭配使用。

5.1 Serial

  • 单线程收集器,收集期间用户线程需要停止(STW)。简单高效,是Client模式下新生代的默认收集器,适用于单CPU或者堆不大的时候选择(一二百兆)。

5.2 ParNew

  • Serial的多线程版本,收集算法,控制参数、回收策略、对象分配规则等细节和Serial几乎一致,二者也共用了很多代码,不过单CPU性能不如Serial。
  • 运行在Server模式下的默认新生代收集算法,一个重要原因是除了Serial只有ParNew可以和CMS搭配使用。
  • 老年代使用CMS的话,新生代就会使用ParNew
-XX:ParallelGCThreads=4;设置垃圾收集时并发线程数
  • 复制算法

5.3 Parallel Scavenge

  • 使用复制算法,关注的是吞吐量而不是GC停顿时间,即关注垃圾收集时间对整个时间的占比。停顿时间短适合即时交互程序,吞吐量适合后台计算交互较少的情况。
-XX:MaxGCPauseMills:控制最大垃圾收集停顿时间(大于0的毫秒数,不过这并不意味着效率的提供,这里会牺牲吞吐量,需要综合考虑)
-XX:GCTimeRatio:设置吞吐量(0到100的数字,比如n代表垃圾收集的时间不超过1/(1+n) )
-XX:+UseAdaptiveSizePolicy:开启之后就不需要它设置新生代大小,新生代比例,晋升年龄等参数了,Parallel Scavenge会使用自适应调节策略来达到预定的吞吐量和最大收集时间。

5.4 Serial Old

  • Serial的老年代版本,标记整理算法,其作用有二,1.5之前只有它能够搭配Parallel Scavenge使用,另外作为CMS的替补,为什么需要替补,在接受CMS的时候会提到。
  • 也会STW,模式和Serial类似,单线程,但是算法不一样。

5.5 Parallel Old

  • Parallel Scavenge的老年代版本,标记整理算法,1.6才有,因此1.5之前需要Serial Old和Parallel Scavenge搭配使用。
  • 在吞吐量优先的场景,1.5之前因为只有Serial Old能够搭配Parallel Scavenge使用,但是Serial Old是单线程的,因此不能充分利用CPU多核优势,导致整体性能未必理想,在1.6 Parallel Old出现之后,吞吐量优先的才有了更好的组合。+Parallel Scavenge + Parallel Old,二者都是多线程版本,关注吞吐量。

5.6 CMS(Concurrent Mark Sweep)

  • Concurrent Mark Sweep:并发标记清除,注意了它和前面两个老年代的算法不一样,前两个是标记整理(无碎片),因此CMS会有内存碎片,这是它的缺点之一。不过CMS追求的是更短的GC停顿时间,整体而言STW的时间会更短。
  • 初始标记(迅速标记直接管理的对象,STW) -> 并发标记(Tracing,耗时最久,并行) -> 重新标记(修正,STW) -> 并发清除(耗时较久)
  • CMS:对CPU敏感,会占用25%的CPU,并且无法处理浮动垃圾(第四阶段产生的垃圾),因此不能等到老年代满了才收集垃圾,默认68的时候收集。如果预留的空间不足则会导致Concurrent Mode Failure,因此需要替补来继续收集垃圾
-XX:CMSInitiatingOccupancyFraction:触发CMS GC的内存使用比例。60%表示当内存使用达到60%触发CMS并发收集,太高的话,可能导致失败继而FullGC,太低则频繁触发GC。
-XX:UseCMSCompactAtFullCollection:这个前面已经提过,用于在每一次CMS收集器清理垃圾后送一次内存整理。
-XX:CMSFullGCsBeforeCompaction:设置在几次CMS垃圾收集后,触发一次内存整理。
  • 缺点:占用CPU、有浮动垃圾(第四阶段垃圾)、有碎片(标记清除)

5.7 G1

  • 初始标记(STW) -> 并发标记 -> 最终标记(STW) -> 筛选回收(STW)
  • 特点:
1.能够充分发挥多核优势,减少STW的时间
2.保留了分代收集的思想,但是新生代和老年代不再物理隔离,而是划分为一个一个区域,G1会建立可预测的停顿时间模型,评估不同区域回收垃圾的价值,
优先回收价值最大的区域,以此来提高垃圾回收的效率
3.整体来看是采用标记整理算法,从局部的2个区域来看是采用复制算法,因此没有碎片
4.可预测的停顿模型:能够指定在N毫秒内垃圾收集时间不超过M毫秒
  • G1通过区域的划分是为了避免垃圾回收的时候进行全堆扫描,他会为每一个堆维护一个Remembered Set,在程序对引用数据类型进行写操作的时候,它会产生一个写屏障中断写操作,检查这个引用是不是跨越了多个区域,如果是的话,他会把这个引用关系记录到该区域对应的Remembered Set中,这样在垃圾回收的时候,在枚举根节点的时候就能够从该Set中知道这些引用是该区域所持有的引用,就不需要扫描全部的堆来寻找该区域所持有的引用,一次来避免全堆扫描。
  • G1在并发标记阶段程序的引用关系还会发生变化(和CMS类似),G1会把该阶段的变化信息记录到Remember Set Logs中,然后在最终标记节点根据这些Logs修正并合并到Remembered Sets中,在筛选回收的时候,根据不同区域的回收价值成本进行排序,优先回收价值大的区域。

六、内存分配与回收策略

6.1 优先Eden分配

  • 一个对象创建的时候优先分配在新生代,如果此时新生代空间不足,则会触发Minor GC,Minor GC比较频繁,并且比较快

6.2 大对象进入老年代

  • 大对象直接分配到老年代可以避免在新生代频繁的GC,比如新生代还要一部分空间但是因为大对象过大导致不得不Minor GC来提供空间。
  • 可以设置阈值,让大于该大小的对象直接分配到老年代
-XX:PretenureSizeThreshold=128145728(3MB)
该参数只对Serial和ParNew有效,对Parallel Scavenge无效

6.3 老对象进入老年代

  • 每一个对象包含一个Age计数器,每次经过一个Minor GC如果对象还存活,则Age增1,到达阈值的时候,对象会被放置到老年代
-XX:MaxTenuringThreshould=10(默认15)

6.4 动态年龄判断

  • 如果绝对的按照6.3所描述的年龄来将对象晋升到老年代,那么相对不灵活,有一种情况,如果Survivor中相同年龄的对象占据空间大于Survivor的一半,那么大于或者等着该年龄的对象将全部移到老年代

6.5 空间分配担保

  • Minor GC的时候,JVM会检查老年代中连续的空间是否大于新生代中存活的对象大小或者是否大于历次晋升的平均大小,如果大于,则进行Minor GC,如果不大于,则进行一次Full GC。

七、收集器选择?

7.1 默认垃圾收集器

  • Client模式下,默认是:Serial+Serial Old (-XX:+UseSerialGC)
  • Server模式下,默认是:Parallel Scavenge + Serial Old ,也是JDK1.8的默认值 (Parallel Scavenge Mark Sweep -XX:+UseParallelGC)。
JVM选项 使用的垃圾收集器 备注 图示
-XX:UseSerialGC Serial + Serial Old client模式下的默认值 1
-XX:+UseParallelGC Parallel Scavenge + Serial Old Server模式下的默认值,JDK8默认 2
-XX:+UseParNewGC ParNew + Serial Old 3
-XX:+UseConcMarkSweepGC ParNew + CMS + Serial Old CMS的Concurrence Mode Failure后需要Serial Old完成Full GC,关注响应时间 4
-XX:+UseParallelOldGC Parallel Scavenge + Parallel Old 关注吞吐量 5
-XX:+UseG1GC G1 G1 G1

JVM选项 使用的垃圾收集器 ManagementFactory得到的垃圾收集器名字 新生代老年代名称
-XX:UseSerialGC Serial + Serial Old Copy / MarkSweepCompact DefNew / Tenured
-XX:+UseParallelGC Parallel Scavenge + Serial Old PS Scavenge / PS MarkSweep PSYoungGen / ParOldGen
-XX:+UseParNewGC ParNew + Serial Old ParNew / MarkSweepCompact ParNew / Tenured
-XX:+UseConcMarkSweepGC ParNew + CMS + Serial Old ParNew / ConcurrentMarkSweep ParNew / CMS
-XX:+UseParallelOldGC Parallel Scavenge + Parallel Old PS Scavenge / PS MarkSweep PSYoungGen / ParOldGen
-XX:+UseG1GC G1 G1 Young Generation / G1 Old Generation
  • 需要注意的是,在-XX:+UseParallelGC 模式下,UseParallelGC收集器架构中本身有 PS MarkSweep 收集器来进行老年代的收集,其实并不是使用的Serial Old,但是PS MarkSweep和 Serial Old 的实现非常接近,很多官方资料都直接使用Serial Old 代替PS MarkSweep,因此可以直接认为UseParallelGC模式下使用:Parallel Scavenge + Serial Old

7.2 不同场景GC策略选择?

  • 默认情况下不需要怎么调优:JDK8就是-XX:+UseParallelGC配置,Parallel Scavenge + Serial Old,老年代是单线程收集。
  • 吞吐量优先的场景,比如计算场景而不是即时响应的服务,可以使用 Parallel Scavenge + Parallel Old 吞吐量优先的组合,-XX:+UseParallelOldGC。
不过在1.6之后才提供Parallel Old,由此在1.6之前Parallel Scavenge很尴尬,因为他只能和老年代的单线程Serial Old使用,因此整体上性能未必有优势。
  • 如果应用的堆大小在100MB以内。 应用在一个单核单线程的服务器或者堆比较小的情况(100MB),考虑使用SerialGC,比如ParNew + Serial Old。
注意单核服务器尽量不要使用CMS,比较占CPU,尤其是核心比较少的时候。
  • 追求快速响应的场景,并且不是CPU密集型,服务器CPU核心多,选择CMS很合适

  • 堆内存很大或者长期运行的系统,使用G1(G1不会产生碎片-内存压缩算法)

  • 下面是不同收集器的对比

垃圾收集器 分代 串/并行 算法 场景
Serial 年轻代 复制 CPU单核+小堆
ParNew 年轻代 复制 CPU多核+小堆
Parallel Scavenge 年轻代 复制 吞吐量优先
CMS 老年代 并发标记清除 CPU多核+响应时间优先
Serial Old 老年代 标记整理 CPU单核+小堆
Parallel Old 老年代 标记压缩 吞吐量优先
G1 整堆 标记整理+复制 大堆+响应时间优先

八、参考

  • [1] 《深入理解Java虚拟机:JVM高级特性与最佳实践》-周志明第二版)

02-垃圾收集器和内存分配策略相关推荐

  1. 垃圾收集器与内存分配策略(五)之垃圾日志与常见参数

    2019独角兽企业重金招聘Python工程师标准>>> 垃圾收集器与内存分配策略(五)--垃圾日志与常见参数 理解GC日志 每个收集器的日志格式都可以不一样,但各个每个收集器的日志都 ...

  2. jvm(3)-垃圾收集器与内存分配策略

    [0]README 0.1)本文部分文字转自:深入理解jvm,旨在学习 垃圾收集器与内存分配策略 的基础知识: [1]垃圾回收概述 1)GC(Garbage Collection)需要完成的3件事情: ...

  3. JVM2:垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 文章目录 垃圾收集器与内存分配策略 对象回收 引用计数算法 可达性分析算法 四种引用类型 生存与死亡 回收方法区 垃圾收集算法 标记清除法 复制算法 标记-整理算法 HotS ...

  4. JVM:垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 1.对象已死吗 1).引用计数算法 引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就 ...

  5. java eden分配参数,JVM垃圾收集器与内存分配策略,

    垃圾收集器与内存分配策略 对象存活判断 引用计数算法 给对象添加一个计数器,每有一个引用+1,当引用失效-1,若为0则不在被使用. 可达性分析算法 对象是否可到达GC roots 或者说GC root ...

  6. 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

  7. java_opts gc回收器_jvm垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略: 以下参考周志明的<>. 判断对象是否存活: 引用计数:通过判断对象被引用的次数(为0,则表示不可被使用),但这很难解决对象相互循环引用的问题. 根搜索算法:即采 ...

  8. jvm垃圾收集器与内存分配策略

    2019独角兽企业重金招聘Python工程师标准>>> 垃圾收集器与内存分配策略: 以下参考周志明的<<深入理解jvm高级特性与最佳实践>>. 判断对象是否存 ...

  9. java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略

    java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略 GC需要完成的三件事情:哪些内存需要回收.什么时候回收.如何回收 垃圾回收器在对堆进行回收前,首先要确定那些对象存活,哪些对象已经死去,判断的 ...

  10. 03-1 手敲八千字,认识垃圾收集器必须清楚的前置知识【垃圾收集器的内存分配策略】

    03-1 50问!了解垃圾收集器必须清楚的前置知识--垃圾收集器的内存分配策略 author:陈镇坤27 创建时间:2021年12月27日01:58:45 字数:7932 文章目录 03-1 50问! ...

最新文章

  1. RelativeLayout布局,不希望文本盖住其他组件
  2. OnFocuChangeListener焦点事件
  3. python【蓝桥杯vip练习题库】ADV-272 change(思维)
  4. 你对Java网络编程了解的如何?Java NIO 网络编程 | Netty前期知识(二)
  5. Hibernate Native SQL查询示例
  6. apk破解工具介绍与使用
  7. 2019全国知识图谱与语义计算大会
  8. 包装应由厂家回收利用
  9. android 单手模式开发,单手操作毫无压力 安卓单指缩放技巧
  10. Ubuntu 16.10(x86) Install WordPress SRV 4.7.1-1-5
  11. bbsmax mysql_MySQL中自己不太常用的命令
  12. max模型怎么导入ue4_UE4模型导入流程,3DMAX贴图丢失问题解决方法
  13. 有趣好玩的python编程网站
  14. ASPICE过程开发
  15. 开发多语言常用国家地区对照表(最全的各国地区对照表) 多语言简称
  16. 内网安全-隧道穿透漫游(二)
  17. GPS基带P码处理总结——P码处理的要素及方法
  18. 武警摔擒、擒敌拳1-16动连贯动作 分解动作
  19. 阿里系App抓包分析(三)
  20. matlab绘制分段函数,二维函数

热门文章

  1. 系统系统应用权限报错Signature|privileged permissions not in privapp-permissions whitelist的解决
  2. Unity导出android工程的奇葩大坑
  3. 大唐地产IPO:脚踏“红线”,业绩下滑明显,吴迪持股约16%
  4. 常熟常客隆广场电能管理系统的设计与应用
  5. 找出符合规则为ABAB,ABCABC的QQ号码
  6. stm32f103rct6使用内部晶振作为时钟源
  7. 三核异构,跨界处理新引擎—君正X2000 的跨界能力
  8. 高等数学笔记:反常积分敛散性判别法
  9. 计算机课程项目设计,计算机专业基于课程群的eip_cdio项目设计新.pdf
  10. Java扩展Nginx之二:编译nginx-clojure源码