热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

编写高性能.NET的实例教程

减少分配率这个几乎不用解释,减少了内存的使用量,自然就减少GC回收时的压力,同时降低了内存碎片与CPU的使用量。你可以用一些方法来达到这一目的,但它可能会与其它设计相冲突。你需要在设计对象时仔细检查每个它并问自己:我真的需要这个对象吗?这个字段是我需要的吗?我能减少数组的尺寸吗?我能缩小primitives的尺寸吗(用Int32替换Int64,其它)?这些对象,是否只有在极少数情况
减少分配率

这个几乎不用解释,减少了内存的使用量,自然就减少GC回收时的压力,同时降低了内存碎片与CPU的使用量。你可以用一些方法来达到这一目的,但它可能会与其它设计相冲突。

你需要在设计对象时仔细检查每个它并问自己:

  1. 我真的需要这个对象吗?

  2. 这个字段是我需要的吗?

  3. 我能减少数组的尺寸吗?

  4. 我能缩小primitives的尺寸吗(用Int32替换Int64,其它)?

  5. 这些对象,是否只有在极少数情况下,或者只有初始化的时候才用到?

  6. 是否能将一些类转为结构体使他们在栈上分配或者成为某个对象的一部分?

  7. 我是否分配了大量内存,但实际只使用其中很小的一部分?

  8. 我可以从其它地方拿到相关数据?

小故事:在服务端一个响应请求的函数里,我们发现在一次请求里会分配一些比内存段要大的内存。这样导致每次请求我们都会触发一次完整的GC,这是因为CLR要求所有的0代对象都在一个内存段里,当前分配的内存段满了,就会开辟一个新的内存段,同时对原先的内存段做一次2代的回收。这不是一个好的实现,因为我们除了减少内存分配外别无它法。

最重要的规则

对于垃圾回收的高性能编程有一个基本规则,事实上也是代码设计的指导规则。

要收集的对象要么在0代,要么不存在
Collect objects in gen 0 or not at all.

不同的是,你希望一个对象拥有极短的生命周期,在GC的时候永远不要碰到它,或者,如果你做不到这一点,它们应该去2代,尽可能的快,永远的呆在那,永远不会被回收。这意味着你永远保持对长生命周期对象的引用。通常,也意味着对象可重复使用,尤其是在大对象堆里的对象。
GC每高一个世代的回收会比上一个世代更加耗时。如果你想保持许多0,1代和少量的2代对象。即使开启后台GC做2代做回收,也会消耗相当CPU运算量,你可能更愿意将这部分的CPU消耗给应用程序,而不是GC。

Note 你可能听过一个说法,每10次0代的回收会产生一次1代的回收,每10次1代的回收会产生1次2代的回收。这其实是不正确的,但是你要明白,你要尽可能产生多次快速的0代回收,以及少量的2代回收。

你最好避免进行1代回收,主要是因为已经从0代提升到1代的对象,会在这时候被转入2代。1代是对象进入2代的一个缓冲区。
理想情况下,你分配的每一个对象应该在下一次0代回收前结束生命周期。你可以测量两次GC的时间间隔,并将其与应用程序里对象的生命周期长度做对比。有关如何使用工具测量生命周期的信息,可以在本章结尾看到。
你可能不习惯这样思考,但这规则切入了应用程序的方方面面,你需要经常思考它,在心态要做根本的转变,这样才能实现这个最重要的规则。

缩短对象的生命周期

一个对象的作用范围越短,在下一个GC出现时,它被提升到下一代的机会就越小。一般来说,在你需要之前,不要创建对象。
同时,当对象创建的代价如此之高时,异常就可以在较早的时候创建,这样不会干扰到其他处理逻辑。
另外,你还要确保对象尽可能早的退出作用域。对于局部变量,你可以在最后一次使用后,甚至在方法结束前将其生命周期结束。你可一个用{}将代码包括起来,这不会对你的运行产生影响,但编译器会认为在这个范围的对象已经完成了他的生命周期,不再被使用了。如果需要调用对象的方法,尽量减少第一次和最后一次的时间间隔,以便GC尽早的回收对象。
如果对象关联(引用)了一些会长时间保持的对象,则需要解除他们的引用关系。你可能会有更多的空值检查(null判断),这可能会让代码变得更复杂。也会在对象的可用状态(always having full state available)上与效率之间造成紧张关系,特别是调试的时候。
解决的一种方法是,将要清空的对象转换为另外一种方式存在,例如:日志消息,这样在后面的调试时可以查询到相关信息。
另外一种方法是为代码增加可配置选项(不解除对象之间的关系):运行程序(或者运行程序里特定的一个部分,例如一个特定的请求),在这个模式中没有解除对象引用关系,而是尽可能让对象一直保持方便调试。

减少对象层次的深度

如本章开头所述,GC在回收时会顺着对象的引用关系进行遍历。在服务器GC模式,GC会以多线程方式运行,但如果一个线程需要处理一个对象层次很深,则所有已经处理完的线程都需要等这个线程完成处理后才能退出。在今后的CLR版本里,你可以不用太关注这个问题,GC在多线程执行时会采用更好的标记算法做负载均衡。但如果你对象层次很深,这个问题还是要关注一下的。

减少对象之间的引用

这与上节的深度有关,但也有一些其它的因素。
一个对象如果引用了很多对象(数组,List吧),那它将花很多时间在遍历对象上。是GC造成长时间的一个问题,因为它有一个复杂的关系图。
另外一个问题是,如果无法轻松的确定对象有多少引用关系,那么你就无法准确的预测对象的生命周期。减少这种复杂度是相当有必要的,它不但可以让代码更健壮,同时也方便调试以及获得更好的性能。
另外,还要注意不同世代对象之间的引用也会导致GC的效率低下,特别是旧对象对新对象的引用。例如,如果2代对象在0代对象里有引用关系,那么每次发生0代的GC时,也需要扫描部分2代对象,看看他们是否仍然保持到0代对象的引用上。虽然这不是一次完整的GC,但它仍然是不要的工作,你应该尽量避免这种情况。

避免钉住对象(Pinning)

钉住对象可以保证从托管代码往本地代码里传递数据的安全。常见的有数组和字符串。如果你的代码不需要与本地代码做交互,则不用考虑它的性能开销。
钉住对象就是让对象在垃圾回收(压缩阶段)时无法移动他。虽然钉住对象不会造成多大开销,但它会妨碍到GC的回收操作,增加内存碎片的可能性。GC在回收时会记录对象的位置,以便在重修分配时利用它们之间的空间,但如果钉住的对象很多,会导致内存碎片的增加。
钉可以是显示的也可以使隐式的。显示的是使用GCHandle时用GCHandleType.Pinned参数进行设置,或者在unsafe模式下使用 fixed 关键字。使用fixed关键字和GCHandle的差别在于是否会显示调用Dispose方法。使用fixed虽然很方便,但是不能在异步情况下使用,但还是可以创建一个句柄对象(GCHandle),在回调时传回并处理。
隐式的钉住对象则比较常见,但也更难排查,也更难移除。最明显的例子就是通过平台调用(P/Invoke)将对象传递给非托管代码。这不仅仅是你的代码—--你经常调用的一些托管API,实际上也是会调用本地代码,也会将对象钉住。
CLR也会将自己的一些数据给钉住,但这通常不需要你来关心。
理想情况下,你应该尽可能的不要钉住对象。如果不能做到,那么遵循之前的重要规则,尽可能让这些被钉的对象尽早释放。如果对象只是简单的被钉住后释放,那么也不会有多少机会影响回收操作。你同时也要避免同时钉住很多个对象。被钉的对象被交换到2代或者在LOH里分配会稍微好些。根据这个规则,你可以在大对象堆上分配一个大的缓冲区,并根据实际需自己对缓冲区做管理。或者在小对象对上分配缓冲区,然后在钉住他们前,使他们升级到2代。这样比你直接将对象钉在0代上要好。

以上就是编写高性能 .NET的实例教程的详细内容,更多请关注其它相关文章!


推荐阅读
  • 一面问题:MySQLRedisKafka线程算法mysql知道哪些存储引擎,它们的区别mysql索引在什么情况下会失效mysql在项目中的优化场景&# ... [详细]
  • 本文介绍了数据库体系的基础知识,涵盖关系型数据库(如MySQL)和非关系型数据库(如MongoDB)的基本操作及高级功能。通过三个阶段的学习路径——基础、优化和部署,帮助读者全面掌握数据库的使用和管理。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 深入理解一致性哈希算法及其应用
    本文详细介绍了分布式系统中的一致性哈希算法,探讨其原理、优势及应用场景,帮助读者全面掌握这一关键技术。 ... [详细]
  • 在项目中使用 Redis 时,了解其不同架构模式(如单节点、主从复制、哨兵模式和集群)对于确保系统的高可用性和扩展性至关重要。本文将详细探讨这些模式的特点和应用场景。 ... [详细]
  • 深入解析Spring Cloud微服务架构与分布式系统实战
    本文详细介绍了Spring Cloud在微服务架构和分布式系统中的应用,结合实际案例和最新技术,帮助读者全面掌握微服务的实现与优化。 ... [详细]
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 使用LVS与ldirectord实现高可用负载均衡
    本文介绍了如何通过LVS(Linux Virtual Server)结合ldirectord工具来实现服务器的健康检查及负载均衡功能。环境设置包括一个LVS节点和两个真实服务器节点,通过配置ldirectord进行健康状态监测,确保系统的高可用性。 ... [详细]
  • Spring Cloud因其强大的功能和灵活性,被誉为开发分布式系统的‘一站式’解决方案。它不仅简化了分布式系统中的常见模式实现,还被广泛应用于企业级生产环境中。本书内容详实,覆盖了从微服务基础到Spring Cloud的高级应用,适合各层次的开发者。 ... [详细]
  • 探讨GET与POST请求数据传输的最大容量
    在Web开发领域,GET和POST是最常见的两种数据传输方法。本文将深入探讨这两种请求方式在不同环境下的数据传输能力及其限制。 ... [详细]
  • 免费获取:全面更新的Linux集群视频教程及配套资源
    本资源包含最新的Linux集群视频教程、详细的教学资料、实用的学习课件、完整的源代码及多种软件开发工具。百度网盘链接:https://pan.baidu.com/s/1roYoSM0jHqa3PrCfaaaqUQ,提取码:41py。关注我们的公众号,获取更多更新的技术教程。 ... [详细]
  • 在金融和会计领域,准确无误地填写票据和结算凭证至关重要。这些文件不仅是支付结算和现金收付的重要依据,还直接关系到交易的安全性和准确性。本文介绍了一种使用C语言实现小写金额转换为大写金额的方法,确保数据的标准化和规范化。 ... [详细]
author-avatar
我是阿冫轶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有