本文是Android面試題整理中的一篇,結合右下角目錄食用更佳,包括:html
- 它們都是接口,都實現了Collection接口
- List元素能夠重複,元素順序與插入順序相同,其子類有LinkedList和ArrayList
- Set元素不能重複,元素順序與插入順序不一樣,子類有HashSet(經過HashMap實現),LinkedHashSet,TreeSet(紅黑樹實現,排序的)
- List繼承了Collection,儲存值,能夠有重複值
- Set繼承了Collection,儲存值,不能有重複值
- Map儲存健值對,能夠一對一或一對多
List、Set 是,Map 不是。Map是鍵值對映射容器,與List和Set有明顯的區別,而Set存儲的零散的元素且不容許有重複元素(數學中的集合也是如此),List是線性結構的容器,適用於按數值索引訪問元素的情形java
- 在1.7中,HashMap採用數組+單鏈表的結構;1.8中,採用數組+單鏈表或紅黑樹的結構(當鏈表size > 8時,轉換成紅黑樹)
- HaspMap中有兩個關鍵的構造函數,一個是初始容量,另外一個是負載因子。
- 初始容量即數組的初始大小,當map中元素個數 > 初始容量*負載因子時,HashMap調用resize()方法擴容
- 在存入數據時,對key的hashCode再次進行hash(),目的是讓hash值分佈均勻
- 對hash() 返回的值與容量進行與運算,肯定在數組中的位置
- key能夠爲null,null的hash值是0
- 對hashCode在調用hash()方法進行計算
- 當超過閾值時進行擴容
- 當發生衝突時使用鏈表或者紅黑樹解決衝突
- hashCode可能很大,數組初始容量可能很小,不匹配,因此須要: hash碼 & (數組長度-1)做爲數組下標
- hashCode可能分佈的不均勻
加大哈希碼低位的隨機性,使得分佈更均勻,從而提升對應數組存儲下標位置的隨機性 & 均勻性,最終減小Hash衝突程序員
- 經過hash值肯定位置,與用戶插入順序不一樣
- 在達到閾值後,HashMap會調用resize方法擴容,擴容後位置發生變化
HashMap經過數組和鏈表實現,數組查找的時間複雜度是O(1),鏈表的時間複雜度是O(n),因此要讓HashMap儘量的塊,就須要鏈表的長度儘量的小,當鏈表長度是1是,HashMap的時間複雜度就變成了O(1);根據HashMap的實現原理,要想讓鏈表長度儘量的短,須要hash算法儘可能減小衝突。面試
hashCode和equals算法
- 它們是final的,不可變,保證了安全性
- 均已經實現了hashCode和equals方法,計算準確
- HashMap線程不安全
- HashMap沒有同步鎖,舉例:好比A、B兩個線程同時觸發擴容,HashMap容量增長2倍,並將原數據從新分配到新的位置,這個時候可能出現原鏈表轉移到新鏈表時生成了環形鏈表,出現死循環。
- 使用Collections.synchronizedMap()方法,該方法的實現方式是使用synchronized關鍵字
- 使用ConcurrentHashMap,性能比Collections.synchronizedMap()更好
- 計算key的Hash值,找到數組中的位置,獲得鏈表的頭指針
- 遍歷鏈表,經過equals比較key,肯定是否是要找的元素
- 找到後調整鏈表,將該元素從鏈表中刪除
//源碼 java8
@Override public V remove(Object key) {
if (key == null) {
return removeNullKey();
}
int hash = Collections.secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
int index = hash & (tab.length - 1);
for (HashMapEntry<K, V> e = tab[index], prev = null;
e != null; prev = e, e = e.next) {
if (e.hash == hash && key.equals(e.key)) {
if (prev == null) {
tab[index] = e.next;
} else {
prev.next = e.next;
}
modCount++;
size--;
postRemove(e);
return e.value;
}
}
return null;
}
複製代碼
- 在初次加載時,會調用resize()進行初始化
- 當put()時,會查看當前元素個數是否大於閾值(閾值=數組長度*負載因子),當大於時,調用resize方法擴容
- 新建一個數組,擴容後的容量是原來的兩倍
- 將原來的數據從新計算位置,拷貝到新的table上
最大變化是當鏈表超過必定的長度後,將鏈表轉換成紅黑樹存儲,在存儲不少數據時,效率提高了。鏈表的查找複雜度是O(n),紅黑樹是O(log(n))數組
- HashTable是線程安全的,而HashMap不是
- HashMap中容許存在null鍵和null值,而HashTable中不容許
- HashTable已經棄用,咱們能夠用ConcurrentHashMap等替代
- ConcurrentHashMap是線程安全的HashMap,效率比直接加cynchronized要好
- 1.7中經過分段鎖實現,讀不枷鎖(經過volatile保證可見性),寫時給對應的分段加鎖。(1.8實現原理變了)
- ConcurrentHashMap經過分段鎖來實現,併發度即爲段數
- 段數是ConcurrentHashMap類構造函數的一個可選參數,默認值爲16
- LinkedHashMap經過繼承HashMap實現,既保留了HashMap快速查找能力,又保存了存入順序
- LinkedHashMap重寫了HashMap的Entry,經過LinkedEntry保存了存入順序,能夠理解爲經過雙向鏈表和HashMap共同實現
- 藉助Collections工具類synchronizedMap和synchronizedList將其轉爲線程安全的
- 使用安全的類替代,如HashTable(不建議使用)或者ConcurrentHashMap替代Hashmap,用CopyOnWriteArrayList替代ArrayList
- HashSet 是經過HashMap來實現的,內部持有一個HashMap實例
- HashSet存入的值即爲HashMap的key,hashMap的value是HashSet中new 的一個Object實例,全部的value都相同
- 天然排序:調用無參構造函數,添加的元素必須實現Comparable接口
- 定製排序:使用有參構造函數,傳入一個Comparator實例
- Array能夠是基本類型和引用類型,ArrayList只能是引用類型
- Array固定大小,ArrayList長度能夠動態改變
- ArrayList有不少方法能夠調用,如addAll()
- String[] a = list.toArray(new String[size]));
- List list = Arrays.asList(array);
- char[] char = string.toCharArray();
和引用類型同樣,在棧中保存一個引用,指向堆地址安全
- TreeMap和TreeSet都是經過紅黑樹實現的,所以要求元素都是可比較的,元素必須實現Comparable接口,該接口中有compareTo()方法。
- Collections的sort方法有兩種重載形式,一種只有一個參數,要求傳入的待排序元素必須實現Comparable接口;第二種有兩個參數,要求傳入待排序容器即Comparator實例
- ArrayList 和Vector都是使用數組方式存儲數據,數組方式節省空間,便與讀取;但插入刪除涉及數組移動,性能較差。
- Vector中的方法因爲添加了synchronized修飾,所以Vector是線程安全的容器。
- LinkedList使用雙向鏈表實現存儲,佔用空間大,讀取慢;插入刪除快
- Vector已經被遺棄,不推薦使用。爲了實現線程安全的list,可使用Collections中的synchronizedList方法將其轉換成線程安全的容器後再使用
PriorityQueue是一個基於優先級堆的無界隊列,它的元素是按照天然順序(natural order)排序的。在建立的時候,咱們能夠給它提供一個負責給元素排序的比較器。PriorityQueue不容許null值,由於他們沒有天然順序,或者說他們沒有任何的相關聯的比較器。PriorityQueue的邏輯結構爲堆(徹底二叉樹),物理結構爲數組。最後,PriorityQueue不是線程安全的,入隊和出隊的時間複雜度是O(log(n))bash
迭代器是一個接口,咱們能夠藉助這個接口實現對集合的遍歷,刪除 擴展(迭代器實現原理):Collection繼承了Iterable接口,iterable接口中有iterator方法,返回一個Iterator迭代器 -> Collection的實現類經過在內部實現自定義Iterator,在iterator時返回這個實例。併發
- ListIterator 繼承自 Iterator
- Iterator可用來遍歷Set和List集合,可是ListIterator只能用來遍歷List
- ListIterator比Iterator增長了更多功能,例如能夠雙向遍歷,增長元素等
- 查找多用有序數組,插入刪除多用無序數組
- 解釋:有序數組最大的好處在於查找的時間複雜度是O(log n),而無序數組是O(n)。有序數組的缺點是插入操做的時間複雜度是O(n),由於值大的元素須要日後移動來給新元素騰位置。相反,無序數組的插入時間複雜度是常量O(1)
- Collections.sort是經過Arrays.sort實現的。當list不爲ArrayList時,先轉成數組,再調用Arrays.sort
- java 8 中Array.Sort()是經過timsort(一種優化的歸併排序)來實現的
- Collection 是一個接口,Set、List都繼承了該接口
- Collections 是一個工具類,該工具類能夠幫咱們完成對容器的判空,排序,線程安全化等。
- 快速失敗:在用迭代器遍歷一個集合對象時,若是遍歷過程當中對集合對象的內容進行了修改(增長、刪除、修改),則會拋出Concurrent Modification Exception
- 安全失敗:操做是對象的副本,這個時候原對象改變並不會對當前迭代器遍歷產生影響。java.util.concurrent類下邊容器都是安全失敗
擴展:快速失敗原理:容器內部有一個modCount,記錄變化的次數,當進行遍歷時,若是mocount值發生改變,責快速失敗
在做爲參數傳遞以前,咱們可使用Collections.unmodifiableCollection(Collection c)方法建立一個只讀集合,這將確保改變集合的任何操做都會拋出UnsupportedOperationException。jvm
- 棧:線程獨有,每一個線程一個棧區。保存基本數據類型,對象的引用,函數調用的現場(棧能夠分爲三個部分:基本類型,執行環境上下文,操做指令區(存放操做指令));優勢是速度快,缺點是大小和生存週期必須是肯定的
- 堆:線程共享,jvm一共一個堆區。保存對象的實例,垃圾回收器回收的是堆區的內存
- 方法區(靜態區):線程共享。保存類信息、常量、靜態變量、JIT編譯器編譯後的代碼等數據,常量池是方法區的一部分。
- 堆:線程共享,存放對象實例,全部的對象的內存都在這裏分配。垃圾回收主要就是做用於這裏的。
- java虛擬機棧:線程私有,生命週期與線程相同。每一個方法執行的時候都會建立一個棧幀(stack frame)用於存放 局部變量表、操做棧、動態連接、方法出口
- native方法棧
- 程序計數器:這裏記錄了線程執行的字節碼的行號,在分支、循環、跳轉、異常、線程恢復等都依賴這個計數器。
- 方法區:線程共享的存儲了每一個類對象的信息(包括類的名稱、方法信息、字段信息)、靜態變量、常量以及編譯器編譯後的代碼等。
- java中存在內存泄漏
- java中雖然有GC幫咱們自動回收內存,可是隻有當實例沒有引用指向它時纔會被回收,若咱們錯誤的持有了引用,沒有在應當釋放時釋放,就會形成內存泄漏,例如在長生命週期對象持有短生命週期對象。
舉例:
import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<T> {
private T[] elements;
private int size = 0;
private static final int INIT_CAPACITY = 16;
public MyStack() {
elements = (T[]) new Object[INIT_CAPACITY];
}
public void push(T elem) {
ensureCapacity();
elements[size++] = elem;
}
public T pop() {
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
分析:這裏用數組實現了一個棧,可是當數據pop以後,數組裏內容並無被清空。
複製代碼
- GC是垃圾收集器(Garbage Collection)的縮寫,是面試中常考的點。瞭解GC的運行方式,對防止內存泄漏,提升運行效率等都有好處
- 垃圾收集器會自動進行內存回收,不須要程序員進行操做,System.gc() 或Runtime.getRuntime().gc() 時並非立刻進行內存回收,甚至不會進行內存回收
- 詳細參見JVM的內存回收機制
- 引用計數(沒法解決循環引用的問題)
- 可達性分析
- 虛擬機棧(棧幀中的本地變量表)中引用的對象;
- 方法區中的類靜態屬性引用的對象
- 方法區中的常量引用的對象
- 原生方法棧(Native Method Stack)中 JNI 中引用的對象。
- 標記-清除(Mark-Sweep)法:減小停頓時間,但會形成內存碎片
- 標記-整理(Mark-Compact)法:能夠解決內存碎片問題,可是會增長停頓時間
- 複製(copying)法:從一個地方拷貝到另外一個地方,適合有大量回收的場景,好比新生代回收
- 分代收集:把內存區域分紅不一樣代,根據代不一樣採起不一樣的策略
新生代(Yong Generation):存放新建立的對象,採用複製回收方法;年老代(old Generation):這些對象垃圾回收的頻率較低,採用的標記整理方法,這裏的垃圾回收叫作 major GC;永久代(permanent Generation):存放Java自己的一些數據,當類再也不使用時,也會被回收。
- minorGc發生在年輕代,是複製回收
- 年輕代能夠分爲三個區域:Eden、from Survivor和to Survivor;當Eden滿了的時候,觸發minorGc
- Gc過程:Eden區複製到to區;from區年齡大的被移到年老區,年齡小的複製到to區;to區變成from區;
- 在年輕代gc過程當中存活的次數超過閾值
- 或者太大了直接放入年老代
- to Survivor滿了,新對象直接放入老年代
- 還有一種狀況,若是在From空間中,相同年齡全部對象的大小總和大於From和To空間總和的一半,那麼年齡大於等於該年齡的對象就會被移動到老年代,而不用等到15歲(默認)
- 調用System.gc時,系統建議執行Full GC,可是沒必要然執行
- 老年代或者永久代空間不足
- 其餘(=-=)
- OOM 是堆內存溢出
- stackoverflow是棧內存溢出
- permgen space說的是溢出的區域在永久代
- stackoverflow:;當線程調用一個方法是,jvm壓入一個新的棧幀到這個線程的棧中,只要這個方法還沒返回,這個棧幀就存在。若是方法的嵌套調用層次太多(如遞歸調用),隨着java棧中的幀的增多,最終致使這個線程的棧中的全部棧幀的大小的總和大於-Xss設置的值,而產生生StackOverflowError溢出異常
- outofmemory:
- java程序啓動一個新線程時,沒有足夠的空間爲改線程分配java棧,一個線程java棧的大小由-Xss設置決定;JVM則拋出OutOfMemoryError異常;
- java堆用於存放對象的實例,當須要爲對象的實例分配內存時,而堆的佔用已經達到了設置的最大值(經過-Xmx)設置最大值,則拋出OutOfMemoryError異常;
- 方法區用於存放java類的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。在類加載器加載class文件到內存中的時候,JVM會提取其中的類信息,並將這些類信息放到方法區中。當須要存儲這些類信息,而方法區的內存佔用又已經達到最大值(經過-XX:MaxPermSize);將會拋出OutOfMemoryError異常
http://www.jcodecraeer.com/a/chengxusheji/java/2015/0520/2896.html 本文是Android面試題整理中的一篇,結合右下角目錄食用更佳,包括:
- 它們都是接口,都實現了Collection接口
- List元素能夠重複,元素順序與插入順序相同,其子類有LinkedList和ArrayList
- Set元素不能重複,元素順序與插入順序不一樣,子類有HashSet(經過HashMap實現),LinkedHashSet,TreeSet(紅黑樹實現,排序的)
- List繼承了Collection,儲存值,能夠有重複值
- Set繼承了Collection,儲存值,不能有重複值
- Map儲存健值對,能夠一對一或一對多
List、Set 是,Map 不是。Map是鍵值對映射容器,與List和Set有明顯的區別,而Set存儲的零散的元素且不容許有重複元素(數學中的集合也是如此),List是線性結構的容器,適用於按數值索引訪問元素的情形
- 在1.7中,HashMap採用數組+單鏈表的結構;1.8中,採用數組+單鏈表或紅黑樹的結構(當鏈表size > 8時,轉換成紅黑樹)
- HaspMap中有兩個關鍵的構造函數,一個是初始容量,另外一個是負載因子。
- 初始容量即數組的初始大小,當map中元素個數 > 初始容量*負載因子時,HashMap調用resize()方法擴容
- 在存入數據時,對key的hashCode再次進行hash(),目的是讓hash值分佈均勻
- 對hash() 返回的值與容量進行與運算,肯定在數組中的位置
- key能夠爲null,null的hash值是0
- 對hashCode在調用hash()方法進行計算
- 當超過閾值時進行擴容
- 當發生衝突時使用鏈表或者紅黑樹解決衝突
- hashCode可能很大,數組初始容量可能很小,不匹配,因此須要: hash碼 & (數組長度-1)做爲數組下標
- hashCode可能分佈的不均勻
加大哈希碼低位的隨機性,使得分佈更均勻,從而提升對應數組存儲下標位置的隨機性 & 均勻性,最終減小Hash衝突
- 經過hash值肯定位置,與用戶插入順序不一樣
- 在達到閾值後,HashMap會調用resize方法擴容,擴容後位置發生變化
HashMap經過數組和鏈表實現,數組查找的時間複雜度是O(1),鏈表的時間複雜度是O(n),因此要讓HashMap儘量的塊,就須要鏈表的長度儘量的小,當鏈表長度是1是,HashMap的時間複雜度就變成了O(1);根據HashMap的實現原理,要想讓鏈表長度儘量的短,須要hash算法儘可能減小衝突。
hashCode和equals
- 它們是final的,不可變,保證了安全性
- 均已經實現了hashCode和equals方法,計算準確
- HashMap線程不安全
- HashMap沒有同步鎖,舉例:好比A、B兩個線程同時觸發擴容,HashMap容量增長2倍,並將原數據從新分配到新的位置,這個時候可能出現原鏈表轉移到新鏈表時生成了環形鏈表,出現死循環。
- 使用Collections.synchronizedMap()方法,該方法的實現方式是使用synchronized關鍵字
- 使用ConcurrentHashMap,性能比Collections.synchronizedMap()更好
- 計算key的Hash值,找到數組中的位置,獲得鏈表的頭指針
- 遍歷鏈表,經過equals比較key,肯定是否是要找的元素
- 找到後調整鏈表,將該元素從鏈表中刪除
//源碼 java8
@Override public V remove(Object key) {
if (key == null) {
return removeNullKey();
}
int hash = Collections.secondaryHash(key);
HashMapEntry<K, V>[] tab = table;
int index = hash & (tab.length - 1);
for (HashMapEntry<K, V> e = tab[index], prev = null;
e != null; prev = e, e = e.next) {
if (e.hash == hash && key.equals(e.key)) {
if (prev == null) {
tab[index] = e.next;
} else {
prev.next = e.next;
}
modCount++;
size--;
postRemove(e);
return e.value;
}
}
return null;
}
複製代碼
- 在初次加載時,會調用resize()進行初始化
- 當put()時,會查看當前元素個數是否大於閾值(閾值=數組長度*負載因子),當大於時,調用resize方法擴容
- 新建一個數組,擴容後的容量是原來的兩倍
- 將原來的數據從新計算位置,拷貝到新的table上
最大變化是當鏈表超過必定的長度後,將鏈表轉換成紅黑樹存儲,在存儲不少數據時,效率提高了。鏈表的查找複雜度是O(n),紅黑樹是O(log(n))
- HashTable是線程安全的,而HashMap不是
- HashMap中容許存在null鍵和null值,而HashTable中不容許
- HashTable已經棄用,咱們能夠用ConcurrentHashMap等替代
- ConcurrentHashMap是線程安全的HashMap,效率比直接加cynchronized要好
- 1.7中經過分段鎖實現,讀不枷鎖(經過volatile保證可見性),寫時給對應的分段加鎖。(1.8實現原理變了)
- ConcurrentHashMap經過分段鎖來實現,併發度即爲段數
- 段數是ConcurrentHashMap類構造函數的一個可選參數,默認值爲16
- LinkedHashMap經過繼承HashMap實現,既保留了HashMap快速查找能力,又保存了存入順序
- LinkedHashMap重寫了HashMap的Entry,經過LinkedEntry保存了存入順序,能夠理解爲經過雙向鏈表和HashMap共同實現
- 藉助Collections工具類synchronizedMap和synchronizedList將其轉爲線程安全的
- 使用安全的類替代,如HashTable(不建議使用)或者ConcurrentHashMap替代Hashmap,用CopyOnWriteArrayList替代ArrayList
- HashSet 是經過HashMap來實現的,內部持有一個HashMap實例
- HashSet存入的值即爲HashMap的key,hashMap的value是HashSet中new 的一個Object實例,全部的value都相同
- 天然排序:調用無參構造函數,添加的元素必須實現Comparable接口
- 定製排序:使用有參構造函數,傳入一個Comparator實例
- Array能夠是基本類型和引用類型,ArrayList只能是引用類型
- Array固定大小,ArrayList長度能夠動態改變
- ArrayList有不少方法能夠調用,如addAll()
- String[] a = list.toArray(new String[size]));
- List list = Arrays.asList(array);
- char[] char = string.toCharArray();
和引用類型同樣,在棧中保存一個引用,指向堆地址
- TreeMap和TreeSet都是經過紅黑樹實現的,所以要求元素都是可比較的,元素必須實現Comparable接口,該接口中有compareTo()方法。
- Collections的sort方法有兩種重載形式,一種只有一個參數,要求傳入的待排序元素必須實現Comparable接口;第二種有兩個參數,要求傳入待排序容器即Comparator實例
- ArrayList 和Vector都是使用數組方式存儲數據,數組方式節省空間,便與讀取;但插入刪除涉及數組移動,性能較差。
- Vector中的方法因爲添加了synchronized修飾,所以Vector是線程安全的容器。
- LinkedList使用雙向鏈表實現存儲,佔用空間大,讀取慢;插入刪除快
- Vector已經被遺棄,不推薦使用。爲了實現線程安全的list,可使用Collections中的synchronizedList方法將其轉換成線程安全的容器後再使用
PriorityQueue是一個基於優先級堆的無界隊列,它的元素是按照天然順序(natural order)排序的。在建立的時候,咱們能夠給它提供一個負責給元素排序的比較器。PriorityQueue不容許null值,由於他們沒有天然順序,或者說他們沒有任何的相關聯的比較器。PriorityQueue的邏輯結構爲堆(徹底二叉樹),物理結構爲數組。最後,PriorityQueue不是線程安全的,入隊和出隊的時間複雜度是O(log(n))
迭代器是一個接口,咱們能夠藉助這個接口實現對集合的遍歷,刪除 擴展(迭代器實現原理):Collection繼承了Iterable接口,iterable接口中有iterator方法,返回一個Iterator迭代器 -> Collection的實現類經過在內部實現自定義Iterator,在iterator時返回這個實例。
- ListIterator 繼承自 Iterator
- Iterator可用來遍歷Set和List集合,可是ListIterator只能用來遍歷List
- ListIterator比Iterator增長了更多功能,例如能夠雙向遍歷,增長元素等
- 查找多用有序數組,插入刪除多用無序數組
- 解釋:有序數組最大的好處在於查找的時間複雜度是O(log n),而無序數組是O(n)。有序數組的缺點是插入操做的時間複雜度是O(n),由於值大的元素須要日後移動來給新元素騰位置。相反,無序數組的插入時間複雜度是常量O(1)
- Collections.sort是經過Arrays.sort實現的。當list不爲ArrayList時,先轉成數組,再調用Arrays.sort
- java 8 中Array.Sort()是經過timsort(一種優化的歸併排序)來實現的
- Collection 是一個接口,Set、List都繼承了該接口
- Collections 是一個工具類,該工具類能夠幫咱們完成對容器的判空,排序,線程安全化等。
- 快速失敗:在用迭代器遍歷一個集合對象時,若是遍歷過程當中對集合對象的內容進行了修改(增長、刪除、修改),則會拋出Concurrent Modification Exception
- 安全失敗:操做是對象的副本,這個時候原對象改變並不會對當前迭代器遍歷產生影響。java.util.concurrent類下邊容器都是安全失敗
擴展:快速失敗原理:容器內部有一個modCount,記錄變化的次數,當進行遍歷時,若是mocount值發生改變,責快速失敗
在做爲參數傳遞以前,咱們可使用Collections.unmodifiableCollection(Collection c)方法建立一個只讀集合,這將確保改變集合的任何操做都會拋出UnsupportedOperationException。
- 棧:線程獨有,每一個線程一個棧區。保存基本數據類型,對象的引用,函數調用的現場(棧能夠分爲三個部分:基本類型,執行環境上下文,操做指令區(存放操做指令));優勢是速度快,缺點是大小和生存週期必須是肯定的
- 堆:線程共享,jvm一共一個堆區。保存對象的實例,垃圾回收器回收的是堆區的內存
- 方法區(靜態區):線程共享。保存類信息、常量、靜態變量、JIT編譯器編譯後的代碼等數據,常量池是方法區的一部分。
- 堆:線程共享,存放對象實例,全部的對象的內存都在這裏分配。垃圾回收主要就是做用於這裏的。
- java虛擬機棧:線程私有,生命週期與線程相同。每一個方法執行的時候都會建立一個棧幀(stack frame)用於存放 局部變量表、操做棧、動態連接、方法出口
- native方法棧
- 程序計數器:這裏記錄了線程執行的字節碼的行號,在分支、循環、跳轉、異常、線程恢復等都依賴這個計數器。
- 方法區:線程共享的存儲了每一個類對象的信息(包括類的名稱、方法信息、字段信息)、靜態變量、常量以及編譯器編譯後的代碼等。
- java中存在內存泄漏
- java中雖然有GC幫咱們自動回收內存,可是隻有當實例沒有引用指向它時纔會被回收,若咱們錯誤的持有了引用,沒有在應當釋放時釋放,就會形成內存泄漏,例如在長生命週期對象持有短生命週期對象。
舉例:
import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<T> {
private T[] elements;
private int size = 0;
private static final int INIT_CAPACITY = 16;
public MyStack() {
elements = (T[]) new Object[INIT_CAPACITY];
}
public void push(T elem) {
ensureCapacity();
elements[size++] = elem;
}
public T pop() {
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
分析:這裏用數組實現了一個棧,可是當數據pop以後,數組裏內容並無被清空。
複製代碼
- GC是垃圾收集器(Garbage Collection)的縮寫,是面試中常考的點。瞭解GC的運行方式,對防止內存泄漏,提升運行效率等都有好處
- 垃圾收集器會自動進行內存回收,不須要程序員進行操做,System.gc() 或Runtime.getRuntime().gc() 時並非立刻進行內存回收,甚至不會進行內存回收
- 詳細參見JVM的內存回收機制
- 引用計數(沒法解決循環引用的問題)
- 可達性分析
- 虛擬機棧(棧幀中的本地變量表)中引用的對象;
- 方法區中的類靜態屬性引用的對象
- 方法區中的常量引用的對象
- 原生方法棧(Native Method Stack)中 JNI 中引用的對象。
- 標記-清除(Mark-Sweep)法:減小停頓時間,但會形成內存碎片
- 標記-整理(Mark-Compact)法:能夠解決內存碎片問題,可是會增長停頓時間
- 複製(copying)法:從一個地方拷貝到另外一個地方,適合有大量回收的場景,好比新生代回收
- 分代收集:把內存區域分紅不一樣代,根據代不一樣採起不一樣的策略
新生代(Yong Generation):存放新建立的對象,採用複製回收方法;年老代(old Generation):這些對象垃圾回收的頻率較低,採用的標記整理方法,這裏的垃圾回收叫作 major GC;永久代(permanent Generation):存放Java自己的一些數據,當類再也不使用時,也會被回收。
- minorGc發生在年輕代,是複製回收
- 年輕代能夠分爲三個區域:Eden、from Survivor和to Survivor;當Eden滿了的時候,觸發minorGc
- Gc過程:Eden區複製到to區;from區年齡大的被移到年老區,年齡小的複製到to區;to區變成from區;
- 在年輕代gc過程當中存活的次數超過閾值
- 或者太大了直接放入年老代
- to Survivor滿了,新對象直接放入老年代
- 還有一種狀況,若是在From空間中,相同年齡全部對象的大小總和大於From和To空間總和的一半,那麼年齡大於等於該年齡的對象就會被移動到老年代,而不用等到15歲(默認)
- 調用System.gc時,系統建議執行Full GC,可是沒必要然執行
- 老年代或者永久代空間不足
- 其餘(=-=)
- OOM 是堆內存溢出
- stackoverflow是棧內存溢出
- permgen space說的是溢出的區域在永久代
- stackoverflow:;當線程調用一個方法是,jvm壓入一個新的棧幀到這個線程的棧中,只要這個方法還沒返回,這個棧幀就存在。若是方法的嵌套調用層次太多(如遞歸調用),隨着java棧中的幀的增多,最終致使這個線程的棧中的全部棧幀的大小的總和大於-Xss設置的值,而產生生StackOverflowError溢出異常
- outofmemory:
- java程序啓動一個新線程時,沒有足夠的空間爲改線程分配java棧,一個線程java棧的大小由-Xss設置決定;JVM則拋出OutOfMemoryError異常;
- java堆用於存放對象的實例,當須要爲對象的實例分配內存時,而堆的佔用已經達到了設置的最大值(經過-Xmx)設置最大值,則拋出OutOfMemoryError異常;
- 方法區用於存放java類的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。在類加載器加載class文件到內存中的時候,JVM會提取其中的類信息,並將這些類信息放到方法區中。當須要存儲這些類信息,而方法區的內存佔用又已經達到最大值(經過-XX:MaxPermSize);將會拋出OutOfMemoryError異常
http://www.jcodecraeer.com/a/chengxusheji/java/2015/0520/2896.html