垃圾回收器GC(Garbage Collection)
一、引用計數算法(Reference Counting)
介紹:給對象添加一個引用計數器,每當一個地方引用它時,數據器加1;當引用失效時,計數器減1;計數器為0的即可被回收。
優點:實現簡單,判斷效率高
缺點:很難解決對象之間的相互循環引用(objA.instance = objB; objB.instance = objA)的問題,所以java語言並沒有選用引用計數法管理內存
二、根搜索算法(GC Root Tracing)
Java和C#都是使用根搜索算法來判斷對象是否存活。通過一系列的名為“GC Root”的對象作為起始點,從這些節點開始向下搜索,搜索所有走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時(用圖論來說就是GC Root到這個對象不可達時),證明該對象是可以被回收的。
在Java中哪些對象可以成為GC Root?
- 虛擬機棧(棧幀中的本地變量表)中的引用對象
- 方法區中的類靜態屬性引用的對象
- 方法區中的常量引用對象
- 本地方法棧中JNI(即Native方法)的引用對象
三、標記-清除算法(Mark-Sweep)
首先標記出需要回收的對象,在標記完成后統一回收掉所有的被標記對象。
缺點:效率問題和空間問題(標記清除后會產生大量的不連續內存碎片,內存碎片過多可能會導致程序需要分配較大對象時找不到足夠大的連續內存空間而不得不提前觸發另一次垃圾回收動作)
四、復制算法(Copying)
將內存划分為大小相等的兩塊,每次只使用其中的一塊。當這塊內存用完了,就將還存活的對象復制到另一塊內存上,然后把已使用過的內存空間一次清理掉。
優點:每次只對其中一塊進行GC,不用考慮內存碎片的問題,並且實現簡單,運行高效
缺點:內存縮小了一半
注:現在的商業虛擬機都是用這種收集算法回收新生代。內存分為一塊較大的Eden空間和兩塊較小的Survior空間,每次使用Eden和其中的一塊Survior.當回收時,將Eden和Survior中還存活的對象一次性拷貝到另外一塊Survior空間上,最后清理Eden和剛才用過的Survior空間。
五、標記-整理算法(Mark-Compact)
讓所有存活對象都向一端移動,然后直接清理掉端邊界以外的所有內存。
六、分代收集算法(Generational Collection)
根據對象的存活周期的不同將內存划分為幾塊,一般就分為新生代和老年代,根據各個年代的特點采用不同的收集算法。新生代(少量存活)用復制算法,老年代(對象存活率高)“標記-清理”算法
補充:分代划分內存介紹
整個JVM內存總共划分為三代:年輕代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)
1、年輕代:所有新生成的對象首先都放在年輕代內存中。年輕代的目標就是盡可能快速的手機掉那些生命周期短的對象。年輕代內存分為一塊較大的Eden空間和兩塊較小的Survior空間,每次使用Eden和其中的一塊Survior.當回收時,將Eden和Survior中還存活的對象一次性拷貝到另外一塊Survior空間上,最后清理Eden和剛才用過的Survior空間。
2、年老代:在年輕代經歷了N次GC后,仍然存活的對象,就會被放在老年代中。因此可以認為老年代存放的都是一些生命周期較長的對象。
3、持久代:基本固定不變,用於存放靜態文件,例如Java類和方法。持久代對GC沒有顯著的影響。持久代可以通過-XX:MaxPermSize=<N>進行設置。