100分问一个简单问题:关于 new 的开销

sameboat 2006-07-20 01:53:05
平台 Linux , 编译器 GCC;
执行下面这段测试内存开销的程序,
==================================================================================
#include <unistd.h>
#include <iostream>
using namespace std;
class A
{
public:
A( char cFlag, long lVal1, long lVal2 )
{
m_cFlag = cFlag;
m_lVal1 = lVal1;
m_lVal2 = lVal2;
}

virtual void Display() { }

private:
char m_cFlag;
long m_lVal1;
long m_lVal2;
};

int main()
{
// new 1M node
long N = 1 << 20;
for( long i = 0; i < N; ++i )
{
A *p = new A( 'a', i, i );
}

cout << "sizeof(A) = " << sizeof(A) << endl;
cout << "Finished!" << endl;
sleep(10);

// release memory
exit(0);

return 0;
}
==================================================================================
[结果]
(1) sizeof(A) = 16
(2) 监视内存,发现是 24M

[解释]
(1) 由于类中有虚函数,系统会在对象内加入一个 v_pointer,故 sizeof(A) = round_up( 9 + 4 ) = 16,
其中 round_up 为调整为 4 的整数倍;
(2) 做 new 操作时系统要记录指针,故需要额外开销 4 个字节。

[我的问题]
另外的 4 个字节的系统开销作了什么?请高手赐教,谢谢。
...全文
659 24 打赏 收藏 转发到动态 举报
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
sameboat 2006-08-05
  • 打赏
  • 举报
回复
谢谢各位!

“做 new 操作时系统要记录指针,故需要额外开销 4 个字节”纯属我的猜测,可能是错的。
美丽海洋 2006-07-20
  • 打赏
  • 举报
回复
我认为,当用new分配了内存后,内存的信息记录是归操作系统管理,程序本身不会对这些进行管理。

“做 new 操作时系统要记录指针,故需要额外开销 4 个字节”是4个字节吗?
lyskyly 2006-07-20
  • 打赏
  • 举报
回复
我的编译器也不存放析构函数的地址,只是在某本书上看到过有的编译器好像是Sun早期的编译器那么干:)
mzg3 2006-07-20
  • 打赏
  • 举报
回复
从情理上来将,记录分配的块大小是必须要做的事情,否则当对象大小不一样是就乱套了.
而且这里你并没有重载operator new 和operator delete

另外一点,分配的块大小很可能是8的整数倍,原因是现在大部分系统中都有64位的数据类型.
pigsanddogs 2006-07-20
  • 打赏
  • 举报
回复
我觉得不会存放析构函数的地址, delete的时候会判断类型,来决定在这个内存上调用哪个类的西够,而这个判断是编译时决定了。
lddLinan 2006-07-20
  • 打赏
  • 举报
回复
刚翻了一下linux内核级的内存分配,初步认定cache和返回的内存是分开的:
void kfree (const void *objp)
{
kmem_cache_t *c;
unsigned long flags;

if (!objp)
return;
local_irq_save(flags);
kfree_debugcheck(objp);
c = GET_PAGE_CACHE(virt_to_page(objp));///virt_to_page(objp)根据地址找 //管 理该地址的信息。
__cache_free(c, (void*)objp);
local_irq_restore(flags);
}

期待高手的解答
lddLinan 2006-07-20
  • 打赏
  • 举报
回复
关键问题是这个长度信息和用户申请的内存一起传给用户是不安全的。更确切的说,如果free依赖于这个从用户空间返回的信息是不安全的。而且这样子意味着底层的实现和上层的应用耦合在一起。
lyskyly 2006-07-20
  • 打赏
  • 举报
回复
很多c++编译器的new调用的就是malloc
hchf_1 2006-07-20
  • 打赏
  • 举报
回复
我对c++不太懂,不过我认为那4各字节是用来记录分配内存的长度的。

a↓ p↓
┬┬┬┬┬┬┬┬┬┬┬┬
┴┴┴┴┴┴┴┴┴┴┴┴
在c里面,Malloc是都是申请(n+4/2)的长度,在a处记录长度,然后返回给使用者p,具体4还是2记不住了。这样当你free是,系统先把指针p减去(4/2)得到a然后在根据这段长度释放掉这段内存。
lyskyly 2006-07-20
  • 打赏
  • 举报
回复
(2) 当然要记录指针(不是 cookie),否则 exit(0) 无法释放内存;
没有用过Linux不太明白,只知道在windows中exit(0)最终调用ExitProcess()把整个进程空间清除调,这样连带把整个堆空间清除掉,不太明白哪里用到了记录指针,看来是自己太孤陋寡闻了
能不能详细讲解一下记录指针的事 :)
sameboat 2006-07-20
  • 打赏
  • 举报
回复
自己 up 一下,呵呵!
sameboat 2006-07-20
  • 打赏
  • 举报
回复
(1) 在 Linux 下看的很清楚,如果 1M 不行可以改成 10M 或更大试一试。
(2) 当然要记录指针(不是 cookie),否则 exit(0) 无法释放内存;
lyskyly 2006-07-20
  • 打赏
  • 举报
回复
我做了一下,极不精确,只有任务管理栏来看用了多少内存

(2) 做 new 操作时系统要记录指针,故需要额外开销 4 个字节。这句话是什么意思,没看得太懂,这不是只cookie,那又是指的什么
sameboat 2006-07-20
  • 打赏
  • 举报
回复
由于系统中的 Object 规模大(大约 4000万 Node ,每个 Node 大约 50 -- 100 字节),
我的内存管理策略:
(1)仿 STL 的内存管理方式;
(2)通过 Map 来交换整理内存;
(3)和程序特点结合,将类适当分组管理;

如果哪位对内存管理有深入研究的话,欢迎交流。
sameboat 2006-07-20
  • 打赏
  • 举报
回复
lyskyly(浮生三笑):
我现在把 class A 中的虚函数注释掉,得到这样的结果:
(1) sizeof(A) = 12
(2) 监视内存,发现是 16M

又如何解释呢?

得到满意答复后立即结帖。
jun_01 2006-07-20
  • 打赏
  • 举报
回复
睡在床板下_ 2006-07-20
  • 打赏
  • 举报
回复
说的 有道理
lyskyly 2006-07-20
  • 打赏
  • 举报
回复
内存池的设计有两个好处,一个是为了提高速度,一个是为了减小空间浪费,少叫空间税就体现在这里
lyskyly 2006-07-20
  • 打赏
  • 举报
回复
单个Node 也要用到,它不过光是用来存放大小,还可能要存放析构函数的地址
这是摘录了MC上的一段
class airplanerep { ... }; // 表示一个飞机对象
//
class airplane {
public:
...
private:
airplanerep *rep; // 指向实际描述
};

一个airplane对象并不大,它只包含一个指针(正如条款14和m24所说明的,如果airplane类声明了虚函数,会隐式包含第二个指针)。但当调用operator new来分配一个airplane对象时,得到的内存可能要比存储这个指针(或一对指针)所需要的要多。之所以会产生这种看起来很奇怪的行为,在于operator new和operator delete之间需要互相传递信息。

因为缺省版本的operator new是一种通用型的内存分配器,它必须可以分配任意大小的内存块。同样,operator delete也要可以释放任意大小的内存块。operator delete想弄清它要释放的内存有多大,就必须知道当初operator new分配的内存有多大。有一种常用的方法可以让operator new来告诉operator delete当初分配的内存大小是多少,就是在它所返回的内存里预先附带一些额外信息,用来指明被分配的内存块的大小。也就是说,当你写了下面的语句,

airplane *pa = new airplane;

你不会得到一块看起来象这样的内存块:

pa——> airplane对象的内存

而是得到象这样的内存块:

pa——> 内存块大小数据 + airplane对象的内存

对于象airplane这样很小的对象来说,这些额外的数据信息会使得动态分配对象时所需要的的内存的大小翻番(特别是类里没有虚拟函数的时候)。

chenhu_doc 2006-07-20
  • 打赏
  • 举报
回复
既然是一个指针,怎么知道new只new一个Node呢?
加载更多回复(4)

65,210

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧