内存泄漏是,当应用程序中有未使用的内存并且GC可以收集它时,通常会发生在应用程序中的某些地方我们保留不需要的对象的强引用,并且GC将能够找到路径(直接和间接)所以它可以释放这个对象,但是这一切都适用于堆内存分配中的引用类型。
但是堆栈呢?据我所知,GC不负责清理堆栈,当函数返回时它会自动清理。
所以我的问题是,堆栈中是否有可能发生内存泄漏?如果是,那么在什么情况下以及避免这种泄漏的最佳做法是什么。
答案 0 :(得分:2)
如果你正在编写递归函数来保持对子递归调用中不需要的大数据的堆栈本地引用,那么这是一种空间泄漏,但在实践中这是一个很难的问题。
更一般地说,如果你有像
这样的东西Main() {
var s = ReadInAGiganticString(); // say 10 Megs long
Server(s.Substring(0,5)); // but I only care about first 5 chars
}
Server(s) {
while(true) { ... } // but 10M is on stack forever
}
然后这是一种堆栈空间泄漏,但同样,在实践中不太可能。修复很简单:
Main() {
var s = ReadInAGiganticString();
var t = s.Substring(0,5);
s = null; // the fix
Server(t);
}
Server(s) {
while(true) { ... }
}
一般情况下,如果在一个持续“很长时间”的调用之前堆栈上有一个巨大的变量并且该变量不再使用,你可以将其清空以确保它之前可以进行GC进入'长期'电话。
(优化器可能会根据可达性分析为您执行此操作。)
请注意,上面的答案是针对以堆栈为根的堆分配对象的引用。另一方面,如果你有堆栈分配的内存(例如一个巨大的结构或stackalloc东西),那么修复并不那么容易,但这在实践中甚至更少(谁创造了巨型结构?)。
答案 1 :(得分:1)
如果无法正确释放非托管资源,则可能发生内存泄漏。所以我担心如果你将一个托管对象发送给一个方法,并且这个对象包装了一个非托管资源,并且它无法正确释放非托管资源,那么就会出现内存泄漏。堆栈将释放托管引用,但非托管资源将保持活动状态。
答案 2 :(得分:0)
在块结束后销毁在堆栈中分配的变量。因此,内存泄漏不可能产生。床练习是创建一个仅在该块之外的给定块中使用的变量
答案 3 :(得分:0)
正如你所说GC没有清理堆栈。但是,在展开期间回收堆栈使用的空间。因此,当弹出堆栈时,堆栈指针“向后”移动,因此可以重用该空间。
用完堆栈空间会引发StackOverflowException。这通常是由于无限递归的编程错误。
答案 4 :(得分:0)
如果你对.NET的内部结构不太了解,我会说如果你有堆栈泄漏,你会有一个崩溃程序,因为函数/方法返回地址也存储在堆栈中(最有可能)。
如果你的堆栈不对齐,你会遇到比内存泄漏更大的问题。
通常方法参数和返回值存储在堆栈中(如果没有优化,则通过cpu寄存器发送)。
答案 5 :(得分:0)
不,只要您使用安全代码,堆栈变量就不会发生内存泄漏。