Java內存釋放機制


轉載:https://www.cnblogs.com/not-alone/articles/8206904.html

★JVM的內存?

  在Java虛擬機規范中(具體章節請看“這里 ”),提及了如下幾種類型的內存空間:

  ◇棧內存(Stack):每個線程私有的。

  ◇堆內存(Heap):所有線程公用的。

  ◇方法區(MethodArea):有點像以前常說的“進程代碼段”,這里面存放了每個加載類的反射信息、類函數的代碼、編譯時常量等信息。

  ◇原生方法棧(Native Method Stack):主要用於JNI中的原生代碼,平時很少涉及。

?

  ★垃圾回收機制簡介?

  其實Java虛擬機規范中並未規定垃圾回收的相關細節。垃圾回收具體該怎么搞,完全取決於各個JVM的設計者。所以,不同的JVM之間,GC的行為可能會有一定的差異。下面咱拿SUN官方的JVM來簡單介紹一下GC的機制。

  ◇啥時候進行垃圾回收?

  一般情況下,當JVM發現堆內存比較緊張、不太夠用時,它就會着手進行垃圾回收工作。但是大伙兒要認清這樣一個殘酷的事實:JVM進行GC的時間點是無法精確預知的。因為GC啟動的時刻會受到各種運行環境因素的影響,隨機性太大。

  雖說咱們無法精確預知,但假如你想知道每次垃圾回收執行的情況,還是蠻方便的。可以通過JVM的命令行參數“-XX:+PrintGC”把相關信息打印出來。

  另外,調用System.gc()只是建議JVM進行GC。至於JVM到底會不會做,那就不好說啦。通常不建議自己手動調用System.gc(),還是讓JVM自行決定比較好。另外,使用JVM命令行參數“-XX:+DisableExplicitGC”可以讓System.gc()不起作用。

  ◇誰來負責垃圾回收?

  一般情況下,JVM會有一個或多個專門的垃圾回收線程,由它們負責清理回收垃圾內存。

  ◇如何發現垃圾對象?

  垃圾回收線程會從“根集(Root Set)”開始進行對象引用的遍歷。所謂的“根集”,就是正在運行的線程中,可以訪問的引用變量的集合(比如所有線程當前函數的參數和局部變量、當前類的成員變量等等)。垃圾回收線程先找出被根集直接引用的所有對象(不妨叫集合1),然后再找出被集合1直接引用的所有對象(不妨叫集合2),然后再找出被集合2直接引用的所有對象......如此循環往復,直到把能遍歷到的對象都遍歷完。

  凡是從根集通過上述遍歷可以到達的對象,都稱為可達對象或有效對象;反之,則是不可達對象或失效對象(也就是垃圾)。

  ◇如何清理/回收垃圾?

  通過上述階段,就把垃圾對象都找出來。然后垃圾回收線程會進行相應的清理和回收工作,包括:把垃圾內存重新變為可用內存、進行內存的整理以消除內存碎片、等等。這個過程會涉及到若干算法,有愛好的同學可以參見“這里”。限於篇幅,咱就不深入聊了。

  ◇分代

  早期的JVM是不采用分代技術的,所有被GC管理的對象都存放在同一個堆里面。這么做的缺點比較明顯:每次進行GC都要遍歷所有對象,開銷很大。其實大部分的對象生命周期都很短(短命對象),只有少數對象比較長壽;在這些短命對象中,又只有少數對象占用的內存空間大;其它大量的短命對象都屬於小對象(很符合二八原理 )。

   有鑒於此,從JDK 1.2之后,JVM開始使用分代的垃圾回收(Generational GarbageCollection)。JVM把GC相關的內存分為年老代(Tenured)和年輕代(Nursery)、持久代(Permanent,對應於JVM規范的方法區)。大部分對象在剛創建時,都位於年輕代。假如某對象經歷了幾輪GC還活着(大齡對象),就把它移到年老代。另外,假如某個對象在創建時比較大,可能就直接被丟到年老代。經過這種策略,使得年輕代總是保存那些短命的小對象。在空間尺寸上,年輕代相對較小,而年老代相對較大。

  因為有了分代技術,JVM的GC也相應分為兩種:主要收集(Major Collection)和次要收集(MinorCollection)。主要收集同時清理年老代和年輕代,因此開銷很大,不常進行;次要收集僅僅清理年輕代,開銷很小,經常進行。

?

  ★GC對性能會有啥影響??

  剛才介紹了GC的大致原理,那GC對性能會造成哪些影響捏?主要有如下幾個方面:

  ◇造成當前運行線程的停頓

  早期的GC比較弱智。在它工作期間,所有其它的線程都被暫停(以免影響垃圾回收工作)。等到GC干完活,其它線程再繼續運行。所以,早期JDK的GC一旦開始工作,整個程序就會陷入假死狀態,失去各種響應。

  經過這些年的技術改進(包括采用分代技術),從JDK1.4開始,GC已經比較精明了。在它干活期間,只是偶然暫停一下其它線程的運行(從長時間假死變為暫時性休克)。

  ◇遍歷對象引用的開銷

  試想假如JVM中的對象很多,那遍歷完所有可達對象肯定是比較費勁的工作,這個開銷可不小。

  ◇清理和回收垃圾的開銷

  遍歷完對象引用之后,對垃圾的清理和回收也有較大的開銷。這部分開銷可能包括復制內存塊、更新對象引用等等。

?

  ★幾種收集器?

  ◇兩個性能指標

  因為今天聊的是性能的話題,必然會提到衡量GC性能的兩個重要指標:吞吐量(Throughput)和停頓時間(PauseTime)。吞吐量這個詞不是很直觀,解釋一下:就是JVM不用於GC的時間占總時間的比率。吞吐量是越大越好,停頓時間是越小越好。

  不同的應用程序對這兩個指標的關注點不一樣(后面具體會說),也就是所謂的“眾口難調”。很多JVM廠商為了迎合“眾口”,不得不提供多種幾種垃圾收集器供使用者選擇。不同的收集器,采用的收集策略是不一樣的,下面具體介紹。

  ◇串行收集器(Serial Collector)

  使用命令行選項“-XX:+UseSerialGC”指定。

  這種收集器是最傳統的收集器。它使用單線程進行垃圾回收,對於單CPU機器比較合適。另外,小型應用或者對上述兩個指標沒有非凡要求的,可以使用串行收集器。

  ◇並行收集器(Parallel Throughput Collector)

  顧名思義,這種收集器使用多個線程進行垃圾回收以達到高吞吐量。垃圾回收線程的數量通過命令行選項“-XX:ParallelGCThreads=n”指定。可以設置該數值以便充分利用多CPU/多核。

  當使用命令行選項“-XX:+UseParallelGC”時:它會針對年輕代使用多個垃圾回收線程,對年老代依然使用單個線程的串行方式。此選項最早在JDK1.5引入。

  當使用命令行選項“-XX:+UseParallelOldGC”時:它針對年輕代和年老代都使用多個垃圾回收線程的方式。不過此選項從JDK1.6才開始引入。

  ◇並發收集器(Concurrent Low Pause Collector)

  使用命令行選項“-XX:+UseConcMarkSweepGC”指定。

  這種收集器優先保證程序的響應。它會盡量讓垃圾回收線程和應用自身的線程同時運行,從而降低停頓時間。此選項從JDK1.4.1開始支持。

  ◇增量收集器(Incremental Collector)

  自從JDK 1.4.2以來,SUN官方就停止維護該收集器了。所以俺就節省點口水,不多說了。

?

  ★如何降低GC的影響??

  ◇盡量減少堆內存的使用

  由於GC是針對存儲在堆內存的對象進行的。咱們假如在程序中減少引用對象的分配(也就相應降低堆內存分配),那對於提高GC的性能是很有幫助滴。上次“字符串過濾實戰”的帖子給出了一個例子,示范了如何通過降低堆內存的分配次數來提升性能。

?

  ◇設置合適的堆內存大小

  JVM的堆內存是有講究的,不能太大也不能太小。假如堆內存太小,JVM老是感覺內存不夠用,可能會導致頻繁進行垃圾回收,影響了性能;假如堆內存太大,以至於操作系統的大部分物理內存都被JVM自個兒霸占了,那可能會影響其它應用程序甚至操作系統本身的性能。

  另外,年輕代的大小(或者說年輕代與年老代的比值)對於GC的性能也有明顯影響。假如年輕代太小,可能導致次要收集很頻繁;假如年輕代太大,導致次要收集的停頓很明顯。

  JVM提供了若干和堆內存大小相關的命令行選項,具體如下:

------------------------------

-Xms  設置初始堆內存

-Xmx  設置最大堆內存

-Xmn  設置年輕代的大小

-XX:NewRatio=n  設置年輕代與年老代的比例為“n”

-XX:NewSize=n  設置年輕代大小為“n”

------------------------------

  一般情況下,JVM的默認參數值已經夠用。所以沒事兒別輕易動用上述選項。假如你非調整不可,一定要做深入的性能對比測試,保證調整后的性能確實優於默認參數值。

?

  ◇吞吐量和停頓的取舍

  前面提到了不同應用的眾口難調。常見的口味有兩種:(1)看重吞吐量,對停頓時間無所謂;(2)側重於停頓時間。

  對於某些在后台的、單純運算密集型的應用,屬於第一種。比如某些科學計算的應用。這時候建議使用並行收集器。

  對於涉及用戶UI交互的、實時性要求比較高、程序需要快速響應的,屬於第二種。比如某些桌面游戲、某些電信交換系統。這時候建議使用並發收集器。

?

  ★相關的參考資料?

  ◇GC調優資料

  SUN官方提供了若干關於JVM垃圾回收調優的說明文檔,JDK 1.4.2請看“這里 ”;JDK 1.5請看“這里 ”;JDK1.6請看“這里 ”。

  ◇JVM命令行選項說明

  這是SUN公司內的某個有心人整理的各種命令行參數大全,在“這里 ”。包括有每個參數所適用的JDK版本。

  ◇虛擬機規范

  “這里 ”是SUN官方的JVM規范。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM