1. 程式人生 > >【資料結構】動態記憶體管理

【資料結構】動態記憶體管理

動態記憶體管理是和作業系統息息相關的一個活動,現在的計算機的記憶體分配和回收基本上都由OS來維護管理,但是一些高階程式語言的記憶體回收和分配都還是由程式設計師來管理,比如C和C++,有malloc方法,也總有建構函式和解構函式。但是有一些語言就不需要,比如Python。

系統每次對申請記憶體的物件分配一段地址連續的記憶體塊,如果已經被佔用了,就叫做佔用塊,如果還沒被分配掉,就叫做可利用空間塊或者空閒塊。在回收記憶體之後,系統的整個記憶體塊會出現參差不齊的狀態,這時候就需要用到動態記憶體管理了。

一、 可利用空間表

可利用空間表是系統中都在使用的一種記憶體管理工具,針對不同的情況需要設定不同的連結串列資料結構。
1、 每次分配的記憶體大小是一樣的,這時候就使用一個鏈棧即可,把記憶體分為大小相同的塊,如果有應用程式來申請,就從棧頂分配一個記憶體塊出去,如果回收回來一個,就放入棧頂。
2、 系統每次分配給使用者的記憶體塊的大小分為若干種規格,且一般都是2的次方。這種情況下采用可利用空間表,且利用夥伴系統對記憶體進行管理。
3、 系統每次按需分配給使用者記憶體塊,大小隨機。這種情況下同樣適用可利用空間表,只不過資料結構會有所變化,管理手段也有不同。

二、 可利用空間表的幾種資料結構

1、普通可利用空間表:即一個可利用空間連結串列,其中包括以下幾個欄位,tag標識該段是否被使用,type或size標識該段的記憶體空間大小,link標識後繼的記憶體塊,space即儲存的空間。
2、採用邊界標識法的可利用空間表:相比普通可利用空間表要複雜一些,主要的欄位有llink即該記憶體塊的前驅,rlink是後繼,tag 和 size 不變,uplink標識也是指標,指向本連結串列結點,其值就是該空閒記憶體塊的首地址。分配的時候需要修改連結串列,也就是修改若干指標的地址;回收的時候要考慮相鄰的記憶體塊是否是空閒塊,如果是的話,就要合併。
3、採用夥伴系統的可利用空間表

:通過一個2的次方的指標陣列來分別指示不同大小塊的連結串列,分配的時候也是修改連結串列,但是在回收的時候,同樣存在相鄰空閒塊合併的操作,但是如果兩個相鄰塊不是同一個大的記憶體塊分化出來的,則不合並,因為容易造成錯誤。

三、分配記憶體策略

1、就近分配,在搜尋可利用空間連結串列的過程中,遇到的第一個符合應用程式申請要求的記憶體塊就被系統選中分配出去。
2、最優化匹配,在搜尋可利用空間表的過程中,要選擇空間最接近申請要求的記憶體塊,這樣能夠避免塊記憶體的浪費。這種分配方法較為複雜,也就是說在分配的時候需要修改指標,回收的時候還是需要修改指標,但是好處是比較適合那些申請記憶體大小千差萬別的系統。
3、最差匹配

,在搜尋可利用空間表的過程中,要選擇最不接近申請要求的記憶體塊,也就是最大的那一個空閒塊。這個策略比較適合每次申請的記憶體塊差不多大小的系統。

四、 無用單元收集

1、 設定記憶體計數器,如果指向某記憶體單元的指標計數為0,則釋放該單元;
2、 收集無用單元:主要包括兩個步驟,給所有的佔用塊新增mark域,當mark為0時表示該結點還沒有被判斷過是否要收集無用單元,mark為1表示已經被系統考察過了;然後,遍歷所有的佔用塊,這種標記遍歷演算法有若干種,具體不述了。

五、 儲存緊縮

無用單元收集的過程主要就是修改單元中的指標和某些結點的標記值,而儲存緊縮則要將資料按照記憶體地址進行整體遷移,其過程相當於搬家。這樣能完整地改善記憶體塊犬牙交錯的狀態。

六、Python的記憶體管理

  1. 物件的引用計數機制
    Python內部使用引用計數,來保持追蹤記憶體中的物件,所有物件都有引用計數。
    引用計數增加的情況:
    1,一個物件分配一個新名稱
    2,將其放入一個容器中(如列表、元組或字典)
    引用計數減少的情況:
    1,使用del關鍵字語句對物件別名顯示的銷燬
    2,引用超出作用域或被重新賦值
    sys.getrefcount( )函式可以獲得物件的當前引用計數
    多數情況下,引用計數比你猜測得要大得多。對於不可變資料(如數字和字串),直譯器會在程式的不同部分共享記憶體,以便節約記憶體。
  2. 垃圾回收
    1,當一個物件的引用計數歸零時,它將被垃圾收集機制處理掉,也就是無用單元收集的一種策略。
    2,當兩個物件a和b相互引用時,del語句可以減少a和b的引用計數,並銷燬用於引用底層物件的名稱。然而由於每個物件都包含一個對其他物件的應用,因此引用計數不會歸零,物件也不會銷燬。(從而導致記憶體洩露)。為解決這一問題,直譯器會定期執行一個迴圈檢測器,搜尋不可訪問物件的迴圈並刪除它們。
  3. 記憶體池機制
    python提供了對記憶體的垃圾收集機制,但是它將不用的記憶體放到記憶體池而不是返回給作業系統。
    1,Pymalloc機制。為了加速Python的執行效率,Python引入了一個記憶體池機制,用於管理對小塊記憶體的申請和釋放。
    2,Python中所有小於256個位元組的物件都使用pymalloc實現的分配器,而大的物件則使用系統的malloc。
    3,對於Python物件,如整數,浮點數和List,都有其獨立的私有記憶體池,物件間不共享他們的記憶體池。也就是說如果你分配又釋放了大量的整數,用於快取這些整數的記憶體就不能再分配給浮點數。
  4. 分代技術
    分代回收的整體思想是:將系統中的所有記憶體塊根據其存活時間劃分為不同的集合,每個集合就成為一個“代”,垃圾收集頻率隨著“代”的存活時間的增大而減小,存活時間通常利用經過幾次垃圾回收來度量。
    Python預設定義了三代物件集合,索引數越大,物件存活時間越長。
    舉例:
    當某些記憶體塊M經過了3次垃圾收集的清洗之後還存活時,我們就將記憶體塊M劃到一個集合A中去,而新分配的記憶體都劃分到集合B中去。當垃圾收集開始工作時,大多數情況都只對集合B進行垃圾回收,而對集合A進行垃圾回收要隔相當長一段時間後才進行,這就使得垃圾收集機制需要處理的記憶體少了,效率自然就提高了。在這個過程中,集合B中的某些記憶體塊由於存活時間長而會被轉移到集合A中,當然,集合A中實際上也存在一些垃圾,這些垃圾的回收會因為這種分代的機制而被延遲。

相關推薦

資料結構動態記憶體管理

動態記憶體管理是和作業系統息息相關的一個活動,現在的計算機的記憶體分配和回收基本上都由OS來維護管理,但是一些高階程式語言的記憶體回收和分配都還是由程式設計師來管理,比如C和C++,有malloc方法,也總有建構函式和解構函式。但是有一些語言就不需要,比如Pyt

資料結構動態記憶體管理機制

通過前面的學習,介紹很多具體的資料結構的儲存以及遍歷的方式,過程中只是很表面地介紹了資料的儲存,而沒有涉及到更底層的有關的儲存空間的分配與回收,從本節開始將做更深入地介紹。 在使用早期的計算機上編寫程式時,有關資料儲存在什麼位置等這樣的問題都是需要程式設計師自己來給資料分配記憶體。而現在的高階語言,大大的

C++動態記憶體管理

一、C/C++記憶體分佈 我們先回顧在C語言學習階段學習過的一張記憶體分佈圖: 然後我們可以根據上邊的這幅圖,做一下下面這道筆試中一定會遇到的判斷儲存區的筆試題: int globalVar = 1

資料結構靜態、動態順序表

靜態順序表 順序表的初始化和銷燬 列印順序表 查詢

C++入門筆記動態記憶體管理

此文針對FishC大佬的《C++快速入門》第三十三講—動態記憶體管理,本文是對其的筆記整理。 到目前為止,所有的示例程式在完成它的任務時所使用的記憶體空間都是固定不變的。 這個固定不變的記憶體空間其實是在編寫程式的時候就可以知道和確定的(一般以變數的形式)。這些程式都不能在程式執行期間動

資料結構—— 1、不要小瞧陣列

2-1、 使用Java中的陣列 2-2 二次封裝屬於我們自己的陣列 2-3 向陣列中新增元素 2-4 陣列中查詢元素和修改元素 2-5 包含,搜尋和刪除 2-6 使用泛型 2-7 動態陣列 2-8 簡單的複雜度分析 2-9 均攤複雜度和防止複雜度的震盪

資料結構多項式連結串列實現相加

#include<bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 1006; struct node { double coef; int exp; struct n

資料結構合併兩個有序連結串列

#include<stdio.h> #include<string.h> #include<stdlib.h> const int maxn = 1e5 + 5; struct node { int num; struct node *next; }; s

雙棧實現整數(支援大於十的整數)計算器資料結構

通過棧的操作實現簡單的計算器 首先定義兩個棧,一個叫s_num用來存放運算元,另一個叫s_opt用來存放操作符。 再定義一個字元陣列用來存放輸入的表示式,初始化為0; 當表示式的字元不為‘/0’時,或者操作符棧中不為空的時候,就要一直執行程式! 在執行的時候做兩個判斷,輸入的不是數字

資料結構二叉樹的相關操作(待更)

#include "stdio.h" #include "stdlib.h" typedef struct node { char data; struct node *rchild,*lchild; }bintnode; typedef bintnode *bintree;//指向該結構體

資料結構陣列實現的線性表(線性表的順序儲存結構)

資料結構 陣列實現線性表 通過陣列實現了一個簡單的線性表 功能: 在陣列尾部新增元素 在陣列指定位置新增元素 根據下標獲取元素 根據下標刪除元素 根據元素刪除元素 獲取當前陣列長度 判斷當前陣列是否為空 列印陣列元素 public

資料結構基本概念

資料基本概念: 資料:描述客觀事物的符號,是計算機中可以操作的物件,是能被計算機識別,並輸入給計算機處理的符號集合.資料元素:是組成資料的,有一定意義的基本單位,在計算機中通常作為整體處理.比如人,牛,羊.資料項:一個數據元素可以由若干個資料項組成,比如人可以有眼,耳,鼻,也可有姓名,年齡,地址

資料結構線段樹2018國慶三校聯考D5T3

題意: 分析: 有一個顯然的暴力方法: 對每個詢問,從左往右做一次,記錄字首和,當某個位置字首和為負後,則刪去當前點。再從右往左做一次。 考慮使這個過程變得高效: 可以將詢問按左端點從右往左排序,然後用棧依次處理每個在從左往右考慮時是否需要刪除。 再利用線段樹,求

資料結構二叉樹的建立和遍歷(非遞迴)

該程式使用的是遞迴地建立方法,以及非遞迴的遍歷演算法 執行環境:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild

資料結構二叉樹的建立與遍歷(遞迴)

該程式全是使用遞迴的操作 執行環境是:Dev-C++ #include <stdio.h> #include <stdlib.h> typedef struct node{ char data; struct node *lchild,*rchild; }bi

C#資料結構001-線性表:順序表

C#資料結構:順序表結構 1、自定義順序表結構 using System.Collections; using System.Collections.Generic; /// <summary> ///線性表介面 /// </summary> /// <type

資料結構

1.有了遞迴,為什麼還需要非遞迴? 嚴格來說,是因為非遞迴的效率比遞迴的效率高,但是,我們都知道,現在的編譯器很強大,優化的效果也非常好,所以非遞迴的效率並不比遞迴好多少,那麼為什麼還要使用非遞迴呢?最主要的原因是遞迴有一個非常大的缺陷:它只適合在分治的情況下使用,當你的應用場景下面遞迴深度太深

資料結構前序遍歷與中序遍歷構造二叉樹

根據一棵樹的前序遍歷與中序遍歷構造二叉樹 具體程式碼如下: struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; }; struct TreeNo

資料結構中序遍歷與後序遍歷構造二叉樹

根據一棵樹的中序遍歷與後序遍歷構造二叉樹。 具體程式碼如下: struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; }; struct TreeNod

資料結構8種排序的比較

  直接插入排序 ①所給元素越接近有序,直接插入排序的時間效率越高 ②時間複雜度: O(N^2) ③空間複雜度: O(1) ④穩定性: 穩定 希爾排序 ①希爾排序是對直接插入排序的優化  ②當gap >