当前位置: 首页 > news >正文

通俗易懂话GC-C#的内存管理

昨天和一个朋友聊到图像处理软件内存占用多的问题,然后很自然聊到了GC,回想起以往很初学者都问到类似的问题:

1、C#自己就会垃圾回收,为什么我还要关心垃圾回收?

2、GC可以回收垃圾,但回收的时候又会让线程卡住,我到底该不该GC.Collect()?

为了回答这些问题,我们先从一个小实验讲起

我们先做这样一个程序,不断从电脑摄像头读取图像,然后显示出来,代码很简单:

using OpenCvSharp;

using var capture = new VideoCapture(0, VideoCaptureAPIs.DSHOW);
if (!capture.IsOpened())
    return;

capture.FrameWidth = 1920;
capture.FrameHeight = 1280;
capture.AutoFocus = true;

const int sleepTime = 10;

using var window = new Window("capture");


while (true)
{
    var image = new Mat();
    capture.Read(image);
    if (image.Empty())
        break;

    window.ShowImage(image);
    int c = Cv2.WaitKey(sleepTime);
    if (c >= 0)
    {
        break;
    }
}

你将看到,内存持续增长,直到发生GC,如此往复:

 现在我们加入手动GC:


using OpenCvSharp;

using var capture = new VideoCapture(0, VideoCaptureAPIs.DSHOW);
if (!capture.IsOpened())
    return;

capture.FrameWidth = 1920;
capture.FrameHeight = 1280;
capture.AutoFocus = true;

const int sleepTime = 10;

using var window = new Window("capture");


while (true)
{
    var image = new Mat();
    capture.Read(image);
    if (image.Empty())
        break;

    window.ShowImage(image);
    int c = Cv2.WaitKey(sleepTime);
   //手动触发GC
     GC.Collect();
    if (c >= 0)
    {
        break;
    }
}

可以看到,随着密集的GC,内存占用平稳了:

但是,问题解决了吗?有经验的小伙伴会知道,GC会让程序变卡,所以手动GC在大多数时间并不是一个万能灵药,相反是毒药,那么真正的解决方案是什么呢?

我们把代码再改一下:

把GC.Collect(); 改成 image.Dispose();


using OpenCvSharp;

using var capture = new VideoCapture(0, VideoCaptureAPIs.DSHOW);
if (!capture.IsOpened())
    return;

capture.FrameWidth = 1920;
capture.FrameHeight = 1280;
capture.AutoFocus = true;

const int sleepTime = 10;

using var window = new Window("capture");


while (true)
{
    var image = new Mat();
    capture.Read(image);
    if (image.Empty())
        break;

    window.ShowImage(image);
    int c = Cv2.WaitKey(sleepTime);
   // GC.Collect();    //手动触发GC
   image.Dispose();   //手动释放对象
    if (c >= 0)
    {
        break;
    }
}

可以看到,即没有发生内存暴涨,也没有发生GC。

为什么呢?GC.Collect(); 和 image.Dispose(); 分别发生了什么?

GC.Collect(); 时,程序内部发生了大迁徙:

第一步 GC线程会把其它线程从合作模式转换到抢占模式,合作模式线程可以访问托管堆和非托管堆,抢占模式只能访问非托管堆,可以简单的认为,GC线程会暂停其它线程。

第二步 把托管堆的对象都标记为垃圾(我不是针对谁)。

第三步 标记出存活的对象。

第四步 清理失活对象,把标记存活的对像向前移动,覆盖空闲内存区,在后部空出整片的空闲区。

第五步 恢复其它线程。

image.Dispose();时,程序只是把imgae对象所占的区域标定为空闲区。成本是极低的。

GC的成本很高,我应该想办法避免GC,尤其是在程序需要高实时响应的场合,那么又有了新的问题:

能避免自动GC吗?

首先,不需要过分担心自动GC,因为它和手动GC的“成本”大多数情况下并不相同。

为了不动辄进行大迁徙,设计者有设计分代回收的优化策略,一般对象初始化时会放在0代,每经历一次GC还能存活,就会上升一代,最终来到2代。大对象(大于85,000 byte) 比如图像初始化时就在2代。0代和1代的空间比较小,这样也是为了加速GC,自动GC一般不会像手动GC那样,把所有的代都整一遍,而是会优先回收低代内存,如果不够才会回收高代。

但避免自动GC还是我们的终极目标,想避免自动GC,首先要明白,自动GC什么时候会发生?

1、给新对象分配空间时,发现不够了。

2、收到系统物理内存不够的通知。

所以,总结一下:

1、大对象要手动销毁。别人实现的.Dispose()的要记得调用,自己写的大对象类要实现.Dispose()。

2、力大砖飞,物理内存要够大。

3、在合适的时候手动GC。

4、避免频繁创建大对象,比如上面的代码最优应该是下面这样:


using OpenCvSharp;

using var capture = new VideoCapture(0, VideoCaptureAPIs.DSHOW);
if (!capture.IsOpened())
    return;

capture.FrameWidth = 1920;
capture.FrameHeight = 1280;
capture.AutoFocus = true;

const int sleepTime = 10;

using var window = new Window("capture");

//在循环外创建大对象
using var image = new Mat();

while (true)
{
    
    capture.Read(image);
    if (image.Empty())
        break;

    window.ShowImage(image);
    int c = Cv2.WaitKey(sleepTime);
 
    if (c >= 0)
    {
        break;
    }
}

 

相关文章:

  • 使用小程序制作一个电子木鱼,功德+1
  • Android Studio App开发之实现底部标签栏BottomNavigationView和自定义标签按钮实战(附源码 超详细必看)
  • Vue/Vuex (actions) 核心概念 使用方法、辅助函数 mapActions使用方法说明
  • Flink学习:Flink支持的数据类型
  • 【论文复现】——FEC: Fast Euclidean Clustering for Point Cloud Segmentation
  • 第十三届蓝桥杯C++B组省赛 I 题——李白打酒加强版 (AC)
  • 队列的简单实现
  • java毕业设计家庭理财记账系统(附源码、数据库)
  • 【ASM】字节码操作 工具类与常用类 asm-utils 与 asm-commons
  • 用Python把附近的足浴店都给采集了一遍,好兄弟:针不戳~
  • 计算机毕业设计之java+javaweb的医院门诊挂号系统
  • 【Linux】基本指令(一)
  • 刷题记录 -- 面试题
  • 信息学奥赛一本通:1308:【例1.5】高精除
  • 【Linux】关于进程的理解、状态、优先级和进程切换
  • 一文吃透JavaScript中的DOM知识及用法
  • 中断和异常理论详解,Linux操作系统原理与应用
  • 对象的比较(上)PriorityQueue中的底层源码解析
  • 【面试官让我十分钟实现一个链表?一个双向带头循环链表甩给面试官】
  • 以太网交换机自学习、转发帧的流程
  • 金融监管总局修订发布《行政处罚办法》,7月1日起施行
  • 两部门调度部署“五一”假期安全防范工作,要求抓好旅游安全
  • 建设银行南昌分行引金融“活水”,精准灌溉乡村沃土
  • 中吉乌铁路重点控制性工程开工建设,包括三座隧道
  • 上海“模速空间”:将形成人工智能“北斗七星”和群星态势
  • 打造沪派水乡的“湿意”,上海正在保护营造一批湿地空间