我应该在应用程序终止时处理泄漏,还是让系统处理它们更有效?我认为系统无论如何都会回收所有内存,所以释放它的额外努力不会是开销吗?
4 回答
引用paxdiablo:
我所知道的所有操作系统都会回收已分配的常规内存。这是因为分配通常来自进程私有地址空间,该地址空间将在退出时回收。
据我所知,这也适用于 iOS。
然而,Apple 可能会拒绝您的申请。单独的内存泄漏通常不是拒绝应用程序的理由,但是它们可能是导致桶溢出的下降。适当的内存管理是一种很好的做法,应该始终坚持下去。
当您的应用程序终止时,没有理由尝试释放所有内存。
这样做是在浪费 CPU 周期。
当然,您可能需要一个“关闭”阶段来保持某些状态,但您的代码还必须假设“关闭”代码路径可能不会运行。
当应用程序终止时,系统将回收应用程序分配的所有资源,无论它是如何终止的。
事实上,UIKit (iOS) 和 AppKit (OS X) 在应用程序终止期间都采用了很多快捷方式,导致在应用程序终止时仍然分配大量内存。这样做完全是出于响应性的原因;当用户请求退出应用程序时,它应该很快退出。
(而且我真的想不出一个不会在进程终止时自动回收资源的现代多任务、任务隔离操作系统。)
所有应用程序(进程)都在它们自己的私有内存空间中运行。操作系统对此进行管理,并且始终确切地知道已为进程分配了哪些“物理”内存。当进程退出时,它能够完全恢复所有使用的内存。
因此,如果您的应用程序要退出,您不需要进行任何内存管理或清理(文件访问或网络连接等也是如此,尽管在这些情况下清理可能符合您的最佳利益)。
但是,您的应用程序不应该“泄漏”内存。
泄漏是指您分配了一块内存,然后在正在运行的程序中丢失了对它的所有引用。
单例不是泄漏,Instruments 不会将其标记为泄漏。
所以,如果你有这样的事情:
static NSString *aStaticString = nil;
+ (void)aFunction {
aStaticString = [[NSString alloc] initWithString:@"aFunction"];
}
这不是泄漏。因此,假设您还有另一个功能:
+ (void)anotherFunction {
aStaticString = [[NSString alloc] initWithString:@"anotherFunction"];
}
现在,假设您使用的是 ARC,调用这些函数不会导致泄漏。编译器/运行时知道管理 NSString 分配,并且随着 aStaticString 变量的变化,释放旧内存。
因此,鉴于此,您如何获得泄漏?一般来说,这将是由于循环引用。例如:
+ (void)aBadFunction {
NSMutableDictionary *aDict = [[NSMutableDictionary alloc] init];
[aDict addObject:aDict forKey:@"aCircularReference"];
}
在这里,aDict 被创建(分配),然后对它自身的引用被添加到它。当函数返回时,由于您的程序不再引用 aDict 变量(如果再次调用该函数,将创建一个新的完全不同的 aDict 变量),内存就会泄漏。现在通常 ARC 会确保 aDict 在函数退出时被释放,但在这种情况下它不能,因为在对象本身中有对它的引用。
一般像这样的循环引用比较复杂,但是原理是一样的。
但是,您应该始终释放内存,请参阅以下评论:
两者之间有区别:
int main() {
int* i = malloc(5);
... // do stuff here
return 0; // i "leaked" here, not that serious, a lot of programmers will skip freeing `i` before return
}
和:
int main() {
int* i;
for(int j = 0; j < 5000; j++)
i = malloc(5); // you leaked 5 bytes 5000 times, very serious, DO NOT DO THIS
// the 24KB here will most likely be reclaimed later, HOWEVER
// what if j changes? what if you have more loops? you may run of memory, especially on an embedded device!
... // do stuff here
return 0;
}
只需遵循一条规则:在处理适当的 C 内存管理(C 编码约定)时,永远不要担心开销。
这样做:
int main() {
int* i;
for(int j = 0; j < 5000; j++) {
i = malloc(5);
... // do stuff with i here
free(i); // you are not leaking anything =D
}
... // do stuff here
return 0;
}