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

减少分配率

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

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

我真的需要这个对象吗?

这个字段是我需要的吗?

我能减少数组的尺寸吗?

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

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

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

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

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

小故事:在服务端一个响应请求的函数里,我们发现在一次请求里会分配一些比内存段要大的内存。这样导致每次请求我们都会触发一次完整的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的实例教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1432429.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月17日 08:04:59
下一篇 2025年12月10日 04:22:14

相关推荐

  • 泛型的概述和具体使用

    一、泛型概述       泛型类和泛型方法兼复用性、类型安全和高效率于一身,是与之对应的非泛型的类和方法所不及。泛型广泛用于容器(collections)和对容器操作的方法中。.net framework 2.0的类库提供一个新的命名空间system.collections.generic,其中包含…

    好文分享 2025年12月17日
    000
  • 使用 VSCode 编写 .NET Core 项目的实例教程

    windows 开发环境搭建: 1.安装最新 VSCode,; 2.安装最新 .NET CORE,; 3. 在 VS Code 中获取 C# extension ;   一、创建C#项目 1.打开VS Code,从命令行打开预先定义的文件夹,如: E:NetCoredemo; 使用快键键调出命令行窗…

    2025年12月17日 好文分享
    000
  • Core实现全面扫盲贴的ASP方法详解

    本篇文章主要介绍了.net core,.net core是一个开源通用的开发框架,支持跨平台,即支持在window,macos,linux等系统上的开发和部署有兴趣的可以了解一下。 1. 前言 .NET发行至今已经过了十四个年头。随着版本的不断迭代更新,.NET在Windows平台上的表现也是越来越…

    2025年12月17日 好文分享
    000
  • .Net 垃圾回收机制原理(二)

    英文原文:Jeffrey Richter 编译:赵玉开 链接http://www.php.cn/ 上一篇文章介绍了.Net 垃圾回收的基本原理和垃圾回收执行Finalize方法的内部机制;这一篇我们看下弱引用对象,代,多线程垃圾回收,大对象处理以及和垃圾回收相关的性能计数器。让我们从弱引用对象说起,…

    2025年12月17日 好文分享
    000
  • .Net 垃圾回收机制原理(一)

    英文原文:Jeffrey Richter 编译:赵玉开 链接:http://www.php.cn/ 有了Microsoft.Net clr中的垃圾回收机制程序员不需要再关注什么时候释放内存,释放内存这件事儿完全由GC做了,对程序员来说是透明的。尽管如此,作为一个.Net程序员很有必要理解垃圾回收是如…

    2025年12月17日 好文分享
    000
  • .NET中异常处理的最佳实践(译)

    原文地址:点击打开链接 本文翻译自CodeProject上的一篇文章,原文地址。 目录 介绍 做最坏的打算 提前检查 不要信任外部数据 可信任的设备:摄像头、鼠标以及键盘  “写操作”同样可能失效 安全编程 不要抛出“new Exception()” 不要将重要的异常信息存储在Message属性中 …

    2025年12月17日
    000
  • .NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱

    内容导读 •概述 •当你声明一个变量背后发生了什么?•堆和栈•值类型和引用类型•哪些是值类型,哪些是引用类型?•装箱和拆箱•装箱和拆箱的性能问题一、概述本文会阐述六个重要的概念:堆、栈、值类型、引用类型、装箱和拆箱。本文首先会通过阐述当你定义一个变量之后系统内部发生的改变开始讲解,然后将关注点转移到…

    2025年12月17日 好文分享
    000
  • .Net 垃圾回收和大对象处理

    英文原文:Maoni Stephens,编译:赵玉开(@玉开Sir) CLR垃圾回收器根据所占空间大小划分对象。大对象和小对象的处理方式有很大区别。比如内存碎片整理 —— 在内存中移动大对象的成本是昂贵的,让我们研究一下垃圾回收器是如何处理大对象的,大对象对程序性能有哪些潜在的影响。 大对象堆和垃圾…

    2025年12月17日 好文分享
    000
  • C# 中 10 个你真的应该学习(和使用!)的功能

    如果你开始探索c#或决定扩展你的知识,那么你应该学习这些有用的语言功能,这样做有助于简化代码,避免错误,节省大量的时间。    1)async / await 使用async / await-pattern允许在执行阻塞操作时解除UI /当前线程的阻塞。async / await-pattern的工…

    好文分享 2025年12月17日
    000
  • 关于.NET异常处理的思考(上)

    在项目开发中,对于系统和代码的稳定性和容错性都是有对应的要求。实际开发项目中的代码与样例代码的区别,更多的是在代码的运行的稳定性、容错性、扩展性的比较。 因为对于实现一个功能来说,实现功能的核心代码是一样的,可能只是在写法上优化而已,但是在实现某一个操作上使用的类来说,这一点是绝大多数时候是一样的。…

    好文分享 2025年12月17日
    000
  • .NET异常设计原则

    异常是使用.net时必然会遇到的问题,但是,有太多的开发人员没有从api设计的角度考虑这个问题。在大部分工作中,他们自始至终都知道需要捕获什么异常以及哪些异常需要写入全局日志。如果你设计了可以让你正确使用异常的api,则可以显著减少修复缺陷的时间。 谁的错? 异常设计背后的基本理论始于这样一个问题,…

    好文分享 2025年12月17日
    000
  • .NET编程之线程池内幕

    本文通过对.net4.5的threadpool源码的分析讲解揭示.net线程池的内幕,并总结threadpool设计的好与不足。 线程池的作用 线程池,顾名思义,线程对象池。Task和TPL都有用到线程池,所以了解线程池的内幕有助于你写出更好的程序。由于篇幅有限,在这里我只讲解以下核心 概念: 线程…

    好文分享 2025年12月17日
    000
  • .NET下几个服务框架介绍

    简介 在公司的服务多了以后,为了调用上的方便,同时为了以后的服务治理,一般都会使用一些服务框架,这里主要介绍我知道的几个服务框架,简析一下这些服务框架的基本概念。 可投入生产环境使用的 以下两个服务框架,我已经见过有公司投入到生产环境,所以对于稳定性,应该不需要有太大的担心。    ServiceS…

    2025年12月17日
    000
  • .NET多线程编程—并发集合

    并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表、集合和数组都不是线程安全的,若无同步机制,他们不适合于接受并发的指令来添加和删除元素。 在并发代码中使用上述经典集合需要复杂…

    2025年12月17日
    000
  • .NET编程中Word/Excel 在线预览

    前言 近日项目中做到一个功能,需要上传附件后能够在线预览。之前也没做过这类似的,于是乎就查找了相关资料,.net实现office文件预览大概有这几种方式: 使用Microsoft的Office组件将文件直接转换为html文件(优点:代码实现最简单,工作强度最小。缺点:效果极差) 使用Microsof…

    2025年12月17日
    000
  • 记一次.NET代码重构(上)

    需求:是这样的,要开发一个短信发送的模板,不同客户可能会使用不同的模板,而不同的客户使用的变量参数也是不同的。 之前为了应急,线上已经完成了一个短信模板发送短信的功能,短信模板表也创建了,而且在表中已经新增了一条记录。我只需要做一个短信模板的增删改查界面就可以了,看上去我的任务挺简单的,老司机应该知…

    2025年12月17日 好文分享
    000
  • 记一次.NET代码重构(下)

    public override bool TryGetMember(GetMemberBinder binder, out object result){ if (!_dictionary.TryGetValue(binder.Name, out result)) { result = null; …

    2025年12月17日
    000
  • 为 Jenkins 配置 .NET 持续集成环境

    去年年底,得益于公司引入 jenkins,让我们在持续集成方面迈出了第一步,本文不赘述如何安装 jenkins,主要关注点在于配置 .net 环境。另外本文是在 windows 环境下安装的 jenkins 进行操作。 一、安装环境 首先我们需要先准备几个安装包,将它们安装到 Windows 上: …

    2025年12月17日
    000
  • c#.net中const和readonly的区别

    (1) readonly和const都是用来标示常量的。(2) 初始化赋值不同。const修饰的常量必须在声明的同时赋值。例如: public class Class1 { public const int MaxValue = 10; //正确声明 public const MInValue; /…

    好文分享 2025年12月17日
    000
  • XML中如何生成XML报表模板_XML生成XML报表模板的方法与示例

    利用XSLT、编程语言或模板引擎可生成XML报表模板:1. XSLT将源XML转换为结构化报表;2. Python等语言通过DOM操作动态构建XML;3. Jinja2等模板引擎支持变量与逻辑控制,实现灵活输出。 在XML中生成XML报表模板,实际上是指利用XML的结构化特性设计一个可复用的数据模板…

    2025年12月17日
    000

发表回复

登录后才能评论
关注微信