高性能-GC2

简介: 上次我们讲到GC的一些基础知识,感谢评论的大佬给我指点。

带着问题去思考!大家好

上次我们讲到GC的一些基础知识,感谢评论的大佬给我指点。

配置参数

  关于配置垃圾回收器的方法不是很多,所以建议不要随意去动,垃圾回收器的配置以及调优,很大程度上由硬件配置,可用资源和程序的行为决定。屈指可数的几个参数也是用于控制很高层的行为,主要取决于程序的类型,

工作站模式还是服务器模式?

  在垃圾回收默认采用的工作站模式。工作站模式,所有的GC都运行于触发垃圾回收的线程中,优先级(Priority praɪˈɔːrəti)也相同。对于单机处理器的计算机而言,工作站模式是唯一选择,配置其他参数也是无效的

  服务器模式下,GC会为每个逻辑处理器或处理器核心创建各自专用的线程。这些线程的优先级最高的,但在需要进行 垃圾回收之前会一直保持挂起状态。垃圾回收完成后,这些线程会再次进如休眠状态。

  此外,CLR还会为每个处理器创建各自肚子的内存堆,每个处理器堆都包含1个小对象堆和1个LOH。从应用程序角度来看,就只有一个逻辑内存堆。你的代码不清楚对象属于哪一个堆,对象引用会在所有堆之间交叉进行。

  多个内存堆的存在会带来一写好处。

  1:垃圾回收可以并行进行,每个垃圾回收线程负责回收一个内存堆,这可以让垃圾回收的速度明显快鱼工作站模式

  2:某些情况下,内存分配的速度也会更快一些,特别是对LOH而言,因为会在所有内存堆中同时进行份分配。

具体配置


<gcServer enabled="true">

在runtime节点下。

说到底到底用哪个好呢?如果应用程序运行于专为你准备的多处理器主机上,那么选择服务器模式。这样大部分情况,都能让垃圾回收占用的时间降至最低。

如果需要多个托管进行共用一台主机,那么服务器模式的垃圾回收会创建多个高优先级的线程。如果多个应用程序都这么设置,那线程调度就会相互带来负面影响,如果你确定想让同一台主机的多个应用程序使用服务器模式的垃圾回收,还有一种做法,就是让存在竞争关系的应用程序都集中在指定的几个处理器上运行,这样CLR只会为这些处理器创建自己的内存堆。

后台垃圾回收

首先BackgroundGC只会影响第2代内存堆的垃圾回收行为,第0代和第1代的垃圾回收仍会采用前台的垃圾回收,也就是会阻塞所有应用程序的线程。后台垃圾回收由一个专用的第2代堆垃圾回收线程完成,

如果关闭,但不建议

<gcConcurrent enabled="false"/>

 

低延迟模式

如果一段时间内确保较高的性能,可以通知GC不要执行开销很大的第2代垃圾回收。请根据其他参数把GCSettings.LatencyModel属性赋为以下值之一。

  LowLatency---仅适用与工作站模式GC,禁止第2代垃圾回收。

  SustainedLowLatency--适用与工作站和服务器模式的GC,禁止第2代完全垃圾回收,但允许第2代后台垃圾回收,必须启用后台垃圾回收。

因为不会再进行碎片整理了,所以这两种参数都会显著增加托管堆的大小,如果你的进程需要大量内存,就应该避免使用这种延迟模式。在即将进入低延迟模式前,最好是能强制执行一次完全垃圾回收,这通过调用GC.Collect即可完成,当代离开低延迟模式后,马上再做一次完全垃圾回收,

以下条件都满足时,才能开启低延迟模式

  • 完全垃圾回收的持续时间过长,是程序正常运行时绝对不能接受的
  • 应用程序的内存占用量远低于可用内存数。
  • 无论是关闭低延迟模式期间,程序重启,还是手动执行完全垃圾回收期间,应用程序都可以保持存活状态。

如何减少内存的分配量

  要想减少内存的分配数量,请严格审查没一个对象。

  • 是否真的需要这个对象?
  • 对象中有没有什么成员可以摒弃
  • 数组能否减小一些?
  • 基元类型(Primitive)是否减少体积(比如Int64换成Int32)?
  • 有些对象是否很少用到,仅必要时在进行分配?
  • 优先类能否转成“结构”(Struct)?这样就能存放在堆栈中,或者是成为其他对象的成员
  • 分配的内存很多,是否只用了一小部分?
  • 是否用其他途径获取数据?

首要规则

  其实我们都已经知道,针对垃圾回收器,存在一条基本的高性能编码规则。其实垃圾回收器明显就是按照这条规则进行设计。

只对第0内存堆中的对象进行垃圾回收

  简而言之,对象的生存期尽可能短暂,这样垃圾回收器根本就不会去触及它们,或者做不到转瞬即逝,就让对象尽快提升到 第2代内存堆并永远留在哪里,再也不会被回收。这意味着需要一直保存一个堆长久存活对象的引用,通常这也意味要把可重用的对象进行池化(Pooling),特别是LOH中的所有对象。

缩短对象的生命期

  • 对象的作用域越小,在垃圾回收时就越没有机会被提升到下一代。
  • 一般来说,对象在使用前不应该被分配内存。除非创建对象的开销太大。需要提早创建才不至于影响到其他操作的执行,
  • 另外在使用对象时,确保对象尽快地离开作用域。对于局部变量而言,可能是最后一次局部使用之后,甚至可以在方法结束之前。
  • 如果你的代码要对某个对象进行多次操作,请尽量缩短第一次和最后一次使用的间隔,这样GC能尽早的回收这个对象了,
  • 如果某个对象的引用是一个长时间存活对象的成员,有时你需要把这个引用显示地设置为null,这也许会稍微增加一点代码的复杂度,因为你需要随时准备多检查一下null值,

减少对象树的深度

  我们都知道,GC将会沿着对象引用遍历,在服务器模式GC中,一次会有多个线程同时遍历,你肯定希望尽可能的利用这种并发机制,但如果有某个线程陷入一条很长的嵌套对象链中,那么整个垃圾回收过程就得等整个线程完成工作后才会结束。

减少对象间的引用

如果对象引用了很多其他对象,垃圾收集器对其遍历时就要耗费更多的时间。如果垃圾回收引用的暂时时间较长,往往意味着有大型,复杂的对象间引用关系存在,如果难以确定对象所有的被引用关系,还有一个风险就是何难预测对象的生存期,减少对象引用的复杂度,不仅对提高代码质量有利,而且可以让代码调试和修正性能问题变得更加容易,还需要注意,不同代的内存堆之间的对象引用可能会导致垃圾回收器的低效运行,特别是从老对象中引用新对象的情况,比如第2代内存堆中有个对象包含了对第0代内存对对象的引用,这样每次第0代垃圾回收,总有一部分第2代内存堆中的对象不得不被遍历到。以便确认它们是否还持有堆第0代对象的引用。

 


相关文章
|
Java 算法 程序员
带你读《新一代垃圾回收器ZGC设计与实现》之一:垃圾回收器概述
JDK 11于2018年9月25日正式发布,这个版本引入了许多新的特性,其中最为引人注目的就是实现了一款新的垃圾回收器ZGC。
|
4月前
|
监控 Java 测试技术
JVM 性能调优 及 为什么要减少 Full GC
JVM 性能调优 及 为什么要减少 Full GC
121 4
|
15天前
|
程序员 开发者
分代回收和手动内存管理相比有何优势
分代回收和手动内存管理相比有何优势
|
4月前
|
存储 算法 安全
(八)JVM成神路之GC分区篇:G1、ZGC、ShenandoahGC高性能收集器深入剖析
在《GC分代篇》中,我们曾对JVM中的分代GC收集器进行了全面阐述,而在本章中重点则是对JDK后续新版本中研发推出的高性能收集器进行深入剖析。
167 12
|
4月前
|
存储 Java 开发者
探索Java内存管理:从垃圾收集到性能优化
本文深入探讨了Java的内存管理机制,重点分析了垃圾收集(GC)的工作原理及其对应用程序性能的影响。通过对比不同的垃圾收集器,并结合具体的性能优化案例,文章为Java开发者提供了一套实用的内存管理和优化策略。旨在帮助读者更好地理解如何通过调优JVM来提升应用的性能和稳定性。
|
6月前
|
存储 分布式计算 前端开发
jvm性能调优实战 - 26一个每秒10万并发的系统如何频繁发生Young GC的
jvm性能调优实战 - 26一个每秒10万并发的系统如何频繁发生Young GC的
140 0
|
存储 算法 Java
JVM架构和内存管理优化
JVM架构和内存管理优化
|
Java
JVM GC频繁优化
JVM GC频繁优化
194 0
|
存储 Java 块存储
JVM性能
JVM性能
130 0
JVM性能
|
监控 Java
【JVM性能优化】面向CMS垃圾回收器的性能优化方案
【JVM性能优化】面向CMS垃圾回收器的性能优化方案
409 0
【JVM性能优化】面向CMS垃圾回收器的性能优化方案