【问题标题】:How to release memory after running program in C#?在 C# 中运行程序后如何释放内存?
【发布时间】:2016-06-19 01:29:23
【问题描述】:

我有一个 C# 程序,我在其中从一个包含大约 40000 个数字的 excel 文件中读取数据。这是我的代码的一部分:

        Microsoft.Office.Interop.Excel.Application _excelApp = new          Microsoft.Office.Interop.Excel.Application();
        _excelApp.Visible = true;
        string fileName = "D:\\data.xlsx";
        Workbook workbook = _excelApp.Workbooks.Open(fileName, Type.Missing);
        Worksheet worksheet = (Worksheet)workbook.Worksheets[1];
        Range excelRange = worksheet.UsedRange;
        Object[,] valueArray =    (object[,])excelRange.get_Value(XlRangeValueDataType.xlRangeValueDefault);
        int num = worksheet.UsedRange.Rows.Count;
        workbook.Close(false, Type.Missing);

        _excelApp.Quit();
        for (int row = 1; row <= num; ++row)
        {
            data1.Add(Convert.ToDouble(valueArray[row, 1]));
            data2.Add(Convert.ToDouble(valueArray[row, 2]));
        }

每次运行程序,excel文件都会出现片刻,然后关闭。但是windows后台进程中有一些打开的excel文件,会消耗内存,降低windows的速度。如何在我的代码中完全关闭 excel 文件并将它们从内存中退出?

谢谢。

【问题讨论】:

    标签: c# excel memory


    【解决方案1】:

    您需要释放您使用的 COM 对象,以便它们可以正确关闭。

    试试这个代码:

    var _excelApp = new Microsoft.Office.Interop.Excel.Application();
    _excelApp.Visible = true;
    var fileName = "D:\\data.xlsx";
    var workbooks = _excelApp.Workbooks;
    var workbook = workbooks.Open(fileName, Type.Missing);
    var worksheet = (Worksheet)workbook.Worksheets[1];
    var excelRange = worksheet.UsedRange;
    Object[,] valueArray = (object[,])excelRange.get_Value(XlRangeValueDataType.xlRangeValueDefault);
    int num = worksheet.UsedRange.Rows.Count;
    
    Marshal.FinalReleaseComObject(excelRange);
    Marshal.FinalReleaseComObject(worksheet);
    
    workbook.Close(false, Type.Missing);
    
    Marshal.FinalReleaseComObject(workbook);
    Marshal.FinalReleaseComObject(workbooks);
    
    _excelApp.Quit();
    
    Marshal.FinalReleaseComObject(_excelApp);
    
    for (int row = 1; row <= num; ++row)
    {
        data1.Add(Convert.ToDouble(valueArray[row, 1]));
        data2.Add(Convert.ToDouble(valueArray[row, 2]));
    }
    

    我没有测试过这段代码,但应该很接近。

    请注意,您绝对必须执行此类代码:

    var workbooks = _excelApp.Workbooks;
    var workbook = workbooks.Open(fileName, Type.Missing);
    

    ...你不能这样做:

    var workbook = _excelApp.Workbooks.Open(fileName, Type.Missing);
    

    ...因为这会使_excelApp.Workbooks 中间对象无法被释放。

    【讨论】:

    • 谜团,我认为你传播错误信息比这更好。致电FinalReleaseComObject() 解决此问题是错误的信息在互联网上猖獗。请参阅 Hans Passant 的 thisthis
    • 我测试了这个建议。但是没有任何效果!
    • @ScottChamberlain - 嗯,我自己在很多个月前就遇到过这种情况,这是正确的方法。我不知道发生了什么变化。
    • @Yaser - 我会尝试这里介绍的两种解决方案,但我知道的关键是每次我的 Excel 关闭代码错误时我都必须重新启动机器.操作系统中的某些东西似乎被破坏了,所以即使你得到了正确的代码,除非你在尝试失败后重新启动它也不会工作。这是一个皮塔饼!
    【解决方案2】:

    你想强制垃圾收集器调用,这基本上是通过调用GC.Collect()来实现的。

    当您调用 GC.Collect 时,GC 将在单独的线程上运行每个对象的终结器。因此,要记住的另一种方法是 GC.WaitForPendingFinalizers。此同步方法在 GC.Collect 完成工作之前不会返回。

    例子:

    public void MethodUsingTonsOfMemory(){
      // do memory intensive stuff
    
      GC.Collect();
      GC.WaitForPendingFinalizers(); // wait for memory to be released
    
      Debug.WriteLine("Memory should be clear now");
    }
    

    【讨论】:

    • 哦,你的意思是如何在后台关闭其他excel文件?
    • 我不知道为什么这会被否决,调用 GC.Collect() 后跟 GC.WaitForPendingFinalizers(); 是解决 excel 问题的正确方法。调用FinalReleaseComObject 是错误的做法,但这个答案在整个互联网上都很猖獗。您唯一需要注意的是,如果您以使用 COM 对象的相同方法执行此操作,则它不会在调试模式下收集对象,或者由于调试器完成对象生命周期扩展而附加调试器。跨度>
    • 我测试了这个建议。但是没有任何效果!
    • @Yaser 您可能使用与 Excel 引用并打开调试器相同的方法调用了 GC.Collect()。请参阅 this answer 以更深入地解释为什么需要从函数外部或不使用调试器调用它。
    猜你喜欢
    • 2011-09-29
    • 2019-09-08
    • 1970-01-01
    • 2013-01-04
    • 1970-01-01
    • 2014-09-20
    • 2015-06-25
    • 2010-09-26
    • 2020-09-10
    相关资源
    最近更新 更多