<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊到JVM(還怕面試官問(wèn)JVM嗎?)

          共 1746字,需瀏覽 4分鐘

           ·

          2020-11-27 14:08

          點(diǎn)擊上方"程序員歷小冰",選擇“置頂或者星標(biāo)”

          ? ?你的關(guān)注意義重大!


          前言?
          無(wú)論什么級(jí)別的Java從業(yè)者,JVM都是進(jìn)階時(shí)必須邁過(guò)的坎。不管是工作還是面試中,JVM都是必考題。如果不懂JVM的話,薪酬會(huì)非常吃虧(近70%的面試者掛在JVM上了)。

          • 請(qǐng)你談?wù)勀銓?duì)JVM的理解?

          • JVM類加載器是怎么樣的?有幾種?

          • 什么是OOM,什么是StackOverFlowError? 怎么分析?

          • JVM常用調(diào)優(yōu)參數(shù)有哪寫(xiě)?

          • GC有幾種算法?分別是怎么執(zhí)行的?

          • 你知道JProfiler嗎,怎么分析Dump文件?


          第一次看到這些真真實(shí)實(shí)的面試題的時(shí)候,我~



          這都什么玩意???????
          經(jīng)過(guò)一段時(shí)間的研究!!接下來(lái),我將以大白話從頭到尾給大家講講Java虛擬機(jī)!!
          不對(duì)的地方還請(qǐng)大家指正~

          1、什么是JVM?在哪??


          JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫(xiě),JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。

          百度的解釋云里霧里,對(duì)于我們Java程序員,說(shuō)白了就是:
          • JVM本質(zhì)上是一個(gè)程序,它能識(shí)別.class?字節(jié)碼文件(里面存放的是我們對(duì).java編譯后產(chǎn)生的二進(jìn)制代碼),并且能夠解析它的指令,最終調(diào)用操作系統(tǒng)上的函數(shù),完成我們想要的操作!


          • 關(guān)于Java語(yǔ)言的跨平臺(tái)性,就是因?yàn)镴VM,我們可以將其想象為一個(gè)抽象層,只要這個(gè)抽象層JVM正確執(zhí)行了.class文件,就能運(yùn)行在各種操作系統(tǒng)之上了!這就是一次編譯,多次運(yùn)行


          對(duì)于JVM的位置
          • JVM是運(yùn)行在操作系統(tǒng)之上的,它與硬件沒(méi)有直接的交互






          2、JVM、JRE、JDK 的關(guān)系?


          JDK(Java Development Kit):Java開(kāi)發(fā)工具包

          JRE(Java Runtime Environment):Java運(yùn)行環(huán)境

          JDK = JRE + javac/java/jar 等指令工具

          JRE = JVM + Java基本類庫(kù)






          3、JVM體系結(jié)構(gòu)?

          Java虛擬機(jī)主要分為五大模塊:

            • ?類裝載器子系統(tǒng)

            • 運(yùn)行時(shí)數(shù)據(jù)區(qū)

            • 執(zhí)行引擎

            • 本地方法接口

            • 垃圾收集模塊

          ????

          ????

          • ?

          • 方法區(qū)是一種特殊的堆

          • 棧里面不會(huì)有垃圾,用完就彈出了,否則阻塞了main方法

          • 垃圾幾乎都在堆里,所以JVM性能調(diào)優(yōu)%99都針對(duì)于堆



          4、三種JVM(了解)?


          Sun公司?HotSpot(我們都用的這個(gè))


          BEA公司?JRockit
          IBM公司?J9 VM

          5、類加載器??


          作用:加載.Class字節(jié)碼文件

          1、回顧new對(duì)象的過(guò)程

          public?class?Student?{
          ????//私有屬性
          ????private?String?name;

          ????//構(gòu)造方法
          ????public?Student(String?name)?{
          ????????this.name?=?name;
          ????}
          }

          類是模板、模板是抽象的;對(duì)象是具體的,是對(duì)抽象的實(shí)例化

          //運(yùn)行時(shí),JVM將Test的信息放入方法區(qū)
          public?class?Test{
          ????//main方法本身放入方法區(qū)
          ??public?static?void?main(String[]?args){
          ????????//s1、s2、s3為不同對(duì)象
          ????????Student?s1?=?new?Student("zsr");??//引用放在棧里,具體的實(shí)例放在堆里
          ????????Student?s2?=?new?Student("gcc");
          ????????Student?s3?=?new?Student("BareTH");
          ????????System.out.println(s1.hashCode());
          ????????System.out.println(s2.hashCode());
          ????????System.out.println(s3.hashCode());
          ????????//class1、class2、class3為同一個(gè)對(duì)象
          ????????Class?class1?=?s1.getClass();
          ????????Class?class2?=?s2.getClass();
          ????????Class?class3?=?s3.getClass();
          ????????System.out.println(class1.hashCode());
          ????????System.out.println(class2.hashCode());
          ????????System.out.println(class3.hashCode());
          ????}
          }
          根據(jù)結(jié)果,我們發(fā)現(xiàn):

            • ?s1、s2、s3的hashcode是不同的,因?yàn)槭侨齻€(gè)不同的對(duì)象,對(duì)象是具體的



            • class1、class2、class3的hashcode是相同的,因?yàn)檫@是類模板,模板是抽象的

          ????

          我們畫(huà)圖分析以下new一個(gè)對(duì)象的流程
          1. 首先Class Loader讀取字節(jié)碼.class文件,加載初始化生成Student模板類


          2. 通過(guò)Student模板類new出三個(gè)對(duì)象


          ?
          那么Class Loader具體是怎么執(zhí)行我們的.class字節(jié)碼文件呢,這就引出了我們類加載器~


          2、類加載器的類別

          我們編寫(xiě)這樣一個(gè)程序



          根據(jù)返回結(jié)果,我們來(lái)講解以下三種加載器:

          級(jí)別從高到底

          1.啟動(dòng)類(根)加載器:BootstrapClassLoader
            1. ?c++編寫(xiě),加載java核心庫(kù)?java.*,構(gòu)造拓展類加載器應(yīng)用程序加載器

            2. 根加載器加載拓展類加載器,并且將拓展類加載器的父加載器設(shè)置為根加載器

            3. 然后再加載應(yīng)用程序加載器,應(yīng)將應(yīng)用程序加載器的父加載器設(shè)置為拓展類加載器

            4. 由于引導(dǎo)類加載器涉及到虛擬機(jī)本地實(shí)現(xiàn)細(xì)節(jié),我們無(wú)法直接獲取到啟動(dòng)類加載器的引用;這就是上面那個(gè)程序我們第三個(gè)結(jié)果為null的原因。

            5. 加載文件存在位置



          ????
          2. 拓展類加載器:PlatformClassLoader
            1. java編寫(xiě),加載擴(kuò)展庫(kù),開(kāi)發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。

            2. java9之前為ExtClassloader,Java9以后改名為PlatformClassLoader

            3. ? ??加載文件存在位置

          ?


          3.應(yīng)用程序加載器:AppClassLoader
          ????? ? ? ?1.java編寫(xiě),加載程序所在的目錄
          ????

            ????2. 是Java默認(rèn)的類加載器


          4.用戶自定義類加載器:CustomClassLoader
          ????????????1.java編寫(xiě),用戶自定義的類加載器,可加載指定路徑的class文件


          6、雙親委派機(jī)制?

          1、什么是雙親委派機(jī)制

            • ?類加載器收到類加載的請(qǐng)求


            • 將這個(gè)請(qǐng)求向上委托給父類加載器去完成,一直向上委托,直到根加載器BootstrapClassLoader


            • 根加載器檢查是否能夠加載當(dāng)前類,能加載就結(jié)束,使用當(dāng)前的加載器;否則就拋出異常,通知子加載器進(jìn)行加載;自加載器重復(fù)該步驟。

          ????

          2、作用

          舉個(gè)例子:我們重寫(xiě)以下java.lang包下的String類



          發(fā)現(xiàn)報(bào)錯(cuò)了,這就是雙親委派機(jī)制起的作用,當(dāng)類加載器委托到根加載器的時(shí)候,String類已經(jīng)被根加載器加載過(guò)一遍了,所以不會(huì)再加載,從一定程度上防止了危險(xiǎn)代碼的植入!!


          作用總結(jié)
            1.? 防止重復(fù)加載同一個(gè).class。通過(guò)不斷委托父加載器直到根加載器,如果父加載器加載過(guò)了,就不用再加載一遍。保證數(shù)據(jù)安全。


            2. 保證系統(tǒng)核心.class,如上述的String類不能被篡改。通過(guò)委托方式,不會(huì)去篡改核心.class,即使篡改也不會(huì)去加載,即使加載也不會(huì)是同一個(gè).class對(duì)象了。不同的加載器加載同一個(gè).class也不是同一個(gè)class對(duì)象。這樣保證了class執(zhí)行安全。


          ????

          7、沙箱安全機(jī)制?

          這里引用了這篇博文引用鏈接,了解即

          什么是沙箱?

          Java安全模型的核心就是Java沙箱(sandbox)


            1. 沙箱是一個(gè)限制程序運(yùn)行的環(huán)境。沙箱機(jī)制就是將 Java 代碼限定在虛擬機(jī)(JVM)特定的運(yùn)行范圍中,并且嚴(yán)格限制代碼對(duì)本地系統(tǒng)資源訪問(wèn),通過(guò)這樣的措施來(lái)保證對(duì)代碼的有效隔離,防止對(duì)本地系統(tǒng)造成破壞。


            沙箱主要限制系統(tǒng)資源訪問(wèn),系統(tǒng)資源包括CPU、內(nèi)存、文件系統(tǒng)、網(wǎng)絡(luò)。不同級(jí)別的沙箱對(duì)這些資源訪問(wèn)的限制也可以不一樣。


          所有的Java程序運(yùn)行都可以指定沙箱,可以定制安全策略。

          java中的安全模型演進(jìn)

          在Java中將執(zhí)行程序分成本地代碼遠(yuǎn)程代碼兩種
          • 本地代碼可信任,可以訪問(wèn)一切本地資源。

          • 遠(yuǎn)程代碼不可信信在早期的Java實(shí)現(xiàn)中,安全依賴于沙箱 (Sandbox) 機(jī)制。

          如下圖所示



          如此嚴(yán)格的安全機(jī)制也給程序的功能擴(kuò)展帶來(lái)障礙,比如當(dāng)用戶希望遠(yuǎn)程代碼訪問(wèn)本地系統(tǒng)的文件時(shí)候,就無(wú)法實(shí)現(xiàn)。
          因此在后續(xù)的?Java1.1?版本中,針對(duì)安全機(jī)制做了改進(jìn),增加了安全策略,允許用戶指定代碼對(duì)本地資源的訪問(wèn)權(quán)限。
          如下圖所示
          Java1.2版本中,再次改進(jìn)了安全機(jī)制,增加了代碼簽名
          • 不論本地代碼或是遠(yuǎn)程代碼,都會(huì)按照用戶的安全策略設(shè)定,由類加載器加載到虛擬機(jī)中權(quán)限不同的運(yùn)行空間,來(lái)實(shí)現(xiàn)差異化的代碼執(zhí)行權(quán)限控制。

          如下圖所示
          ?

          當(dāng)前最新的安全機(jī)制實(shí)現(xiàn),則引入了域 (Domain)?的概念。
          • 虛擬機(jī)會(huì)把所有代碼加載到不同的系統(tǒng)域應(yīng)用域

          • 系統(tǒng)域部分專門負(fù)責(zé)與關(guān)鍵資源進(jìn)行交互

          • 應(yīng)用域部分則通過(guò)系統(tǒng)域的部分代理來(lái)對(duì)各種需要的資源進(jìn)行訪問(wèn)。

          • 虛擬機(jī)中不同的受保護(hù)域 (Protected Domain),對(duì)應(yīng)不一樣的權(quán)限 (Permission)。存在于不同域中的類文件就具有了當(dāng)前域的全部權(quán)限,如下圖所示

          組成沙箱的基本組件

          1.?字節(jié)碼校驗(yàn)器(bytecode verifier)

          確保Java類文件遵循Java語(yǔ)言規(guī)范。這樣可以幫助Java程序?qū)崿F(xiàn)內(nèi)存保護(hù)。但并不是所有的類文件都會(huì)經(jīng)過(guò)字節(jié)碼校驗(yàn),比如核心類(如上述java.lang.String)。

          2.?類裝載器(class loader)

          其中類裝載器在3個(gè)方面對(duì)Java沙箱起作用
          • 它防止惡意代碼去干涉善意的代碼;

          • 它守護(hù)了被信任的類庫(kù)邊界;

          • 它將代碼歸入保護(hù)域,確定了代碼可以進(jìn)行哪些操作。

          虛擬機(jī)為不同的類加載器載入的類提供不同的命名空間,命名空間由一系列唯一的名稱組成,每一個(gè)被裝載的類將有一個(gè)名字,這個(gè)命名空間是由Java虛擬機(jī)為每一個(gè)類裝載器維護(hù)的,它們互相之間甚至不可見(jiàn)。
          類裝載器采用的機(jī)制是雙親委派模式。


          1. 從最內(nèi)層JVM自帶類加載器開(kāi)始加載,外層惡意同名類得不到加載從而無(wú)法使用;


          2. 由于嚴(yán)格通過(guò)包來(lái)區(qū)分了訪問(wèn)域,外層惡意的類通過(guò)內(nèi)置代碼也無(wú)法獲得權(quán)限訪問(wèn)到內(nèi)層類,破壞代碼就自然無(wú)法生效。


          • 存取控制器(access controller):存取控制器可以控制核心API對(duì)操作系統(tǒng)的存取權(quán)限,而這個(gè)控制的策略設(shè)定,可以由用戶指定。

          • 安全管理器(security manager):是核心API和操作系統(tǒng)之間的主要接口。實(shí)現(xiàn)權(quán)限控制,比存取控制器優(yōu)先級(jí)高。

          • 安全軟件包(security package):java.security下的類和擴(kuò)展包下的類,允許用戶為自己的應(yīng)用增加新的安全特性,包括:


            1. 安全提供者

            2. 消息摘要

            3. 數(shù)字簽名

            4. 加密

            5. 鑒別


          8、Native本地方法接口?

          JNI:Java Native Interface

          本地接口的作用是融合不同的編程語(yǔ)言為Java所用,它的初衷是融合C/C++程序


          native:凡是帶native關(guān)鍵字的,說(shuō)明java的作用范圍達(dá)不到了,會(huì)去調(diào)用底層c語(yǔ)言的庫(kù)!進(jìn)入本地方法棧,調(diào)用本地方法接口JNI,拓展Java的使用,融合不同的語(yǔ)言為Java所用
            • Java誕生的時(shí)候C、C++橫行,為了立足,必須要能調(diào)用C、C++的程序


            • 于是在內(nèi)存區(qū)域中專門開(kāi)辟了一塊標(biāo)記區(qū)域:Native Method Stack,登記Native方法


            • 最終在執(zhí)行引擎執(zhí)行的的時(shí)候通過(guò)JNI(本地方法接口)加載本地方法庫(kù)的方法


          目前該方法使用的越來(lái)越少了,除非是與硬件有關(guān)的應(yīng)用,比如通過(guò)Java程序驅(qū)動(dòng)打印機(jī)或者Java系統(tǒng)管理生產(chǎn)設(shè)備,在企業(yè)級(jí)應(yīng)用中已經(jīng)比較少見(jiàn)。因?yàn)楝F(xiàn)在的異構(gòu)領(lǐng)域間通信很發(fā)達(dá),比如可以使用 Socket通信,也可以使用 Web service等等,了解即可!


          9、PC寄存器??

          程序計(jì)數(shù)器:Program Counter Register
            • 每個(gè)線程都有一個(gè)程序計(jì)數(shù)器,是線程私有的,就是一個(gè)指針,指向方法區(qū)中的方法字節(jié)碼(用來(lái)存儲(chǔ)指向像一條指令的地址,也即將要執(zhí)行的指令代碼),在執(zhí)行引擎讀取下一條指令,是一個(gè)非常小的內(nèi)存空間,幾乎可以忽略不計(jì)




          10、方法區(qū)?

          方法區(qū):Method Area
          • 方法區(qū)是被所有線程共享,所有字段和方法字節(jié)碼,以及一些特殊方法,如構(gòu)造函數(shù),接口代碼也在此定義,簡(jiǎn)單說(shuō),所有定義的方法的信息都保存在該區(qū)域,此區(qū)域?qū)儆?/span>共享區(qū)間


          • 方法區(qū)與Java堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)已被虛擬機(jī)加載的類信息常量靜態(tài)變量即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。雖然Java 虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是它卻有一個(gè)別名叫做Non-Heap(非堆),目的應(yīng)該是與Java 堆區(qū)分開(kāi)來(lái)。



          1. 方法區(qū)中有啥?

          1. 靜態(tài)變量(static)

          2. 常量(final)

          3. 類信息(構(gòu)造方法、接口定義)

          4. 運(yùn)行時(shí)的常量池


          2. 創(chuàng)建對(duì)象內(nèi)存分析



          • 創(chuàng)建一個(gè)對(duì)象時(shí),方法區(qū)中會(huì)生成對(duì)應(yīng)類的抽象模板;還有對(duì)應(yīng)的常量池、靜態(tài)變量、類信息、常量


          • 我們通過(guò)類模板去new對(duì)象的時(shí)候


            • 堆中存放實(shí)例對(duì)象

            • 棧中存放對(duì)象的引用,每個(gè)對(duì)象對(duì)應(yīng)一個(gè)地址指向堆中相同地址的實(shí)例對(duì)象


          例如這個(gè)例子中,生成了對(duì)應(yīng)的Person模板類,name常量“zsr”放在常量池中,三個(gè)對(duì)象的引用放在棧中,該引用指向放在堆中的三個(gè)實(shí)例對(duì)象。
          這就是堆、棧、方法區(qū)的交互關(guān)系

          11、棧??

          又稱棧內(nèi)存,主管程序的運(yùn)行,生命周期和線程同步,線程結(jié)束,棧內(nèi)存就釋放了,不存在垃圾回收
          • 棧:先進(jìn)后出

          • 隊(duì)列:先進(jìn)先出(FIFO)

          1、棧中存放啥?

          1. 8大基本類型

          2. 對(duì)象引用

          3. 實(shí)例的方法


          2、棧運(yùn)行原理

          • 棧表示Java方法執(zhí)行的內(nèi)存模型


          • 每調(diào)用一個(gè)方法就會(huì)為每個(gè)方法生成一個(gè)棧幀(Stack Frame),每個(gè)方法被調(diào)用和完成的過(guò)程,都對(duì)應(yīng)一個(gè)棧幀從虛擬機(jī)棧上入棧和出棧的過(guò)程。


          • 程序正在執(zhí)行的方法一定在棧的頂部





          3、堆棧溢出StackOverflowError

          舉個(gè)例子

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????new?Test().a();
          ????}

          ????public?void?a()?{
          ????????b();
          ????}

          ????public?void?b()?{
          ????????a();
          ????}
          }


          最開(kāi)始,main()方法壓入棧中,然后執(zhí)行a(),a()壓入棧中;再調(diào)用b(),b()壓入棧中;以此往復(fù),a與b方法不斷被壓入棧中,最終導(dǎo)致棧溢出




          12、堆??

          Heap,一個(gè)JVM只有一個(gè)堆內(nèi)存(棧是線程級(jí)的),堆內(nèi)存的大小是可以調(diào)節(jié)的



          1、堆中有啥?

          實(shí)例化的對(duì)象

          2、堆內(nèi)存詳解


          1、Young 年輕代

          對(duì)象誕生、成長(zhǎng)甚至死亡的區(qū)

          • Eden Space(伊甸園區(qū)):所有的對(duì)象都是在此new出來(lái)的

          • Survivor Space(幸存區(qū))

            • 幸存0區(qū)From Space)(動(dòng)態(tài)的,F(xiàn)rom和To會(huì)互相交換)

            • 幸存1區(qū)To Space


          Eden區(qū)占大容量,Survivor兩個(gè)區(qū)占小容量,默認(rèn)比例是8:1:1


          2、Tenured 老年代

          3、Perm 元空間

          存儲(chǔ)的是Java運(yùn)行時(shí)的一些環(huán)境或類信息,這個(gè)區(qū)域不存在垃圾回收!關(guān)閉虛擬機(jī)就會(huì)釋放這個(gè)區(qū)域內(nèi)存!

          這個(gè)區(qū)域常駐內(nèi)存,用來(lái)存放JDK自身攜帶的Class對(duì)象、Interface元數(shù)據(jù)。

          名稱演變
          • jdk1.6之前:永久代

          • jdk1.7:永久代慢慢退化,去永久代

          • jdk1.8之后:永久代改名為元空間

          注意:元空間在邏輯上存在,在物理上不存在
          新生代 + 老年代的內(nèi)存空間 = JVM分配的總內(nèi)存
          如圖所示:


          3、什么是OOM?

          內(nèi)存溢出java.lang.OutOfMemoryError

          產(chǎn)生原因
          1. 分配的太少

          2. 用的太多

          3. 用完沒(méi)釋放

          4、GC垃圾回收

          GC垃圾回收,主要在年輕代和老年代

          首先,對(duì)象出生再伊甸園區(qū)
          • 假設(shè)伊甸園區(qū)只能存一定數(shù)量的對(duì)象,則每當(dāng)存滿時(shí)就會(huì)觸發(fā)一次輕GC(Minor GC)


          • 輕GC清理后,有的對(duì)象可能還存在引用,就活下來(lái)了,活下來(lái)的對(duì)象就進(jìn)入幸存區(qū);有的對(duì)象沒(méi)用了,就被GC清理掉了;每次輕GC都會(huì)使得伊甸園區(qū)為空



          • 如果幸存區(qū)伊甸園都滿了,則會(huì)進(jìn)入老年代,如果老年代滿了,就會(huì)觸發(fā)一次重GC(FullGC)年輕代+老年代的對(duì)象都會(huì)清理一次,活下的對(duì)象就進(jìn)入老年代


          • 如果新生代老年代都滿了,則OOM


          Minor GC:伊甸園區(qū)滿時(shí)觸發(fā);從年輕代回收內(nèi)存
          Full GC:老年代滿時(shí)觸發(fā);清理整個(gè)堆空間,包含年輕代和老年代
          Major GC:清理老年代

          什么情況永久區(qū)會(huì)崩?
          一個(gè)啟動(dòng)類加載了大量的第三方Jar包,Tomcat部署了過(guò)多應(yīng)用,或者大量動(dòng)態(tài)生成的反射類
          這些東西不斷的被加載,直到內(nèi)存滿,就會(huì)出現(xiàn)OOM

          13、堆內(nèi)存調(diào)優(yōu)?

          1、查看并設(shè)置JVM堆內(nèi)存

          查看我們jvm的堆內(nèi)存

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????//返回jvm試圖使用的最大內(nèi)存
          ????????long?max?=?Runtime.getRuntime().maxMemory();
          ????????//返回jvm的初始化內(nèi)存
          ????????long?total?=?Runtime.getRuntime().totalMemory();
          ????????//默認(rèn)情況下:分配的總內(nèi)存為電腦內(nèi)存的1/4,初始化內(nèi)存為電腦內(nèi)存的1/64
          ????????System.out.println("max="?+?max?/?(double)?1024?/?1024?/?1024?+?"G");
          ????????System.out.println("total="?+?total?/?(double)?1024?/?1024?/?1024?+?"G");
          ????}
          }



          默認(rèn)情況下
          • JVM最大分配內(nèi)存為電腦內(nèi)存的1/4

          • JVM初始化內(nèi)存為電腦內(nèi)存的1/64

          我們可以手動(dòng)調(diào)堆內(nèi)存大小


          VM options中可以指定jvm試圖使用的最大內(nèi)存jvm初始化內(nèi)存大小
          -Xms1024m -Xmx1024m -Xlog:gc*


          • -Xmx用來(lái)設(shè)置jvm試圖使用的最大內(nèi)存,默認(rèn)為1/4

          • -Xms用來(lái)設(shè)置jvm初始化內(nèi)存,默認(rèn)為1/64

          • -Xlog:gc*用來(lái)打印GC垃圾回收信息




          2、怎么排除OOM錯(cuò)誤?

          1. 嘗試擴(kuò)大堆內(nèi)存看結(jié)果

          利用上述方法指定jvm試圖使用的最大內(nèi)存jvm初始化內(nèi)存大小

          2. 利用內(nèi)存快照工具JProfiler

          內(nèi)存快照工具
          • MAT(Eclipse)

          • JProfiler

          作用:
          • 分析Dump內(nèi)存文件,快速定位內(nèi)存泄漏

          • 獲得堆中的文件

          • 獲得大的對(duì)象

          3. 什么是Dump文件?如何分析?

          Dump文件是進(jìn)程內(nèi)存鏡像,可以把程序的執(zhí)行狀態(tài)通過(guò)調(diào)試器保存到dump文件中

          import?java.util.ArrayList;

          public?class?Test?{
          ????byte[]?array?=?new?byte[1024?*?1024];//1M

          ????public?static?void?main(String[]?args)?{
          ????????ArrayList?list?=?new?ArrayList<>();
          ????????int?count?=?0;
          ????????try?{
          ????????????while?(true)?{
          ????????????????list.add(new?Test());
          ????????????????count++;
          ????????????}
          ????????}?catch?(Exception?e)?{
          ????????????System.out.println("count="?+?count);
          ????????????e.printStackTrace();
          ????????}
          ????}
          }

          運(yùn)行該程序,報(bào)錯(cuò)OOM


          接下來(lái)我們?cè)O(shè)置以下堆內(nèi)存,并附加生成對(duì)應(yīng)的dump文件的指令


          -Xms1m?-Xmx8m?-XX:+HeapDumpOnOutOfMemoryError

          -XX:+HeapDumpOnOutOfMemoryError表示當(dāng)JVM發(fā)生OOM時(shí),自動(dòng)生成DUMP文件。

          再次點(diǎn)擊運(yùn)行,下載了對(duì)應(yīng)的Dump文件



          我們右鍵該類,點(diǎn)擊Show in Explorer


          一直點(diǎn)擊上級(jí)目錄,直到找到.hprof文件,與src同級(jí)目錄下



          我們雙擊打開(kāi),可以看到每塊所占的大小,便于分析問(wèn)題



          點(diǎn)擊Thread Dump,里面是所有的線程,點(diǎn)擊對(duì)應(yīng)的線程可以看到相應(yīng)的錯(cuò)誤,反饋到具體的行,便于排錯(cuò)



          每次打開(kāi)Dump文件查看完后,建議刪除,可以在idea中看到,打開(kāi)文件后生成了很多內(nèi)容,占內(nèi)存,建議刪除



          附:安裝Jprofiler教程

          1.idea中安裝插件

          2.下載客戶端 https://www.ej-technologies.com/download/jprofiler/files



          3.安裝客戶端
          選擇自定義安裝,注意:路徑不能有中文和空格


          這里name和Company任意,License Key大家可以尋找對(duì)應(yīng)版本的注冊(cè)機(jī)獲得



          后續(xù)默認(rèn),安裝成功即可!!!
          4. 安裝完成后,重啟IDEA,可以看到我們的內(nèi)存快照工具

          ?打開(kāi)IDEA的設(shè)置,找到Tools里面的JProfiler,沒(méi)有設(shè)置位置則設(shè)置位置

          此時(shí)則全部安裝完成!


          14、GC垃圾回收?

          1、回顧

          Garbage Collection:垃圾回收



          在12.4中,我們已經(jīng)對(duì)GC的流程進(jìn)行了大概的講解,這里做一些總結(jié)
          • JVM在進(jìn)行GC時(shí),并不是對(duì)年輕代老年代統(tǒng)一回收;大部分時(shí)候,回收都是在年輕代

          • GC分為兩種

            • 輕GC(清理年輕代)

            • 重GC(清理年輕代+老年代)

          2、GC算法

          1、引用計(jì)數(shù)算法(很少使用)

          • 每個(gè)對(duì)象在創(chuàng)建的時(shí)候,就給這個(gè)對(duì)象綁定一個(gè)計(jì)數(shù)器。

          • 每當(dāng)有一個(gè)引用指向該對(duì)象時(shí),計(jì)數(shù)器加一;每當(dāng)有一個(gè)指向它的引用被刪除時(shí),計(jì)數(shù)器減一。

          • 這樣,當(dāng)沒(méi)有引用指向該對(duì)象時(shí),該對(duì)象死亡,計(jì)數(shù)器為0,這時(shí)就應(yīng)該對(duì)這個(gè)對(duì)象進(jìn)行垃圾回收操作。



          2、復(fù)制算法

          復(fù)制算法主要發(fā)生在年輕代(?幸存0區(qū)?和?幸存1區(qū)
          • 當(dāng)Eden區(qū)滿的時(shí)候,會(huì)觸發(fā)輕GC,每觸發(fā)一次,活的對(duì)象就被轉(zhuǎn)移到幸存區(qū),死的就被GC清理掉了,所以每觸發(fā)輕GC時(shí),Eden區(qū)就會(huì)清空;

          • 對(duì)象被轉(zhuǎn)移到了幸存區(qū),幸存區(qū)又分為From SpaceTo Space,這兩塊區(qū)域是動(dòng)態(tài)交換的,誰(shuí)是空的誰(shuí)就是To Space,然后From Space就會(huì)把全部對(duì)象轉(zhuǎn)移到To Space去;

          • 那如果兩塊區(qū)域都不為空呢?這就用到了復(fù)制算法,其中一個(gè)區(qū)域會(huì)將存活的對(duì)象轉(zhuǎn)移到令一個(gè)區(qū)域去,然后將自己區(qū)域的內(nèi)存空間清空,這樣該區(qū)域?yàn)榭眨殖蔀榱?/span>To Space

          • 所以每次觸發(fā)輕GC后,Eden區(qū)清空,同時(shí)To區(qū)也清空了,所有的對(duì)象都在From區(qū)

          這也就是幸存0區(qū)幸存1區(qū)總有一塊為空的原因



          好處:沒(méi)有內(nèi)存的碎片(內(nèi)存集中在一塊)
          壞處
          1. 浪費(fèi)了內(nèi)存空間(浪費(fèi)了幸存區(qū)一半空間)

          2. 對(duì)象存活率較高的場(chǎng)景下(比如老年代那樣的環(huán)境),需要復(fù)制的東西太多,效率會(huì)下降。

          最佳使用環(huán)境:對(duì)象存活度較低的時(shí)候,也就是年輕代

          3、標(biāo)記–清除算法

          為每個(gè)對(duì)象存儲(chǔ)一個(gè)標(biāo)記位,記錄對(duì)象的生存狀態(tài)
          1. 標(biāo)記階段:這個(gè)階段內(nèi),為每個(gè)對(duì)象更新標(biāo)記位,檢查對(duì)象是否死亡;

          2. 清除階段:該階段對(duì)死亡的對(duì)象進(jìn)行清除,執(zhí)行 GC 操作。



          缺點(diǎn):兩次掃描嚴(yán)重浪費(fèi)時(shí)間,會(huì)產(chǎn)生內(nèi)存碎片
          優(yōu)點(diǎn):不需要額外的空間

          4、標(biāo)記–整理算法

          標(biāo)記-整理法?是?標(biāo)記-清除法?的一個(gè)改進(jìn)版。
          又叫做?標(biāo)記-清楚-壓縮法
          1. 標(biāo)記階段,該算法也將所有對(duì)象標(biāo)記為存活和死亡兩種狀態(tài);

          2. 不同的是,在第二個(gè)階段,該算法并沒(méi)有直接對(duì)死亡的對(duì)象進(jìn)行清理,而是將所有存活的對(duì)象整理一下,放到另一處空間,然后把剩下的所有對(duì)象全部清除。


          可以進(jìn)一步優(yōu)化,在內(nèi)存碎片不太多的情況下,就繼續(xù)標(biāo)記清除,到達(dá)一定量的時(shí)候再壓縮.


          總結(jié)

          內(nèi)存(時(shí)間復(fù)雜度)效率:復(fù)制算法 > 標(biāo)記清除算法 > 標(biāo)記壓縮算法
          內(nèi)存整齊度:復(fù)制算法 = 標(biāo)記壓縮法 > 標(biāo)記清除法
          內(nèi)存利用率:標(biāo)記壓縮法 = 標(biāo)記清除法 > 復(fù)制算法

          思考:有沒(méi)有最優(yōu)的算法?

          沒(méi)有最優(yōu)的算法,只有最合適的算法
          GC 也稱為?分代收集算法
          對(duì)于年輕代
          • 對(duì)象存活率低

          • 用復(fù)制算法

          對(duì)于老年代
          • 區(qū)域大,對(duì)象存活率高

          • 標(biāo)記清除+標(biāo)記壓縮混合實(shí)現(xiàn)



          結(jié)束!


          ????

          ????作者:?Baret H

          ????原文鏈接:http://i8n.cn/iWLG4r






          -關(guān)注我


          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色日逼视频 | 777奇米色网站 | ezrabebe | 91性视频| 日本日逼视频 |