C#性能,内存溢出,PerfView分析

一、GC

1、基本概念

GC:在托管进程中存在两种内存堆(托管堆、本机堆)。

托管堆也就是GC堆,它又分为2种:小对象堆、大对象堆LOH,以85000字节分界。

小对象堆有3代,0、1、2。

对象总诞生于0,每次回收,还存在的对象,会提升一代。

0、1回收被称为 瞬时回收,0、1总是在同一个段中。

在回收时有可能进行 碎片整理,将空闲空间连续起来(在划算的时候)。

大对象堆LOH不会自动进行回收。

GC通过任一已知的根对象,层层访问到某个对象,那么它就是存活的。

2、垃圾回收

4个阶段:挂起、标记、碎片整理、恢复

3、服务器工作模式

服务器模式适合大型的服务端应用,比如 ASP.NET Core 程序。

服务器模式下 GC 的回收会尽量的延迟,从而减少停顿。为了获得更高的吞吐量与性能,程序会分配更多的内存。

服务器模式下 CLR 根据 CPU 核心数量来分配 GC 堆的数量。

单处理器的服务器模式是无效的

在项目里的app.config配置

4、低延迟模式

开启不会执行2代垃圾回收

如果你的项目,在进入某一功能模块时,不希望此时进行垃圾回收。
可采取
进入低延迟模式前先执行一次完全垃圾回收,离开低延迟模式时也做一次完全垃圾回收。

5、GC优化

转瞬即逝:对象的生存期尽可能短暂;

池化:让对象尽快提升到2代中,确保回收发生在0/1代中;

永远不要使用终结方法(析构函数)
终结函数并不能保证会及时被执行
从一个对象变得不可到达开始,到它的终结函数被执行,这段时间的长度是任意的、不确定的,有时不一定会被执行(gc只会执行一次)。
我们可以使用 try-finally 去显示的去释放资源。

尽可能避免在LOH分配内存

6、垃圾回收通知

因为回收开销大,会对性能产生负面影响,你可以先停止程序的运行,等回收后在执行

会返回1-99数字,越小,离下一次的回收时间越近。

二、异步编程

1、基本概念

每个处理器只能执行一个线程,在给处理器安排线程的时刻,Windows需要进行上下文切换。

如果线程被任何IO阻塞,则可能进入等待状态。

sleep可以主动进入等待状态,但会阻塞IO

无论什么情况,你都应该使用Task

将sleep 替换为 await Task.Delay(ms) 不会阻塞IO

2、Task

串行:你的一个函数需要等待上一个函数执行完,才进行。
上一个任务执行完成后自动启动下一个任务,实现任务的按序进行。
task.ContinueWith(xxx1).ContinueWith(xxx2);

3、并行循环

3.1、Parallel.For

简单的并行计算的方法。

在 Parallel.For中,不能使用与顺序循环中相同的 break 或 Exit 语句,这是因为这些语言构造对于循环是有效的,而并行“循环”实际上是方法,不是循环

可以使用 Stop 或 Break 方法

Stop:尚未开始的循环的任何迭代都无需运行。 它可以有效地取消循环的任何其他迭代。 但是, 它不会停止已经开始执行的任何迭代。
调用方法会导致此IsStopped属性返回true到仍在执行的循环的任何迭代。 对于长时间运行的迭代特别有用, 它可以检查IsStopped属性为true提前退出。

Break:应运行当前迭代之后的任何迭代。 它可以有效地取消循环的任何其他迭代。 但是, 它不会停止已经开始执行的任何迭代。
例如, 如果Break是从0到1000的并行循环的第100迭代调用的, 则所有小于100的迭代仍应运行, 但不会执行从101到1000的迭代。

Stop和Break的区别就在于,Stop仅仅通知其他迭代尽快结束,而Break不仅通知其他迭代尽快结束,同时还要保证退出之前要完成之前的迭代。

3.2、Partitioner

把一个大集合,或大数组分成若干个区来执行

由于Parallel.For的开销过大,可以使用 Partitioner类,进行分区循环

Partitioner.GetPartitions(number):会查看本机的cpu核心数,把分区的上限限制在核心数上,可以少于等于这个值;
多了没有cpu去运算,也没有意义了。

4、阻塞

lock和其他种类的直接线程同步方式,都是很明显的阻塞式调用

反面教材,在异步方法里,不使用异步

5、async,await

当task结束时机不确定,或者使用多级task,就不在适用了,此时可以使用async,await。

注意:如果我们没有使用await 关键字,那么该方法就作为一个同步方法。编译器将向我们显示警告,但不会显示任何错误。

按照从不等待的原则,不使用Wait
Wait是一种同步方法,它会导致调用线程等待当前任务完成。 如果当前任务尚未开始执行,Wait 方法会尝试从计划程序中删除该任务,并在当前线程上内联执行该任务。 如果它无法执行此操作,或者如果当前任务已经开始执行,它会阻止调用线程,直到任务完成。

请勿创建大量的定时器

不能中止线程
当线程被强行中止时,它可能正执行某个关键操作的过程中,如文件操作、数据库访问或持有锁等。这可能导致数据不一致、资源泄露或其他不可预知的行为。
如果线程正在等待获取某个锁或同步,而被强行中止,那么它可能永远无法释放这些资源,导致死锁或其他同步问题。
线程可能拥有一些需要显式释放的资源(如非托管资源)。如果线程被中止,这些资源可能不会被正确释放,导致资源泄露。

volatile:值发生了变化,其他线程立马可见
指示一个字段可以由多个同时执行的线程修改
不会被线程调度或硬件优化所中断。使用volatile关键字可以确保所有线程都看到该字段的最新值,而不是可能存在的本地缓存中的旧值。

WaitAsync:异步等待,逻辑上看是递归,但实则不是;
异步等待的时候可以指定一个 Timeout 时间或者一个取消令牌 CancellationToken

三、编码一般规则

避免装箱

for要明显快一点,相比foreach;

类型转化,AS,如果不是会返回null,请勿先is,在as

异常消耗巨大,数据类型转换,使用TryParse,解析失败会返回bool

四、内存溢出问题

软件:PerfView、windbg、dotTrace

1、PerfView

1.1、收集

1.a启动方式:在工具栏选择Collect下的Run。
“Run”是直接指定需要启动的应用程序的名称,以便启动该程序。

在这里插入图片描述

​ Command输入exe全路径名称,并点击“Run Command”

在这里插入图片描述

1.b启动方式:在工具栏选择Collect下的Collect。
“Collect”则是直接启动Perfview并开始收集,

在这里插入图片描述

​ 高级选项中Focus process可以输入PID, os Heap Process 输入PID。PID从任务管理器看
​ Max Collect Sec 为启动多少秒,可以不填。

在这里插入图片描述

2、zip复选框:选中你可以把数据复制到其他电脑分析,不选会快一点,默认选中的;

3、在处理过程中,Perfview的右下角会闪动,并且可以查看运行日志。

在这里插入图片描述

4、在收集完毕后,会创建“PerfViewData.etl.zip”。

在这里插入图片描述

1.2、分析CPU

1、选择“CPU Stacks”,选择你想看的进程,双击进入堆栈图。以微信举例。

在这里插入图片描述

2、Metric/Interval: 0.01 如果值是0.01-0.02,那么查看CPU就不是好的选择;
when:代表时间范围,要确保数据是好的;

在这里插入图片描述

3、CallTree:树,自上而下,展示程序从一开始到结束时的堆栈数据。

从最上面100%的一层层打开,找到哪个函数占用性能多,在跳转到源码,进行优化。

在这里插入图片描述

4、 Fold% ,你可以输入数字,表示你不关心CPU少于多少的堆栈

​ 表格里的列,代表的意思:
​ Exc% 表示使用了多少时间
​ Inc%表示谁用了最多的性能

在这里插入图片描述

5、By Name:查看同一名称的,占用了多少资源,可以选中它,鼠标右键,选择goto,选择F10,到CallTree中查看树

在这里插入图片描述

​ 在CallTree中,可以选中方法名,右键选择Goto source ,查看源代码

在这里插入图片描述

1.3、分析内存

1、Collect收集。

2、收集后,打开Memory下的Net OS Heap Alloc Stacks

在这里插入图片描述

3、清空GraupPats文本框内容,然后回车

在这里插入图片描述

4、与CPU分析一致

  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值