2010-01-19 78 views
5

我一直在毆打我的頭靠在牆上試圖弄清楚我是如何在垃圾收集的可可應用程序中發生內存泄漏的。 (活動監視器中的內存使用量將會增長並增長,使用GC Monitor儀器運行應用程序也會顯示不斷增長的圖形。)Cocoa垃圾收集泄漏內存

我最終將其縮小爲我的代碼中的單個模式。數據被加載到一個NSData中,然後被一個C庫解析(數據的字節和長度傳遞給它)。 C庫具有回調函數,可以觸發並返回子字符串的起始指針和長度(以避免內部複製)。然而,爲了我的目的,我需要將它們轉換成NSStrings並保持一段時間。我通過使用NSString的initWithBytes:length:encoding:方法來做到這一點。我認爲會複製字節,並且NSString會適當地管理它,但有些事情出錯了,因爲這個漏洞像瘋了似的。

這段代碼將「泄漏」或以某種方式欺騙垃圾收集器:

- (void)meh 
{ 
    NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"holmes" ofType:@"txt"]]; 
    const int substrLength = 80; 

    for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) { 
     NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding]; 
     [cocoaString length]; 
    } 
} 

我可以用活動監視器把這個計時器,只是看內存的使用上去了,以及與GC監測儀器。 (holmes.txt是594KB)

這不是世界上最好的代碼,但它顯示了問題。 (我正在運行10.6,該項目的目標是10.5 - 如果這很重要)。我閱讀了垃圾收集文檔,發現了一些可能的陷阱,但我不認爲我在這裏違反規則做任何事情。不過,不要傷害。謝謝!

Project zip

這裏只是種植和生長所述對象圖的圖:

alt text

+0

當我運行它時,我無法看到任何泄漏,它在運行幾分鐘後徘徊在7.5MB和9MB的內存使用量之間。 – 2010-01-19 17:37:47

+0

這很奇怪。我在10.6.2。我建立在調試和發佈。在所有情況下,我都看到了不斷增長的記憶。 wtf ... – Sean 2010-01-19 17:45:04

+0

我現在已經有其他人在10.5上運行它了,並且他報告說它對他來說似乎也沒有增長。我需要讓10.6+以上的其他人試用。 – Sean 2010-01-19 18:02:09

回答

13

這是一個不幸的邊緣的情況下。請提交錯誤(http://bugreport.apple.com/)並附上您的極好的最小示例。

問題是兩倍;

  • 主事件循環未運行,因此收集器未通過MEL活動觸發。這會使收集器僅執行基於閾值的正常集合。

  • 數據將從文件中讀取的數據存儲到從malloc區域分配的malloc'd緩衝區中。因此,GC分配 - NSData對象本身 - 實際上很小,但指向非常大的一些內容(malloc分配)。最終的結果是收集器的閾值沒有被擊中,也不會收集。顯然,改善這種行爲是需要的,但這是一個難題。

這是一個很容易在微基準或單獨重現的bug。實際上,通常情況下,這個問題不會發生。但是,在某些情況下,它確實存在問題。

將您的代碼更改爲此,收集器將收集數據對象。請注意,你不應該經常使用collectExhaustively - 它確實吃掉了CPU。

- (void)meh 
{ 
    NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"holmes" ofType:@"txt"]]; 
    const int substrLength = 80; 

    for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) { 
     NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding]; 
     [cocoaString length]; 
    } 
    [data self]; 
    [[NSGarbageCollector defaultCollector] collectExhaustively]; 
} 

[data self]保持最後一個引用後的數據對象活着。

+0

非常感謝解釋和可能的解決方法。歸檔的雷達:// 556417。 – Sean 2010-01-19 18:39:13

+1

謝謝 - 雷達:// 7556417,順便說一句。 – bbum 2010-01-19 18:58:10