C#线程不释放内存

4

我有一个用C#.Net编写的Windows服务。当服务启动时,我会像下面展示的一样创建一个新的线程。

new Thread(new ThreadStart(Function1)).Start();

这个线程会无限循环并执行我所需的服务职责。每天一次,我需要同时执行不同的操作,为此我的线程会像下面展示的那样生成第二个线程。

new Thread(new ThreadStart(Function2)).Start(); 

这个第二个线程执行的功能非常简单。它使用FileReadAllLines读取文本文件的所有行,快速处理信息并退出。
我的问题是,读取文件的第二个线程使用的内存没有被收集。我让我的服务运行了3个小时,希望GC会被调用,但什么也没有发生,任务管理器仍然显示我的服务正在使用150mb的内存。读取和处理文本文件的函数非常简单,我确信在包含文本的字符串数组中没有隐藏的引用。有人能否解释一下这是为什么?可能是另一个生成的线程无法清理自己吗?
谢谢

我认为实际看到在第二个线程上运行的代码会有所帮助。 - Rex Morgan
你的第二个线程是否正确处理了用于读取文件的流的释放? - Nathan
1
你如何知道你的程序存在内存泄漏。仅仅从任务管理器中读取数字是极易出错的。除非你真正了解Windows内存管理和.NET内存管理,否则你会误解这些数字。 - David Heffernan
1
这个问题有点难以理解,但是我可以建议你使用ThreadPool而不是手动创建新线程或使用System.Threading.Timer——在WinServices中安排一些后台工作是常见做法。 - Disposer
2
使用150兆字节的内存这种说法是相当毫无意义的。你所使用的“内存”是否指地址空间?如果是,其中有多少是共享的?在未共享的工作集中,有多少被分配、多少被提交,以及有多少存在于物理内存而非页面文件中?在弄清所有这些问题之前,分析内存使用是没有意义的。但说实话,150兆字节太小了,你可能不需要担心它。你可以解释一下为什么关心它吗?此外,不要使用任务管理器。使用内存分析器来分析内存使用情况,因为那才是它所用来做的。 - Eric Lippert
显示剩余2条评论
2个回答

11

相信垃圾收集器,不要担心。150兆没什么好担心的,你甚至没有计算文件的大小;其中大部分将是代码。

如果你关心内存的去向,首先了解现代操作系统中内存如何工作。你需要理解虚拟内存和物理内存之间的区别,已提交和已分配内存之间的区别等所有内容,然后再开始使用“150兆已分配内存”这样的数字。请记住,在32位进程中有2000兆虚拟地址空间;我认为150兆进程并不算大。

正如Jon所说,你要担心的是私有字节的缓慢而稳定的增长。如果这种情况没有发生,那么你就没有内存泄漏的问题。让垃圾收集器完成它的工作,不必担心。

如果你仍然担心,千万不要使用任务管理器。获取内存分析器并学习如何使用它。任务管理器用于从30000英尺高度审查进程。你需要使用显微镜而不是望远镜来分析进程如何释放单个文件的字节。


1
谢谢。显微镜让我微笑,而不是望远镜。 - newidforu
可能最好的工具是Process Explorer(http://technet.microsoft.com/en-us/sysinternals/bb896653)来开始。它包含许多.NET特定信息。 - Matt Warren
2
Process Explorer很有用,但我更倾向于从CLR Profiler开始(下载1.12.04.0)。 - Brian

5
如果你使用Windows任务管理器来尝试计算内存使用情况,那么它很可能会欺骗你。据我所知,CLR使用的内存通常不会返回给操作系统...因此,即使大部分内存仍然可在进程中重复使用,你仍可能看到一个高工作集。
如果你让服务运行一个星期,你是否看到内存使用量在整个星期稳步上升,还是只在第一天增加,然后趋于平稳?如果是这样,你是否确定将其视为问题?如果是这样,你可能需要将第二个任务放在单独的进程中。

这可能是解决方案。有没有办法强制将内存返回给操作系统,或者如果另一个应用程序需要此内存,这是否会自动发生? - newidforu
@newidforu:在操作系统中,您有大量的虚拟内存...尽管我相信垃圾回收器可能会对全局压力做出响应。我承认在内存方面发生了什么事情并不是非常清楚。理想情况下,您不希望将“浪费”的内存交换,但这可能最终会发生... - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接