2012-12-20 26 views
-1

我想計算一個對象使用的內存的字節數量。鑑於如何計算類對象在C++中使用的內存量?

struct A 
{ 
    float data[16]; 
    int index; 
}; 

struct B 
{ 
    A a; 
}; 

下面是正確的方法來做到這一點?

template <class Type> 
size_t footprint(); 

template <> 
size_t footprint<A>() 
{ 
    return sizeof(float) * 16 + sizeof(int); 
} 

template <> 
size_t footprint<B>() 
{ 
    return footprint<A>(); 
} 

我不知道足跡(),因爲我聽說,編譯器可能會添加額外的信息只是儲存成員變量,我不知道足跡(),因爲它是指一類對象。這是否也需要一些記憶?

編輯:好的,說形勢發生了變化,使得我們沒有使用一個靜態數組,但實際指針:

#include <iostream> 

using namespace std; 

struct A 
{ 
    A(int size_) 
    { 
     data = new float[size_]; 
     size = size_; 
    } 

    ~A() 
    { 
     delete [] data; 
    } 

    float* data; 
    int size; 
}; 

struct B 
{ 
    B() : a(16) {} 

    A a; 
}; 

size_t footprint(const A& a) 
{ 
    return sizeof(float) * a.size + sizeof(int); 
} 

size_t footprint(const B& b) 
{ 
    return footprint(b.a); 
} 

int main() 
{ 
    A a(16); 
    B b; 

    cout << "sizeof(A) = " << sizeof(A) << endl; 
    cout << "sizeof(B) = " << sizeof(B) << endl; 
    cout << "footprint(a) = " << footprint(a) << endl; 
    cout << "footprint(b) = " << footprint(b) << endl; 
} 

在這裏,您將需要實際上有一個特殊功能的sizeof(這裏稱爲足跡)對?

+3

爲什麼'sizeof(A)'或'sizeof(B)'? – chris

+0

sizeof將把任何變量類型作爲參數。只要sizeof(A)/ sizeof(B) – Aaron

+0

我相當肯定,只會返回sizeof(int)+ sizeof(float *) –

回答

3

正確的方式做,這是sizeof(A)sizeof(B)。添加成員的大小是不正確的,因爲編譯器可以自由地放入額外的空間,稱爲填充,以正確對齊事物。這個填充爲sizeof

您也表達了你的陣列衰變成一個指針的關注。 sizeof並非如此。 sizeof(an array)將給出數組佔用的字節總數,前提是它仍然是數組形式並且沒有衰減爲指針。事情的事實是,標準中明確禁止這種衰變中sizeof發生,所以你是安全的有:

C++ 11 N3485§5.3.3/4重點煤礦]:

的左值到右值(4.1),陣列到指針(4.2),和功能到指針(4.3)標準轉換不 施加到的sizeof的操作數。

sizeof(A) == 16 * sizeof(float) + sizeof(int) + sizeof(additional padding)

+0

謝謝。假設A現在有一個float *指向構造函數中堆上分配的數組。計算A實例大小的正確方法是sizeof(float)* a.size + sizeof(A)? –

+0

@Phineas,實例的大小仍然是sizeof(A)'。免費商店只有額外的內存。如果你也對此感興趣,你的版本是正確的,假設'a.size'是數組的長度。 – chris

+0

我覺得我的一些不好的措辭。實際上,我想知道的不僅僅是一個對象佔用多少內存,而是它的總和以及它在堆上分配的內容。 –

1

由於示例具有然後使用的sizeof(A)會給你的正確尺寸的靜態分配的陣列。然而,有時由於位填充的原因,其大小會與您預期的不同。

但是如果你動態分配的數組,你需要做這樣的事情:

lengthOfArray*sizeof(arrayElement); 
1

不僅是return sizeof(float) * 16 + sizeof(int);對於更復雜的對象很麻煩,但也可能是不正確的。 C語言規範允許編譯器「填充」結構。說,例如,我們有

class X { 
public: 
    int i; 
    char c; 
    double d; 
}; 

你的上漿結構會說,它佔用了4 + 1 + 8(或一些這樣的),但實際sizeof(X)會給4 + 4 + 8.極端混合方法的小型和大型數據類型,我們可以很容易地看到50-75%的錯誤。 [成員的確切佈局取決於編譯器,處理器體系結構,編譯器開關等]。

1

在動態分配的情況下,它變得更加複雜。實際解釋你的類的用法的最好的解決方案是實現你自己的operator :: new(size_t size)[並且可能調用malloc來分配大小的字節數],然後計算大小 - 但是,這不會解釋分配器本身的開銷,每個調用都會有12個字節左右的空間。分配器也可以將正在分配的實際大小加起來。

+0

這當然只有在你不能輕描淡寫地說明的情況下才有意義。這將有助於理解你實際正在嘗試做什麼 - 因爲現在的問題有點假設,我懷疑你有一個真實的用例,在那裏你真的關心你的對象的內存使用。 –

+0

謝謝。我相信你和其他人提供了我想要做的事情所需要的信息;我正在從磁盤實施核心流式傳輸。爲了知道何時從核心內存中彈出最近最少使用的緩存對象,我必須能夠計算每個緩存對象使用的內存量。 –

+1

當然,如果你正在處理一個緩存,你會有相當不錯的檢查你有多少緩存對象。這些佔用的字節的確切數量將比爲您使用的緩存元素的數量設置合理的限制更重要。 [我想,也使用相當體面的大小緩存元素會有所幫助] –

1

作爲對以前答案的補充,編譯器爲您提供了控制填充行爲的方式(因此,如果內存佔用對您程序中的某處特別重要,則可能會影響sizeof(A) ,如果你想通過網絡發送你的結構,或其他)。

您可以查看#pragma pack聲明來實現這樣的目標。

1

我還沒有看到提到的一個問題。當你做

float * data = new float[size_]; 
delete [] data; 

然後sizeof(float)* size_是分配的內存量。但是,當刪除發生,它需要知道size_是什麼在新的(),所以它知道多少次調用的std :: string析構函數,當你做

std::string * data = new std::string[size_]; 
delete [] data; 

。 C++有時可以將一些字節添加到分配中,以跟蹤您分配了多少個陣列成員。

1

鑑於上述評論,我認爲需要另一個答案。

我可以考慮一些相當不錯的解決方案來限制緩存使用的內存量: 1.開始時,分配X KB的內存(無論您的緩存是「允許」使用的)。根據需要將它們分成幾個部分。完整時,取最老的並重新使用它(如果它們不是全部相同的尺寸,則可能需要多於一個)。

你需要某種方式來標記「最近使用」或「老化」的東西,這樣你才能扔掉最老的東西。可能通過使用Splay Tree,當您獲取某些東西時自動重新排序,以便最近的對象位於樹頂部。

  1. 上述類似,但使用的數據的固定大小的塊固定號碼 - 比方說16或32(或256或500或任何使最適合您的典型緩存條目是什麼尺寸)。在一天的開始時,將所有緩存條目放在「空閒」容器(例如列表)中。當你需要一個新的緩存條目時,獲取空閒列表的頂部。如果列表爲空,請查找最舊的一個,並將其重用用於緩存。

  2. 如果你存儲的對象是不同類型和大小的,你可能會發現自己創建堆,並且爲你的類使用operator :: new()是一個更好的選擇(顯然也是相應的operator :: delete ))。有多種解決方案可以讓你自己堆(自由存儲,因爲它有時被稱爲C++)。我會傾向於將某些東西放到一個有限的尺寸範圍內,這樣你就不會受到如此糟糕的碎片化。