高性能-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),如果某一块缓冲区确实需要被垃圾回收了,那么下一次分配的缓冲区就有很大概率会落在这块空闲内存上。而不会放到堆的末尾去。

 

相关文章
|
缓存 监控 API
抖音抖店 API 请求获取宝贝详情数据的调用频率限制如何调整?
抖音抖店API请求获取宝贝详情数据的调用频率受限,需遵循平台规则。开发者可通过提升账号等级、申请更高配额、优化业务逻辑(如缓存数据、异步处理、批量请求)及监控调整等方式来应对。
|
安全 关系型数据库 MySQL
Navicat工具设置MySQL权限的操作指南
通过上述步骤,您可以使用Navicat有效地为MySQL数据库设置和管理用户权限,确保数据库的安全性和高效管理。这个过程简化了数据库权限管理,使其既直观又易于操作。
1137 4
|
安全 Java
UUID太长怎么办?快来试试NanoId(Java版本)
UUID太长怎么办?快来试试NanoId(Java版本)
587 5
|
Linux 开发工具 git
IntelliJ IDEA配置git工作效率翻倍
IntelliJ IDEA 是一个强大的集成开发环境,用于编程语言如 Java、Kotlin、Scala 和其他多种语言。Git 是一个开源的分布式版本控制系统,用于追踪项目过程中的代码变更。
871 0
IntelliJ IDEA配置git工作效率翻倍
|
人工智能 搜索推荐 机器人
AI发展已经一段时间了,当前社会身边哪些功能已经在运用了AI技术?未来AI技术还将有哪些地方会运用?
AI技术现已被广泛应用在智能家居(如自动化控制与安全)、个性化教育(定制化学习与辅助教学)、精准医疗(疾病诊断与药物研发)、智能服务(如智能客服)和金融服务(风险评估)等领域。未来,预计AI将在AI PC、人机协创、超级视野、机器人和零搜索等领域发挥更大作用,实现信息主动推送、无缝沟通和创新服务。随着技术进步,AI将持续影响并改变我们的生活。【6月更文挑战第2天】
801 0
|
监控 前端开发 Java
什么是 SpringBoot?为什么要用 SpringBoot?
什么是 SpringBoot?为什么要用 SpringBoot,2、Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?,3、运行 Spring Boot 有哪几种方式?,4、如何理解 Spring Boot 中的 Starters?,5、如何在 Spring Boot 启动的时候运行一些特定的代码?,6、Spring Boot 需要独立的容器运行吗?,7、Spring Boot 中的监视器是什么?,8、如何使用 Spring Boot 实现异常处理?,9、你如何理解 Spring Boot 中的 Starters?,10、springboot 常用的 starter 有哪些.
|
测试技术 Android开发 数据格式
|
API
Navigator和导航栏之间的区别以及用法场景的分析
区别:    1.navigator是属于小程序组件中的,导航栏是属于小程序API中的    2.navigator组件是用在axml页面中跳转的导航,它有4种类型(见下表);导航栏API是用在js中实现页面跳转的    3.
1100 12