熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

Java堆的管理--透視垃圾回收機制

2022-06-13   來源: JSP教程 

   引言
  Java的堆是一個運行時數據區類的實例(對象)從中分配空間Java虛擬機(JVM)的堆中儲存著正在運行的應用程序所建立的所有對象這些對象通過newnewarrayanewarray和multianewarray等指令建立但是它們不需要程序代碼來顯式地釋放一般來說堆的是由垃圾回收 來負責的盡管JVM規范並不要求特殊的垃圾回收技術甚至根本就不需要垃圾回收但是由於內存的有限性JVM在實現的時候都有一個由垃圾回收所管理的堆垃圾回收是一種動態存儲管理技術它自動地釋放不再被程序引用的對象按照特定的垃圾收集算法來實現資源自動回收的功能
  
   垃圾收集的意義
  在C++中對象所占的內存在程序結束運行之前一直被占用在明確釋放之前不能分配給其它對象而在Java中當沒有對象引用指向原先分配給某個對象的內存時該內存便成為垃圾JVM的一個系統級線程會自動釋放該內存塊垃圾收集意味著程序不再需要的對象是無用信息這些信息將被丟棄當一個對象不再被引用的時候內存回收它占領的空間以便空間被後來的新對象使用事實上除了釋放沒用的對象垃圾收集也可以清除內存記錄碎片由於創建對象和垃圾收集器釋放丟棄對象所占的內存空間內存會出現碎片碎片是分配給對象的內存塊之間的空閒內存洞碎片整理將所占用的堆內存移到堆的一端JVM將整理出的內存分配給新的對象
  
  垃圾收集能自動釋放內存空間減輕編程的負擔這使Java 虛擬機具有一些優點首先它能使編程效率提高在沒有垃圾收集機制的時候可能要花許多時間來解決一個難懂的存儲器問題在用Java語言編程的時候靠垃圾收集機制可大大縮短時間其次是它保護程序的完整性 垃圾收集是Java語言安全性策略的一個重要部份
  
  垃圾收集的一個潛在的缺點是它的開銷影響程序性能Java虛擬機必須追蹤運行程序中有用的對象 而且最終釋放沒用的對象這一個過程需要花費處理器的時間其次垃圾收集算法的不完備性早先采用的某些垃圾收集算法就不能保證%收集到所有的廢棄內存當然隨著垃圾收集算法的不斷改進以及軟硬件運行效率的不斷提升這些問題都可以迎刃而解
  
   垃圾收集的算法分析
  Java語言規范沒有明確地說明JVM使用哪種垃圾回收算法但是任何一種垃圾收集算法一般要做件基本的事情)發現無用信息對象)回收被無用對象占用的內存空間使該空間可被程序再次使用
  
  大多數垃圾回收算法使用了根集(root set)這個概念所謂根集就量正在執行的Java程序可以訪問的引用變量的集合(包括局部變量參數類變量)程序可以使用引用變量訪問對象的屬性和調用對象的方法垃圾收集首選需要確定從根開始哪些是可達的和哪些是不可達的從根集可達的對象都是活動對象它們不能作為垃圾被回收這也包括從根集間接可達的對象而根集通過任意路徑不可達的對象符合垃圾收集的條件應該被回收下面介紹幾個常用的算法
  
   引用計數法(Reference Counting Collector)
  
  引用計數法是唯一沒有使用根集的垃圾回收得法該算法使用引用計數器來區分存活對象和不再使用的對象一般來說堆中的每個對象對應一個引用計數器當每一次創建一個對象並賦給一個變量時引用計數器置為當對象被賦給任意變量時引用計數器每次加當對象出了作用域後(該對象丟棄不再使用)引用計數器減一旦引用計數器為對象就滿足了垃圾收集的條件
  
  基於引用計數器的垃圾收集器運行較快不會長時間中斷程序執行適宜地必須 實時運行的程序但引用計數器增加了程序執行的開銷因為每次對象賦給新的變量 計數器加而每次現有對象出了作用域生計數器減
  
   tracing算法(Tracing Collector)
  
  tracing算法是為了解決引用計數法的問題而提出它使用了根集的概念基於tracing算法的垃圾收集器從根集開始掃描識別出哪些對象可達哪些對象不可達並用某種方式標記可達對象例如對每個可達對象設置一個或多個位在掃描識別過程中基於tracing算法的垃圾收集也稱為標記和清除(markandsweep)垃圾收集器
  
   compacting算法(Compacting Collector)
  
  為了解決堆碎片問題基於tracing的垃圾回收吸收了Compacting算法的思想在清除的過程中算法將所有的對象移到堆的一端堆的另一端就變成了一個相鄰的空閒內存區收集器會對它移動的所有對象的所有引用進行更新使得這些引用 在新的位置能識別原來 的對象在基於Compacting算法的收集器的實現中一般增加句柄和句柄表
  
   coping算法(Coping Collector)
  
  該算法的提出是為了克服句柄的開銷和解決堆碎片的垃圾回收它開始時把堆分成 一個對象 面和多個空閒面 程序從對象面為對象分配空間當對象滿了基於coping算法的垃圾 收集就從根集中掃描活動對象並將每個 活動對象復制到空閒面(使得活動對象所占的內存之間沒有空閒洞)這樣空閒面變成了對象面原來的對象面變成了空閒面程序會在新的對象面中分配內存
  
  一種典型的基於coping算法的垃圾回收是stopandcopy算法它將堆分成對象面和空閒區域面在對象面與空閒區域面的切換過程中程序暫停執行
  
   generation算法(Generational Collector)
  
  stopandcopy垃圾收集器的一個缺陷是收集器必須復制所有的活動對象這增加了程序等待時間這是coping算法低效的原因在程序設計中有這樣的規律多數對象存在的時間比較短少數的存在時間比較長因此generation算法將堆分成兩個或多個每個子堆作為對象的一代(generation)由於多數對象存在的時間比較短隨著程序丟棄不使用的對象垃圾收集器將從最年輕的子堆中收集這些對象在分代式的垃圾收集器運行後上次運行存活下來的對象移到下一最高代的子堆中由於老一代的子堆不會經常被回收因而節省了時間
  
   adaptive算法(Adaptive Collector)
  
  在特定的情況下一些垃圾收集算法會優於其它算法基於Adaptive算法的垃圾收集器就是監控當前堆的使用情況並將選擇適當算法的垃圾收集器
  
   透視Java垃圾回收
   命令行參數透視垃圾收集器的運行
  
  使用Systemgc()可以不管JVM使用的是哪一種垃圾回收的算法都可以請求Java的垃圾回收在命令行中有一個參數verbosegc可以查看Java使用的堆內存的情況它的格式如下
  
  java verbosegc classfile
  
  可以看個例子
  
  class TestGC
  {
   public static void main(String[] args)
   {
   new TestGC();
   Systemgc();
   SystemrunFinalization();
   }
  }
  
  在這個例子中一個新的對象被創建由於它沒有使用所以該對象迅速地變為可達程序編譯後執行命令 java verbosegc TestGC 後結果為
  
  [Full GC K>K(K) secs]
  
  機器的環境為Windows + JDK箭頭前後的數據K和K分別表示垃圾收集GC前後所有存活對象使用的內存容量說明有KK=K的對象容量被回收括號內的數據K為堆內存的總容量收集所需要的時間是秒(這個時間在每次執行的時候會有所不同)
  
   finalize方法透視垃圾收集器的運行
  
  在JVM垃圾收集器收集一個對象之前 一般要求程序調用適當的方法釋放資源但在沒有明確釋放資源的情況下Java提供了缺省機制來終止化該對象心釋放資源這個方法就是finalize()它的原型為
  
  protected void finalize() throws Throwable
  
  在finalize()方法返回之後對象消失垃圾收集開始執行原型中的throws Throwable表示它可以拋出任何類型的異常
  
  之所以要使用finalize()是由於有時需要采取與Java的普通方法不同的一種方法通過分配內存來做一些具有C風格的事情這主要可以通過固有方法來進行它是從Java裡調用非Java方法的一種方式C和C++是目前唯一獲得固有方法支持的語言但由於它們能調用通過其他語言編寫的子程序所以能夠有效地調用任何東西在非Java代碼內部也許能調用C的malloc()系列函數用它分配存儲空間而且除非調用了free()否則存儲空間不會得到釋放從而造成內存漏洞的出現當然free()是一個C和C++函數所以我們需要在finalize()內部的一個固有方法中調用它也就是說我們不能過多地使用finalize()它並不是進行普通清除工作的理想場所
  
  在普通的清除工作中為清除一個對象那個對象的用戶必須在希望進行清除的地點調用一個清除方法這與C++破壞器的概念稍有抵觸在C++中所有對象都會破壞(清除)或者換句話說所有對象都應該破壞若將C++對象創建成一個本地對象比如在堆棧中創建(在Java中是不可能的)那麼清除或破壞工作就會在結束花括號所代表的創建這個對象的作用域的末尾進行若對象是用new創建的(類似於Java)那麼當程序員調用C++的delete命令時(Java沒有這個命令)就會調用相應的破壞器若程序員忘記了那麼永遠不會調用破壞器我們最終得到的將是一個內存漏洞另外還包括對象的其他部分永遠不會得到清除
  
  相反Java不允許我們創建本地(局部)對象無論如何都要使用new但在Java中沒有delete命令來釋放對象因為垃圾收集器會幫助我們自動釋放存儲空間所以如果站在比較簡化的立場我們可以說正是由於存在垃圾收集機制所以Java
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19351.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.