小結:php
一、垃圾回收的本質:找到並回收再也不被使用的內存空間;程序員
二、標記清除方式和複製收集方式的對比;算法
三、複製收集方式的局部性優勢;編程
https://en.wikipedia.org/wiki/C_(programming_language)#Memory_management數組
One of the most important functions of a programming language is to provide facilities for managing memory and the objects that are stored in memory. C provides three distinct ways to allocate memory for objects:[27]緩存
malloc
from a region of memory called the heap; these blocks persist until subsequently freed for reuse by calling the library function realloc
or free
These three approaches are appropriate in different situations and have various trade-offs. For example, static memory allocation has little allocation overhead, automatic allocation may involve slightly more overhead, and dynamic memory allocation can potentially have a great deal of overhead for both allocation and deallocation. The persistent nature of static objects is useful for maintaining state information across function calls, automatic allocation is easy to use but stack space is typically much more limited and transient than either static memory or heap space, and dynamic memory allocation allows convenient allocation of objects whose size is known only at run-time. Most C programs make extensive use of all three.安全
Where possible, automatic or static allocation is usually simplest because the storage is managed by the compiler, freeing the programmer of the potentially error-prone chore of manually allocating and releasing storage. However, many data structures can change in size at runtime, and since static allocations (and automatic allocations before C99) must have a fixed size at compile-time, there are many situations in which dynamic allocation is necessary.[27] Prior to the C99 standard, variable-sized arrays were a common example of this. (See the article on malloc
for an example of dynamically allocated arrays.) Unlike automatic allocation, which can fail at run time with uncontrolled consequences, the dynamic allocation functions return an indication (in the form of a null pointer value) when the required storage cannot be allocated. (Static allocation that is too large is usually detected by the linker or loader, before the program can even begin execution.)併發
Unless otherwise specified, static objects contain zero or null pointer values upon program startup. Automatically and dynamically allocated objects are initialized only if an initial value is explicitly specified; otherwise they initially have indeterminate values (typically, whatever bit pattern happens to be present in the storage, which might not even represent a valid value for that type). If the program attempts to access an uninitialized value, the results are undefined. Many modern compilers try to detect and warn about this problem, but both false positives and false negatives can occur.app
Another issue is that heap memory allocation has to be synchronized with its actual usage in any program in order for it to be reused as much as possible. For example, if the only pointer to a heap memory allocation goes out of scope or has its value overwritten before free()
is called, then that memory cannot be recovered for later reuse and is essentially lost to the program, a phenomenon known as a memory leak. Conversely, it is possible for memory to be freed but continue to be referenced, leading to unpredictable results. Typically, the symptoms will appear in a portion of the program far removed from the actual error, making it difficult to track down the problem. (Such issues are ameliorated in languages with automatic garbage collection.) less
https://zh.wikipedia.org/wiki/C動態內存分配
C動態內存分配是在C語言中爲了實現動態內存分配而進行的手動內存管理。這種管理是經過C標準庫中的 malloc
、realloc
、calloc
、free
等函數進行的。[1][2]
C++ 爲了兼容 C 語言也提供這些函數,可是更推薦使用 new
、delete
操做符來完成相似的操做。[3]
malloc
所實際使用的內存分配機制有不少不一樣的實現,執行時間和內存消耗各有不一樣。
C 動態內存分配函數在頭文件 stdlib.h
中聲明(C++ 中對應的頭文件名稱爲 cstdlib
)。[1]
函數 | 描述 |
---|---|
malloc |
分配指定數量的字節 |
realloc |
調整指定內存塊的大小,必要時會從新分配 |
calloc |
分配指定數量的字節,並初始化爲 0 |
free |
釋放指定的內存塊 |
malloc
所執行的內存分配基於字節數而不是類型,其返回類型爲 void 指針(void *
),表示該指針所指向區域的數據類型未知。C++ 因爲其強類型系統,實際使用該指針時須要進行強制類型轉換,而 C 語言中則沒必要進行。
int * ptr; ptr = malloc(10 * sizeof(int)); /* 不進行強制類型轉換 */ ptr = (int *)malloc(10 * sizeof(int)); /* 進行強制類型轉換 */
https://zh.wikipedia.org/wiki/C語言
C語言的特點之一是:程序員必須親自處理內存的分配細節。
C語言使用棧(Stack)來保存函數返回地址/棧幀基址、完成函數的參數傳遞和函數局部變量的存儲。 若是程序須要在運行的過程當中動態分配內存,能夠利用堆(Heap)來實現。
基本上C程序的元素存儲在內存的時候有3種分配策略:
若是一個變量聲明爲全局變量或者是函數的靜態變量,這個變量的存儲將使用靜態分配方式。靜態分配的內存通常會被編譯器放在數據段或代碼段來存儲,具體取決於實現。這樣作的前提是,在編譯時就必須肯定變量的大小。 以IA32的x86平臺及gcc編譯器爲例,全局及靜態變量放在數據段的低端;全局及靜態常量放在代碼段的高端。
函數的自動局部變量應該隨着函數的返回會自動釋放(失效),這個要求在通常的體系中都是利用棧(Stack)來知足的。相比於靜態分配,這時候,就沒必要絕對要求這個變量在編譯時就必須肯定變量的大小,運行時才決定也不遲,可是C89仍然要求在編譯時就要肯定,而C99放鬆了這個限制。但不管是C89仍是C99,都不容許一個已經分配的自動變量運行時改變大小。
因此說C函數永遠不該該返回一個局部變量的地址。
要指出的是,自動分配也屬於動態分配,甚至能夠用alloca函數來像分配堆(Heap)同樣進行分配,並且釋放是自動的。
還有一種更加特殊的狀況,變量的大小在運行時有可能改變,或者雖然單個變量大小不變,變量的數目卻有很大彈性,不能靜態分配或者自動分配,這時候可使用堆(Heap)來知足要求。ANSI C定義的堆操做函數是malloc、calloc、realloc和free。
使用堆(Heap)內存將帶來額外的開銷和風險。
《代碼的將來》p76
垃圾回收 garbage collection GC
對象本質:
在Java和Rub中,程序在運行時會建立不少對象。從編程角度來看,它們是對象;但從計算機角度來看,它們也就是一些裝有數據的內存空間而已。
麻煩來源:
在C和C++這樣的語言中,這些內存空間是由人手動進行管理的。當須要內存空間時,要請求操做系統進行分配,不須要的時候要返還給操做系統。
然而,正是「再也不須要」這一點,帶來了各類各樣的麻煩;而若是認爲某些內存空間「可能還要用到」而不還給操做系統或者是用完了卻忘記返還,這些沒法訪問的空間就會
一直保留下來,形成內存的白白浪費,最終引起性能降低和產生抖動。
術語:
一、垃圾 garbage
GC本質:
若是程序(經過某個變量)可能會直接或間接地引用一個對象,那麼這個對象就被視爲「存活」;
與之相反,已經引用不到的對象被視爲「死亡」。將這些「死亡」的對象找出來,而後做爲垃圾進行回收,這就是垃圾回收的本質。
二、根 root
判斷對象是否可被引用的起始點。
至於哪裏纔是根,不一樣的語言個編編譯器有不一樣的規定,但基本上是將變量和運行棧空間做爲根。
3大垃圾回收算法
一、
標記清除 mark and sweep 1960
從根開始將可能被引用的對用遞歸的方式進行標記,而後將沒有標記到的對象做爲來及進行回收。
耗費時間和存活對象數的綜合相關。
變形:標記壓縮 mark and compact 算法,它不是將被標記的對象清除而是將它們不斷壓縮。
二、
複製收集方式
標記清除算法缺點:在分配了大量對象而且其中只有一小部分存活的狀況下,所消耗的時間會大大超過必要的值,這是由於
在清除階段還須要對大量死亡對象進行掃描。
複製收集 copy and collection 則試圖客服這一缺點。
將從根開始被引用的對象複製到另外的空間中,而後,再將複製的對象所可以引用的對象 用遞歸的方式不斷複製下去。
複製收集方式中,只存在至關於標記清除方式中的標記階段。因爲清除階段中須要對現存的全部對象進行掃描,
在存在大量對象且其中大部分都即將死亡的狀況下,所有掃描一遍的開銷實在是不小。
而在複製收集方式中,就不存在這樣的開銷,但將對選哪一個複製一份所須要的開銷則比較大;
所以在「存活」對象比例比較高的狀況下,反而會比較不利。
此該算法「具備局部性(locality)」的優點。
在複製收集過程當中,會按照對象被引用的順序將對象複製到新空間中。因而,關係較近的對象被放在距離較近的內存空間中的
可能性會提升,這被稱爲局部性。局部性高的狀況下,內存緩存會更容易有效運做,程序的運行性性能也能獲得提升。
三、
引用計數方式 reference count
容易實現是引用計數算法最大優勢。
缺點
最大缺點
沒法釋放循環引用對象。
A、B、C三個對象沒有被其餘對象引用,而是互相之間循環引用,所以它們的引用計數永遠不會爲0,
結果這些對象就永遠不會被釋放。
缺點2:
必須在引用發生增減時對引用計數作出正確的增減,而若是漏掉某個增減的話,就會引起很難
找到緣由的內存錯誤。
引用數忘了增長,會對不恰當的對象進行釋放;
而引用數忘了減小的話,對象會一直殘留在內存中,從而致使內存泄漏。
缺點3:
引用計數管理並不適合並行處理。若是多個線程同時對引用計數進行增減的話,引用即便的值就可能
產生不一致的問題,結果則會致使內存錯誤。
爲了不這種狀況的而發生,對引用計數的操做必須採用獨佔的方式進行。若是引用操做頻繁發生,每次
都要使用加鎖等併發控制機制的話,其開銷也是不可小覷的。