日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JAVA GC

發布時間:2024/10/5 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA GC 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

與C語言(一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收(堆內存))不同(由此程序會經常擔心會不會出現內存泄露和溢出的問題),Java內存(堆內存)的分配與回收由JVM垃圾收集器自動完成。Java堆是被所有線程共享的一塊內存區域,所有對象實例和數組都在堆上進行內存分配。Java GC機制主要完成3件事:確定哪些內存需要回收,確定什么時候需要執行GC,如何執行GC。學習Java GC機制,可以幫助我們在日常工作中排查各種內存溢出或泄露問題,解決性能瓶頸,達到更高的并發量,寫出更高效的程序。

要理解javaGC,首先我們需要了解java的內存機制

在Java運行時的數據區里,由JVM管理的內存區域分為下圖幾個模塊:

1.程序計數器(Program Counter Register):程序計數器是一個比較小的內存區域,用于指示當前線程所執行的字節碼執行到了第幾行,可以理解為是當前線程的行號指示器。字節碼解釋器在工作時,會通過改變這個計數器的值來取下一條語句指令。

2.虛擬機棧(JVM Stack):一個線程的每個方法在執行的同時,都會創建一個棧幀(Statck Frame),棧幀中存儲的有局部變量表、操作站、動態鏈接、方法出口等,當方法被調用時,棧幀在JVM棧中入棧,當方法執行完成時,棧幀出棧。

虛擬機棧中定義了兩種異常,如果線程調用的棧深度大于虛擬機允許的最大深度,則拋出StatckOverFlowError(棧溢出);不過多數Java虛擬機都允許動態擴展虛擬機棧的大小(有少部分是固定長度的),所以線程可以一直申請棧,直到內存不足,此時,會拋出OutOfMemoryError(內存溢出)。每個線程對應著一個虛擬機棧,因此虛擬機棧也是線程私有的。

3.本地方法棧(Native Method Statck):本地方法棧在作用,運行機制,異常類型等方面都與虛擬機棧相同,唯一的區別是:虛擬機棧是執行Java方法的,而本地方法棧是用來執行native方法的,在很多虛擬機中(如Sun的JDK默認的HotSpot虛擬機),會將本地方法棧與虛擬機棧放在一起使用。

本地方法棧也是線程私有的。

4.堆區(Heap):堆區是理解Java GC機制最重要的區域,沒有之一。在JVM所管理的內存中,堆區是最大的一塊,堆區也是Java GC機制所管理的主要內存區域,堆區由所有線程共享,在虛擬機啟動時創建。堆區的存在是為了存儲對象實例,原則上講,所有的對象都在堆區上分配內存(不過現代技術里,也不是這么絕對的,也有棧上直接分配的)。

一般的,根據Java虛擬機規范規定,堆內存需要在邏輯上是連續的(在物理上不需要),在實現時,可以是固定大小的,也可以是可擴展的,目前主流的虛擬機都是可擴展的。如果在執行垃圾回收之后,仍沒有足夠的內存分配,也不能再擴展,將會拋出OutOfMemoryError:Java heap space異常。

5,方法區(Method Area):在Java虛擬機規范中,將方法區作為堆的一個邏輯部分來對待,但事實上,方法區并不是堆(Non-Heap);另外,不少人的博客中,將Java GC的分代收集機制分為3個代:青年代,老年代,永久代,這些作者將方法區定義為“永久代”,這是因為,對于之前的HotSpot Java虛擬機的實現方式中,將分代收集的思想擴展到了方法區,并將方法區設計成了永久代。不過,除HotSpot之外的多數虛擬機,并不將方法區當做永久代,HotSpot本身,也已經取消永久代。

方法區是各個線程共享的區域,用于存儲已經被虛擬機加載的類信息(即加載類時需要加載的信息,包括版本、field、方法、接口等信息)、final常量、靜態變量、編譯器即時編譯的代碼等。

6.直接內存(Direct Memory):直接內存并不是JVM管理的內存,可以這樣理解,直接內存,就是JVM以外的機器內存,比如,你有4G的內存,JVM占用了1G,則其余的3G就是直接內存,JDK中有一種基于通道(Channel)和緩沖區(Buffer)的內存分配方式,將由C語言實現的native函數庫分配在直接內存中,用存儲在JVM堆中的DirectByteBuffer來引用。由于直接內存收到本機器內存的限制,所以也可能出現OutOfMemoryError的異常。

Java內存分配和回收的機制概括的說,就是:分代分配,分代回收。

為了進行高效的垃圾回收,虛擬機(我們將在下一節中詳細介紹java虛擬機)把堆內存劃分成新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)3個區域。

新生代

新生代由 Eden 與 Survivor Space(S0,S1)構成,大小通過-Xmn參數指定,Eden 與 Survivor Space 的內存大小比例默認為8:1,可以通過-XX:SurvivorRatio 參數指定,比如新生代為10M 時,Eden分配8M,S0和S1各分配1M。

Eden:希臘語,意思為伊甸園,在圣經中,伊甸園含有樂園的意思,根據《舊約·創世紀》記載,上帝耶和華照自己的形像造了第一個男人亞當,再用亞當的一個肋骨創造了一個女人夏娃,并安置他們住在了伊甸園。

大多數情況下,對象在Eden中分配,當Eden沒有足夠空間時,會觸發一次Minor GC,虛擬機提供了-XX:+PrintGCDetails參數,告訴虛擬機在發生垃圾回收時打印內存回收日志。

Survivor:意思為幸存者,是新生代和老年代的緩沖區域。
當新生代發生GC(Minor GC)時,會將存活的對象移動到S0內存區域,并清空Eden區域,當再次發生Minor GC時,將Eden和S0中存活的對象移動到S1內存區域。

存活對象會反復在S0和S1之間移動,當對象從Eden移動到Survivor或者在Survivor之間移動時,對象的GC年齡自動累加,當GC年齡超過默認閾值15時,會將該對象移動到老年代,可以通過參數-XX:MaxTenuringThreshold 對GC年齡的閾值進行設置。

老年代

老年代的空間大小即-Xmx 與-Xmn 兩個參數之差,用于存放經過幾次Minor GC之后依舊存活的對象。當老年代的空間不足時,會觸發Major GC/Full GC,速度一般比Minor GC慢10倍以上。

永久代

在JDK8之前的HotSpot實現中,類的元數據如方法數據、方法信息(字節碼,棧和變量大小)、運行時常量池、已確定的符號引用和虛方法表等被保存在永久代中,32位默認永久代的大小為64M,64位默認為85M,可以通過參數-XX:MaxPermSize進行設置,一旦類的元數據超過了永久代大小,就會拋出OOM異常。

擬機團隊在JDK8的HotSpot中,把永久代從Java堆中移除了,并把類的元數據直接保存在本地內存區域(堆外內存),稱之為元空間。

如何判斷對象是否存活

GC動作發生之前,需要確定堆內存中哪些對象是存活的,一般有兩種方法:引用計數法和可達性分析法。

1、引用計數法
在對象上添加一個引用計數器,每當有一個對象引用它時,計數器加1,當使用完該對象時,計數器減1,計數器值為0的對象表示不可能再被使用。

引用計數法實現簡單,判定高效,但不能解決對象之間相互引用的問題。

2.可達性分析法
通過一系列稱為 “GC Roots” 的對象作為起點,從這些節點開始向下搜索,搜索路徑稱為 “引用鏈”,以下對象可作為GC Roots:

  • 本地變量表中引用的對象
  • 方法區中靜態變量引用的對象
  • 方法區中常量引用的對象
  • Native方法引用的對象

當一個對象到 GC Roots 沒有任何引用鏈時,意味著該對象可以被回收。

在可達性分析法中,判定一個對象objA是否可回收,至少要經歷兩次標記過程:
1、如果對象objA到 GC Roots沒有引用鏈,則進行第一次標記。
2、如果對象objA重寫了finalize()方法,且還未執行過,那么objA會被插入到F-Queue隊列中,由一個虛擬機自動創建的、低優先級的Finalizer線程觸發其finalize()方法。finalize()方法是對象逃脫死亡的最后機會,GC會對隊列中的對象進行第二次標記,如果objA在finalize()方法中與引用鏈上的任何一個對象建立聯系,那么在第二次標記時,objA會被移出“即將回收”集合。

總結

以上是生活随笔為你收集整理的JAVA GC的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。