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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

jvm_垃圾收集算法讲解(一)

發布時間:2024/4/13 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jvm_垃圾收集算法讲解(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
GC本質上就是垃圾回收的概念,而是JVM幫你去做這個事情,剛才我們看的代碼是rabbitmq內部幫你實現的主從選舉包括投票,包括分發,包括連接重新建,但是如果后期我們講RocketMQ,或者是KAFKA的時候,那個事情咱們就不需要做了,KAFKA是用zookeeper幫你去協調了,然后rocketmq是用nameserver的機制自動幫你協調了,當然每種MQ都有自己的優勢,市面上最好的是之前講過的activemq,然后性能比較好的我們之前也研究過ZeroMQ,雖說他的性能比較好,斷點就掛了,KAFKA也是一樣的,但是都是基于內存的,其實要保證高可用,數據不丟失,那無非是rabbitmq和RocketMQ這種方案,由于RocketMQ已經閉源了,如果你想實現數據不丟失,恢復能力也特別強,只要你整個集群別爆炸了,就算是恢復不了了,也照樣可用,所以保證可靠性,尤其是我們做支付這塊,所以要選的話只能選Rabbitmq,這是一個技術選型,垃圾回收有很多種算法,最古老的引用技術法,標記壓縮法,復制算法,分代,分區的思想 引用計數法: 這是比較古老而經典的垃圾收集算法,其核心就是對象被其他所引用時計數器加1,而引用失效的時候減1,但是這種碰到循環引用的時候就無法處理了,這個對象被引用就加1,失去引用就減1,但是有一種是循環引用遞歸,父類引用子類對象,JAVA基礎的API操作的時候,包括有一種繼承,引用來引用去的,這種計數法就有bug,而且每次加減的時候要保證對象的原子性,內部肯定有算法的保證,性能也不是特別好,有的時候加加減減就成負數了,然后在古老一點的就是標記清除法.標記清除法:就是把標記和清除分為兩個階段去做,子啊內存中是兩個階段去操作這個對象,當然這種方式也是有非常大的弊端的,就是存在空間碎片的問題,垃圾回收后空間不是連續的,不連續的內存空間的工作效率低于連續的內存空間,這種方式叫做標記清除法,比如在內存中有好多好多的內存對象,然后現在我就采用標記清除法去做,我把所有被標記的元素,這四塊東西統一都被我標記,我的GC就去找被標記的對象,去進行一個clear的操作,但是會有一個問題,這種JVM垃圾收集包括打印這種東西,它不是非常非常準確的,標記清除法是什么概念呢,正常的時候清除的時候會把這4M內容給釋放,但是GC的時候不會百分百的去清空這1M的空間,它可能清空0.9999M,這個咱們有目共睹的,有很多東西并不是那么絕對的,尤其是清理內存這一塊,所以它會存在一個內存的碎片,這里就會有一個盲點,沒法去存數據,就造成內存不連續,所以這是比較早期的兩種方式,一種是計數法,還有一種就是標記清除法,這兩種方式都不是特別好的,由于說有這么兩種方式,只有之前的不好的,所以你才會有一種更好的方案,之后就有兩套比較不錯的方案,第一種不錯的方案是復制算法,第二種不錯的方案是標記壓縮法,這兩種方案它分別應用在JVM的新生代和老年代中 復制算法:核心思想就是將內存分為兩塊,每次只使用其中一塊,在垃圾回收的時候,將正在使用的內存對象復制到未被使用的內存塊中去,然后把第一塊給清除,然后反復去交換兩個角色,去完成垃圾的收集,這種復制算法是非常好的,JAVA中新生代的from和to空間就是使用這種算法,或者叫s0和s1區,相互轉換角色.標記壓縮法:它是在標記清除的方法之上,做了一個優化,他叫做標記壓縮法,其實更全面的說叫做標記壓縮清除法,把存活的對象壓縮到內存的一端,然后進行垃圾清理,這樣就好玩了,這樣就解決了之前的問題了,原先有兩塊對象,把所有被標記的對象壓縮到另一端,然后我就劃清界限,然后我把這塊整體的去clear,這樣就不會產生空間碎片的問題,標記完了壓縮到一端,然后再整體清空,為什么新生代和老年代使用不同的算法,為什么新生代使用復制算法老年代使用標記清除壓縮法,你想一想它為什么要這么去做,為什么新生代使用復制算法,而老年代使用標記壓縮法,為什么JVM分成兩塊,為什么新生代和老年代不都使用復制算法,或者都是用標記壓縮法,原因是新生代它太頻繁了,新生代它里面的對象死的快,存活的少,比如100個對象只有3,4個存活的,剩下的都是要被GC的,所以要大塊的空間轉換,整體的clear,并不是一條條的delete,所以性能是比較高的,而老年代的大部分都經過了老年代的GC了,老年代比新生代要高出太多了,至少是15個等級的,經歷過15次GC的,所以老年代要經過清空的時候,一般都是很少很少的一部分,100個可能清空一兩個,內存操作盡量最少化一些,來提高整個JVM的性能,這個就是關于垃圾回收算法比較經典的 其實剛才所說的就是一個分代的算法,把不同的算法去提升我們整體的內存的性能了,對于新生代和老年代來講,他就是一個分代的算法,新生代回收的頻率比較高,每次耗時都很短,老年代回收的頻繁非常低,但是耗時很長,所以我們要盡量的去減少老年代的GC,老年代的GC會嚴重的影響你程序的性能,我們新生代和老年代一劃分,是很重要很關鍵的一個問題,新生代怎么劃分,新生代和老年代怎么區劃分,這個是和JVM調優,和系統的性能是有很大的影響的,所以所很重要,并不是你代碼寫好了應用程序不調優,代碼優化多好怎樣怎樣,還是和整個的硬件,整個的JVM,整個的軟件,跟你的訪問量,跟等等一系列的因數,所以你如果想做一個負責人的話,但是現在其實分的很細,咱們有運維,有架構師,各司其職,對于分區算法,ORACLE收購JAVA之后,去做了一個嘗試分區算法:主要就是將整個內存分為N多個獨立的空間,每個空間都可以獨立的使用,這樣細粒度的控制一次GC的時候回收一小塊空間,就好像一個網格一樣,如果你數據都存在在這塊區域,我就整體的去清理這塊區域,然后其他的不變,這樣的好處是減少GC的停頓,比如新生代的話,你把數據copy到另一塊的空間,GC的停頓的意思就是系統停頓,GC介入系統停頓,你就說程序在這里跑的好好的,我系統級的JVM去垃圾收集,垃圾收集通知系統你得停一會,我這邊把垃圾都收集玩之后自己去玩,如果GC只是介入內存中的一小塊的話,那對于其他模塊不影響的話,也讓我們的系統性能也是比較好的,它是這么一種做法,這有點想Map中的分段鎖的概念,所以這塊包括現在的JDK1.8來講,他這種分區算法是應用了,但是性能并不見得特別特別好,都是不斷的在改進,JDK1.9,JDK2.0的時候也許就成熟了這種算法,我們以后垃圾回收的事什么都不用管了,基本上什么也不用管了,而且JVM參數什么都可以去調整,JVM的架構整體都采用分區的話,你就不用考慮調優的事了,人家內部就幫你完成了 GC的停頓,是GC介入我們的系統,要理解這個停頓是什么概念,垃圾收集器的任務是識別和回收垃圾對象進行內存清理,大部分的情況下,會要求系統進入一個停頓的狀態,停頓的目的是終止所有的應用線程,只有這樣才不會有新的垃圾產生,同時停頓也保證了系統狀態在某一瞬間的一致性,也有益于更好的標記垃圾對象,在這個時間點GC的時候,都會產生一個停頓的現象,這個就是垃圾收集會產生一個停頓現象. 新生代和老年代默認是經過15次會晉級,到底有沒有什么閥值,對象首次創建會被放置在新生代的eden區,就是我第一次new的時候我會把它放在這里,如果沒有GC的介入的話,對象就不會離開Eden區,那么Eden的對象如何進入老年代呢,就是eden區直接進入老年代,一般來講,只要對象的年齡達到一定的大小,經過一次GC就會進入s0或者s1區,這樣進入之后他的年齡也會增長,年齡達到一定的大小,就會自動的離開年輕代,就直接進入老年代,對象的年齡是由對象經歷GC的次數決定的,新生代每次GC之后,如果對象沒有被垃圾回收器回收,年齡就加1,新生代每次GC的時候,每次還被別的對象引用著,那就是沒有被GC回收,那我就把對象的年齡加1,然后虛擬機也提供一些參數來控制GC的大小,超過這個閥值就會晉升到老年代,默認有一個參數 -XX:MaxTenuringThreshold,就是一個對象從出生到最后盡力過15次GC還沒有被垃圾收集回去,默認情況下會進入到老年代,當然這個值你就會去設置,它這個參數就是 -XX:PretenureSizeThreshold,通過-XX也就知道它是一個系統級別的參數,然后另外還有幾點你是需要知道的,大對象剛實例化的時候,無法裝入Eden區的時候,也會直接進入到老年代,同理如果老年代也裝不進去呢,超出內存會報exception,會報OOM內存溢出,JVM里有個參數可以設置對象的大小超過在指定的大小之后,直接進入到老年代,這些參數也是都有設置的, -XX:PretenureSizeThreshold,所以JVM的調優參數是很多的

對象初始化的時候要在Eden區,最大的內存分配64M,打印一下情況,for循環是5次,每次申請1M的內存

之前我們都是加了-XX:UseSerialGC,默認都是使用串行垃圾收集器,這種是PSYoungGen,這種是JDK默認自帶的垃圾收集器,我們的數據是新生代,老年代,基本上我們used是0%,也就是我們的對象都用到了新生代

測試對象進入老年代

實例化出來一個hashMap,循環5次,每次申請1M的內存,內存不釋放,直接放在map里面,接著每次申請好多好多M,6000次的循環,6000乘以1M,6個G,我要做6個G的內存,這樣的話我要配置這個參數之后,這個參數我初始化是一個G,最大也是一個G,然后使用的是串行的垃圾收集器,然后我進入老年代的次數是15,對象進入老年代的次數是15,然后我去打印一下詳細信息,后來這個程序的目的是讓程序直接停

我們在走完這5次之后,我把數據都放在map里了,map一直都是被引用著的,map是一直都被數據引用著的,除非后面的map等于空,手動的去把Map去清空,才會有一些效果,我在這里不清空,就表示我這個對象一直被引用著,可能一開始的時候就進入到新生代,它不經歷GC,下面我模擬了很大的6個G的不斷的生產和消亡,我現在是6個G,但是現在我的上限才是1G,也就是它會經歷n多次不斷的GC,我們要看的結果是15次之后發生變化,既然這塊代碼讀懂了之后

上面這塊和下面這塊完全不同,DefNew是新生代存的嗎,然后16次的時候新生代就直接降為0了,也就是新生代一直引用的5M就直接扔到老年代了,新生代到16次的時候才降到0

5次放到老年代,

第5次的時候新生代就清空了,我們怎么去升級怎么去晉級,根據這個參數做調整就可以,正常來講這個參數很少去配置,采用它默認的15次就挺好,不是你會這個就瞎配置,除非你有那種特殊的需求

?

總結

以上是生活随笔為你收集整理的jvm_垃圾收集算法讲解(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

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