我的.net应用程序执行了一些繁重的字符串加载/操作,不幸内存消耗不断上升和上升,当使用分析器查看它时,我看到很多未发布的字符串实例。现在在某个时间点,我确实需要所有对象都有这些字符串字段,但一旦完成,我可以摆脱例如它的一半和我Dispose()并将实例设置为null,但垃圾收集器不会选择它...它们保留在内存中(即使在处理后半小时等)。
现在如何让正确清除不需要的字符串/对象实例才能释放它们?
它们不再被引用(afaik),但是例如aspose的内存分析器说他们到gc根的距离是'3'?
更新:原始字符串来自互操作实例。这些是否可能导致泄漏?
如...分配
myClass.StringProperty = interopInstance.Description.Text;
..我的.stringProperty仍然有一个对互操作的引用,因此'泄漏'/保持interopInstance uncollected&没有正确释放/解组?
答案 0 :(得分:3)
当仍然使用包含该字段的类时,将私有字段设置为null
会很有用。它将允许收集引用的对象。但是,当该私有字段的实例本身未被引用时,它引用的对象也将被引用。换句话说,将对象很快取消引用的对象的空字段无效。
我认为您没有应用程序泄漏。也许您正在创建存储在大对象堆(LOH)中的字符串。当大于或等于85000字节(具有42492个字符或更多)时,对象存储在LOH中。只有当完全垃圾收集(第2代)发生时才收集LOH。因此,由于您似乎没有任何OutOfMemoryExceptions
,我认为没有泄漏。您的应用程序不会耗尽内存,GC也不会收集Gen2。
您可以致电GC.Collect()
进行检查。这将删除所有未使用的大对象。
因此,虽然应用程序中可能没有泄漏,但内存占用量可能不大。特别是当您编写的桌面应用程序不是唯一需要内存的应用程序时。除此之外,大量内存也可能导致性能问题,因为GC必须更频繁地运行。
也许有可能以一种使用更少内存的方式重构代码。例如,使用StringBuilder
个对象(如果您还没有这样做)。或者甚至可能在池中缓存StringBuilder
个对象并重用它们而不是创建一个新对象。特别是在使用从池中获取的实例时设置Capacity属性。这可能特别有用,因为LOH容易碎片化,这可能导致OutOfMemoryExceptions
发生。这对于32位系统来说只是一个问题,因为64位系统几乎没有虚拟地址空间。 64位将在未来几年变得越来越主流,我相信微软还没有投入到为32位版本的CLR修复这个问题,因此。
<强> 更新:强> 在interop的情况下,字符串被序列化和反序列化。字符串作为字节数组发送,因此通常不会更改它保持引用。但是,每次从interop获取字符串时,都会创建一个新的字节数组,而在.NET中,byte []将被复制到一个字符串中。这将使用的内存量增加一倍。
答案 1 :(得分:1)
只是对此采取行动,但你是否连接了许多字符串?如果是这样,你使用'+'运算符这样做?字符串是不可变的,这意味着每次处理时都可以在内存中创建一个新的字符串对象。
如果要连接字符串,则应考虑使用StringBuilder对象(如果您还没有这样做),甚至是String.Concat()方法。这可能是问题所在,尤其是如果您在任何时候在循环中连接字符串。
答案 2 :(得分:0)
我一直致力于在许多终端之间传输数据的系统。这些终端生成文本数据(以XML格式),然后压缩并传输到其他终端。最初我们遇到了与应用程序完全相同的问题,使用越来越多的Ram并最终耗尽内存。连接到它的终端越多,它耗尽的速度指数越快。
该系统的性能不是最重要的功能,但长期正常运行时间是。
在通过剖析器搜寻以找到记忆消失的地方后,我们发现如果其中有任何实质内容,我们的字符串最终会在LOH中结束。
我知道这是有争议的,许多人说不要强迫GC.Collect,但我也发现其他网站,例如Rico Mariani,如果你有特殊需求,你确定你的对象最终在LOH然后继续做,只记得GC.Collect会影响性能。
好消息是,一旦我们将这个添加到程序的字符串处理区域,我们就会在应用程序的整个生命周期中使用相同的ram使用情况,并且我们运行的一些系统具有正常运行时间&GT;60天
。