自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

來說說垃圾回收怎么樣~

云計算 虛擬化
JVM 的自動內存管理,讓原本應該是開發(fā)人員去做的事情,變成了垃圾回收器來做的事情,既然是別人幫忙做的事情,那么可能就不是自己想要的,所以就需要我們了解一下垃圾回收相關的內容。

[[354376]]

本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲 。轉載本文請聯(lián)系Java極客技術公眾號。  

JVM 的自動內存管理,讓原本應該是開發(fā)人員去做的事情,變成了垃圾回收器來做的事情

既然是別人幫忙做的事情,那么可能就不是自己想要的,所以就需要我們了解一下垃圾回收相關的內容

引用計數(shù)法與可達性分析

垃圾回收,垃圾回收,那就是有的內存分配給了一些對象,但是這些對象已經用完了,那么它所占用的內存也就應該該釋放掉了,卻還沒有釋放

那么,這里就有個問題:該如何確定一個對象用完了呢?

其中一種方法就是引用計數(shù)法

引用計數(shù)法就是給每個對象添加一個引用計數(shù)器,來統(tǒng)計指向該對象的引用個數(shù)

比如:如果有一個引用,被賦值為某一個對象,那么這個對象的引用計數(shù)器就 +1 ,如果一個指向這個對象的引用,被賦值為了其他的值,那么這個對象的引用計數(shù)器就 -1 ,這樣如果這個對象的引用計數(shù)器為 0 ,我們就可以認為這個對象已經使用完畢,它所占用的內存空間可以回收掉了

這種方案聽上去無懈可擊,但是有一個致命的漏洞,就是沒辦法處理循環(huán)引用的問題

比如說: A 和 B 互相引用,除此之外也沒有其他的引用指向 A 或者 B ,在這種情況下,其實 A 和 B 所占用的內存就可以釋放掉了,但是因為它們互相都有引用,所以此時的引用計數(shù)器并不為 0 ,在這種情況下,就不能對它們進行回收

現(xiàn)在只是兩個對象,如果再來兩個,再來兩個,這樣循環(huán)引用的對象多了之后,就會造成內存泄露

基于引用計數(shù)法的弊端,當前 JVM 主流的垃圾回收器采取的是可達性分析算法

這個算法本質就是將一系列的 GC Roots 作為初始的存活對象合集( live set ),然后從這個合集出發(fā),探索所有能夠被該集合引用到的對象,并把這些對象加入到集合中來,這個過程就叫做標記( mark ),遍歷到最后,沒有被探索到的對象就是可以回收的對象

那么什么是 GC Roots 嘞?一般包括(但不限于)以下幾種:

  • Java 方法棧楨中的局部變量
  • 已加載類的靜態(tài)變量
  • JNI handles
  • 已啟動并且沒有停止的 Java 線程

剛才說因為引用計數(shù)法存在循環(huán)引用的問題,所以目前主流垃圾回收器選用的都是可達性分析法,也就是說,它解決了循環(huán)引用問題,其實這一點也比較好理解,雖然 A 和 B 相互引用,但是這個時候從 GC Roots 開始出發(fā),是沒有辦法到達 A 和 B 的,那么就不會把它們放到存活對象合集之中,自然也就會被回收掉

但是在實際中還是會有問題的,比如:在多線程環(huán)境下,就會有其他線程更新已經訪問過的對象中的引用,但是是多線程并行的嘛,這個時候可達性分析法已經把這個引用設置成了 null ,或者這個對象還在使用,但可達性分析法把它標記為了沒有被訪問過的對象,被回收掉了,這種情況可能直接導致 JVM 崩潰掉

Stop-the-world & safepoint

既然可達性分析法也有自己的一些缺陷,總得有解決方案吧?比較暴力的一種方法就是 Stop-the-world ,估計聽名字也能知道,就是讓全世界都停下來,也就是說,在進行垃圾回收的時候,其他所有非垃圾回收線程的工作都需要停下來,先讓垃圾回收器工作完畢再說。這就是所謂的暫停時間( GC pause )

Stop-the-world 是通過安全點( safepoint )機制來實現(xiàn)的。啥意思嘞?咱先想個場景,現(xiàn)在你敲代碼敲的特別開心,又有思路,狀態(tài)又好,美滋滋的正在工作,突然毫無緣由的就讓你現(xiàn)在不準敲代碼,你會不會不開心?好不容易思路來了對吧,就一點兒理由都不給的就讓我停下,不合理吧?

同樣的場景,一個線程現(xiàn)在跑的特別 happy ,而且再有一秒鐘就完成了任務,這個時候 JVM 收到了 Stop-the-world 請求,二話不說就把所有的線程給停掉,不太好吧?那么這個時候安全點( safepoint )機制就登場了。有了安全點機制,當 JVM 收到 Stop-the-world 請求的時候,它就會等待所有的線程都達到安全點,才允許請求 Stop-the-world 的線程進行獨占的工作

那么,什么時候是安全點呢?舉個例子來說:當 Java 程序通過 JNI 執(zhí)行本地代碼時,如果這段代碼不訪問 Java 對象,不調用 Java 方法,不返回到原 Java 方法,那么 Java 虛擬機的堆棧就不會發(fā)生改變,那這段本地代碼就可以作為一個安全點。只要不離開這個安全點, JVM 就可以在垃圾回收的同時,繼續(xù)運行這段本地代碼

因為本地代碼需要通過 JNI 的 API 來完成上述三個操作,因此 JVM 只需要在 API 的入口處進行安全點檢測( safepoint poll ),看看有沒有其他線程請求停留在安全點這里,就可以在必要的時候掛起當前線程

垃圾回收的三種方式

當標記好存活的對象之后,就可以進行垃圾回收了

主流的垃圾回收方式,可以分為三種:清除( sweep ),壓縮( compact ),復制( copy )

清除,就是把死亡對象所占據(jù)的內存標記成空閑內存,并把它記錄在一個空閑列表( free list )中,當需要新建對象的時候,就直接在空閑列表中尋找空閑內存,劃分給新建的對象就完了

但是這里會產生一個問題,因為死亡的對象所占據(jù)的內存可能是隨機的,回收完畢之后,內存就是碎片化的,如果此時有對象申請一塊連續(xù)的內存空間,盡管碎片化的內存空間是夠用的,也沒辦法進行分配

壓縮,就是把存活的對象聚集到內存區(qū)域的起始位置,這樣就可以留下一段連續(xù)的內存空間。這樣去做的話,可以解決內存碎片化的問題,代價就是壓縮算法帶來的性能開銷

復制,就是把內存區(qū)域分成兩等分,分別用兩個指針 from 和 to 來維護,并且只是用 from 指針指向的內存區(qū)域來分配內存。當進行垃圾回收時,就把存活的對象復制到 to 指針指向的內存區(qū)域中,并且交換 from 指針和 to 指針的內容。

復制這種方式也可以解決內存碎片化的問題,但是它的缺點也是比較明顯的,因為把內存區(qū)域分成了兩等分嘛,那利用率就比較低咯,最高也是 50% 了,不能再高了

垃圾回收在 JVM 中的應用

上面說的三種垃圾回收方式是理論上的,那么在 JVM 中是如何應用的呢?

這就先要來了解下 JVM 的堆劃分,大概就是這樣子:

JVM 將堆劃分為新生代和老年代,在新生代中又劃分為 Eden 區(qū),還有兩個大小相同的 Survivor 區(qū)

當程序調用 new 指令時,會在 Eden 區(qū)中劃出一塊作為存儲對象的內存,但是因為堆空間是線程共享的,所以在這里面劃分空間的話就需要同步,要不然出現(xiàn)了兩個對象共用一段內存,那不就該打架了嘛

JVM 為了避免兩個對象打架的事情發(fā)生,就讓每個線程向 JVM 申請一段連續(xù)的內存,來作為線程私有的 TLAB ( Thread Local Allocation Buffer ,對應虛擬機參數(shù) -XX:+UseTLAB ,默認開啟的)

Eden 區(qū)一直進行分配,總有空間分配完畢的時候,該怎么辦?此時 JVM 就會觸發(fā)一次 Minor GC ,來收集新生代的垃圾,存活下來的對象就會被送到 Survivor 區(qū)

在圖中可以看到, Survivor 區(qū)有兩個,一個是 from ,一個是 to ,其中 to 指向的 Survivor 區(qū)是空的

當發(fā)生 Minor GC 時, Eden 區(qū)和 from 指向的 Survivor 區(qū)中的存活對象會被復制到 to 指向的 Survivor 區(qū),然后交換 from 和 to 指針,這樣就保證了下一次 Minor GC 時, to 指向的 Survivor 區(qū)還是空的

同時 JVM 會記錄 Survivor 區(qū)的對象一共被來回復制了幾次,如果一個對象被復制的次數(shù)為 15 (對應虛擬機參數(shù) -XX:+MaxTenuringThreshold ),這個對象就會被晉升( promote )到老年代

那么在發(fā)生 Minor GC 時,采用哪種垃圾回收方式會比較好一些呢?采用復制方式,也就是 標記-復制 算法會好一些。為什么呢?因為在新生代中,大部分的 Java 對象只存活一小段時間,那么我們就可以采用耗時比較短的垃圾回收算法,讓大部分的垃圾都能在新生代被回收掉。使用 標記-復制 算法的話,理想情況下就是 Eden 區(qū)中的對象基本都死亡了,那么需要復制的數(shù)據(jù)非常少,此時這種算法的優(yōu)勢就被極大的體現(xiàn)了出來

 

責任編輯:武曉燕 來源: Java極客技術
相關推薦

2021-01-09 14:03:37

Vrrp協(xié)議網關

2022-01-20 10:34:49

JVM垃圾回收算法

2017-08-04 10:53:30

回收算法JVM垃圾回收器

2021-01-04 10:08:07

垃圾回收Java虛擬機

2022-03-21 11:33:11

JVM垃圾回收器垃圾回收算法

2014-02-18 11:24:07

云計算PaaS

2021-11-05 15:23:20

JVM回收算法

2020-12-14 11:35:22

SPI Java機制

2010-12-13 11:14:04

Java垃圾回收算法

2023-08-08 10:29:55

JVM優(yōu)化垃圾回收

2023-12-19 21:52:51

Go垃圾回收開發(fā)

2009-06-25 17:48:24

Java垃圾回收

2020-07-09 08:26:42

Kubernetes容器開發(fā)

2021-03-03 08:13:57

模式垃圾回收

2023-06-30 08:23:36

Spring!SolonJavalin

2019-07-19 15:42:57

Hadoop大數(shù)據(jù)YuniKorn

2018-04-24 14:34:54

機器學習機器人互聯(lián)網

2015-07-23 11:49:31

程序猿

2024-08-20 16:27:54

2015-07-06 10:14:25

Java垃圾回收實戰(zhàn)
點贊
收藏

51CTO技術棧公眾號