• C#内存释放(垃圾回收)


    问题背景----

    今天写了个很小的程序,程序的功能仅仅是截图,但是如果长时间开启并截图的时候,程序会变的很大,从刚开始的运行在任务管理器中只有十几K大小,运行一段时间后在任务管理器中看到程序可以达到1G或2G甚至更大;最初想到的是所有的截图都保存在内存中,没有释放造成的。去检查代码,发现程序中已经使用GC.Collect();但是为什么程序还是会一直增加呢?由于程序中逻辑判断等比较多,不方便跟踪及查找。所以我自己单独写了个测试程序,去看看调用GC.Collect();释放的问题?

    测试环境----

    首先准备一个对象(由于程序中使用了一些静态变量),所以准备的对象如下:

    public class CountObject
        {
            public static int Count = 0;
            public CountObject()
            {
                Count++;
            }
    
            ~CountObject()
            {
                Count--;
            }
        }

    程序很简单,只有一个静态的计数变量。下面在看看主程序:

     1 static void Main(string[] args)
     2         {
     3             CountObject obj;
     4             for (int i = 0; i < 5; i++)
     5             {
     6                 obj = new CountObject();
     7                 //obj = null; // 这一步,只是为了更清晰些验证引用的对象是否释放!
     8                 GC.Collect();
     9 
    10             }
    11             //GC.Collect();
    12             //GC.WaitForPendingFinalizers();
    13 
    14             // Count不会是1,因为Finalizer不会马上被触发,要等到有一次回收操作(GC.Collect())后才会被触发。 GC.Collect();GC.WaitForPendingFinalizers();
    15             Console.WriteLine(CountObject.Count);
    16             Console.ReadKey();
    17         }

    程序也比较简单,我做了如下测试:

    1)使用以上程序运行,发现15行会输出5,说明我们调用了GC.Collect();但程序并没有执行释放,因为查GC的官方解释,是不确定的某个时刻进行回收。

    2)把循环每次增大5个。当循环增加到125的时候,多次执行后发现,我本机测试,在第15行的输出是1或125,当增加到10000,每次都输出1,说明符合官方解释;

    根据以上代码测试知道,当循环5次的时候,GC并不会立即执行,所以当执行5次循环的时候第8行没起作用。既然不起作用,我们把他注释暂时不用,把11和12行开启。

    3)把地8行注释,11,12行开启,执行5次循环,发现15行输出1,多次执行结果相同。

    4)再把11行注释,12行开启,执行5次循环,发现15行输出5,多次执行结果相同。

    5)再把12行注释,11行开启,执行5次循环,发现15行输出5,多次执行结果相同。

    根据4和5的才测试可以看到,当少量的循环时Finalizer不会马上被触发,要等到有一次回收操作(GC.Collect())执行后才会被触发。所以我们可以显式调用 GC.Collect();GC.WaitForPendingFinalizers();这两行代码进行强制回收的执行。

    6)验证,把第7行开启,执行测试第15行为0,说明对象如果没有任何的引用则可以强制回收。

    以上是本人的一些测试,如果你还有更好的想法,可以提出一起讨论;

    版权归个人所有,转载请注明出处;


    由内存释放导致的问题:

    软件在测试力度加大情况下,可能导致的内存不足及崩溃的问题可能快速暴露,针对这些问题可以通过下面方式解决,欢迎补充。
    1. 常用方式:
    A)类文件中占用内存较大的全局变量,公共变量,类私有变量及类的实例用完之后手动设置为null或Dispose(),对局部变量不需要置null,但局部的实例需要Dispose或置null。
    B)占用内存较大的变量或实例,在循环创建这些类或实例的地方适当进行置null或Dispose()后进行GC.Collect();
     
    2. 结合代码业务进行代码重构:
    A) 将主程序中的功能模块化,如封装到动态库中后,通过订阅的方式不再进行主动的业务请求,降低主进程负担。
    B) 对程序中会频繁重复使用的类如心跳,网络监控和弹出窗体,历史信息类等,避免重复实例化,通过定义全局唯一静态变量的方式即单例模式实现循环使用。
    C) 优化代码或重构
     
    结论,通常合理使用方式1基本可以解决大部分内存不足导致的崩溃问题,但垃圾回收有时效性等底层判断机制,主动垃圾回收对于内存快速消耗的情况可能效果不好(比如进行1秒百万级,或者只需要几千个并发,在置null和GC之前程序就已经死掉,即垃圾回收不能根本解决程序内存消耗和性能问题,需要不产生垃圾或少产生垃圾),如果对程序性能和质量有更好的要求,结合两种方式使用。
     
    举例:

    1.public void Dispose()
       {
            GC.Collect();
            GC.SuppressFinalize(this);
       }

    2.线程终止及清理

              _thread.Abort();
              _thread.DisableComObjectEagerCleanup();
              _thread = null;

    3.更彻底的垃圾回收

        /// <summary>
        ///设置线程工作的空间
        /// </summary>
        /// <param name="process">线程</param>
        /// <param name="minSize">最小空间</param>
        /// <param name="maxSize">最大空间</param>
        /// <returns></returns>
        [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
        public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
        /// <summary>      
        /// 释放内存      
        /// </summary>      
        public static void ClearMemory()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
            }
        }

    4.代码重构太宽泛,自行总结

    出处:https://blog.csdn.net/jiandanji123/article/details/79416398

    ==========================================================

    C#如何立即回收内存

    =

    1.把对象赋值为null

    2.立即调用GC.Collect();
     
    注意:这个也只是强制垃圾回收器去回收,但具体什么时候执行不确定。 
     
    代码:
      public partial class Form1 : Form
        {
            [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet =System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
            private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize);
     
     
            public Form1()
            {
                InitializeComponent();
            }
     
     
            private void button1_Click(object sender, EventArgs e)
            {
                ClearMemory();
            }
     
     
            #region 内存回收
       public  void ClearMemory()
     {
         GC.Collect();
         GC.SuppressFinalize(this);
     
     
         if (Environment.OSVersion.Platform == PlatformID.Win32NT)
         {
             SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
         }
     }
     #endregion
     
     
       private void timer1_Tick(object sender, EventArgs e)
       {
           string s="clearMemory";
           Form dt = new Form();
           dt.Text = s;
           //如果垃圾产生于timer中可能无法立刻回收资源,需加载timer tick事件中,立刻回收资源,或使用另外的timer控制回收时间。
           GC.Collect();
       }
     
    }
    [System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet =System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)]
            private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize);
    
     #region 内存回收
       public  void ClearMemory()
     {
         GC.Collect();
         GC.SuppressFinalize(this);
     
     
         if (Environment.OSVersion.Platform == PlatformID.Win32NT)
         {
             SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
         }
     }
     #endregion
    
    ClearMemory();

    =

    出处:https://blog.csdn.net/xwnxwn/article/details/78009071

    https://www.cnblogs.com/Leo_wl/p/11507878.html

  • 相关阅读:
    hive表链接
    hive聚合函数和表生成函数
    hive条件函数
    hive日期函数
    hive之size函数和cast转换函数
    hive数学函数
    hive排序
    5G基站概述
    MEC边缘云平台
    ELK日志系统的架构
  • 原文地址:https://www.cnblogs.com/mq0036/p/3707257.html
Copyright © 2020-2023  润新知