高性能-GC3

简介: 对象固定(Pinning)是为了能够安全地将托管内存的引用传递给本机代码,最常见的用处就是 传递数组和字符串。

带着问题去思考!大家好

今天我们继续优化。

避免对象固定

  对象固定(Pinning)是为了能够安全地将托管内存的引用传递给本机代码,最常见的用处就是 传递数组和字符串。如果不与本机代码进行交互,就完全不应该有对象固定的需求。

对象固定会把内存的地址固定下来, 垃圾回收器就无法移动这些对象,会增加内存碎片的可能,垃圾回收器会记住那些被固定的对象,以便能利用固定对象之间的空闲内存。过多的情况,还会导致内存碎片的产生和内存堆的扩大

  对象固定既可能是显式的,也可能是隐式的,使用GCHandleType.Pinned类型的GCHandle或者fixed关键字,可以完成显式对象固定,代码块必须标记为unsafe。用fixed/using用起来更方便,(fixed和GCHandle之间的区别类似于using和显式调用Dispose的差别),异步环境下是无法使用的,因为异步状态不能传递handle,也不能在回调方法中销毁handle。

避免使用终结方法

  非必要情况下,永远不要实现终结方法(Finalizer).终结方法是一段由垃圾回收器引发调用的代码,用于清理非托管资源。终结方法由一个独立的线程调用,排成队列依次完成,而且只有依次垃圾回收之后,对象被垃圾回收器声明已销毁,才会进行调用。如果类实现了终结方法,对象就一定会滞留在内存中,即便在垃圾回收时应该被销毁的情况下。

  如果实现了终结方法,那就必须同时实现IDisposable接口以启用显示清理,还要在Dispise方法中调用GC.SuppressFinalize(this)来吧对象从移除终结队列中移除。只要能在下次垃圾回收之前调用Dispose,就可以适时把对象清理干净。



class Foo:IDisposeable {    Foo(){Dispose(false);}    publicvoid Dispose()    {       Dispose(true);       GC.SuppressFinalize(this);     }      protectedvirtualvoid Dispose(bool disposing)    {      if(disposing)      {        this.manageResoure.Dispose();       }      //清理非托管资源      UnsafeClose(this.handle);    } }

 

避免分配大对象

大对象的界限被为85000字节,任何大于这个值的对象都被认为是大对象,并独立的内存堆中进行分配,尽量避免,不仅是因为LOH的垃圾回收开销大,更多原因是因为内存碎片会导致内存用量不断增长

避免缓冲区复制

任何时候都应该避免复制数据,比如你已经把文件数据读入了MemorySteam(如果需要较大的缓存区,最好是用池化的流),一旦内存分配完毕,就应把此MemoryStream视为只读流,所有需要访问的MemoryStream的组件都能从同一份数据备份中读取数据,

如果需要表示整个缓冲区的一段,请使用ArraySegment<T>类,可用来代表底层byte[]类型缓冲区的一部分区域。此ArraySegment可以传给API,而与原来的流无关,甚至可以被绑定到一个新的MemoryStream对象上,这些过程都不会发生数据复制。

var memoryStream=new MemoryStream();var segment=new ArraySegment<byte>(memoryStream.GetBuffer(),100,1024); ....var blockStream=new MemoryStream(segment.Array,segment.Offset,segment.Count);

内存复制造成的最大影响肯定不是CPU,而是垃圾回收。

对长期存活对象和大型对象进行池化

对象要么转瞬即逝,要么一直存活;要么在第0代垃圾回收时消失,要么就在第2代内存堆中一直留下去。有些对象基本上是静态的,伴随程序自然诞生,并在程序生存期间保持存活,还有一些对象看不出有一直存活的必要。但它们在程序上下文中体现出来的生存期,决定了它们会历经第0代(或者1代)垃圾回收并仍然存活。这时候应该考虑对这类对象进行池化。还有LOH中分配的对象,典型例子就是集合类对象,

.NET已提供了一种针对受限托管资源的处理模式--IDisposable模式。比较合理的设计就是派生一个新类型并实现IDisposable接口,在Dispose方法中将池化对象归还共享池(Pool)。

View Code

被池化对象必须要实现自定义接口,需要有点工作量。为了实现池化和对象重用,必须完全了解并掌握这些对象。因为在每次把池化对象归还共享池时,你的代码必须把对象重置为已知的,安全的状态,

共享池中的对象永远不会被销毁,这与内存泄漏难以区分开。通常不要把池化作为默认解决方案,主要处理一些LOH分配。能消除99%的LOH问题。这类对象就是MemorySteam,我们用它来序列化并通过网络传递数据。

减少LOH的碎片整理

如果做不到完全避免LOH,就尽力避免碎片整理,

保证LOH的每次分配都是同一尺寸的,或者至少有几种标准尺寸的组合。比如LOH的一种常规用途就是缓冲区池,不能让各个缓冲区大小不易,而应该所有缓冲区都是相同尺寸,或者固定大小(1MB),如果某一块缓冲区确实需要被垃圾回收了,那么下一次分配的缓冲区就有很大概率会落在这块空闲内存上。而不会放到堆的末尾去。

 

相关文章
|
Java 算法 程序员
带你读《新一代垃圾回收器ZGC设计与实现》之一:垃圾回收器概述
JDK 11于2018年9月25日正式发布,这个版本引入了许多新的特性,其中最为引人注目的就是实现了一款新的垃圾回收器ZGC。
|
4月前
|
监控 Java 测试技术
JVM 性能调优 及 为什么要减少 Full GC
JVM 性能调优 及 为什么要减少 Full GC
121 4
|
15天前
|
程序员 开发者
分代回收和手动内存管理相比有何优势
分代回收和手动内存管理相比有何优势
|
3月前
|
存储 监控 算法
Java内存管理:从垃圾收集到性能优化
【8月更文挑战第4天】在Java的世界中,内存管理是一块神秘的领域,它不仅关乎程序的稳定运行,更直接影响到系统的性能表现。本文将带你深入理解Java的垃圾收集机制,探讨如何通过合理的内存管理策略来提升应用效率。我们将一起分析JVM内存结构,探索不同的垃圾收集算法,并借助实际代码示例,学习如何监控和调优内存使用,以期达到减少延迟、防止内存泄漏的目的。
|
4月前
|
存储 算法 安全
(八)JVM成神路之GC分区篇:G1、ZGC、ShenandoahGC高性能收集器深入剖析
在《GC分代篇》中,我们曾对JVM中的分代GC收集器进行了全面阐述,而在本章中重点则是对JDK后续新版本中研发推出的高性能收集器进行深入剖析。
167 12
|
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垃圾回收器的性能优化方案