前言

这是续第三节。

概况垃圾回收与我们写代码的关系:

  1. 强引用和弱引用
  2. 针对共享 Web 承载优化
  3. 垃圾回收和性能
  4. 应用程序域资源监视

正文

强引用和弱引用

垃圾回收器不能回收仍在引用的对象的内存——这是一个强引用。它可以回收不在根表中直接或间接的托管内存。然而,有时可能会忘记释放内存。

注意:如果对象相互引用,但是没有在根表中引用,例如:对象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# 托管和非托管资源与代码相关(四)的更多相关文章

  1. 重学c#系列——c# 托管和非托管资源(三)

    前言 c# 托管和非托管比较重要,因为这涉及到资源的释放. 现在只要在计算机上运行的,无论玩出什么花来,整个什么概念,逃不过输入数据修改数据输出数据(计算机本质),这里面有个数据的输入,那么我们的内存 ...

  2. 重学c#系列——字典(十一)

    前言 重学c#系列继续更新,简单看一下字典的源码. 看源码主要是解释一下江湖中的两个传言: 字典foreach 顺序是字典添加的顺序 字典删除元素后,字典顺序将会改变 正文 那么就从实例化开始看起,这 ...

  3. 有关 Azure IaaS VM 磁盘以及托管和非托管高级磁盘的常见问题解答

    本文将对有关 Azure 托管磁盘和 Azure 高级存储的一些常见问题进行解答. 托管磁盘 什么是 Azure 托管磁盘? 托管磁盘是一种通过处理存储帐户管理来简化 Azure IaaS VM 的磁 ...

  4. [.net 面向对象程序设计进阶] (8) 托管与非托管

    本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收.掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常. 1.什么是托 ...

  5. NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配

    在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时 ...

  6. C# 托管和非托管混合编程

    在非托管模块中实现你比较重要的算法,然后通过 CLR 的平台互操作,来使托管代码调用它,这样程序仍然能够正常工作,但对非托管的本地代码进行反编译,就很困难.   最直接的实现托管与非托管编程的方法就是 ...

  7. C# using 三种使用方式 C#中托管与非托管 C#托管资源和非托管资源区别

    1.using指令.using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间,类似于Java的import,这个功能也是最常用的,几乎每个cs的程序都会用到. ...

  8. 利用C#Marshal类实现托管和非托管的相互转换

    Marshal 类 命名空间:System.Runtime.InteropServices 提供了一个方法集,这些方法用于分配非托管内存.复制非托管内存块.将托管类型转换为非托管类型,此外还提供了在与 ...

  9. [转]C# 之DLL调用(托管与非托管)

    每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍.首先,您需要了解什么是托管,什么是非托管.一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX ...

随机推荐

  1. 如何下载 Ubuntu 镜像文件?

    Ubuntu,是一款基于 Debian Linux 的以桌面应用为主的操作系统,内容涵盖文字处理.电子邮件.软件开发工具和 Web 服务等,可供用户免费下载.使用和分享. 但是对于国内的用户来说如果直 ...

  2. robot framework使用小结(四)

    robot framework可以采用读excel表这种形式实现数据分离,本文没有实现批量读取excel表内容(还不会),后续有必要就学习哈再更新~~~ 新建测试案例baidu05, 右键项目名rob ...

  3. MongoDB快速入门教程 (4.3)

    4.3.Mongoose模块化 4.3.1.为什么要进行模块化拆分? 模块化拆分的目的是为了代码的复用,让整个项目的结构更加清晰,举个例子:当数据库中的集合变多的时候,例如有课程.订单.分类.教师等多 ...

  4. 实现MFC扩展DLL中导出类和对话框

    如果要编写模块化的软件,就要对对动态链接库(DLL)有一定的了解,本人这段时间在修改以前的软件时,决定把重复用的类和对话框做到DLL中,下面就从一个简单的例子讲起,如何实现MFC扩展DLL中导出类和对 ...

  5. 如何在linux下安装tomcat服务器

    linux作为现在比较主流的服务器操作系统,使用的机器广泛,安全稳定.tomcat作为应用容器当然可以有linux版本的tomcat.在linux上安装tomcat的方式也很简单,只需要运行脚本基本配 ...

  6. Markdown-it-latex2img

    Markdown-it-latex2img LaTex plugin for markdown-it markdown parser,Server side MathJax Renderer. Bac ...

  7. Jmeter(十四) - 从入门到精通 - JMeter定时器 - 下篇(详解教程)

    1.简介 用户实际操作时,并非是连续点击,而是存在很多停顿的情况,例如:用户需要时间阅读文字内容.填表.或者查找正确的链接等.为了模拟用户实际情况,在性能测试中我们需要考虑思考时间.若不认真考虑思考时 ...

  8. 《SpringBoot判空处理》接开@valid的面纱

    一.事有起因 我们在与前端交互的时候,一般会遇到字段格式校验及非空非null的校验,在没有SpringBoot注解的时候, 我们可能会在service进行处理: if(null == name){ t ...

  9. requests接口自动化1-json序列化与反序列化

    json序列化与反序列化: 序列化:将对象转换为json字符串 反序列化:将json字符串转换为对象,比如转换为python里的字典 import json #定义字典 dict1={'usernam ...

  10. shell专题(六):条件判断

    1.基本语法 [ condition ](注意condition前后要有空格) 注意:条件非空即为true,[ atguigu ]返回true,[] 返回false. 2. 常用判断条件 (1)两个整 ...