JVM是什么:
JVM(Java虛擬機(jī))是Java程序的
運行環(huán)境
,它是Java平臺的核心組成部分之一。JVM提供了一個
運行Java字節(jié)碼的虛擬機(jī)
,負(fù)責(zé)將
Java程序解釋和執(zhí)行。
Java程序員可以在JVM上編寫和運行Java程序,而不用考慮底層操作系統(tǒng)的差異性
。JVM的特性使得Java具備了跨平臺性
,同一份Java代碼可以在不同
的操作系統(tǒng)上運行
。
組成部分如下:
一、JVM組成
1.什么是程序計數(shù)器
程序計數(shù)器:線程私有的
,內(nèi)部保存的字節(jié)碼的行號
。用于記錄正在執(zhí)行的字節(jié)碼指令的地址。(通俗的來說就是記錄當(dāng)前線程的程序執(zhí)行的字節(jié)碼指令的行號)
2.什么是Java堆?
線程共享的區(qū)域:主要用來保存對象實例
,數(shù)組
等,當(dāng)堆中沒有內(nèi)存空間可分配給實例,也無法再擴(kuò)展時,則拋出OutOfMemoryError異常。
Java1.8-JVM內(nèi)存結(jié)構(gòu)
其中:堆分為兩部分
年輕代
包括三部分,Eden區(qū)
和兩個大小嚴(yán)格相同的Survivor區(qū)
,根據(jù)JVM的策略,在經(jīng)過
幾次垃圾收集
后,任然存活于Survivor的對象將被移動到老年代區(qū)間。
老年代
主要保存生命周期長
的對象,一般是一些老的對象
元空間
保存的類信息
、靜態(tài)變量
、常量
、編譯后的代碼
Java1.7-JVM內(nèi)存結(jié)構(gòu)
唯一的不同就是在java8的JVM中,元空間是在本地內(nèi)存中的
,而java7當(dāng)中元空間是作為方法區(qū)/永久代
存在于堆空間中的
因而在1.8之后做出改動,主要是為了防止OMM內(nèi)存泄漏,因為元空間(方法區(qū)/永久代)保存的是類信息
、靜態(tài)變量
、常量
、編譯后的代碼
,隨著程序不斷龐大,實現(xiàn)很難預(yù)估元空間的大小,久而久之就會產(chǎn)生OMM。
在舊的Java版本(如Java 7及更早版本)中,方法區(qū)和永久代(PermGen)都是
堆
的一部分,用于存儲類的結(jié)構(gòu)信息、常量池、靜態(tài)變量等。但永久代容易導(dǎo)致內(nèi)存溢出的問題,因為它的大小是固定
的,并且無法在運行時動態(tài)調(diào)整。
為了解決這個問題,并改進(jìn)類的元數(shù)據(jù)的存儲方式,Java 8 引入了元空間(Metaspace)。元空間通過使用本地內(nèi)存
來存儲類的元數(shù)據(jù),有效地解決了永久代的限制和內(nèi)存溢出問題。與永久代不同,元空間的大小并不受堆內(nèi)存大小的限制
,而是受系統(tǒng)的物理內(nèi)存限制
。
元空間還具有自動調(diào)整大小的能力
,可以根據(jù)需要動態(tài)地分配和釋放內(nèi)存
。這使得元空間更具靈活
性和可靠
性,并能更好地適應(yīng)各種應(yīng)用程序的需求。
3.能不能介紹一下方法區(qū)(元空間)
在不同的JVM實現(xiàn)中,方法區(qū)存在位置是不一樣的。
這里舉例在jdk1.7時的jvm
方法區(qū)(Method Area)是各個線程共
享的內(nèi)存區(qū)域
主要存儲類的信息
、運行時常量池
虛擬機(jī)啟動
的時候創(chuàng)建
,關(guān)閉
虛擬機(jī)時釋放
如果方法區(qū)域中的內(nèi)存無法滿足分配請求,則會拋出OutOfMemoryError: Metaspace
普通常量池
可以看作是一張表,虛擬機(jī)指令
根據(jù)這張常量表找到要執(zhí)行的類名、方法名、參數(shù)類型、字面量等信息
通俗的說,就是記錄了虛擬機(jī)指令和執(zhí)行的類名、方法名等信息的
映射關(guān)系
。
編譯階段:常量池的符號引用為這種
#+數(shù)字
,因為只是編譯階段,沒到運行階段,只需要直到機(jī)器指令和方法、類名等等的映射關(guān)系,能找到對應(yīng)上就算編譯通過
運行時常量池
在類加載階段,JVM會將符號引用
解析為直接引用
(將符號替換為真實的地址值),并將其存儲在常量池中,以供運行
時使用。
*常量池是 .class 文件中的,當(dāng)該類被加載,它的常量池信息就會放入運行時常量池,并把里面的
符號地址
變?yōu)?code>真實地址
4.你聽過直接內(nèi)存嗎
直接內(nèi)存:并不屬于JVM中的內(nèi)存結(jié)構(gòu)
,不由JVM進(jìn)行管理
。是虛擬機(jī)的系統(tǒng)內(nèi)存
,常見于NIO
(非阻塞io)操作時,用于數(shù)據(jù)緩沖區(qū),它分配回收成本較高,但讀寫性能高
直接內(nèi)存最直接的體現(xiàn)就是常規(guī)IO和NIO
的區(qū)別:
我們都知道NIO是非阻塞的,IO是傳統(tǒng)阻塞式
IO傳統(tǒng)阻塞式
相當(dāng)于java程序不能直接去讀系統(tǒng)內(nèi)存給的數(shù)據(jù),需要一個媒介就是堆內(nèi)存java緩沖區(qū)
NIO非阻塞式
相比較IO是直接到直接內(nèi)存讀數(shù)據(jù),所以只要將
磁盤文件讀到直接內(nèi)存
,數(shù)據(jù)準(zhǔn)備就緒,程序切換到內(nèi)核態(tài)就直接從直接內(nèi)存讀數(shù)據(jù)
。
5.什么是虛擬機(jī)棧
通俗來說就是線程的方法棧
Java Virtual machine Stacks (java 虛擬機(jī)棧)
每個線程運行時所需要的內(nèi)存
,稱為虛擬機(jī)棧,先進(jìn)后出
每個棧由多個棧幀
(frame)組成(一個棧幀代表一個方法占用的內(nèi)存),對應(yīng)著每次方法調(diào)用時所占用的內(nèi)存
每個線程
只能有一個活動棧幀
,對應(yīng)著當(dāng)前正在執(zhí)行的那個方法
也就是一個方法(主方法)可以調(diào)用其他很多別的方法(子方法)
6.垃圾回收是否涉及棧內(nèi)存?
垃圾回收主要指就是堆內(nèi)存
,當(dāng)棧幀彈棧
以后,內(nèi)存就會釋放
7.棧內(nèi)存分配越大越好嗎?
未必,默認(rèn)的棧內(nèi)存通常為1024k
棧幀過大會導(dǎo)致線程數(shù)變少
,例如,機(jī)器總內(nèi)存為512m,目前能活動的線程數(shù)則為512個,如果把棧內(nèi)存改為2048k,那么能活動的棧幀就會減半
很好理解,機(jī)器內(nèi)存就那么多,單獨把
棧內(nèi)存調(diào)大了
,那么棧幀的個數(shù)自然的得變少
,所以對應(yīng)的線程數(shù)就會變少
8.方法內(nèi)的局部變量是否線程安全?
如果方法內(nèi)局部變量沒有逃離方法的作用范圍
,它是線程安全
的
如果是局部變量引用了對象,并逃離方法的作用范圍
,需要考慮線程安全
通俗來說,就是看
方法內(nèi)的局部變量是不是完全包圍在方法內(nèi)
(像如果是作為方法參數(shù)
傳過來的、作為返回值返回
的都可以算是逃離了方法
的作用范圍),這樣別的線程可以肆意修改方法內(nèi)的局部變量
9.什么情況下會導(dǎo)致棧內(nèi)存溢出?
棧幀過多導(dǎo)致棧內(nèi)存溢出,典型問題:遞歸調(diào)用
棧幀過大導(dǎo)致棧內(nèi)存溢出(基本上不存在)
棧的容量較小:相對于堆內(nèi)存而言,棧內(nèi)存的容量通常比較有限。
每個線程都有自己的棧空間
,而每個棧的大小通常只有幾MB到幾十MB
。這限制了棧幀的大小
,使得棧溢出的概率較低。
10.堆棧的區(qū)別是什么
分配對象和數(shù)據(jù)類型:堆內(nèi)存用于分配Java對象
和數(shù)組
。所有通過關(guān)鍵字new創(chuàng)建的對象以及通過反射或JNI創(chuàng)建的對象都存儲在堆中。而棧內(nèi)存主要用于存儲基本數(shù)據(jù)類型的變量
和方法調(diào)用時的局部變量。
存儲位置:堆內(nèi)存位于JVM的堆區(qū)
,是一個共享的內(nèi)存區(qū)域
。棧內(nèi)存位于JVM的棧區(qū)
,每個線程
都有自己的獨立棧
。
內(nèi)存管理方式:堆
內(nèi)存的分配和回收由Java虛擬機(jī)的垃圾回收器負(fù)責(zé)管理
。垃圾回收器會自動回收不再使用的對象,并釋放對應(yīng)的內(nèi)存空間。而棧
內(nèi)存的分配和回收是由編譯器自動管理的
,當(dāng)方法執(zhí)行結(jié)束
或變量超出作用域時,棧上的內(nèi)存會自動被釋放
。
內(nèi)存分配速度:堆
內(nèi)存的分配速度相對較慢,因為需要進(jìn)行復(fù)雜的垃圾回收算法
和對象定位操作。而棧
內(nèi)存的分配速度較快,僅僅是簡單地進(jìn)行指針移動。
內(nèi)存空間大小限制:堆內(nèi)存的大小一般比棧內(nèi)存大得多
。堆內(nèi)存的大小可以通過JVM的配置參數(shù)進(jìn)行調(diào)整
。而棧
內(nèi)存的大小是由線程的啟動參數(shù)
決定的,每個線程的棧大小通常是固定的。
對象的生命周期:堆內(nèi)存中的對象生命周期可以很長
,可以在程序的任意位置被引用和訪問。而棧
內(nèi)存中的局部變量生命周期與方法調(diào)用密切相關(guān),當(dāng)方法執(zhí)行結(jié)束時,棧上的局部變量會自動銷毀。
總結(jié)來說,堆內(nèi)存主要用于存儲Java對象和數(shù)組,由垃圾回收器管理,分配和回收速度相對較慢;而棧內(nèi)存主要用于存儲基本數(shù)據(jù)類型的變量和方法調(diào)用時的局部變量,由編譯器自動管理,分配和回收速度相對較快。兩者在內(nèi)存管理方式、存儲位置、分配速度和大小等方面有較大的區(qū)別。
二、類加載器
11.什么是類加載器,類加載器有哪些
類加載器
JVM只會運行二進(jìn)制文件,類加載器
的作用就是將字節(jié)碼文件
加載到JVM中,從而讓Java程序能夠啟動起來。
類加載器包括四種
啟動類加載器(加載像庫里自帶的類string integer等等
)(BootStrap ClassLoader):加載JAVA_HOME/jre/lib目錄下的庫
擴(kuò)展類加載器(ExtClassLoader):主要加載JAVA_HOME/jre/lib/ext目錄中的類
應(yīng)用類加載器(加載自己java程序?qū)懙念?/code>)
(AppClassLoader):用于加載classPath下的類
自定義類加載器(很少自己寫加載器
)(CustomizeClassLoader):自定義類繼承ClassLoader,實現(xiàn)自定義類加載規(guī)則。
12.什么是雙親委派模型?
加載某一個類,先委托上一級的加載器進(jìn)行加載
,如果上級加載器也有上級,則會繼續(xù)向上委托
,如果該類委托上級沒有被加載
,子加載器嘗試加載該類
舉例:
假設(shè)有一個Student類
,需要加載
這個時候就會定位到AppclassLoader(應(yīng)用類加載器),發(fā)現(xiàn)應(yīng)用記載器有上級加載器,就先不加載,看看上級加載器里面有沒有加載過的Student類,一直往上找,發(fā)現(xiàn)都沒有加載過的Student類,那么此時AppclassLoader才會去加載Student類
假設(shè)有一個String類
,需要加載,也是定位到AppclassLoader(應(yīng)用類加載器)往上委托直到找到啟動類加載器
,找到了加載好的String類,直接加載。
13.JVM為什么采用雙親委派機(jī)制?
JVM采用雙親委派機(jī)制(Parent Delegation Model)是為了解決兩個主要問題:安全性
和避免類的重復(fù)加載
。
安全性體現(xiàn)在:
確保核心Java庫的安全性,避免惡意代碼通過自定義的類偽裝成核心類庫
,從而提高系統(tǒng)的安全性。
例如下面我們自己創(chuàng)建一個String包裝類,此時核心類庫是本身就會加載這個類,這個時候就會報錯,不允許加載自定義的核心庫的類。
雙親委派機(jī)制可以確保核心Java庫的安全性。當(dāng)一個類需要被加載時,首先會
委派給父類加載器進(jìn)行查找和加載
,只有在父類加載器無法找到該類時
,才會由當(dāng)前類加載器自己去加載。
避免類的重復(fù)加載體現(xiàn)在
通過雙親委派機(jī)制,當(dāng)一個類需要被加載時,首先由父類加載器嘗試加載,如果父類加載器已經(jīng)加載了該類,就直接返回;否則,再由子類加載器嘗試加載。這種機(jī)制可以確保在整個類加載器層次結(jié)構(gòu)中
,每個類只被加載一次
,避免了類的重復(fù)加載,提高了運行效率
。
14.說一下類裝載的執(zhí)行過程
類在裝載的時候會經(jīng)歷7個過程:
- 加載:查找和導(dǎo)入class文件
- 驗證:保證加載類的準(zhǔn)確性
- 準(zhǔn)備:為類變量分配內(nèi)存并設(shè)置類變量初始值
- 解析:把類中的符號引用轉(zhuǎn)換為直接引用
- 初始化:對類的靜態(tài)變量,靜態(tài)代碼塊執(zhí)行初始化操作
- 使用:JVM 開始從入口方法開始執(zhí)行用戶的程序代碼
- 卸載:當(dāng)用戶程序代碼執(zhí)行完畢后,JVM便開始銷毀創(chuàng)建的Class對象。
詳細(xì)參考鏈接:【JVM】類裝載的執(zhí)行過程
三、垃圾回收
15.強(qiáng)引用、軟引用、弱引用、虛引用區(qū)別:
強(qiáng)引用:只有所有 GC Roots 對象都不通過【強(qiáng)引用】引用該對象
,該對象才能被垃圾回收
向以下這種情況就不會被回收
軟引用:僅有軟引用引用該對象時,在垃圾回收后
,內(nèi)存仍不足時會再次出發(fā)垃圾回收
弱引用:僅有弱引用引用該對象時,在垃圾回收時,無論內(nèi)存是否充足
,都會回收弱引用對象
虛引用:必須配合引用隊列使用
,被引用對象回收時
,會將虛引用入隊
,由 Reference Handler 線程調(diào)用虛引用相關(guān)方法釋放
直接內(nèi)存
總結(jié):
-
強(qiáng)引用:
只要所有 GC Roots 能找到,就不會被回收
-
軟引用:需要
配合SoftReference使用
,當(dāng)垃圾多次回收,內(nèi)存依然不夠的時候會回收軟引用對象
-
弱引用:需要
配合WeakReference使用
,只要進(jìn)行了垃圾回收,就會把弱引用對象回收
-
虛引用:必須配合
引用隊列使用
,被引用對象回收時,會將虛引用入隊
,由Reference Handler線程
調(diào)用虛引用相關(guān)方法釋放直接內(nèi)存
15.1 對象什么時候可以被垃圾器回收
垃圾回收,回收的是針對于堆的
簡單一句就是:如果一個或多個對象沒有任何的引用指向它
了,那么這個對象現(xiàn)在就是垃圾,如果定位了垃圾
,則有可能會被垃圾回收器回收。
如果要定位什么是垃圾,有兩種方式來確定,第一個是引用計數(shù)法,第二個是可達(dá)性分析算法
引用計數(shù)法(不常用)
一個對象被引用了一次,在當(dāng)前的對象頭上遞增一次引用次數(shù)(ref=1)
,如果這個對象的引用次數(shù)為0(ref=0)
,代表這個對象可回收
但是當(dāng)對象間出現(xiàn)了循環(huán)引用
的話,則引用計數(shù)法就會失效
可達(dá)性分析算法
現(xiàn)在的虛擬機(jī)采用的都是通過可達(dá)性分析算法來確定哪些內(nèi)容是垃圾。
比如下面的例子
X,Y這兩個節(jié)點是可回收的
Java 虛擬機(jī)中的垃圾回收器采用可達(dá)性分析來探索所有存活的對象
掃描堆中的對象,看是否能夠沿著 GC Root 對象
為起點的引用鏈
找到該對象,找不到,表示可以回收
哪些對象可以作為 GC Root ?
-
虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象
-
方法區(qū)中類靜態(tài)屬性引用的對象
-
方法區(qū)中常量引用的對象
-
本地方法棧中 JNI(即一般說的 Native 方法)引用的對象
16.JVM 垃圾回收算法有哪些?
參考鏈接:GC詳解、GC四大算法和GC Root
總共分為三種:標(biāo)記清除算法
:垃圾回收分為2個階段,分別是標(biāo)記和清除,效率高,有磁盤碎片,內(nèi)存不連續(xù)
標(biāo)記整理算法
:標(biāo)記清除算法一樣,將存活對象都向內(nèi)存另一端移動,然后清理邊界以外的垃圾,無碎片,對象需要移動,效率低
復(fù)制算法:
將原有的內(nèi)存空間一分為二,每次只用其中的一塊,正在使用的對象復(fù)制到另一個內(nèi)存空間中,然后將該內(nèi)存空間清空,交換兩個內(nèi)存的角色,完成垃圾的回收;無碎片,內(nèi)存使用率低
16.1 標(biāo)記清除算法
標(biāo)記清除算法,是將垃圾回收分為2個階段,分別是標(biāo)記和清除
。
1.根據(jù)可達(dá)性分析算法
得出的垃圾進(jìn)行標(biāo)記
2.對這些標(biāo)記為可回收的內(nèi)容
進(jìn)行垃圾回收
注意:標(biāo)記GC Root引用鏈的對象,回收的就是沒有被標(biāo)記的
缺點:
- 需要兩次掃描,耗時嚴(yán)重。
先掃描一次,對
存活的對象進(jìn)行標(biāo)記。
再次掃描,回收沒有被標(biāo)記的對象。
- 會產(chǎn)生內(nèi)存碎片,導(dǎo)致內(nèi)存空間不連續(xù)。
當(dāng)需要分配一個較大的內(nèi)存塊時,由于
沒有足夠的連續(xù)內(nèi)存空間
,可能會導(dǎo)致分配失敗即內(nèi)存溢出。
16.2 標(biāo)記整理算法( 標(biāo)記清除算法的升級—多了一個整理階段)
優(yōu)缺點同標(biāo)記清除算法,解決了標(biāo)記清除算法的碎片化的問題
,同時,標(biāo)記壓縮算法多了一步,對象移動內(nèi)存位置的步驟,其效率也有有一定的影響。
相比較標(biāo)記清除算法在垃圾回收后,會將活著的對象滑動到一側(cè),這樣就能讓空出的內(nèi)存空間是連續(xù)的。
16.3 復(fù)制算法
相當(dāng)于把一塊堆內(nèi)存分成兩半來用,一般拿來存對象,另一半拿來做垃圾回收后的整理收納倉(必須為空閑空間)
復(fù)制算法需要將存活的對象
從一個區(qū)域
復(fù)制到另一個區(qū)域
(空白區(qū)域),然后直接清空存活對象和待回收對象的那一片區(qū)域
注意:要求堆內(nèi)存的
使用比例不超過50%
,因為每次垃圾回收時,需要保證目標(biāo)區(qū)域有足夠的空間來存放從源區(qū)域復(fù)制過來的對象。
優(yōu)點:
在垃圾對象多的情況下,效率較高
清理后,內(nèi)存無碎片
缺點:
分配的2塊內(nèi)存空間,在同一個時刻,只能使用一半,內(nèi)存使用率較低
17.說一下JVM中的分代回收
一、堆的區(qū)域劃分
堆被分為了兩份:新生代和老年代
【1:2】
對于新生代,內(nèi)部又被分為了三個區(qū)域。Eden區(qū)
,幸存者區(qū)survivor(分成from和to)
【8:1:1】
二、對象回收分代回收策略
- 新創(chuàng)建的對象,都會先分配到eden區(qū)
- 當(dāng)伊甸園內(nèi)存不足,標(biāo)記伊甸園與 from(現(xiàn)階段沒有)的存活對象
- 將存活對象采用復(fù)制算法復(fù)制到to中,復(fù)制完畢后,伊甸園和 from 內(nèi)存都得到釋放
- 經(jīng)過一段時間后伊甸園的內(nèi)存又出現(xiàn)不足,標(biāo)記eden區(qū)域to區(qū)存活的對象,將其復(fù)制到from區(qū)
- 當(dāng)幸存區(qū)對象熬過幾次回收(最多15次),晉升到老年代(幸存區(qū)內(nèi)存不足或大對象會提前晉升)
參考鏈接:【JVM】JVM中的分代回收
17.1 MinorGC、 Mixed GC 、 FullGC的區(qū)別是什么
STW(Stop-The-World):暫停所有應(yīng)用程序線程,等待垃圾回收的完成
MinorGC、 Mixed GC 、 FullGC 相當(dāng)于垃圾回收的等級:
MinorGC:【young GC】發(fā)生在新生代
的垃圾回收,暫停時間短(STW)
Mixed GC: 新生代 + 老年代部分區(qū)域
的垃圾回收,G1 收集器特有
FullGC: 新生代 + 老年代完整
垃圾回收,暫停時間長(STW),應(yīng)盡力避免
18.說一下JVM有哪些垃圾回收器?
在jvm中,實現(xiàn)了多種垃圾收集器,包括:
-
串行垃圾收集器
Serial 作用于新生代,采用復(fù)制算法
Serial Old 作用于老年代,采用標(biāo)記-整理算法 -
并行垃圾收集器(
JDK8默認(rèn)使用此垃圾回收器
)
Parallel New作用于新生代,采用復(fù)制算法
Parallel Old作用于老年代,采用標(biāo)記-整理算法 -
CMS(并發(fā))垃圾收集器
針對老年代垃圾回收的 -
G1垃圾收集器(
在JDK9之后默認(rèn)使用
)
應(yīng)用于新生代和老年代
詳細(xì)參考鏈接:【JVM】JVM垃圾收集器
19.詳細(xì)聊一下G1垃圾回收器
詳細(xì)參考鏈接:【JVM】JVM垃圾收集器
G1垃圾收集器的設(shè)計目標(biāo)是
在可控的停頓時間
內(nèi)實現(xiàn)高吞吐量的垃圾回收。
- 應(yīng)用于
新生代
和老年代
- 劃分成
多個區(qū)域
,每個區(qū)域
都可以充當(dāng) eden,survivor,old, humongous,其中humongous 專為大對象準(zhǔn)備
- 采用
標(biāo)記整理算法
因為基本上G1主要針對大型堆內(nèi)存進(jìn)行垃圾回收,而復(fù)制算法在大型堆內(nèi)存上的應(yīng)用存在一些挑戰(zhàn)和限制。(必須考慮內(nèi)存空間使用率)
-
響應(yīng)時間與吞吐量兼顧
-
分成三個階段:新生代回收、并發(fā)標(biāo)記、混合收集(在不同的條件下被觸發(fā))
-
如果并發(fā)失?。椿厥账俣融s不上創(chuàng)建新對象速度),會觸發(fā) Full GC(盡量避免)
如果對象內(nèi)存分配速度過快,mixed gc來不及回收,導(dǎo)致老年代被填滿,就會觸發(fā)一次full gc,G1的full gc算法就是單線程執(zhí)行的serial old gc,會導(dǎo)致異常長時間的暫停時間,需要進(jìn)行不斷的調(diào)優(yōu),盡可能的避免full gc.
四、JVM實踐
20.JVM 調(diào)優(yōu)的參數(shù)可以在哪里設(shè)置
war包部署在tomcat中設(shè)置
jar包部署在啟動參數(shù)設(shè)置
21.用的 JVM 調(diào)優(yōu)的參數(shù)都有哪些?
-
設(shè)置堆空間大小
設(shè)置堆的初始大小和最大大小,為了防止垃圾收集器在初始大小、最大大小之間收縮堆而產(chǎn)生額外的時間,通常把最大、初始大小設(shè)置為相同的值
。
堆空間設(shè)置多少合適?
最大大小的默認(rèn)值是物理內(nèi)存的1/4,初始大小是物理內(nèi)存的1/64
堆太小,可能會頻繁的導(dǎo)致年輕代和老年代的垃圾回收,會產(chǎn)生stw,暫停用戶線程
堆內(nèi)存大肯定是好的,存在風(fēng)險,假如發(fā)生了fullgc,它會掃描整個堆空間,暫停用戶線程的時間長
設(shè)置參考推薦:盡量大,也要考察一下當(dāng)前計算機(jī)其他程序的內(nèi)存使用情況
-
虛擬機(jī)棧的設(shè)置
虛擬機(jī)棧的設(shè)置:每個線程默認(rèn)會開啟1M的內(nèi)存
,用于存放棧幀、調(diào)用參數(shù)、局部變量等,但一般256K就夠用
。通常減少每個線程的堆棧,可以產(chǎn)生更多的線程,但這實際上還受限于操作系統(tǒng)。
- 年輕代中Eden區(qū)和兩個Survivor區(qū)的大小比例
設(shè)置年輕代中Eden區(qū)和兩個Survivor區(qū)的大小比例。該值如果不設(shè)置,則默認(rèn)比例為8:1:1
。通過增大Eden區(qū)的大小,來減少YGC發(fā)生的次數(shù)
,但有時我們發(fā)現(xiàn),雖然次數(shù)減少了,但Eden區(qū)滿的時候,由于占用的空間較大,導(dǎo)致釋放緩慢,此時STW的時間較長,因此需要按照程序情況去調(diào)優(yōu)。
-
年輕代晉升老年代閾值
-
設(shè)置垃圾回收收集器
22.說一下 JVM 調(diào)優(yōu)的工具?
命令工具
-
jps 進(jìn)程狀態(tài)信息
-
jstack 查看java進(jìn)程內(nèi)線程的堆棧信息
-
jmap 查看堆轉(zhuǎn)信息
-
jhat 堆轉(zhuǎn)儲快照分析工具
-
jstat JVM統(tǒng)計監(jiān)測工具
可視化工具
-
jconsole 用于對jvm的內(nèi)存,線程,類 的監(jiān)控
-
VisualVM 能夠監(jiān)控線程,內(nèi)存情況(只有jdk8才有)
23.Java內(nèi)存泄露的排查思路?
內(nèi)存泄漏通常是指堆內(nèi)存
,通常是指一些大對象不被回收的情況
1、通過jmap或設(shè)置jvm參數(shù)獲取堆內(nèi)存快照dump
2、通過工具, VisualVM去分析dump文件
,VisualVM可以加載離線的dump文件
3、通過查看堆信息的情況,可以大概定位內(nèi)存溢出是哪行代碼出了問題
4、找到對應(yīng)的代碼,通過閱讀上下文的情況,進(jìn)行修復(fù)即可
參考鏈接:【JVM】Java內(nèi)存泄露的排查思路?文章來源:http://www.zghlxwxcb.cn/news/detail-644345.html
24.CPU飆高排查方案與思路?
1.使用top命令查看占用cpu的情況
2.通過top命令查看后,可以查看是哪一個進(jìn)程占用cpu較高
3.使用ps命令查看進(jìn)程中的線程信息
4.使用jstack命令查看進(jìn)程中哪些線程出現(xiàn)了問題,最終定位問題
參考鏈接:【JVM】CPU飆高排查方案與思路文章來源地址http://www.zghlxwxcb.cn/news/detail-644345.html
到了這里,關(guān)于【高頻面試題】JVM篇的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!