有一个静态方法
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。

问题解决了。
决定把左边的项目全部查一遍,慢慢调代码,结果在新项目上正常清空内存竟然是奇迹(只需要调用GC.Collect())
首先,我决定检查 .net 版本,一切正常的项目是 4.7.1,但更改版本并没有帮助。然后我开始逐行添加并检查代码。
原因原来是在 app.config 中,即在向服务添加链接时 VS 生成的部分中(此处指示超时等)。具体来说,问题是在绑定(basicHttpBinding 部分)中指定了超时(CloseTimeout、OpenTimeout、ReceiveTimeout、SendTimeout)。
如果我在本节中指定了这些超时,则不会清除内存(无论是在主项目中,还是在任何其他项目中),即如果我这样做:
但如果我通过代码指定超时:
然后垃圾收集器成功收集了垃圾。
为了从配置中进行时间设置,我使用了 ConfigurationManager
然后在代码中我这样做:
并且垃圾收集器也成功清理了内存
所有使用垃圾收集器的语言都因不喜欢将内存返回给系统而臭名昭著。最简单的解决方法是,通常情况下,未使用的内存会很快在页面文件中安顿下来,并且不会打扰那里的任何人。只有当交换文件已经结束并且交换文件结束时,问题才会开始。
但是,有几种方法可以强制释放内存。
方法 1 是一个单独的过程。
您可以将 GetXml 调用移至一个单独的程序,该程序将完成所有占用大量内存的工作并退出。
这是最慢的方式(甚至比什么都不做还要慢),但它可以让你快速解决内存不足的问题。
方法 2 - 在线处理。
此方法需要重写客户端,使其不返回字符串列表,但可以处理 I/O 流。这个想法是携带一个 Stream(或 PipeReader,请参阅 System.Threading.Pipelines 库)并且永远不会在内存中存储超过 4 KB 的数据。
如果操作正确,这种方法的优点是非常低的内存消耗。缺点是诸如“将流一分为二”或“从文档开头咬住 XML 声明”之类的操作需要复杂的架构转换。
因此,例如,您完全有可能无法从客户端获取线程列表,而是必须订阅一个事件。它可能看起来像这样:
方法 3 - 非托管内存
此方法还需要重写客户端。它可以比线程更简单(由于更简单的算法)或更复杂(由于更复杂的 API)。这种方法的缺点是不可移植性,因为它与特定的平台 API 相关联。
您需要通过 P / Invoke 分配一块非托管内存(最好使用几个
VirtualAlloc/函数VirtualFree- 它们肯定会将内存返回给系统),并且(旧方法)要么将其包装SafeBuffer在UnmanagedMemoryAccessor+中UnmanagedMemoryStream,要么 (新方法)MemoryManager<byte>从系统库 .Memory 中包装它并进一步使用Memory<byte>垃圾收集器的行为是不确定的,因此它会在它认为合适的时候运行。
这样做是为了优化目的,因为垃圾收集是一项资源密集型操作:
在显式调用的情况下,
GC.Collect();如果您正在使用托管资源并且没有泄漏,则仍应清除内存。由于程序在调试模式下运行,并且在这种模式下生命被变量所抛弃,因此内存可能不会被清除,这是合乎逻辑的,因为即使在退出方法之前也可以收集垃圾,如果很清楚的话变量是不可见的,但是在调试过程中我们可以观察变量,因此如果变量在调试过程中从 Watch 中消失会很不方便。
尝试在发布模式下运行各种优化。更多关于这方面的内容写在这里。
此外,Richetr 似乎写道,垃圾收集器可能不会立即为系统提供内存,因为它可能假设在不久的将来会需要它。