| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 964 人关注过本帖, 1 人收藏
标题:動態分配内存malloc()的一些測試結果
取消只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
结帖率:100%
收藏(1)
已结贴  问题点数:100 回复次数:10 
動態分配内存malloc()的一些測試結果
C語言的動態分配内存,使用malloc()函數(在stdlib或malloc頭中),它在堆内存中申請一塊指定大小size(字節計數)的空間,可能有人以爲size是多少,就佔用了多少堆空間,下面就是關於這個問題的測試。

malloc()函數是返回分配空間入口地址的,測試的原理如下:連續申請兩塊空間,看兩者的入口地址差是多少,即知實際佔用的内存是否等於所申請的尺寸。代碼如下:
程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

void Pause(const char* message = "\nPress any key to continue...");

int main(void)
{
    // 這裏不作任何別的動作,緊鄰申請兩塊空間,理論上它們是連續的
    char* p1 = (char*)malloc(1000);
    char* p2 = (char*)malloc(1000);

    // 查看兩塊空間的入口地址差,p2 - p1的結果,就是p1所佔用的堆空間
    printf_s("p2 - p1 = %d\n", p2 - p1);

    Pause();
    return EXIT_SUCCESS;
}

void Pause(const char* message)
{
    printf_s(message);
    _getch();
}


運行結果如下:
图片附件: 游客没有浏览图片的权限,请 登录注册


這個結果,我們可得到如下結論:
1.malloc()申請實際所佔用的堆内存,可能是不等於申請尺寸的,如圖示,申請1000字節的内存,實際上分配了1024字節。這與幫助文檔中所描述的一致:
The malloc function allocates a memory block of at least size bytes. The block may be larger than size bytes because of the space that's required for alignment and maintenance information.

2.修改測試數據,把第一個malloc()的參數改爲10,結果爲9992(比申請的10大得多),并非負數,這表明我們根本不能假定兩塊空間的先後次序。事實上,到50才出現負值結果,這中間不管size是多少,都是9992。所以,我們不要自作聰明,以爲後申請的在後,或在前,甚至,我們連實際上佔用了多少都不能假定,因爲這極可能與平臺有關。平常見不少人,用malloc()申請一個int之類,以爲節省了多少空間,殊不知浪費了多少,這還不說你用遠指針間接訪問的低效了(那些用指針存取基本數據類型的尤其如此)。指針和動態分配内存不是那般用的。



[ 本帖最后由 TonyDeng 于 2015-2-14 10:44 编辑 ]
2015-02-14 02:01
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
我沒看出你不同意我什麽。

授人以渔,不授人以鱼。
2015-02-14 09:41
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
在物理内存堆上用鏈式分配空間,是DOS時代的方式,NT之後,堆在邏輯内存上,這叫“虛擬内存”。我前面說了,這些東西跟平臺有關,不要去做假定,以爲是如何的,實際上并不是,所以編程不要總去鑽系統實現的細節,比如假定int是多少字節、内存必定是連續的之類。理論上,兩次連續申請的空間,如果中間沒有任何別的操作,那麽被斷開的可能性并不大,這從上述代碼反復執行的結果不變可知——如果是物理堆,多任務系統隨時會佔用内存,那麽不同的執行結果應是不同的居多,但事實是每次都是同樣的結果。

在棧上連續分配兩個數組,我們經常見到前一個數組溢出蔓延到下一個數組的現象,即這兩個數組是連續的,但如果在兩個數組定義的中間插入一個別的變量,則會被這個變量分隔。在堆上,理論上也如此,這就是我前面測試代碼强調緊鄰申請的原因。


[ 本帖最后由 TonyDeng 于 2015-2-14 10:40 编辑 ]

授人以渔,不授人以鱼。
2015-02-14 10:01
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
看點資料:
图片附件: 游客没有浏览图片的权限,请 登录注册


源自《Windows核心編程》(第五版)

授人以渔,不授人以鱼。
2015-02-14 10:38
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
其實這也是内存對齊的問題,不同的機器、不同的系統有不同的方式,精通那些并不會使我們編程更牛。按照規範,以人類邏輯編程就好了,總是思考機器的問題,沒必要,除非你就是做機器的。

授人以渔,不授人以鱼。
2015-02-14 10:52
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
可能你和那個百度作者寫的内存管理程序才會用鏈表形式搜尋空隙分配内存,還要多少分多少呢。

授人以渔,不授人以鱼。
2015-02-14 11:14
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
其實,我1樓給出MSDN的解釋已經很清楚了:malloc()將會獲得不少於申請的空間,但這個塊也可能大於所申請的,因爲需要對齊和維護一些信息(alignment and maintenance)。那段英文并不複雜,關鍵部分也描紅了。測試結果衹是證實了這段話而已。btw: 那個需要維護的信息,我猜可能是塊大小,因爲用free()釋放的内存不需要再給出尺寸了,系統怎麽知道原來申請的是多少,那一定是在這空間中有地方儲存起來的。

1樓的代碼,申請的是1000字節,但實際上是1024字節,不是申請多少給多少,也不是4096。


[ 本帖最后由 TonyDeng 于 2015-2-14 11:57 编辑 ]

授人以渔,不授人以鱼。
2015-02-14 11:45
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
在Windows中,進程的虛擬内存是它單獨佔有的(這也是NT之後實際上不需要釋放動態申請内存的原因,因爲操作系統確保在進程結束之後把這些内存全部抹掉,不是像DOS/Win98那樣會有内存泄漏),不會有別的進程過來干擾,申請必定是按順序依次分配,這是可想而知的。一個32位的進程,它擁有接近2GB的虛擬内存(對我們所寫的普通程序來説,這已是極奢侈的資源,不釋放内存基本上沒什麽問題),誰寫的内存管理程序還會到處搜尋空隙降低效率?

1024這個結果,也表明malloc()的分配不是頁尺寸4096。

順帶說一下:MS系統的棧尺寸默認是1M,以我以前DOS時代的編程經驗來説,已經怎麽都用不完(大尺寸數據是在堆上申請的),不知道gcc預設4M是怎麽用的。


[ 本帖最后由 TonyDeng 于 2015-2-14 12:05 编辑 ]

授人以渔,不授人以鱼。
2015-02-14 11:54
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
後面再測試了一下,一般是比申請的大24字節,隨著申請塊的增大而增大,有到32字節的。但是,MS的堆内存管理顯然有某種算法,在多次申請之後,偶爾某塊會跳回到前面,好像與申請塊的大小有關,有大數據堆和小數據堆,具體規律暫時沒測出來。

那個百度說申請多少給多少,估計是看_msize()函數返回的值來說的,但那個不過是如strlen()返回字符串長度那樣的值,大家都知道字符數組真正佔用的空間是strlen()返回值加1,這個也一樣。尤其在面向對象的環境中,對象返回的一些尺寸是給你看的,它真正佔用的尺寸,你猜都不要猜——編程規範確實是這般告誡程序員的。在C中,FILE這個數據結構,就是典型的對象結構,標準庫手冊也勸告程序員不要以爲可以看到它的某些字段,就一知半解地亂改數據,因爲它隱藏的東西、以及怎麽使用的暗手段,是你不知道的。

授人以渔,不授人以鱼。
2015-02-14 19:53
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
以下是引用chimeixing在2015-2-15 16:55:43的发言:

"在堆上,理论上也如此"
堆和栈的实现明显是有很大不同的,不知道这个理论是从哪里看到的呢?

舉個例子:“理論上人人平等”,“理論上你是很聰明的”,這都是什麽理論?堆和棧在實現上的確有不同,但往一個方向發展是正常的想法。這個東西,實測也的確是向一個方向發展的,衹是在積纍到一定程度(大概分配10次左右)就往回跳一下不知是什麽原因,具體是怎麽回事,沒有做可視化的觀察,暫時說不上來。不過,正如有人說的,探討這個細節沒多大用處,因爲我前面說了,寫程序不做那些假定,不刻意利用副作用,什麽事都沒有。它跟“對齊”是同一性質的,不要假定結構體中每個字段是緊密相鄰的,不要假定結構體有2個char就以爲結構體的尺寸是2Byte,等等。在這裏,我尤其要指出的是,不要用malloc()申請太小的數據空間,堆的浪費往往超乎想象,碎片化的問題其實真的很嚴重。

我這個測試,初衷是針對有人說動態分配空間是申請多少給多少,直覺上就認爲那是不可能的。棧有對齊,堆也不會例外,這就是“理論”。


[ 本帖最后由 TonyDeng 于 2015-2-16 11:08 编辑 ]

授人以渔,不授人以鱼。
2015-02-16 11:02
快速回复:動態分配内存malloc()的一些測試結果
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.018361 second(s), 9 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved