RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 930793
Accepted
FunkyShit
FunkyShit
Asked:2020-01-11 14:04:54 +0000 UTC2020-01-11 14:04:54 +0000 UTC 2020-01-11 14:04:54 +0000 UTC

c#垃圾收集器不清除内存

  • 772

有一个静态方法

private static bool GetXml(string date)
{
    try
    {
        //Подключаемся к сервису
        KBODataProviderClient client = new KBODataProviderClient();
        //Получаем данные
        string[] result = client.getKBOContracts("2018-12-25", null);

        //Если данных нет
        if (result == null || result.Length <= 0)
        {
            log.InfoFormat("[{0}]|Pbo return 0 rows", runGuid);
            return false;
        }

        if (File.Exists(filePath))
            File.Delete(filePath);

        //Сохраняем XML
        using (StreamWriter sw = new StreamWriter(filePath, true, Encoding.UTF8))
        {
            //пишем первую запись с заголовков
            sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
            sw.WriteLine("<CONTRACTS>");
            for (int i = 0; i < result.Length; i++)
            {
                //удаляем заголовок
                string temp = result[i].Replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>", "");
                sw.WriteLine(temp);
            }
            sw.WriteLine("</CONTRACTS>");
        }


        log.InfoFormat("[{0}]|Create XML success", runGuid);

        return true;

    }
    catch (Exception ex)
    {
        log.ErrorFormat("[{0}]|GetXml error {1}", runGuid, ex + Environment.NewLine + ex.StackTrace);
        return false;
    }
}

当该方法被调用时,它连接到 WCF 服务,一个字符串数组来自那里(返回 string[]),然后我将其写入文件。问题是,接收到数据的时候,内存当然是吃光的,而且方法完成后,内存并没有被清空。

其实我知道string[]结果变量是一个临时变量,方法完成后应该被垃圾回收器回收,但是这并没有发生(数据占用大约400MB内存)

首先想到的是由于静态方法引起的问题。然后我决定擦链接

result = null;
GC.Collect();

并调用收集器本身。但是即使在这里效果也是0。有什么问题?

更新:

使用方法如下:

if (GetXml(date))
{
    //парсинг
}

添加了这样的方法:

private static bool GetXml(string date)
{
    try
    {
        //Подключаемся к сервису
        KBODataProviderClient client = new KBODataProviderClient();
        //Получаем данные
        string[] result = client.getKBOContracts("2018-12-25", null);

        //Если данных нет
        if (result == null || result.Length <= 0)
        {
            log.InfoFormat("[{0}]|Pbo return 0 rows", runGuid);
            return false;
        }

        if (File.Exists(filePath))
            File.Delete(filePath);

        //Сохраняем XML
        using (StreamWriter sw = new StreamWriter(filePath, true, Encoding.UTF8))
        {
            //пишем первую запись с заголовков
            sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
            sw.WriteLine("<CONTRACTS>");
            for (int i = 0; i < result.Length; i++)
            {
                //удаляем заголовок
                string temp = result[i].Replace("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>", "");
                sw.WriteLine(temp);
            }
            sw.WriteLine("</CONTRACTS>");
        }

        log.InfoFormat("[{0}]|Create XML success", runGuid);

        client.Close();
        client = null;

        result = null;

        GC.Collect();

        return true;

    }
    catch (Exception ex)
    {
        log.ErrorFormat("[{0}]|GetXml error {1}", runGuid, ex + Environment.NewLine + ex.StackTrace);
        return false;
    }
}

我关闭了服务客户端,甚至分配了 null 以防万一。接下来,我将 null 分配给字符串并调用收集器。

结果,在接收数据时,内存增加到 320MB (+ - ofk) 并在完成后下降到 + -211 并继续工作。

解析方式不会占用这么多内存,因为解析时不接收数据,内存保持在+-60MB。

在此处输入图像描述 试过 Array.Clear,也是 0 效果(屏幕上图)

c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    FunkyShit
    2020-01-11T19:30:33Z2020-01-11T19:30:33Z

    问题解决了。

    决定把左边的项目全部查一遍,慢慢调代码,结果在新项目上正常清空内存竟然是奇迹(只需要调用GC.Collect())

    首先,我决定检查 .net 版本,一切正常的项目是 4.7.1,但更改版本并没有帮助。然后我开始逐行添加并检查代码。

    原因原来是在 app.config 中,即在向服务添加链接时 VS 生成的部分中(此处指示超时等)。具体来说,问题是在绑定(basicHttpBinding 部分)中指定了超时(CloseTimeout、OpenTimeout、ReceiveTimeout、SendTimeout)。

    如果我在本节中指定了这些超时,则不会清除内存(无论是在主项目中,还是在任何其他项目中),即如果我这样做:

    <basicHttpBinding>
      <binding name="KBODataProviderPortBinding" closeTimeout="00:30:00" openTimeout="00:30:00" receiveTimeout="00:30:00" sendTimeout="00:30:00" maxBufferPoolSize="600000000" maxBufferSize="600000000" maxReceivedMessageSize="600000000"/>
    </basicHttpBinding>
    

    但如果我通过代码指定超时:

    client.Endpoint.Binding.CloseTimeout = new TimeSpan(0, 30, 0);
    client.Endpoint.Binding.OpenTimeout = new TimeSpan(0, 30, 0);
    client.Endpoint.Binding.ReceiveTimeout = new TimeSpan(0, 30, 0);
    client.Endpoint.Binding.SendTimeout = new TimeSpan(0, 30, 0);
    

    然后垃圾收集器成功收集了垃圾。

    为了从配置中进行时间设置,我使用了 ConfigurationManager

    <appSettings>
      <add key="timeout" value="30"/>
    </appSettings>
    

    然后在代码中我这样做:

    //Получаем время, переводим его в TimeSpan (если указать их в конфиге, то память не будет очищена)
    TimeSpan time = new TimeSpan(TimeSpan.TicksPerMinute * int.Parse(ConfigurationManager.AppSettings.Get("timeout")));
    client.Endpoint.Binding.CloseTimeout = time;
    client.Endpoint.Binding.OpenTimeout = time;
    client.Endpoint.Binding.ReceiveTimeout = time;
    client.Endpoint.Binding.SendTimeout = time;
    

    并且垃圾收集器也成功清理了内存

    • 9
  2. Pavel Mayorov
    2020-01-11T16:37:57Z2020-01-11T16:37:57Z

    所有使用垃圾收集器的语言都因不喜欢将内存返回给系统而臭名昭著。最简单的解决方法是,通常情况下,未使用的内存会很快在页面文件中安顿下来,并且不会打扰那里的任何人。只有当交换文件已经结束并且交换文件结束时,问题才会开始。

    但是,有几种方法可以强制释放内存。

    方法 1 是一个单独的过程。

    您可以将 GetXml 调用移至一个单独的程序,该程序将完成所有占用大量内存的工作并退出。

    这是最慢的方式(甚至比什么都不做还要慢),但它可以让你快速解决内存不足的问题。

    方法 2 - 在线处理。

    此方法需要重写客户端,使其不返回字符串列表,但可以处理 I/O 流。这个想法是携带一个 Stream(或 PipeReader,请参阅 System.Threading.Pipelines 库)并且永远不会在内存中存储超过 4 KB 的数据。

    如果操作正确,这种方法的优点是非常低的内存消耗。缺点是诸如“将流一分为二”或“从文档开头咬住 XML 声明”之类的操作需要复杂的架构转换。

    因此,例如,您完全有可能无法从客户端获取线程列表,而是必须订阅一个事件。它可能看起来像这样:

    using (StreamWriter sw = new StreamWriter(filePath, true, Encoding.UTF8))
    {
        sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
        sw.WriteLine("<CONTRACTS>");
    
        var client = new KBODataProviderClient();
        client.ProcessResult += stream => 
        {
            sw.Flush();
            stream.Position += Encoding.UTF8.GetByteCount("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
            stream.CopyTo(sw.BaseStream);
        };
        client.getKBOContracts("2018-12-25", null);
    
        sw.WriteLine("</CONTRACTS>");
    }
    

    方法 3 - 非托管内存

    此方法还需要重写客户端。它可以比线程更简单(由于更简单的算法)或更复杂(由于更复杂的 API)。这种方法的缺点是不可移植性,因为它与特定的平台 API 相关联。

    您需要通过 P / Invoke 分配一块非托管内存(最好使用几个VirtualAlloc/函数VirtualFree- 它们肯定会将内存返回给系统),并且(旧方法)要么将其包装SafeBuffer在UnmanagedMemoryAccessor+中UnmanagedMemoryStream,要么 (新方法)MemoryManager<byte>从系统库 .Memory 中包装它并进一步使用Memory<byte>

    • 5
  3. iluxa1810
    2020-01-11T14:23:57Z2020-01-11T14:23:57Z

    垃圾收集器的行为是不确定的,因此它会在它认为合适的时候运行。

    这样做是为了优化目的,因为垃圾收集是一项资源密集型操作:

    1. 需要查找没有引用的对象
    2. 然后释放内存
    3. 执行堆压缩(碎片整理)

    在显式调用的情况下,GC.Collect();如果您正在使用托管资源并且没有泄漏,则仍应清除内存。

    由于程序在调试模式下运行,并且在这种模式下生命被变量所抛弃,因此内存可能不会被清除,这是合乎逻辑的,因为即使在退出方法之前也可以收集垃圾,如果很清楚的话变量是不可见的,但是在调试过程中我们可以观察变量,因此如果变量在调试过程中从 Watch 中消失会很不方便。

    尝试在发布模式下运行各种优化。更多关于这方面的内容写在这里。

    此外,Richetr 似乎写道,垃圾收集器可能不会立即为系统提供内存,因为它可能假设在不久的将来会需要它。

    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5