2013-05-16 29 views
0

我一直在大型項目中使用tcmalloc幾個月,到目前爲止,我必須說我對此非常高興,最重要的是它的HeapProfiling功能,它允許跟蹤內存泄漏並刪除他們。tcmalloc意外的行爲

儘管我們在應用程序中遇到了隨機崩潰,但在過去的幾周內,我們無法找到隨機崩潰的來源。在一個非常特殊的情況下,當應用程序崩潰時,我們發現自己的某個應用程序線程完全損壞了堆棧。好幾次,我發現線程卡在tcmalloc :: PageHeap :: AllocLarge(),但由於我沒有tcmalloc鏈接調試符號,我不明白是什麼問題。

經過近一個星期的調查,今天我嘗試了最簡單的事情:從鏈接中刪除tcmalloc以避免使用它,只是爲了看看發生了什麼。嗯...我終於發現了問題所在,並有問題的代碼看起來非常像這樣:

void AllocatingFunction() 
    { 
     Object object_on_stack; 
     ProcessObject(&object_on_stack); 

    } 

    void ProcessObject(Object* object) 
    { 
     ... 
     // Do Whatever 
     ... 
     delete object; 
    } 

使用libc中的應用程序仍然崩潰,但我終於看到了,我在呼喚刪除對象這是上分配在堆棧上。

我仍然無法弄清楚爲什麼tcmalloc會讓應用程序繼續運行,而不管這種風險如何(如果不是完全錯誤的話)對象釋放,以及當AllocatingFunction結束時object_on_stack超出範圍時的雙重釋放。事實是,違規代碼可以被重複調用,而不會有任何暗示憎惡的暗示。

我知道,當未正確使用時,內存釋放是那些「未定義的行爲」之一,但我驚訝的是「標準」libc和tcmalloc之間的這種不同的行爲。

有沒有人有某種洞察力的解釋,爲什麼tcmalloc保持應用程序運行?

感謝提前:)

擁有美好的一天

+0

他們選擇了他們的「未定義行爲」來*不崩潰*。我會依靠這個嗎? *一定不行*。 – WhozCraig

回答

2

非常危險的(如果不是完全錯誤的)對象釋放

好,我不同意在這裏,它是完全錯了,而且既然你調用了UB,任何事情都可能發生。

這很大程度上取決於tcmalloc代碼在釋放時如何執行,以及它如何在該位置使用堆棧周圍的數據(可能是垃圾)。

我也看到tcmalloc在這樣的場合也崩潰,以及glibc進入無限循環。你看到的只是巧合。

+0

那麼......我不敢稱它爲「完全錯誤的」,因爲你永遠不知道程序員試圖完成什麼樣的詭計。也許世界上有些人聲稱他們要做這樣的事情。在我的情況下,它絕對是錯的,修復有問題的代碼修復了一切。仍然花了我和其他人7天的毫無意義的調查-.-' – BaroneAshura

+0

@BaroneAshura:即使在這種情況下,我個人會認爲它是完全錯誤的。順便說一句。通過valgrind運行你的代碼會在現場發現這一點。 – PlasmaHH

+0

這是幾個月前我們尋找內存泄漏時的一個選擇......不幸的是,我們無法成功地在valgrind環境中運行我們的應用程序:(無論如何,我同意你稱它爲「完全錯誤的」..我只是不想讓任何人跳過我,說這不是「完全錯誤的」:P – BaroneAshura

0

首先,你的情況沒有雙重free。當object_on_stack超出範圍時,不會調用free調用,只是堆棧指針減少(或者增加,隨着堆棧增長減少...)。

其次,在刪除期間,TcMalloc應該能夠識別堆棧中的地址不屬於程序堆。這裏是free(ptr)實現部分:

const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; 
Span* span = NULL; 
size_t cl = Static::pageheap()->GetSizeClassIfCached(p); 

if (cl == 0) { 
    span = Static::pageheap()->GetDescriptor(p); 
    if (!span) { 
     // span can be NULL because the pointer passed in is invalid 
     // (not something returned by malloc or friends), or because the 
     // pointer was allocated with some other allocator besides 
     // tcmalloc. The latter can happen if tcmalloc is linked in via 
     // a dynamic library, but is not listed last on the link line. 
     // In that case, libraries after it on the link line will 
     // allocate with libc malloc, but free with tcmalloc's free. 
     (*invalid_free_fn)(ptr); // Decide how to handle the bad free request 
     return; 
    } 

電話invalid_free_fn崩潰。