目錄
1. 描述 Java 語言中 JVM 的標記清除垃圾回收算法。
2. 根據一維數組的內存尋址公式,寫出二維數組的內存尋址公式。
一、爲什麼很多編程語言的數組都是從0開始編號的?
1、從數組存儲的內存模型上來看,“下標”確切的說法就是一種“偏移”,相比從1開始編號,從0開始編號會少一次減法運算,數組作爲非常基礎的數組結構,通過下標隨機訪問元素又是非常基礎的操作,效率的優化就要儘可能的做到極致。
2、主要的原因是歷史原因,C語言的設計者是從0開始計數數組下標的,之後的Java、JS等語言都進行了效仿,或者說是爲了減少從C轉向Java、JS等的學習成本。
二、什麼是數組?
數組是一個線性數據結構,用一組連續的內存空間存儲一組具有相同類型的數據。
其實數組、鏈表、棧、隊列都是線性表結構;樹、圖則是非線性表結構。
三、數組和鏈表的面試糾錯?
1、數組中的元素存在一個連續的內存空間中,而鏈表中的元素可以不存在於連續的內存空間。
2、數組支持隨機訪問,根據下標隨機訪問的時間複雜度是O(1);鏈表適合插入、刪除操作,時間複雜度爲O(1)。
四、容器是否完全替代數組?
容器的優勢:對於Java語言,容器封裝了數組插入、刪除等操作的細節,並且支持動態擴容。
對於Java,一些更適合用數組的場景:
1、Java的ArrayList無法存儲基本類型,需要進行裝箱操作,而裝箱與拆箱操作都會有一定的性能消耗,如果特別注意性能,或者希望使用基本類型,就可以選用數組。
2、若數組大小事先已知,並且對數組只有非常簡單的操作,不需要使用到ArrayList提供的大部分方法,則可以直接使用數組。
3、多維數組時,使用數組會更加直觀。
五、標記清除算法?
GC最基礎的收集算法就是標記-清除算法,如同他們的名字一樣,此算法分爲“標記”、“清除”兩個階段,先標記出需要回收的對象,再統一回收標記的對象。不足有二,一是效率不高,二是產生碎片內存空間。
六、警惕數組的訪問越界問題
C 語言代碼的運行結果分析
int main(int argc, char* argv[]){
int i = 0;
int arr[3] = {0};
for(; i<=3; i++){
arr[i] = 0;
printf("hello world\n");
}
return 0;
}
函數體內的局部變量存在棧上,且是連續壓棧。在Linux進程的內存佈局中,棧區在高地址空間,從高向低增長。變量i和arr在相鄰地址,且i比arr的地址大,所以arr越界正好訪問到i。當然,前提是i和arr元素同類型,否則那段代碼仍是未決行爲。
這段代碼無限循環原因有二,以及一個附加條件:
1. 函數體內的局部變量存在棧上,且是連續壓棧, 棧空間從高往低依次分配,i佔4字節,接着arr佔12字節,內存從高往低是這樣:存i的4字節|arr[2]|arr[1]|arr[0],數組訪問是通過“baseAddr+index乘typeSize”得到,算下來當index=3時,剛好是i的地址
2. 這裏剛好滿足字節對齊,系統爲64位系統,字長64,那麼字節對齊必須是8字節的倍數,剛好i變量和arr變量佔了16字節,對齊了;如果這裏將arr[3]改爲arr[4],爲了對齊,內存從高往低是這樣:存i的4字節|空4字節|arr[3]|arr[2]|arr[1]|arr[0],那麼arr[4]剛好是空的4字節,無法影響到i的值,則並不會無限循環
3.附加條件:編譯時gcc默認會自動添加越界保護,此處要達到無限循環效果,編譯時需加上-fno-stack-protector去除該保護
七、課後思考
1. 描述 Java 語言中 JVM 的標記清除垃圾回收算法。
解答:
1)判斷對象是否存活:
採用可達性分析算法來判斷對象是否存活,會遍歷所有 GC ROOTS,將所有 GC ROOTS 可達的對象標記爲存活
2)分代回收
(1)年輕代:複製算法,8:1:1的比例eden區和兩個survivor(survivor0,survivor1)區;
(2)老年代:標記整理
- 標記階段:首先進行標記;
- 清除階段:將存活的對象往一端移動,最後直接清除另一段端空間,不會造成對應內存碎片問題
- 不足:標記和清理效率都不高,少量垃圾產生時纔會高效;
3)可以作爲GC Roots的對象
- 虛擬機(棧幀中的本地變量表)中引用的對象
- 方法區中類靜態屬性引用的對象
- 方法區中常量引用的對象
- 本地方法棧中JNI(即一般說的native方法)中引用的對象
4)JVM垃圾收集器分類:
(1)新生代
Serial (第一代)
PraNew (第二代)
Parallel Scavenge (第三代)
G1收集器(第四代) JDK1.7後
(2)老年代
Serial Old (第一代)
Parallel Old (第二代)
CMS (第三代)
G1收集器 (第四代) JDK1.7後
2. 根據一維數組的內存尋址公式,寫出二維數組的內存尋址公式。
解答:
一維數組:a[i]_address=base_address+i*type_size
二維數組:二維數組假設是m*n, a[i][j]_address=base_address + (i*n+j)*type_size
三維數組:三維數組假設是m*n*q, a[i][j][k]_address=base_address + (i*n*q + j*q + k)*type_size