我有一个ASP.NET网站,在大约3-4天内将使用约2GB的物理内存,这对我来说听起来非常糟糕。目前,我已经配置了IIS,当它达到500MB时,重新启动应用程序池进程。我想尝试追踪问题。
在.NET中创建对象的新实例时,我认为不需要释放它,因为.NET垃圾回收器会代替我执行此操作。
这是真的吗?或者这可能是我遇到问题的原因之一吗?
我有一个ASP.NET网站,在大约3-4天内将使用约2GB的物理内存,这对我来说听起来非常糟糕。目前,我已经配置了IIS,当它达到500MB时,重新启动应用程序池进程。我想尝试追踪问题。
在.NET中创建对象的新实例时,我认为不需要释放它,因为.NET垃圾回收器会代替我执行此操作。
这是真的吗?或者这可能是我遇到问题的原因之一吗?
.NET会高效地为您管理垃圾回收。尽管对于实现IDisposable接口的类型,调用Dispose方法是明智的,但这可能不是您的问题。在.NET中,内存泄漏可能有很多原因。以下是一些原因:
我希望这为您提供了一些查找问题的思路。
更新:您应该观看此视频以了解关于ASP.NET调试的更多信息。
更新2: 关于您在我的答案中发表的评论,CLR将收集所有托管内存,因此使用new创建的所有对象都将被收集。在这种意义上,对象是否实现IDisposable接口并不重要。然而,有许多情况需要直接或间接使用本机资源(如文件句柄、图形句柄、数据库连接、使用本机-因此是非托管的-内存等)。CLR不知道如何释放这些资源。为此,.NET有终结器的概念。终结器是开发人员可以实现的虚方法。当您这样做时,CLR将在该类型的实例未被引用且在其收集之前调用此方法。终结器通常包含释放这些资源的逻辑。换句话说,当类型需要本机资源时,通常会具有允许类型释放这些资源的终结器方法。
就CLR而言,这里的故事到此为止了。CLR没有特定的处理实现IDisposable接口的对象的方法。然而,.NET垃圾回收器是不确定性的。这意味着您不知道它何时运行以及是否运行。这意味着它可能需要很长时间才能清理您的本机资源(因为垃圾回收器只会在收集后调用终结器)。然而,对于许多资源,只有在完成时才释放它们是必要的。例如,在通过System.Drawing名称空间使用GDI+时或者当您不关闭数据库连接时,您很快就会用完它们。出于这个原因,引入了IDisposable接口。再次强调一下,CLR和垃圾回收器不看这个接口。它是类型与其用户之间的契约,允许用户直接释放对象的基础资源。在正常设计中,对象的终结器和对象的Dispose方法都将调用相同的私有或受保护方法来释放这些资源。当类型实现IDisposable时,最好在完成后调用其Dispose方法,或者将对象包装在using语句中以使本机资源的释放具有确定性。
因此,回到您的问题。所有托管对象都将被GC收集,但本机资源不会被回收。因此,类型可能会实现终结器方法,并且这些对象通常也会实现IDisposable接口。在它们上调用Dispose将显式地和直接地释放这些本机资源。
希望这有意义。
XmlSerializer
。尽管它实现了IDisposable
,但它会在内存中创建新的程序集,并且这些程序集永远不会被处理(一旦程序集加载到进程中,它就会一直存在,直到进程终止)。 - Vinicius你的内存使用率很高可能有很多原因,但.NET中的垃圾回收是一个非常精确的事情。也就是说,它可以为你做很多事情,但有时不如你所期望的那样。
具体而言,它只能清理掉没有活动引用的对象,因此如果你完成了一个类,但仍然有某些东西引用它,你需要删除该引用,以便GC可以为你恢复该内存。此外,如果你有任何非活动的打开连接(比如到数据库或其他地方),不要忘记关闭和处理它们。通常,我们会像这样将这些对象包装在using
块中:
using(SqlConnection conn = new SqlConnection(myConnString))
{ ...rest of code here }
这将自动关闭和处理连接。它是作为try...finally块实现的,因此即使在using块中抛出异常,连接也将被关闭。
除此之外,答案就是“性能分析,性能分析,性能分析”,直到找到您的泄漏/瓶颈/任何问题。
以下是需要注意的几个问题:
首先,您是否在使用会话?它们是进程内还是SQL会话?如果是进程内的,超时设置为多少?如果超时时间非常长,这可能会解释为什么您会使用如此多的内存(用户会话将被存储很长时间)。
其次,对象的处理。.NET垃圾回收器会为您清除引用,但是当您创建实现IDisposable
接口的对象时,应始终使用using关键字。
using(Foo foo = new Foo())
{
...
}
Foo foo;
try
{
foo = new Foo();
...
}
finally
{
foo.Dispose();
}
这样做可以确保您高效地处理对象。
如果您在代码中仍然找不到明显的问题,可以通过对调用最多的方法进行分析来优化性能。您可以在这里找到关于好的分析工具的信息。这一定会帮助您找到问题的根源。
尽管在.NET中内存泄漏依然有可能发生。虽然对于大多数对象你不需要手动释放(如Graphics对象等仍需手动释放),但是如果你保存了这些对象的引用,它们将不会被垃圾回收,因为它们仍被引用着。
如果GC发现一个对象在应用程序的任何位置被引用,则不会将其清除。
请参阅Code Project有关.NET内存泄漏及如何定位和修复它们的文章。
如果你的ASP.NET应用程序使用事件(哪个不使用呢) 那么你必须确保取消订阅事件。
经验法则:
如果你使用+=
来订阅事件,那么你需要在Dispose()
方法中使用-=
来取消订阅。
这个主题有很多 资源, [在这里][5], 我曾经在我工作的应用程序中遇到过一些痛苦,因为普通程序员没有意识到事件可能会导致内存泄漏。
[5]: https://www.google.com/?gws_rd=ssl#hl=zh-CN&source=hp&q=.NET 事件 内存泄漏&aq=f&aqi=g1&aql=&oq=&gs_rfai=CnCywlWZZTMSQDYL-jQOA1ozyCAAAAKoEBU_Q8wsE&fp=ea2cd8eab02d18af
当内存超过500 MB时,请使用内存分析器(无论是在内存转储还是在实时环境中)来获取占用所有空间的对象的线索。
在开发环境中进行分析时,您可能会发现在浏览站点时只有一个特定的对象类型增长,而其他对象则保持全局不变。
虽然不必显式释放内存,垃圾回收器会为您完成这项工作,但代码中可能会无意中保留对对象的引用以防止其被收集 - 这实际上是一种内存泄漏。
在ASP.NET中,只要应用程序和会话缓存引用了对象,它们就会保持“活动状态”。在不知道您的应用程序的情况下,我建议您获取一个内存分析器,并更仔细地查看内存中到底有什么,以及应用程序或会话缓存是否保留了不应该保留的内容。