高性能-GC

简介: 相对.NET 来说。CLR去处理了,C,C++这些就需要手动去垃圾回收。

带着问题去思考!大家好

相对.NET 来说。CLR去处理了,C,C++这些就需要手动去垃圾回收。

  GC大部分容易察觉的性能问题。其实很多问题实际是哪个都是由于对垃圾回收器的行为和预期结果理解有误。在,NET环境中,你需要更多的关注内存的性能,那么接下里我们主要是讲内存性能问题。

  GC实际上会调整体提高内存堆[1]的性能,因为他能高效的完成内存分配和碎片整理工作。

  在Windows的本机代码模式下,内存堆维护着一张空闲内存块的列表,用于内存的分配,尽量用低碎片化的内存堆,因为长时间运行,还要考虑内存碎片问题,内存占用率会持续增长。所以本机代码程序用大量代码实现了自己的内存分配机制,把默认的malloc函数给替换掉

  在.NET环境中,内存分配的工作量会很小,因为内存总是整段分配的。所以不会比内存的扩大,减小和比较增加多少开销,不存在遍历空闲内存列表,几乎不会出现内存碎片,GC内存堆的效率还会更高的,因为连续分配的多个对象往往在内存堆中也是连续存放,提高就近访问的可能性。

  在默认的内存分配流程中,有一小段代码会先检查目标对象的大小,看看内存分配缓冲区中所剩的内存够不够,如果内存分配缓冲区已耗尽,就会交由GC分配程序来检索足以容纳目标对象的空闲内存。然后一个新的分配缓冲区就会被保留下来。

  接下里看下内存分配的过程

class MyObject         {             int x;             int y;             int z;         }         staticvoid Main(string[] args)         {             var x = new MyObject();         }

了解汇编语言的都知道这一流程,

image.png

MOV指令是数据传送指令,其他的大家可以自行去查约。大致是说,把类的方法表指针拷贝到ecx(计数暂存器)中,作为new ()的参数,调用new,把返回值(对象的地址)拷贝到寄存器中。这里大家大概了解一下就可以了

基本运作方式

 

在托管进程中存在两种内存堆(本机堆和托管堆),这里我们说下托管堆,本机堆大大家可以自行了解下,

CLR在托管堆(Managed Heap)上为所有的.NET托管对象分配内存,也称之为GC堆,因为其中的对象都要受到垃圾回收机制的控制。

  托管堆又分为两种,小对象堆和大对象堆(LOH),都拥有自己的内存段(Segment[ˈseɡmənt]).内存段的大小视配置和硬件环境而定。

小对象堆

  小对象堆有什么?它又是怎么变化的?

  小对象堆的内存堆分为3代,0,1,2代。第0代和第1代总是位于同一个内存段中,第2代可能跨越多个内存段,LOH也可以跨越多个内存段。包含第0代和第1代堆的内存段被称为暂时段(Ephemeral [ɪˈfemərəl] Segment ˈseɡmənt]

image.png

小对象堆中分配内存的对象生存期。如果 对象小于85000字节,CLR会把它分配在小对象堆中的第0代,通常紧挨当前已用内存空间往后分配,如果扩大内存堆时超越了内存段的边界,则就会触发垃圾回收过程。

  对象总是诞生于第0代内存堆中,只要对象保持存活,每当发生垃圾回收时,GC都会把他提升一代。第0代和第1代内存堆的垃圾回收有时候被称为瞬时回收(Ephemeral Collection)

  在垃圾回收的时候,可能会进行碎片整理(Compaction),也及时GC把对象物理迁移到新的位置中去,以便让内存段中的空闲空间能够连续起来使用。如果为发生碎片整理,那就只需要重新调整各块内存的边界。以下是经历几次未做碎片整理的垃圾回收之后,内存堆的分布可能如下

image.png

对象的位置没有移动,但是各代的内存堆的边界发生了变化。

  如果对象到达第2代内存堆,它就会一直留在哪里直至终结。但不代表第2代内存堆只会一直变大, 如果第2代内存堆中的对象都终结了,整个内存段有没有存活[2]的对象,垃圾回收器会把整个内存段交换给操作系统,或者作为其他几代内存堆的附加段,在进行完全垃圾回收(Full Garbge Collection)时,就会可能发生第2代内存堆的回收。

 如果第0代堆即将占满一个内存段,垃圾回收也无法通过碎片整理获取足够空闲内存,那么GC会分配一个新的内存段。如图:

image.png

如果第2代堆继续变大,就可能会跨越多个内存段。LOH也是。但是无论存在多少内存段,第0代和第1代总是位于同一个内存段中。以后我们找出内存堆中有哪些对象存活时。这些会用到。

LOH

LOH,大于85000字节的对象将自动在LOH中分配内存。没有代的概念,垃圾回收期间也不会自动进行碎片化整理,但是可以人为的碎片整理。

垃圾回收的时候会造成什么影响呢?

  垃圾回收是针对某一代及以下几代内存堆进行的。如果回收1代,就会回收0代。如果发生了第0代或第1代垃圾回收,程序在回收期间就会暂停运行,第二代垃圾回收,有部分回收是在后台线程运行进行的。

垃圾回收的4个阶段

  • 挂起(Suspension)---在垃圾回收发生之前,所有托管线程都被强行中止
  • 标记(Mark)--从GC根对象开始,垃圾回收器沿着所有对象引用进行遍历并把所见对象记录下来
  • 碎片整理(Compact)--将对象重新紧挨着存放并更新所有引用,以便减少内存碎片,在小对象堆中,碎片整理会按需进行,无法控制。在LOH中,碎片整理不会自动进行,可以必要时通知垃圾回收器来上一次。
  • 恢复(Resume)--托管线程恢复运行

在标记阶段,不需要遍历内存堆中的所有对象,只要访问那些需要回收的部分即。比如第0代回收只涉及到第0代内存堆中的对象,第1代回收将会标记第0代和第1代内存中的对象。第2代和完全回收,需要遍历内存堆中的所有存活对象,这一开销很大。

  1:垃圾回收过程的耗时几乎完全取决于所涉及的“代”内存堆中的对象数量,而不是你分配到的对象数量,你分配1棵包含100万个对象的树,只要在下次垃圾回收之前把根对象的引用解除。就不会增加垃圾回收的耗时

  2:只要已分配的内存超过某个内部阀值,就会发生这个代垃圾回收,这个阀值是持续变化的。GC会进行调整。

计数器


相关文章
|
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垃圾回收器的性能优化方案