国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

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

2019-11-18 13:16:37
字體:
來源:轉載
供稿:網友

  1 引言
  java的堆是一個運行時數據區,類的實例(對象)從中分配空間。Java虛擬機(JVM)的堆中儲存著正在運行的應用程序所建立的所有對象,這些對象通過new、newarray、anewarray和multianewarray等指令建立,但是它們不需要程序代碼來顯式地釋放。一般來說,堆的是由垃圾回收 來負責的,盡管JVM規范并不要求非凡的垃圾回收技術,甚至根本就不需要垃圾回收,但是由于內存的有限性,JVM在實現的時候都有一個由垃圾回收所治理的堆。垃圾回收是一種動態存儲治理技術,它自動地釋放不再被程序引用的對象,按照特定的垃圾收集算法來實現資源自動回收的功能。
  
  2 垃圾收集的意義
  在C++中,對象所占的內存在程序結束運行之前一直被占用,在明確釋放之前不能分配給其它對象;而在Java中,當沒有對象引用指向原先分配給某個對象的內存時,該內存便成為垃圾。JVM的一個系統級線程會自動釋放該內存塊。垃圾收集意味著程序不再需要的對象是"無用信息",這些信息將被丟棄。當一個對象不再被引用的時候,內存回收它占領的空間,以便空間被后來的新對象使用。事實上,除了釋放沒用的對象,垃圾收集也可以清除內存記錄碎片。由于創建對象和垃圾收集器釋放丟棄對象所占的內存空間,內存會出現碎片。碎片是分配給對象的內存塊之間的空閑內存洞。碎片整理將所占用的堆內存移到堆的一端,JVM將整理出的內存分配給新的對象。
  
  垃圾收集能自動釋放內存空間,減輕編程的負擔。這使Java 虛擬機具有一些優點。首先,它能使編程效率提高。在沒有垃圾收集機制的時候,可能要花許多時間來解決一個難懂的存儲器問題。在用Java語言編程的時候,靠垃圾收集機制可大大縮短時間。其次是它保護程序的完整性, 垃圾收集是Java語言安全性策略的一個重要部份。
  
  垃圾收集的一個潛在的缺點是它的開銷影響程序性能。Java虛擬機必須追蹤運行程序中有用的對象, 而且最終釋放沒用的對象。這一個過程需要花費處理器的時間。其次垃圾收集算法的不完備性,早先采用的某些垃圾收集算法就不能保證100%收集到所有的廢棄內存。當然隨著垃圾收集算法的不斷改進以及軟硬件運行效率的不斷提升,這些問題都可以迎刃而解。
  
  3 垃圾收集的算法分析
  Java語言規范沒有明確地說明JVM使用哪種垃圾回收算法,但是任何一種垃圾收集算法一般要做2件基本的事情:(1)發現無用信息對象;(2)回收被無用對象占用的內存空間,使該空間可被程序再次使用。
  
  大多數垃圾回收算法使用了根集(root set)這個概念;所謂根集就量正在執行的Java程序可以訪問的引用變量的集合(包括局部變量、參數、類變量),程序可以使用引用變量訪問對象的屬性和調用對象的方法。垃圾收集首選需要確定從根開始哪些是可達的和哪些是不可達的,從根集可達的對象都是活動對象,它們不能作為垃圾被回收,這也包括從根集間接可達的對象。而根集通過任意路徑不可達的對象符合垃圾收集的條件,應該被回收。下面介紹幾個常用的算法。
  
  3.1 引用計數法(Reference Counting Collector)
  
  引用計數法是唯一沒有使用根集的垃圾回收得法,該算法使用引用計數器來區分存活對象和不再使用的對象。一般來說,堆中的每個對象對應一個引用計數器。當每一次創建一個對象并賦給一個變量時,引用計數器置為1。當對象被賦給任意變量時,引用計數器每次加1。當對象出了作用域后(該對象丟棄不再使用),引用計數器減1,一旦引用計數器為0,對象就滿足了垃圾收集的條件。
  
  基于引用計數器的垃圾收集器運行較快,不會長時間中斷程序執行,適宜地必須 實時運行的程序。但引用計數器增加了程序執行的開銷,因為每次對象賦給新的變量 ,計數器加1,而每次現有對象出了作用域生,計數器減1。
  
  3.2 tracing算法(Tracing Collector)
  
  tracing算法是為了解決引用計數法的問題而提出,它使用了根集的概念。基于tracing算法的垃圾收集器從根集開始掃描,識別出哪些對象可達,哪些對象不可達,并用某種方式標記可達對象,例如對每個可達對象設置一個或多個位。在掃描識別過程中,基于tracing算法的垃圾收集也稱為標記和清除(mark-and-sweep)垃圾收集器.
  
  3.3 compacting算法(Compacting Collector)
  
  為了解決堆碎片問題,基于tracing的垃圾回收吸收了Compacting算法的思想,在清除的過程中,算法將所有的對象移到堆的一端,堆的另一端就變成了一個相鄰的空閑內存區,收集器會對它移動的所有對象的所有引用進行更新,使得這些引用 在新的位置能識別原來 的對象。在基于Compacting算法的收集器的實現中,一般增加句柄和句柄表。
  
  3.4 coping算法(Coping Collector)
  
  該算法的提出是為了克服句柄的開銷和解決堆碎片的垃圾回收。它開始時把堆分成 一個對象 面和多個空閑面, 程序從對象面為對象分配空間,當對象滿了,基于coping算法的垃圾 收集就從根集中掃描活動對象,并將每個 活動對象復制到空閑面(使得活動對象所占的內存之間沒有空閑洞),這樣空閑面變成了對象面,原來的對象面變成了空閑面,程序會在新的對象面中分配內存。
  
  一種典型的基于coping算法的垃圾回收是stop-and-copy算法,它將堆分成對象面和空閑區域面,在對象面與空閑區域面的切換過程中,程序暫停執行。
  
  3.5 generation算法(Generational Collector)
  
  stop-and-copy垃圾收集器的一個缺陷是收集器必須復制所有的活動對象,這增加了程序等待時間,這是coping算法低效的原因。在程序設計中有這樣的規律:多數對象存在的時間比較短,少數的存在時間比較長。因此,generation算法將堆分成兩個或多個,每個子堆作為對象的一代(generation)。由于多數對象存在的時間比較短,隨著程序丟棄不使用的對象,垃圾收集器將從最年輕的子堆中收集這些對象。在分代式的垃圾收集器運行后,上次運行存活下來的對象移到下一最高代的子堆中,由于老一代的子堆不會經常被回收,因而節省了時間。
  
  3.6 adaptive算法(Adaptive Collector)
  
  在特定的情況下,一些垃圾收集算法會優于其它算法。基于Adaptive算法的垃圾收集器就是監控當前堆的使用情況,并將選擇適當算法的垃圾收集器。
  
  4 透視Java垃圾回收
  4.1 命令行參數透視垃圾收集器的運行
  
  使用System.gc()可以不管JVM使用的是哪一種垃圾回收的算法,都可以請求Java的垃圾回收。在命令行中有一個參數-verbosegc可以查看Java使用的堆內存的情況,它的格式如下:
  
  java -verbosegc classfile
  
  可以看個例子:
  
  class TestGC
  {
   public static void main(String[] args)
   {
   new TestGC();
   System.gc();
   System.runFinalization();
   }
  }
  
  在這個例子中,一個新的對象被創建,由于它沒有使用,所以該對象迅速地變為可達,程序編譯后,執行命令: java -verbosegc TestGC 后結果為:
  
  [Full GC 168K->97K(1984K), 0.0253873 secs]
  
  機器的環境為,windows 2000 + JDK1.3.1,箭頭前后的數據168K和97K分別表示垃圾收集GC前后所有存活對象使用的內存容量,說明有168K-97K=71K的對象容量被回收,括號內的數據1984K為堆內存的總容量,收集所需要的時間是0.0253873秒(這個時間在每次執行的時候會有所不同)。
  
  4.2 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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 舒城县| 松溪县| 泾川县| 美姑县| 探索| 瓦房店市| 海原县| 临朐县| 开平市| 海盐县| 塘沽区| 滕州市| 茌平县| 万载县| 安远县| 崇义县| 本溪市| 叶城县| 满城县| 武穴市| 扶余县| 思茅市| 江山市| 涟源市| 博湖县| 喀喇沁旗| 平邑县| 屯昌县| 台北市| 南安市| 库尔勒市| 冀州市| 青海省| 开阳县| 南昌市| 鄢陵县| 湟中县| 墨竹工卡县| 横峰县| 宜阳县| 凤台县|