http://i46.tinypic.com/2nquzag.png
我有一个应用程序,可以在很长的计算过程中创建并释放数百万个NSNumber。我正在使用@autoreleasepools来创建和处理这些对象的各种循环。正如您可以通过仪器屏幕上看到的那样,我只有10656个生活CFNumbers,但此时应用程序因为无法为其他对象分配内存而崩溃。 “暂时”状态下有超过5700万CFNumber。这是否意味着他们被释放了?为什么malloc还没有使用这些内存位置?
我已经包含了我正在使用的代码的模型。运行testProbabilities方法,它将计算每10000次迭代。在发生alloc错误之前,我使用此代码进行了790000次迭代。仪器看起来很好。
@implementation MMTest
NSMutableSet *predictedSet;
-(NSMutableArray *)calculateCDF {
//// NORMALIZE PROBABILITIES ////
float probabilitySum = 0;
float runningCDFTotal = 0;
float normaledProbability = 0;
@autoreleasepool {
NSMutableArray *CDF = [[NSMutableArray alloc] init];
probabilitySum = 4.5; ////This is just a placholder for additive iteration of an array which creates this sum.
//// CREATE CDF ////
int x;
for (x=0; x<50; x++) {
normaledProbability = .2/probabilitySum;////This is placeholder for calculation on same array as above.
runningCDFTotal += normaledProbability;
[CDF addObject:[NSNumber numberWithFloat:runningCDFTotal]];
}
return CDF;
}
}
-(NSMutableSet *)generateNumbers {
@autoreleasepool {
int x;
double r = 0;
if (!predictedSet) {
predictedSet = [[NSMutableSet alloc] init];
}
else {
[predictedSet removeAllObjects];
}
for (x=0; x<5;) {
r = arc4random_uniform(1000)/1000;////I'm actually using SFMT here instead.
int num = 0;
__weak id CDF = [self calculateCDF];
if (r <= [[CDF objectAtIndex:[CDF count]-1] floatValue]) {
for (NSNumber *cdf in CDF) {
if (r <= [cdf floatValue]) {
num = [CDF indexOfObject:cdf]+1;
[predictedSet addObject:[NSNumber numberWithInt:num]];
x++;
break;
}
}
}
}
return predictedSet;
}
}
-(void)testProbability {
int x = 0;
BOOL nextSetFound = NO;
NSSet *nextSet = [[NSSet alloc] initWithObjects:
[NSNumber numberWithInt:1],
[NSNumber numberWithInt:2],
[NSNumber numberWithInt:3],
[NSNumber numberWithInt:4],
[NSNumber numberWithInt:5],
nil];
while (nextSetFound == NO) {
@autoreleasepool {
__weak id newSet = [self generateNumbers];
x++;
if ([nextSet isSubsetOfSet:newSet]) {
nextSetFound = YES;
NSLog(@"%@", newSet);
}
if (fmod (x,10000) == 0) {
NSLog(@"%i", x);
}
}
}
NSLog(@"%i", x);
}
@end
答案 0 :(得分:1)
我错了还是你没有在任何地方发布CDF
?这样,每次拨打CDF
时,您都会泄露calculateCDF
。
试试这个:
id CDF = [self calculateCDF];
而不是
__weak id CDF = [self calculateCDF];
和
id newSet = [self generateNumbers];
而不是
__weak id newSet = [self generateNumbers];
指定weak
会使对象不归newSet
所有,因此不会由ARC自动释放。
另一方面,Apple explicitly adives against using __weak
for stack variables:
在堆栈上使用__weak变量时要小心。请考虑以下示例:
NSString * __weak string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]]; NSLog(@"string: %@", string);
虽然在初始赋值后使用了字符串,但在赋值时没有对字符串对象的其他强引用;因此,它立即被解除分配。 log语句显示string的值为null。 (如果你使用
NSString * __weak string = [[NSString alloc] initWithString:@"First Name"]
,你将看不到效果。在这种情况下,initWithString:
只返回永远不会被释放的字符串常量。)
据我了解此段落,您要么立即释放,要么发生内存泄漏。
我在iOS 4.3 iPhone上尝试了你的代码(删除了__weak),它在乐器下显示了一个扁平的“物理自由记忆”。
老答案:
autorelease
的棘手问题是自动释放池在不可预测的时间耗尽(通常在应用程序执行运行循环时)。这与使用release
不同,因为它在您完成对象的时刻与释放内存的时刻之间会增加一些延迟。
如果将这个小延迟(如autorelease
系统所具有的惯性)乘以自动释放对象的数量,则可能导致内存使用高峰。如果iOS在其中一个峰值期间检查内存使用情况,并且您的应用无法对随后发生的内存警告做出正确反应,那么您的应用就会被杀死。
另一方面,如果你分配一个发布池,并且它在运行周期的同一步骤中变得很大,这也可能导致过多的内存使用。
你提供的代码细节很少,所以我不能更精确;无论如何,如果可能的话,我会尝试使用明确的release
个对象。这将摆脱上述两个问题。或者您可以尝试处理较小的自动释放池(并且您将解决第二类问题)...
我还建议你为仪器添加一个内存监视器,这样你就可以看到系统释放内存的速度有多快。
PS:现在,你所附的图片让我印象深刻的是分配图是绝对平坦的:没有分配,没有解除分配。我希望内存能够上下移动(根据我的经验,这是一个健康的iOS应用程序的标志)。
答案 1 :(得分:0)
不确定原因,但上面这个确切的代码现在正常工作。在测试各种修改的过程中,我启用并禁用了Xcode中的Guard Malloc和Malloc Stack Logging。突然之间我的修改版本开始运行而没有崩溃。然后我再次尝试了这个原始版本,现在它运行良好而不会崩溃。这没有任何意义,但我很高兴它正在发挥作用。
@sergio:我想提出你的答案,至少提供很多帮助和建议,以进一步优化这些代码,但我没有足够的声誉。谢谢您的帮助。我很感激。