Qt學習06——內存分配策略

QString中的內存分配策略

QString在一個連續的內存塊中保存字符串數據,當字符串長度不斷增長時,QString需要重新分配內存空間,QString使用的內存分配策略如下:

1. size <= 20, 每次以4個字符空間(8字節,即1B)的步進增長;

2. size > 20 && size < 4084,內存塊以2倍速度增長;

3. size >= 4084,每次以2048個字符空間(4096字節,即4KB)的步進增長;

QString test(){
    QString str;
    for(int i=0;i<9000;++i)
        str.append("a");
    return str;
}
這裏定義了一個QString對象str,然後爲它追加9000個字符。根據QString的內存分配策略,該循環將進行14次內存分配:

4、8、16、20、52、116、244、500、1012、2036、4084、6132、8180、10228。最後一次內存重新分配之後,QString對象str具有一個10228個Unicode字符大小的內存塊(20456字節),其中有9000個字符空間(18000字節)被使用。


來源:陸文周《Qt5 開發及實例(第2版)》

//===============================================分割線=========================================================//

分配策略第二點說的不夠準確,這裏補充一下,其中涉及一些內存管理相關知識已附上鍊接。

關於QString的內存分配策略,在官方文檔中是這樣寫的:

Consider the following code, which builds a QString from another QString:

QString onlyLetters(const QString &in)
{
    QString out;
    for (int j = 0; j < in.size(); ++j) {
        if (in[j].isLetter())
            out += in[j];
    }
    return out;
}

We build the string out dynamically by appending one character to it at a time. Let's assume that we append 15000 characters to the QString string. Then the following 18 reallocations (out of a possible 15000) occur when QString runs out of space: 4, 8, 12, 16, 20, 52, 116, 244, 500, 1012, 2036, 4084, 6132, 8180, 10228, 12276, 14324, 16372. At the end, the QString has 16372 Unicode characters allocated, 15000 of which are occupied.

The values above may seem a bit strange, but here are the guiding principles:

  • QString allocates 4 characters at a time until it reaches size 20.
  • From 20 to 4084, it advances by doubling the size each time. More precisely, it advances to the next power of two, minus 12. (Some memory allocators perform worst when requested exact powers of two, because they use a few bytes per block for book-keeping.)
  • From 4084 on, it advances by blocks of 2048 characters (4096 bytes). This makes sense because modern operating systems don't copy the entire data when reallocating a buffer; the physical memory pages are simply reordered, and only the data on the first and last pages actually needs to be copied.
重要的是分配策略那三點,我解釋一下:
  • QString每次分配4個字符,直到大小達到20個字符;
  • 當大小在20到4084個字符之間,每次分配內存塊爲當前空間大小(即爲當前的2倍)。準確地說,是分配下一個2的整數冪減12(即2^n-12)。這是因爲在某些內存分配器中,會預分配幾個字節的空間用於簿記內存開銷(實現內存分配的時候會使用Bookkeeping,深入瞭解可前往內存管理內幕中查看“其它malloc實現”),因此分配大小爲2的整數冪時性能較低。
  • 從4084字符開始,每次分配2048個字符(4096字節,即4KB,剛好等於一個32位邏輯地址空間計算機系統的頁的大小),原因是現代操作系統重新分配一個緩衝區時不會將整個數據全部複製(隱式共享),物理頁只進行簡單地重新排序,實際上只需要複製首頁和尾頁的數據。
此外,QByteArray和QList也使用類似的算法。

用一個實例測試一下
QString textediter::test(){
    QString str;
    for(int i=0;i<9000;i++)
    {
        str.append("a");
        ui->textEdit->append(QString::number(i,10)+"  "+
                             QString::number(str.size(),10)+"  "+
                             QString::number(str.capacity(),10));
//        ui->textEdit->append(QString::number(sizeof(str),10));
//        ui->textEdit->append(QString::number(str.size(),10));
//        ui->textEdit->append(QString::number(str.capacity(),10));
    }
    return str;
}
由於不同位數的系統內存分配有所差異,我分別在32位和64位環境下編譯運行:
Qt5.4.0+MGW491_32+Qt Creator3.3.0
Qt5.8.0+MSVC2015_64+Qt Creator4.2.1
得到不同結果(左邊32位,右邊64位):
   
這裏的capacity,文檔說明是:
Returns the maximum number of characters that can be stored in the string without forcing a reallocation.
意思是:在不經過強制重新分配時,返回字符串所能存儲的最大字符空間。
這裏經過11次內存分配,在capacity達到20之後,每次增量均爲2的下一個整數冪。

因此這裏有2個疑問:
1、capacity的增量是否是內存增量?
2、在32位環境下,sizeof(str) = 4;在64位環境下,sizeof(str) = 8。同時capacity在20之前的增量相差了4字符。這之間的聯繫是什麼?

希望有機會找到答案。

發佈了49 篇原創文章 · 獲贊 41 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章