[英]C++ objects memory consumption
首先:這個問題不是關於“如何使用刪除操作符”,而是關於“為什么許多小尺寸的類對象消耗大量內存”。 假設我們有這個代碼:
class Foo
{
};
void FooTest()
{
int sizeOfFoo = sizeof(Foo);
for (int i = 0; i < 10000000; i++)
new Foo();
}
空類Foo的大小是1個字節,但是當執行代碼時它消耗大約600Mb的內存。 那個怎么樣?
UPDATE。 我在Visual Studio 2010中對Win10 x64進行了測試。來自OS任務管理器的內存使用情況。
C ++堆管理器有4種不同的“模式”,它可以在對象周圍保留更少或更多的空間。 這些是
附加內存用於無域(0xFDFDFDFD),對齊到16字節邊界(0xBAADF00D),堆管理等。
我建議閱讀這篇文章並查看調試器中的4個場景,打開原始內存視圖。 你會學到很多東西。 對於情況1和3,插入一個暫停,您可以將調試器附加到正在運行的進程,而在情況2和4中,您應首先運行調試器,然后從那里啟動可執行文件。
我曾經在演示緩沖區溢出時演示了C ++堆的工作原理。 這是一個你可以使用的演示程序,不完美,但也許有用:
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <stdio.h>
void SimpleBufferOverrunDemo( int argc, _TCHAR* argv[] ) ;
int _tmain(int argc, _TCHAR* argv[])
{
SimpleBufferOverrunDemo(argc, argv);
getchar();
return 0;
}
void SimpleBufferOverrunDemo( int argc, _TCHAR* argv[] )
{
if (argc != 2)
{
std::cout << "You have to provide an argument!\n";
return;
}
// Allocate 5 bytes
byte* overrunBuffer = new byte[5];
// Demo 1: How does the memory look after delete? Uncomment the following to demonstrate
//delete [] overrunBuffer; //0xfeeefeee in debug mode.
//DebugBreak();
// Demo 2: Comment Demo 1 again.
// Provide a 5 byte sequence as argument
// Attach with WinDbg and examine the overrunBuffer.
// 2.1. How many heaps do we have?
// !heap -s
// 2.2. How to find the heap block and how large is it?
// !heap -x [searchAddress]
// !heap -i [blockAddress] -> Wow 72 bytes block size for 5 allocated bytes!
// 2.3. Show that _HEAP_ENTRY does not work any more.
// Demo 3: Write behind the 5 allocated bytes.
// Provide more than 5 bytes as argument, depending on how what you want to destroy
// 3.1 Write into the no mans land.
// 3.2 Write into the guard bytes.
// 3.3 Write into the meta data section of the following heap block! -> When does it crash?
std::wstring arg = argv[1];
for (size_t i = 0; i < arg.size(); i++)
{
overrunBuffer[i] = (byte)arg[i];
}
// Crash happens not where it was caused(!) This is important!
std::cout << "Now we do a plenty of other work ...";
::Sleep(5000);
delete[] overrunBuffer;
// Demo 4: Demonstrate page heap / application verifier!
}
類Foo
大小可能是1個字節,但由於您分別分配了許多Foo
,它們可以(並且可能)在某些字節對齊的地址上分配,並且由於碎片消耗的內存比預期的多。
此外,還有內部存儲器管理系統使用的內存。
您必須知道,從OS的角度來看,進程所消耗的內存不僅與代碼中分配的對象有關。
這是一個嚴重依賴於實現的東西,但總的來說,出於性能原因,內存分配很少一對一地傳遞給操作系統,而是通過免費存儲的管理匯集 。 一般原則如下:
因此,從任務管理器中查看的600MB,可能只有一小部分被有效地分配給您的對象,而更大的部分實際上仍然是免費的,並且可以在免費商店中使用。
這就是說,消耗的內存將大於x個對象的大小:對於每個分配的對象,內存管理功能必須管理一些附加信息(如分配對象的大小)。 實際上,空閑內存池還需要指針(通常是鏈表)來跟蹤空閑塊(如果它們不是連續的)。
關於Windows的非常有趣的帖子。
為了比較,在Ubuntu 15.10(64)上:
int t407(void)
{
std::cout << "\nsizeof(Foo): " << sizeof(Foo) << std::endl;
std::cout << "\nsizeof(Foo*): " << sizeof(Foo*) << std::endl;
std::vector<Foo> fooVec;
fooVec.reserve(10000000);
for (size_t i=0; i<10000000; ++i)
{
Foo t;
fooVec.push_back(t);
}
std::cout << "\nfooVec.size(): " << fooVec.size()
<< " elements" << std::endl
<< "fooVec.size() * sizeof(Foo): "
<< fooVec.size() * sizeof(Foo) << " bytes" << std::endl
<< "sizeof(fooVec): " << sizeof(fooVec)
<< " bytes (on stack)" << std::endl;
return(0);
}
隨着輸出:
sizeof(Foo): 1
sizeof(Foo*): 8
fooVec.size(): 10000000 elements
fooVec.size() * sizeof(Foo): 10000000 bytes
sizeof(fooVec): 24 bytes (on stack)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.