JVM學習—-快速入門JVM

JVM的定位

JVM是運行在操作系統之上的,它與硬件沒有直接的交互。
在這裏插入圖片描述

JVM體系結構概覽

在這裏插入圖片描述

程序計數器

每個線程都有一個程序計數器,是線程私有的,就是一個指針,指向方法區中的方法字節碼(用來存儲指向下一條指令的地址,也即將要執行的指令代碼),由執行引擎讀取下一條指令,是一個非常小的內存空間,幾乎可以忽略不記。

Heap堆(Java7之前)

一個JVM實例只存在一個堆內存,堆內存的大小是可以調節的。類加載器讀取了類文件後,需要把類、方法、常變量放到堆內存中,保存所有引用類型的真實信息,以方便執行器執行。
堆內存邏輯上分爲三部分:新生+養老+永久
新生區
新生區是類的誕生、成長、消亡的區域,一個類在這裏產生,應用,最後被垃圾回收器收集,結束生命。新生區又分爲兩部分: 伊甸區(Eden space)和倖存者區(Survivor pace) ,所有的類都是在伊甸區被new出來的。倖存區有兩個: 0區(Survivor 0 space)和1區(Survivor 1 space)。當伊甸園的空間用完時,程序又需要創建對象,JVM的垃圾回收器將對伊甸園區進行垃圾回收(Minor GC),將伊甸園區中的不再被其他對象所引用的對象進行銷燬。然後將伊甸園中的剩餘對象移動到倖存0區.若倖存0區也滿了,再對該區進行垃圾回收,然後移動到1區。那如果1區也滿了呢?再移動到養老區。若養老區也滿了,那麼這個時候將產生MajorGC(FullGC),進行養老區的內存清理。若養老區執行了Full GC之後發現依然無法進行對象的保存,就會產生OOM異常“OutOfMemoryError”。

如果出現java.lang.OutOfMemoryError: Java heap space異常,說明Java虛擬機的堆內存不夠。原因有二:
(1)Java虛擬機的堆內存設置不夠,可以通過參數-Xms、-Xmx來調整。
(2)代碼中創建了大量大對象,並且長時間不能被垃圾收集器收集(存在被引用)。

棧也叫棧內存,主管Java程序的運行,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對於棧來說不存在垃圾回收問題,只要線程一結束該棧就Over,生命週期和線程一致,是線程私有的。基本類型的變量、實例方法、引用類型變量都是在函數的棧內存中分配。

方法區

1:方法區是線程共享的,通常用來保存裝載的類的元結構信息。
比如:運行時常量池+靜態變量+常量+字段+方法字節碼+在類/實例/接口初始化用到的特殊方法等。
2:通常和永久區關聯在一起(Java7之前),但具體的跟JVM的實現和版本有關。

本地方法棧Native Method Stack

它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載本地方法庫。

本地接口Native Interface

Java語言本身不能對操作系統底層進行訪問和操作,但是可以通過JNI接口調用其他語言來實現對底層的訪問。
本地接口的作用是融合不同的編程語言爲Java所用,它的初衷是融合 C/C++程序,Java誕生的時候是C/C++橫行的時候,要想立足,必須有調用C/C++程序,於是就在內存中專門開闢了一塊區域處理標記爲Native的代碼,它的具體做法是Native Method Stack中登記Native方法,在Execution Engine 執行時加載Native libraries。
目前該方法使用的越來越少了,除非是與硬件有關的應用,比如通過Java程序驅動打印機或者Java系統管理生產設備,在企業級應用中已經比較少見。因爲現在的異構領域間的通信很發達,比如可以使用Socket通信,也可以使用WebService等等,不多做介紹。

執行引擎Execution Engine

Execution Engine執行引擎負責解釋命令,提交操作系統執行。

類加載器ClassLoader

負責加載class文件,class文件在文件開頭有特定的文件標示,並且ClassLoader只負責class文件的加載,至於它是否可以運行,則由Execution Engine決定。
① 定位和導入二進制class文件
② 驗證導入類的正確性
③ 爲類分配初始化內存
④ 幫助解析符號引用.
在這裏插入圖片描述

類加載器的分類:

在這裏插入圖片描述
sun.misc.Launcher它是一個java虛擬機的入口應用。
**雙親委派機制:**某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。
通俗的講,虛擬機是根據類的全限定名來加載類的,那麼有個問題,如果同時存在兩個或多個全限定名完全一致的情況下。該如何選擇加載哪個類。這就是雙親委派機制要做的工作。
在這裏強加個知識點:比較兩個類是否“相等”,只有在這兩個類是由同一個類加載器加載的前提下才有真正的意義,否則,即使這兩個類來源於同一個class文件,被同一個虛擬機加載,只要加載他們的類加載器不同,那這兩個類就必定不相等。
雙親委派模型的好處在於Java類隨着它的類加載器一起具備了一種帶有優先級的層次關係。例如類java.lang.Object,它存在在rt.jar中,無論哪一個類加載器要加載這個類,最終都是委派給處於模型最頂端的Bootstrap ClassLoader進行加載,因此Object類在程序的各種類加載器環境中都是同一個類。相反,如果沒有雙親委派模型而是由各個類加載器自行加載的話,如果用戶編寫了一個java.lang.Object的同名類並放在ClassPath中,那系統中將會出現多個不同的Object類,程序將混亂。因此,如果開發者嘗試編寫一個與rt.jar類庫中重名的Java類,可以正常編譯,但是永遠無法被加載運行。

堆參數調優入門

Java7

在這裏插入圖片描述

Java8

JDK 1.8之後將最初的永久代取消了,由元空間取代。
在這裏插入圖片描述

堆內存調優簡介

在這裏插入圖片描述
在這裏插入圖片描述
發現默認的情況下分配的內存是總內存的“1 / 4”、而初始化的內存爲“1 / 64”
在這裏插入圖片描述
VM參數: -Xms1024m -Xmx1024m -XX:+PrintGCDetails
在這裏插入圖片描述

GC垃圾回收機制

1.什麼是“垃圾回收”機制?
當程序創建對象,數組等引用類型實體時,系統會在堆內存中爲之分配一塊內存區,對象就保存在內存區中,當內存不再被任何引用變量引用時,這塊內存就變成了垃圾,等待垃圾回收機制去進行回收。

垃圾回收機制是由垃圾收集器Garbage Collection GC來實現的,GC是後臺的守護進程。它的特別之處是它是一個低優先級進程,但是可以根據內存的使用情況動態的調整他的優先級。因此,它是在內存中低到一定限度時纔會自動運行,從而實現對內存的回收。這就是垃圾回收的時間不確定的原因。

爲何要這樣設計:因爲GC也是進程,也要消耗CPU等資源,如果GC執行過於頻繁會對java的程序的執行產生較大的影響(java解釋器本來就不快),因此JVM的設計者們選着了不定期的gc。

何爲垃圾?
Java中那些不可達的對象就會變成垃圾。那麼什麼叫做不可達?其實就是沒有辦法再引用到該對象了。主要有以下情況使對象變爲垃圾:
1.對非線程的對象來說,所有的活動線程都不能訪問該對象,那麼該對象就會變爲垃圾。
2.對線程對象來說,滿足上面的條件,且線程未啓動或者已停止。

例如:

(1)改變對象的引用,如置爲null或者指向其他對象。 
   Object x=new Object();//object1 
   Object y=new Object();//object2 
   x=y;//object1 變爲垃圾 
   x=y=null;//object2 變爲垃圾 

(2)超出作用域 
   if(i==0){ 
      Object x=new Object();//object1 
   }//括號結束後object1將無法被引用,變爲垃圾 
(3)類嵌套導致未完全釋放 
   class A{ 
      A a; 
   } 
   A x= new A();//分配一個空間 
   x.a= new A();//又分配了一個空間 
   x=null;//將會產生兩個垃圾 
(4)線程中的垃圾 
   class A implements Runnable{   
     void run(){ 
       //.... 
     } 
   } 
   //main 
   A x=new A();//object1 
   x.start(); 
   x=null;//等線程執行完後object1才被認定爲垃圾 
   這樣看,確實在代碼執行過程中會產生很多垃圾,不過不用擔心,java可以有效地處理他們。

2.垃圾回收機制的特點:
垃圾回收機制只負責回收堆內存中的對象,不會回收任何物理資源(網絡io等)。
程序無法精準控制垃圾回收的運行,垃圾回收在合適的時候進行,當對象永久性失去了引用後。系統會在合適的時候回收它所佔的內存。
在垃圾回收機制回收任何對象之前,總會調用它的finalize()方法,該方法可能使該對象重新復活(使用一個引用變量重新去引用該對象),從而導致垃圾回收機制取消回收。

3.對象在內存中的狀態?
可達狀態:當一個對象創建後,如果有一個以上的引用變量去引用它,則處於可達狀態。
可恢復狀態:如果程序中的某個對象不再有任何引用變量去引用它,它就進入了可恢復狀態,在這種狀態下,系統的垃圾回收機制準備回收該對象所佔用的內存,在回收該對象前,系統會調用它的finalize()方法實現資源清理,如果這時有引用變量進行引用時該對象,則該對象會再次變爲可達狀態,否則會變爲不可達狀態。
不可達狀態:當對象和所有引用變量的關聯都被切斷,且系統已經調用所有對象的finalize()後,對象依然沒有達到可達狀態,則該對象永久性失去引用,最後達到不可達狀態,系統回收該對象所佔有的所有資源。

4.強制垃圾回收
當對象失去引用後,程序不知曉什麼時候進行資源清理,垃圾回收,程序只能控制對象何時不再被任何引用變量引用,絕不能控制對象何時被回收。
程序無法控制java垃圾回收的時機,但是可以通知系統進行垃圾回收(強制垃圾回收),但是隻是通知,系統是否垃圾回收還是不確定。大部分時候,程序強制系統回收後總會有一些效果

強制回收方法

*System.gc()

*Runtime.getRuntime().gc()

在這裏插入圖片描述

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