【问题标题】:running out of memory from for loop从 for 循环中耗尽内存
【发布时间】:2013-05-17 15:17:08
【问题描述】:

嘿,所以我想如果我制作一个生成随机密码的小应用程序,然后让应用程序运行所有可能性并尝试查看密码是什么,告诉我它尝试了多少次,那会很酷。有时应用程序工作有时它崩溃取决于密码是什么。我想知道我可以做些什么来阻止它通过使用最多的内存而崩溃。这就是方法。

-(void)hackString 
 {
    NSString *string;
    NSString *string1;
    NSString *string2;
    NSString *string3;
    NSString *string4;
    NSString *string5;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void){


    NSString *hackedString;

    for (string in self.validLetters) {
    //[hackedString appendString:string];
        for (string1 in self.validLetters) {
            //[hackedString appendString:string1];
            for (string2 in self.validLetters) {
                //[hackedString appendString:string1];
                for (string3 in self.validLetters) {
                    //[hackedString appendString:string1];
                    for (string4 in self.validLetters) {
                        for (string5 in self.validLetters) {
                             hackedString = [NSString stringWithFormat:@"%@%@%@%@%@%@",string,string1,string2,string3,string4,string5];
                           // NSLog(@"%@",hackedString);
                            [self testStringWithPassword:hackedString];

                        }

                    }
                }
            }
        }
    }
         });

} 

我应该在那里做些什么来阻止它崩溃?

【问题讨论】:

  • 你正在运行一个 O(n^6) 的循环!
  • 对于我的一生,我无法理解你到底想用那个疯狂的 6 深循环来完成什么。用有效字母构建每个可能的字符串?
  • @John Yup——据我所知,它是一个 6 个字符的密码破解程序。有趣的学习技巧;这个兔子洞很深。
  • 有趣的事实:如果validLetters 包含大写和小写的拉丁字母加上从 0 到 9 的数字,即 A-Z、a-z 和 0-9,您将生成 570 亿种不同的组合。每个字符串长 12 个字节(NSString 在内部使用 UTF-16)+ 一些未知的开销,因此您可能在谈论超过 700 GB 的存储空间。我推荐一些自动释放池。

标签: objective-c memory


【解决方案1】:

如果您的应用程序内存不足,很可能是因为您正在用大量对象填充自动释放池以达到内存耗尽的程度。

天真地,您可以在内部 for() 循环之一周围使用 @autoreleasepool{}

但是,大多数密码输入系统会立即将密码散列成一些不可反向散列的字符串,然后将其与存储的散列进行比较。因此,您的方法不太可能对测试密码有用。您可能需要实现相同的散列。

我曾经在我公司的用户密码数据库上运行密码破解程序(有点像开膛手约翰),自动向密码弱的任何人发送电子邮件。我们最终解雇了一个人,因为他们无法想出一个无法在几分钟内被破解者破解的密码(还有其他原因,性质类似)。

【讨论】:

    【解决方案2】:

    我应该在那里做些什么来阻止它崩溃?

    您确定程序崩溃了吗?如果您的程序真的崩溃了,您应该会收到一个崩溃日志或控制台消息,告诉您它在哪里崩溃,并且您应该能够在调试器中看到它崩溃的地方。

    您的程序可能只是需要很长时间才能运行,因为您要求它生成来自validLetters 的六个字符串的每个组合。根据具体情况,您的程序也有可能被操作系统中的看门狗进程杀死。

    另一方面,您的程序确实可能因为内存不足而崩溃。每次调用 -stringWithFormat: 时都会分配内存,这会创建一个新的自动释放字符串。由于您永远不会让运行循环运行,因此自动释放池永远不会耗尽。如果您的程序需要很长时间才能找到它正在寻找的字符串,那么您很容易用完所有可用内存。因此,首先,尝试将最里面的 for 循环放在 @autorelease 块中,例如:

    @autorelease {
        for (string5 in self.validLetters) {
            hackedString = [NSString     stringWithFormat:@"%@%@%@%@%@%@",string,string1,string2,string3,string4,string5];
                               // NSLog(@"%@",hackedString);
            [self testStringWithPassword:hackedString];
    
        }
    }
    

    【讨论】:

    • OP 说他的程序内存不足,鉴于循环结构,我当然可以理解原因。由于它是一个 dispatch_sync(),它不会阻塞主线程。
    • @bbum 谢谢,我错过了dispatch_async()
    【解决方案3】:

    stringWithFormat: 返回一个自动释放的字符串。这意味着在该方法调用返回(并且自动释放池耗尽)之前它不会被清理,因此您的内存不足。如果您不使用 ARC,则可以显式初始化并释放字符串:

    hackedString = [[NSString alloc] initWithFormat:@"%@%@%@%@%@%@",string,string1,string2,string3,string4,string5]];
    [self testStringWithPassword:hackedString];
    [hackedString release];
    

    如果您使用 ARC,请在其中一个内部循环周围添加 @autoreleasepool,我建议您使用 string4 循环。

    【讨论】:

    • @flexaddicted 我知道,但是直接初始化/释放比池更有效。
    • 毫无疑问。我指的是 如果您使用的是 ARC,... 干杯。
    • @Kevin 1. 正如您的回答所暗示的,如果使用 MRC,您只能使用 release;鉴于他使用的是initWithFormat,您可以在ARC 中使用hackedString = nil 来达到与您在MRC 中的显式release 相同的效果; 2. 您还假设他的testStringWithPassword 方法中也没有任何自动释放对象。您必须仔细检查它(以及它调用的任何方法),确保您也为其中的所有内容进行显式发布。就个人而言,虽然您在效率问题上可能是对的,但我认为自动释放池(无论是否为 ARC)是最安全的。
    • 感谢大家的回答。我不知道 stringWithFormat 为某些东西实际分配了空间,尽管它会在之后释放它,以便明确解释为什么它因内存用完而崩溃。我正在使用 ARC,看起来 @autoreleasepool 解决了它!我不希望这个破解人们的密码,我只是认为这将是一个很酷的项目。至于说使用 6 for in 循环的人。有没有更好的方法来做到这一点?
    猜你喜欢
    • 2019-04-25
    • 1970-01-01
    • 2014-10-04
    • 2014-12-13
    • 2016-09-17
    • 2015-05-27
    • 1970-01-01
    • 2021-08-09
    • 2015-08-23
    相关资源
    最近更新 更多