Java基礎篇之Java虛擬機(一)------內存模型和結構; 垃圾回收機制;類加載機制

點個關注,一起進步!

開始Java虛擬機篇章,主要內容包括以下三塊內容:

內存模型和結構;

垃圾回收機制;

類加載機制;

詳細內容可以參照 周志明老師的《深入理解Java虛擬機》

####################################

一,Java內存模型與JVM內存結構

JVM內存模型與內存結構是兩個截然不同的東西,大家都知道Java內存分爲堆和棧,被問到內存相關都會這樣回答,但是今天要弄清楚什麼是內存模型什麼是內存結構?

JVM內存模型簡稱(JMM)是一種抽象的概念,並不是像JVM內存結構一樣是實際存在的,而是因爲Java的多線程之間是通過共享內存進行通信的,而由於採用共享內存進行通信,在通信過程中會存在一系列如可見性、原子性、順序性等問題,而JMM就是圍繞着多線程通信以及與其相關的一系列特性而設計建立出來的的模型。

目的是解決由於多線程通過共享內存進行通信時候,存在本地內存數據不一致,,編譯器對代碼重排序,以及處理器對代碼亂序執行帶來的問題,從而保證Java程序在各種平臺下對內存的訪問都能保證效果的一致性。

JVM內存結構是一種實際存在的結構體,由Java虛擬機規範定義,是Java程序執行過程中,由JVM管理的不同數據區域,並且各個區域有其特定的功能。

(注:參考來自https://www.hollischuang.com/archives/2509)

詳細可參考https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5(Java虛擬機規範)

二,JVM垃圾回收機制

Java語言一般情況下不需要開發人員自己去處理內存問題,本身存在垃圾回收機制,那麼Java的垃圾回收機制到底是什麼?

在說明垃圾回收機制之前先說一下Java中的內存溢出以及內存泄漏的是怎麼回事?

內存溢出:out of memory,是指在程序申請內存時候,沒有足夠的空間可供提供,比如加載一個大型遊戲出現內存不足,再簡單一點就是在僅僅只有Integer大小的空間內放置Long大小的數據,那麼就會造成內存溢出。

內存泄漏:memory leak,是指程序申請到了內存,但是程序沒有在不需要內存空間時候即使釋放,造成內存空間只會一直減少,這種行爲被稱爲內存泄露,內存泄漏一次沒有關係,但是多次的內存泄漏積累的結果一定會造成內存不足。

好的我們開始從以下幾點進行逐步瞭解Java主流虛擬機hotspot的垃圾回收機制,其他類型的虛擬機感興趣的可以去做更深入瞭解。

1,什麼是垃圾?

    沒有被引用的對象在Java中就被稱爲垃圾

2,怎麼判斷垃圾?

    判斷一個對象是否成爲垃圾有以下幾種方式

        引用計數法

        爲對象添加一個引用計數器,當對象增加一個引用時計數器就加1,引用失效計數器就減1,當引用技術爲0時對象可被回收。不過,當兩個對象出現循環引用時,兩個對象的引用計數器永遠不會爲0,這樣會導致他們無法被回收。

        引用可達法

        以 GC Roots 爲起始點進行搜索,可達的對象都是存活的,不可達的對象可被回收。

        GCRoot一般爲以下幾種:

  • 虛擬機棧中局部變量表中引用的對象

  • 本地方法棧中 JNI 中引用的對象

  • 方法區中類靜態屬性引用的對象

  • 方法區中的常量引用的對象

        方法區回收

        方法區存的一般就是永久代(年老代)對象,回收起來比年輕代要更加困難所以對方法區的回收條件更加嚴格,主要也是對常量池的回收和類的卸載,滿足以下條件纔有可能被回收,但是也不一定會被回收。

  • 該類所有的實例都已經被回收,此時堆中不存在該類的任何實例。

  • 加載該類的 ClassLoader 已經被回收。

  • 該類對應的 Class 對象沒有在任何地方被引用,也就無法在任何地方通過反射訪問該類方法。

        finalize()

       finalize()是Object的方法,存在的意義看似是,當類複寫了finalize()方法,可以在對象被回收之前只調一次,來達到所謂的完成最後的功能,但是即使調用了,也不能保證方法會全部執行,可能會在方法執行過程中被回收,

3,垃圾怎麼回收

    標記清除算法

    分爲兩個階段,先標記出來要回收的區域,然後同意進行清除。

    存在問題:

        1,內存不連續,空間碎片太多

        2,標記和清除效率低

    複製算法(新生代)

    將可用內存分爲兩等份,每次使用其中一份,進行垃圾回收的時候將,存活對象複製到另一塊沒有使用的可用空間上面,然後把使用過的內存空間一次性全部清理。

    優點:簡單高效,內存連續

    不足:內存利用不充分,當對象存活率普遍較高時,由於內存只是用一半,需要多次執行。

 

    標記整理算法(老年代)

    標記整理,與上面的標記清理類似,只是標記整理不是將被標記的進行清除,而是將存活對象移動到一起,將剩餘外界的進行清理。

    

    分代收集算法

    Java會把Java堆區域分爲新生和年老代,然後根據不同的年代進行不同算法的回收。

新生代:大批會被回收,只有少量會繼續存活,所以複製算法更加合適。

老年代:被回收的機率較低,所以適合標記整理,標記清除算法。

4,Java不同類型的引用

強引用:

Object obj = new Object()

    普遍存在的引用,只要引用還在就永遠不會被回收。
軟引用:

軟引用用來描述一些還有用但是非必須的對象,系統在將要發生內存溢出之前,將會把這些對象列進回收範圍,並進行第二次回收,如果將弱引用對象及進行回收之後依然沒有足夠的內存,那麼會拋出內存異常。

弱引用

非必須對象的引用,強度要比軟引用更弱,被弱引用指向的對象只能生存到下一次垃圾回收之前。當垃圾回收器工作時候,無論內存是否足夠,都會回收掉被弱引用指向的對象。

虛引用

虛引用存在不存在沒有太大意義,僅僅是爲了這個被虛引用指向的對象在被垃圾回收器回收的時候收到一個系統通知。

三,類加載機制

什麼是類加載機制?

Class文件中的各種信息,都是需要加載到虛擬機中才能運行,虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗,轉換解析和初始化,最終形成可以被虛擬機使用的Java類型,這就是Java虛擬機的類加載機制。

類加載過程:加載-->驗證-->準備-->解析-->初始化-->使用-->卸載

雙親委派模型是什麼?

雙親委派模型(Parents Delegation Model)要求除了頂層的啓動類加載器,其餘加載器都應當有自己的父類加載器,類加載器之間通過組合關係複用。

雙親委派模型工作過程:如果一個類加載器收到類加載請求,他不會自己去加載這個類,而是請求給父類加載器去完成,每個層次的加載器都是如此,因此所有的加載任務都會傳到頂層的啓動加載類,只有在父類反饋無法完成加載任務的時候,纔會由子類自己去完成加載工作。

爲什麼要有雙親委派模型?

採用雙親委派模型使得Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係。例如類java.lang.Object,它存放在rt.jar中,無論哪個類加載器要加載這個類,最終都會委派給啓動類加載器進行加載,因此Object類在程序的各種類加載器環境中都是同一個類。相反,如果用戶自己寫了一個名爲java.lang.Object的類,並放在程序的Classpath中,那系統中將會出現多個不同的Object類,java類型體系中最基礎的行爲也無法保證,應用程序也會變得一片混亂。

有興趣可關注微信公衆號:Java成長錄

微信公衆號持續更新------------部分會在csdn同步

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章