亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JVM有哪些常用的功能

發布時間:2021-06-28 17:02:57 來源:億速云 閱讀:152 作者:chen 欄目:編程語言

本篇內容介紹了“JVM有哪些常用的功能”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

Hello,今天給各位童鞋們分享JVM,趕緊拿出小本子記下來吧!

JVM有哪些常用的功能

垃圾回收場景

新生代GC場景

在jvm內存模型中,新生代的內存分為為Eden和兩個Survivor

JVM有哪些常用的功能

在系統不停的運行過程中,Eden區會被塞滿,這個時候就會觸發Minor GC,進行垃圾回收有專門的垃圾回收線程,不同的內存區域會有不同的垃圾回收器,相當于垃圾回收線程和垃圾回收器配合起來,使用自己的垃圾回收算法,對指定的內存區域進行垃圾回收,如下圖所示:

JVM有哪些常用的功能

針對新生代采用ParNew垃圾回收器來進行回收,然后ParNew垃圾回收器針對新生代采用的就是復制算法來垃圾回收

這個時候垃圾回收器,就會把Eden區中的存活對象都標記出來,然后全部轉移到Survivor1去,接著一次性清空掉Eden中的垃圾對象

JVM有哪些常用的功能

當Eden再次塞滿的時候,就又要觸發Minor GC了,此時已然是垃圾回收線程運行垃圾回收器中的算法邏輯,也就是采用復制算法邏輯,去標記出來Eden和Survivor1中的存活對象,然后一次性把存活對象轉移到Survivor2中去,接著把Eden和Survivor1中的垃圾對象都回收掉

在發生GC的時候,我們寫好的JAVA系統在運行期間還能不能繼續在新生代里創建新的對象?

假如在GC期間,允許創建新的對象,那么垃圾回收器在把Eden和Survivor1里的存活對象標記轉移到Survivor2去,然后還在想辦法把Eden和Survivor1里的垃圾對象都清理掉,結果這個時候系統程序還在不停的在Eden里創建新的對象,那么這些新對象很快就成了垃圾對象,有的還有人引用是存活對象,這對垃圾回收器完全亂套,一邊回收一邊還在創建新的對象。

Stop the World

JVM最大的痛點,就是垃圾回收的過程,在垃圾回收的時候,盡可能讓垃圾回收器專心的工作,不能隨便讓我們的Java應用繼續創建對象,所以此時JVM會在后臺進入“入“Stop the World”狀態,也就是說會直接停止我們的Java系統的所有工作線程,讓我們的代碼不再運行

這樣的話,就可以讓我們的系統暫停運行,然后不再創建新的對象,同時讓垃圾回收線程盡快完成垃圾回收的工作,就是標記和轉移Eden以及Survivor1的存活對象到Survivor2中去,然后盡快一次性回收掉Eden和Survivor1中的垃圾對象,等垃圾回收完畢后,繼續恢復我們寫的Java系統的工作線程,然后繼續運行我們的代碼邏輯,繼續在Eden區創建新的對象

JVM有哪些常用的功能

Stop the World造成的系統停頓

在運行GC的時候會無法創建新的對象,則會造車系統停頓,如果Minor GC要運行50ms,則可能會導致我們的系統在50ms內不能接受任何請求,在這50ms期間用戶發起的所有請求都會出現短暫的卡頓,因為系統的工作線程不在運行,不能處理請求

可能由于內存分配不合理,導致對象頻繁進入老年代,平均七八分鐘一次Full GC,而Full GC比較慢,一次回收可能需要幾秒甚至幾十秒,所以一旦頻繁的Full GC,就會造成系統每隔幾分鐘卡死個幾十秒,讓用戶體驗極差

所以說,無論是新生代GC還是老年代GC,都盡量不要讓頻率過高,也避免持續時間過長,避免影響系統正常運行,這也是使用JVM過程中一個最需要優化的地方,也是最大的一個痛點。

不同的垃圾回收器的不同的影響

Serial垃圾回收器(新生代)

  • 用一個線程進行垃圾回收,然后此時暫停系統工作線程

  • 一般我們在服務器程序中很少用這種方式

ParNew垃圾回收器(新生代)

  • 常用的新生代垃圾回收器

  • 針對服務器一般都是多核CPU做了優化,他是支持多線程個垃圾回收的,可以大幅度提升回收的性能,縮短回收的時間

垃圾回收器

Serial和Serial Old垃圾回收器

  • 分別用來回收新生代和老年代的垃圾對象

  • 工作原理就是單線程運行,垃圾回收的時候會停止我們自己寫的系統的其他工作線程,讓我們系統直接卡死不動,然后讓他們垃圾回收,這個現在一般寫后臺Java系統幾乎不用。

ParNew和CMS垃圾回收器

  • ParNew現在一般都是用在新生代的垃圾回收器,采用的就是復制算法來垃圾回收

  • CMS是用在老年代的垃圾回收器

  • 都是多線程并發的機制,性能更好,現在一般是線上生產系統的標配組合

ParNew

理論

  • 沒有最新的G1垃圾回收器的話,通常大家線上系統都是ParNew垃圾回收器作為新生代的垃圾回收器當然現在即使有了G1,其實很多線上系統還是用的ParNew

  • 通常運行在服務器上Java系統,都可以充分利用服務器的多核CPU優勢,如果對新生代回收的時候,僅僅使用單線程進行垃圾回收,會導致浪費CPU的資源

  • 新生代的ParNew垃圾回收器主打的就是多線程垃圾回收機制,另外一種Serial垃圾回收器主打的是單線程垃圾回收,他們倆都是回收新生代的,唯一的區別就是單線程和多線程的區別,但是垃圾回收算法是完全一樣

  • ParNew垃圾回收器如果一旦在合適的時機執行Minor GC的時候,就會把系統程序的工作線程全部停掉,禁止程序繼續運行創建新的對象,然后自己就用多個垃圾回收線程去進行垃圾回收,回收的機制和算法都是一樣的

JVM有哪些常用的功能

參數設置

部署到Tomcat時可以在Tomcat的catalina.sh中設置Tomcat的JVM參數,使用Spring Boot也可以在啟動時指定JVM參數。

  • 指定使用ParNew垃圾回收器

使用“-XX:+UseParNewGC”選項,只要加入這個選項,JVM啟動之后對新生代進行垃圾回收的,就是ParNew垃圾回收器

  • ParNew垃圾回收器默認情況下的線程數量

一旦我們指定了使用ParNew垃圾回收器之后,他默認給自己設置的垃圾回收線程的數量就是跟CPU的核數是一樣的

如果你一定要自己調節ParNew的垃圾回收線程數量,也是可以的,使用“-XX:ParallelGCThreads”參數即可,通過他可以設置線程的數量

CMS

理論

  • 老年代選擇的垃圾回收器是CMS,他采用的是標記清理算法

  • 標記清理算法:先通過GC Roots的方法,看各個對象是否被GC Roots給引用,如果是的話,那就是存活對象,否則就是垃圾對象。先將垃圾對象都標記出來,然后一次性把垃圾對象都回收掉,這種方法最大問題:就是會造成很多內存碎片,這種內存碎片不大不小,可能放不下任何一個對象,則會造成內存浪費

  • CMS的STW(Stop the World)問題:如果停止一切工作線程,然后慢慢的執行“標記-清理”算法,會導致系統卡死時間過長,很多響應無法處理。所以CMS垃圾回收器采取的是:垃圾回收線程和系統工作線程盡量同時執行的模式來處理

如何實現系統一邊工作的同時進行垃圾回收?

CMS在執行一次垃圾回收的過程共分為4個階段:

  • 初始標記

  • 并發標記

  • 重新標記

并發清理

1、初始標記

CMS在進行垃圾回收時,會先執行初始標記階段。這個階段會讓系統的工作線程全部停止,進入“Stop The World”狀態,初始標記執行STW影響不大,因為他的速度比較快,只是標記出GC Roots直接應用的對象

2、并發標記

這個階段會讓系統可以隨意創建各種新對象,繼續運行,在運行期間可能會創建新的存活對象,也可能會讓部分存活對象失去引用,變成垃圾對象。在這個過程中,垃圾回收線程,會盡可能的對已有的對象進行GC Roots追蹤,但是這個過程中,在進行并發標記的時候,系統程序會不停的工作,他可能會各種創建出來新的對象,部分對象可能成為垃圾

這個階段就是對老年代所有對象進行GC Roots追蹤,其實是最耗時的,需要追蹤所有對象是否從根源上被GC Roots引用了,但是這個最耗時的階段,是跟系統程序并發運行的,所以其實這個階段不會對系統運行造成影響。

3、重新標記

因為第二階段里,你一邊標記存活對象和垃圾對象,一邊系統在不停運行創建新對象,讓老對象變成垃圾,所以第二階段結束之后,絕對會有很多存活對象和垃圾對象,是之前第二階段沒標記出來的。所以此時進入第三階段,要繼續讓系統程序停下來,**再次進入“Stop the World”階段。**然后重新標記下在第二階段里新創建的一些對象,還有一些已有對象可能失去引用變成垃圾的情況

這個重新標記的階段,是速度很快的,他其實就是對在第二階段中被系統程序運行變動過的少數對象進行標記,所以運行速度很快,接著重新恢復系統程序的運行。

4、并發清理

讓系統程序隨意運行,然后他來清理掉之前標記為垃圾的對象,這個階段比較耗時,需要進行對象的清理,但是他是跟著系統程序并發運行的,所以也不影響系統程序的執行

JVM有哪些常用的功能

CMS垃圾回收器問題

1、并發回收導致CPU資源緊張

CMS垃圾回收器有一個最大的問題,雖然能在垃圾回收的同時讓系統同時工作,在并發標記和并發清理兩個最耗時的階段,垃圾回收線程和系統工作線程同時工作,會導致有限的CPU資源被垃圾回收線程占用了一部分

并發標記的時候,需要對GC Roots進行深度追蹤,看所有對象里面到底有多少人是存活的但是因為老年代里存活對象是比較多的,這個過程會追蹤大量的對象,所以耗時較高。并發清理,又需要把垃圾對象從各種隨機的內存位置清理掉,也是比較耗時的

所以在這兩個階段,CMS的垃圾回收線程是比較耗費CPU資源的。CMS默認啟動的垃圾回收線程的數量是(CPU核數 + 3)/ 4

2、Concurrent Mode Failure問題

在并發清理階段,CMS只不過是回收之前標記好的垃圾對象,但是這個階段系統一直在運行,可能會隨著系統運行讓一些對象進入老年代,同時還變成垃圾對象,這種垃圾對象是“浮動垃圾”。因為他雖然成為了垃圾,但是CMS只能回收之前標記出來的垃圾對象,不會回收他們,需要等到下一次GC的時候才會回收他們。所以為了保證在CMS垃圾回收期間,還有一定的內存空間讓一些對象可以進入老年代,一般會預留一些空間。CMS垃圾回收的觸發時機,其中有一個就是當老年代內存占用達到一定比例了,就自動執行GC。

“-XX:CMSInitiatingOccupancyFaction”參數可以用來設置老年代占用多少比例的時候觸發CMS垃圾回收,JDK 1.6里面默認的值是92%

也就是說,老年代占用了92%空間了,就自動進行CMS垃圾回收,預留8%的空間給并發回收期間,系統程序把一些新對象放入老年代中。

那么如果CMS垃圾回收期間,系統程序要放入老年代的對象大于了可用內存空間,此時會如何?

這個時候,會發生Concurrent Mode Failure,就是說并發垃圾回收失敗了,我一邊回收,你一邊把對象放入老年代,內存都不夠

此時就會自動用“Serial Old”垃圾回收器替代CMS,就是直接強行把系統程序“Stop the World”,重新進行長時間的GC Roots追蹤,標記出來全部垃圾對象,不允許新的對象產生,然后一次性把垃圾對象都回收掉,完事后再恢復系統線程

3、內存碎片問題

老年代的CMS采用“標記-清理”算法,每次都是標記出來垃圾對象,然后一次性回收掉,這樣會導致大量的內存碎片產生。如果內存碎片太多,會導致后續對象進入老年代找不到可用的連續內存空間了,然后觸發Full GC

所以CMS不是完全就僅僅用“標記-清理”算法的,因為太多的內存碎片實際上會導致更加頻繁的Full GC

CMS有一個參數是“-XX:+UseCMSCompactAtFullCollection”,默認就打開,意思是在Full GC之后要再次進行“Stop the World”,停止工作線程,然后進行碎片整理,就是把存活對象挪到一起,空出來大片連續內存空間,避免內存碎片

還有一個參數是“-XX:CMSFullGCsBeforeCompaction”,這個意思是執行多少次Full GC之后再執行一次內存碎片整理的工作,默認是0,意思就是每次Full GC之后都會進行一次內存整理

觸發老年代GC的時機

1、老年代可用內存小于新生代全部對象的大小,如果沒開啟空間擔保參數,會直接觸發Full GC,所以一般空間擔保參數都會打開;

2、老年代可用內存小于歷次新生代GC后進入老年代的平均對象大小,此時會提前Full GC;

3、新生代Minor GC后的存活對象大于Survivor,那么就會進入老年代,此時老年代內存不足;

4、-XX:CMSInitiatingOccupancyFaction:老年代的已用內存大于設定的閥值,就會觸發Full GC;

5、顯示調用System.gc

ParNew + CMS帶給我們的痛點是什么

Stop the World,這個是大家最痛的一個點

無論是新生代垃圾回收,還是老年代垃圾回收,都會或多或少產生“Stop the World”現象,對系統的運行是有一定影響的。所以其實之后對垃圾回收器的優化,都是朝著減少“Stop the World”的目標去做的。

在這個基礎之上,G1垃圾回收器就應運而生了,他可以提供比“ParNew + CMS”組合更好的垃圾回收的性能

G1垃圾回收器

特點

G1垃圾回收器是可以同時回收新生代和老年代的對象的,不需要兩個垃圾回收器配合起來運作,他一個人就可以搞定所有的垃圾回收。

1、把Java堆內存拆分為多個大小相等的Region

G1也會有新生代和老年代的概念,但是只不過是**邏輯上的概念**

也就是說新生代可能包含了某些Region,老年代可能包含了某些Region。

2、可以設置一個垃圾回收的預期停頓時間

也就是說比如我們可以指定:希望G1在垃圾回收的時候,可以保證,在1小時內由G1垃圾回收導致的“Stop the World”時間,也就是系統停頓的時間,不能超過1分鐘,這樣相當于我們就可以直接控制垃圾回收對系統性能的影響

3、Region可能屬于新生代也可能屬于老年代

剛開始Region可能誰都不屬于,然后接著就分配給了新生代,然后放了很多屬于新生代的對象,接著就觸發了垃圾回收這個Region,下一次同一個Region可能又被分配了老年代了,用來放老年代的長生存周期的對象,所以其實在G1對應的內存模型中,Region隨時會屬于新生代也會屬于老年代,所以沒有所謂新生代給多少內存,老年代給多少內存這一說

實際上新生代和老年代各自的內存區域是不停的變動的,由G1自動控制

G1是如何做到對垃圾回收導致的系統停頓可控的?

其實G1如果要做到這一點,他就必須要追蹤每個Region里的回收價值,啥叫做回收價值呢?

他必須搞清楚每個Region里的對象有多少是垃圾,如果對這個Region進行垃圾回收,需要耗費多長時間,可以回收掉多少垃圾?G1通過追蹤發現,1個Region中的垃圾對象有10MB,回收他們需要耗費1秒鐘,另外一個Region中的垃圾對象有20MB,回收他們需要耗費200毫秒。

然后在垃圾回收的時候,G1會發現在最近一個時間段內,比如1小時內,垃圾回收已經導致了幾百毫秒的系統停頓了,現在又要執行一次垃圾回收,那么必須是回收上圖中那個只需要200ms就能回收掉20MB垃圾的Region;于是G1觸發一次垃圾回收,雖然可能導致系統停頓了200ms,但是一下子回收了更多的垃圾,就是20MB的垃圾

所以簡單來說,G1可以做到讓你來設定垃圾回收對系統的影響,他自己通過把內存拆分為大量小Region,以及追蹤每個Region中可以回收的對象大小和預估時間,最后在垃圾回收的時候,盡量把垃圾回收對系統造成的影響控制在你指定的時間范圍內,同時在有限的時間內盡量回收盡可能多的垃圾對象。這就是G1的核心設計思路

如何設定G1對應的內存大小?

G1對應的是一大堆的Region內存區域,每個Region的大小是一致的,默認情況下自動計算和設置的,可以給整個堆內存設置一個大小,比如說用“-Xms”和“-Xmx”來設置堆內存的大小

JVM啟動的時候,發現使用的是G1垃圾回收器(通過:用“-XX:+UseG1GC”來指定使用G1垃圾回收器),此時會自動用堆大小除以2048,JVM最多可以有2048個Region,然后Region的大小必須是2的倍數,比如說1MB、2MB、4MB之類,可以通過手動方式來指定,則是**“-XX:G1HeapRegionSize“**

剛開始的時候,默認新生代對堆內存的占比是5%,這個是可以通過“-XX:G1NewSizePercent”來設置新生代初始占比的,其實維持這個默認值即可

在系統運行中,JVM其實會不停的給新生代增加更多的Region,但是最多新生代的占比不會超過60%,可以通過“-XX:G1MaxNewSizePercent”,而且一旦Region進行了垃圾回收,此時新生代的Region數量還會減少,這些其實都是動態

新生代還有Eden和Survivor的概念?

  • G1中雖然把內存劃分為很多的 Region,但是其實還是有新生代、老年代的區分,而且新生代里還是有Eden和Survivor的劃分

  • 通過參數,“-XX:SurvivorRatio=8”,可以設置新生代中80%的Region屬于Eden,兩個Survivor各自占10%

  • 隨著對象不停的在新生代里分配,屬于新生代的Region會不斷增加,Eden和Survivor對應的Region也會不斷增加

G1的新生代垃圾回收觸發機制?

既然G1的新生代也有Eden和Survivor的區分,那么觸發垃圾回收的機制都是類似的,隨著不停的在新生代的Eden對應的Region中放對象,JVM就會不停的給新生代加入更多的Region,直到新生代占據堆大小的最大比例60%。

一旦新生代達到了設定的占據堆內存的最大大小60%,這個時候還是會觸發新生代的GC,G1就會用之前說過的復制算法來進行垃圾回收,進入一個“Stop the World”狀態,然后把Eden對應的Region中的存活對象放入S1對應的Region中,接著回收掉Eden對應的Region中的垃圾對象,但是這個過程跟之前是有區別的,因為G1是可以設定目標GC停頓時間的,也就是G1執行GC的時候最多可以讓系統停頓多長時間,可以通過“-XX:MaxGCPauseMills”參數來設定,默認值是200ms。

那么G1就會通過之前說的,對每個Region追蹤回收他需要多少時間,可以回收多少對象來選擇回收一部分的Region,保證GC停頓時間控制在指定范圍內,盡可能多的回收掉一些對象。

對象什么時候進入老年代?

可以說跟之前幾乎是一樣的,還是這么幾個條件:

1、對象在新生代躲過了很多次的垃圾回收,達到了一定的年齡了,“-XX:MaxTenuringThreshold”參數可以設置這個年齡,就會進入老年代

2、 動態年齡判定規則,如果一旦發現某次新生代GC過后,存活對象超過了Survivor的50%

大對象Region

在之前,大對象是直接進入老年代,在G1的內存模型中,G1提供了專門的Region來存放大對象,而不是讓大對象直接進入老年的Region中。

在G1中,大對象的判定規則就是一個大對象超過了一個Region大小的50%,如果每個Region是2MB,只要一個大對象超過了1MB,就會被放入大對象專門的Region中,而且一個大對象如果太大,可能會橫跨多個Region來存放在新生代、老年代回收的時候,會順帶帶著大對象Region一起回收

“JVM有哪些常用的功能”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

jvm
AI

措勤县| 庆城县| 宁城县| 建阳市| 侯马市| 剑河县| 措美县| 江都市| 林周县| 万荣县| 天等县| 遵义县| 阳曲县| 静安区| 扬州市| 上高县| 石狮市| 德州市| 开化县| 黎城县| 霸州市| 大理市| 临澧县| 铜鼓县| 沧州市| 中西区| 安阳县| 海宁市| 荔波县| 镶黄旗| 旺苍县| 新绛县| 长宁县| 芷江| 金门县| 布尔津县| 永兴县| 玛纳斯县| 北宁市| 明溪县| 措美县|