重学c#系列——c# 托管和非托管资源与代码相关(四)
前言
这是续第三节。
概况垃圾回收与我们写代码的关系:
- 强引用和弱引用
- 针对共享 Web 承载优化
- 垃圾回收和性能
- 应用程序域资源监视
正文
强引用和弱引用
垃圾回收器不能回收仍在引用的对象的内存——这是一个强引用。它可以回收不在根表中直接或间接的托管内存。然而,有时可能会忘记释放内存。
注意:如果对象相互引用,但是没有在根表中引用,例如:对象A 引用对象B,B引用C,C引用A,这时候如果ABC没有在根表中引用那么直接会被销毁。
补充一下根表在垃圾回收中的作用:
垃圾回收在引用的根表中找到所有引用对象,接着在引用的对象树中查找。
恰恰正好弱类型就没有在根表中,然后垃圾处理器首先开刀的就是弱类型引用。可以这么理解,理论上弱引用关联的对象只能生存到下一次垃圾收集发生为止,但是往往不会那么短时间,因为垃圾收集器并不是那么容易发现这些弱引用。
强引用很好理解:
如果应用程序的代码可以访问一个正由该程序使用的对象,垃圾回收器就不能回收该对象, 那么,就认为应用程序对该对象具有强引用。
var student=new Student();
一但student离开了所在作用区域那么引用对象就开始要被销毁了。
所以我们有缓存这个概念:
var myCache=new MyCache();
myCache.add(student);
缓存的本质目的不就是为了延长垃圾回收吗?或者说不让其垃圾回收,持续在内存中。当student 超出作用区后,还是不能释放student 的引用内存,因为此时对象在缓存对象中引用。
即使是student=null后,那么这个时候new Student()还是在内存中,因为被缓存对象引用了,student在栈中的指向(无论是清空回收还是置空)控制不了释放垃圾回收了。
那么能不能这样,即使被myCache引用了还是可以自动被消耗?这个时候就是弱类型登场的时候。
官方文档这样介绍道:
弱引用允许应用程序访问对象,同时也允许垃圾回收器收集相应的对象。
如果不存在强引用,则弱引用的有限期只限于收集对象前的一个不确定的时间段。
使用弱引用时,应用程序仍可对该对象进行强引用,这样做可防止该对象被收集。
但始终存在这样的风险:垃圾回收器在重新建立强引用之前先处理该对象。
占用大量内存,但通过垃圾回收功能回收以后很容易重新创建的对象特别适合使用弱引用。
假设 Windows 窗体应用中的树状视图向用户显示层次结构复杂的选项。 如果基础数据量很大,则用户使用应用程序中的其他部分时,在内存中保留该树会导致效率低下。
这里有些关键的地方,一个体现就是:数据量很大,也就是弱类型适合占有内存比较大的对象。为什么这样说呢?
是这样子的,我们创造一个弱类型就是要内存开销的,本身目的就是为了及时回收降低内存,这个时候整弱类型这不是添堵吗?
第二个在于容易创建,如果不容易创建,那么这个时候是空间换时间的代价有点大啊。
如何延长弱类型的生命周期呢?这时候应该使用七星灯[强类型]进行续命。
举个栗子:
var myWeaKReference=new WeakReference(new DataObject());
if(myWeaKReference.isAlive)
{
DataObject strongReference=myWeaKReference.Target as DataObject;
}
这时候吧弱类型给了一个强类型引用。
起码可以续命到if结束,也就不用担心用到一半的时候突然挂了,那么就非常尴尬。
垃圾回收和性能
垃圾回收机制和影响到性能,最简单的例子就是垃圾回收不好,导致了内存过大。
那么我们就需要去排除是不是垃圾回收的问题。
首先第一步要确定是否是垃圾回收问题,可能出现下面的问题:
1. 引发内存不足异常
2. 进程占用过多内存
3. 垃圾回收器回收对象的速度不够快
4. 托管堆太零碎
5. 垃圾回收暂停时间太长
6. 第 0 代太大
7. 垃圾回收期间的 CPU 使用率太高
那么如何去排除呢?这时候就要使用工具了。
举个容易出现的例子:托管堆太零碎,这与我们代码息息相关。
我们代码可能会出现,下面的情况:
频繁加载和卸载许多小的程序集。
与非托管代码互操作时,保留了太多对 COM 对象的引用。
大型暂时性对象的创建会导致大型对象堆频繁分配和释放堆段。
这些会导致托管堆太零碎。
如何去排查?
这时候可以使用windbg,这个工具还是很好用的。最主要是windows10现在自带了,没有版本不够升级一下,对了不会windows 10还用盗版吧?
我们都是正经人,能白嫖肯定白嫖啊,不给钱就不算嫖啊。
下面是我调试的内容:
可能有些人没用过windbg,简单过下流程。
打开windbg后:
选择对应的进程,进程很多,那么这个时候你应该打印出来。如果调试打包好的,直接看程序名。
Console.WriteLine(Process.GetCurrentProcess().Id);
然后开始调试。
你需要加载sos,来查看托管程序。
.net core 加载是这样子的.load C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.8\sos
然后查看一下是否加载完毕: !help.
然后你就可以查询一些托管的东西。
在这里!dumpheap -type Free -stat 显示堆里面的一些使用情况,上图windbg就是了。
若要确定第 0 代中的可用空间,请键入以下命令以获取代的内存使用信息:
!eeheap -gc
当然这是一个漫长查看过程,但是想要高性能,这又是必须的。
针对共享 Web 承载优化
我直接把文档里面的贴过来吧,因为这很详细了。
由于垃圾回收器保留内存以供将来分配,因此它提交的空间可能会超过真正所需。 可以减少此空间来适应系统内存负载过重的情况。 减少提交的此空间可提升性能,并将容量扩展为托管更多网站。
如果启用 gcTrimCommitOnLowMemory 设置,垃圾回收器会计算系统内存负载,并在负载达到 90% 时进入修整模式。 除非负载下降到不到 85%,否则会一直处于修整模式。
如果条件允许,垃圾回收器可以决定 gcTrimCommitOnLowMemory 设置对当前应用没有帮助并忽略它。
然后给了一个示例:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<runtime>
. . .
<gcTrimCommitOnLowMemory enabled="true"/>
</runtime>
. . .
</configuration>
应用程序域资源监视
这个是什么呢?就是说用来监控应用域监视cpu和内存的使用情况。里面是这样解释的,说多个应用在服务器上运行,可以监听到哪个程序占用过多,同时告诉我们这个arm消耗小。
这个呢,其实个人觉得现在容器化了,监控容器专门的工具了,很容易监听到。
有四种启动资源监控的东西。
1.可以在 CLR 启动时启用 ARM,具体操作是向配置文件添加 <appDomainResourceMonitoring> 元素,并将 enabled 属性设置为 true。 值 false(默认值)只表示不在启动时启用 ARM;稍后可以使用其他激活机制之一来激活它。
2.主机可以请求获取 ICLRAppDomainResourceMonitor 托管接口来启用 ARM。 成功获取此接口后,就会启用 ARM。
3.托管代码可以将静态 AppDomain.MonitoringIsEnabled 属性(Visual Basic 中的 Shared)设置为 true,从而启用 ARM。 设置此属性后,就会启用 ARM。
4.启动后,可以通过侦听 ETW 事件来启用 ARM。 使用 AppDomainResourceManagementKeyword 关键字启用公共提供程序 Microsoft-Windows-DotNETRuntime 后,ARM 便会启用,并开始抛出所有应用域的事件。 若要将数据与应用域及线程相关联,还必须使用 ThreadingKeyword 关键字启用 Microsoft-Windows-DotNETRuntimeRundown 提供程序。
首先改配置文件的放弃。然后 Windows 事件跟踪 (ETW)是windows的。去调用api感觉麻烦。应用程序域资源监视非常重要,但是还是找个第三方监听吧。
结
前面一直介绍托管资源,后面介绍非托管资源,整理了一点点。
注:上述纯属个人的整理,如有误,望指出。
重学c#系列——c# 托管和非托管资源与代码相关(四)的更多相关文章
- 重学c#系列——c# 托管和非托管资源(三)
前言 c# 托管和非托管比较重要,因为这涉及到资源的释放. 现在只要在计算机上运行的,无论玩出什么花来,整个什么概念,逃不过输入数据修改数据输出数据(计算机本质),这里面有个数据的输入,那么我们的内存 ...
- 重学c#系列——字典(十一)
前言 重学c#系列继续更新,简单看一下字典的源码. 看源码主要是解释一下江湖中的两个传言: 字典foreach 顺序是字典添加的顺序 字典删除元素后,字典顺序将会改变 正文 那么就从实例化开始看起,这 ...
- 有关 Azure IaaS VM 磁盘以及托管和非托管高级磁盘的常见问题解答
本文将对有关 Azure 托管磁盘和 Azure 高级存储的一些常见问题进行解答. 托管磁盘 什么是 Azure 托管磁盘? 托管磁盘是一种通过处理存储帐户管理来简化 Azure IaaS VM 的磁 ...
- [.net 面向对象程序设计进阶] (8) 托管与非托管
本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收.掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常. 1.什么是托 ...
- NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配
在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...
- C# 托管和非托管混合编程
在非托管模块中实现你比较重要的算法,然后通过 CLR 的平台互操作,来使托管代码调用它,这样程序仍然能够正常工作,但对非托管的本地代码进行反编译,就很困难. 最直接的实现托管与非托管编程的方法就是 ...
- C# using 三种使用方式 C#中托管与非托管 C#托管资源和非托管资源区别
1.using指令.using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间,类似于Java的import,这个功能也是最常用的,几乎每个cs的程序都会用到. ...
- 利用C#Marshal类实现托管和非托管的相互转换
Marshal 类 命名空间:System.Runtime.InteropServices 提供了一个方法集,这些方法用于分配非托管内存.复制非托管内存块.将托管类型转换为非托管类型,此外还提供了在与 ...
- [转]C# 之DLL调用(托管与非托管)
每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍.首先,您需要了解什么是托管,什么是非托管.一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX ...
随机推荐
- kubernetes资源均衡器Descheduler
背景 Kubernetes中的调度是将待处理的pod绑定到节点的过程,由Kubernetes的一个名为kube-scheduler的组件执行.调度程序的决定,无论是否可以或不能调度容器,都由其可配置策 ...
- Python 简明教程 --- 5,Python 表达式与运算符
微信公众号:码农充电站pro 个人主页:https://codeshellme.github.io 靠代码行数来衡量开发进度,就像是凭重量来衡量飞机制造的进度. -- Bill Gates 目录 1, ...
- STM32内存受限情况下摄像头驱动方式与图像裁剪的选择
1.STM32图像接收接口 使用stm32芯片,128kB RAM,512kB Rom,资源有限,接摄像头采集图像,这种情况下,内存利用制约程序设计. STM32使用DCMI接口读取摄像头,协议如下. ...
- Flask项目实战:创建电影网站(2)
flask网站制作后台时候常见流程总结 安利一个神神器: 百度脑图PC版 创建数据库 下面是创建User数据库,需要导入db库 #coding:utf8 from flask import Flask ...
- POJ 3463 Sightseeing 【最短路与次短路】
题目 Tour operator Your Personal Holiday organises guided bus trips across the Benelux. Every day the ...
- 【Xamarin.Forms 1】App的创建与运行
引言 本篇文章将从介绍Xamarin.Forms创建开始. 开发环境 Visual Studio 2019 16.6.2 Xamarin.Forms 4.6.0.726 Android 5.0 (AP ...
- 计算机组成原理Day-1
- CRC循环冗余校验码
原文转载自:https://blog.csdn.net/hm108106/article/details/73332465 1.CRC CRC循环冗余校验码是数据通信中的一种查错校验码. 循环冗余检查 ...
- java语言基础(二)_IDEA_方法
IDEA使用 项目结构: 所有代码放置在src文件夹内 新建包:在src文件夹上,右键新建包.包的命名:英文小写.数字.英文句点. 例如:使用公司域名倒写,如cn.itcast.day04.demo0 ...
- less的使用几个技巧
1.层级关系 让这个box范围内的全部包进来,这样的话就完美的进行调节,再也不用到处找第几行第几个,我刚才在哪个位置给覆盖了.一看便知! .box{ width: 100%; height: 300p ...