- JVM的內(nèi)存劃分

- 類加載器
四種類加載器:
- 啟動類加載器:C++編寫,在Java中看不到
- 擴(kuò)展類加載器:ExtClassLoader
- 應(yīng)用類加載器:AppicationClassLoader
- 自定義類加載器:創(chuàng)建一個類繼承java.lang.ClassLoader,定制類加載方式
關(guān)系:啟動類加載器是擴(kuò)展類加載器的父加載器,擴(kuò)展類加載器是應(yīng)用類加載器的父加載器.
雙親委派模型:

優(yōu)點(diǎn):保證了類的唯一性,避免類的重復(fù)加載,父加載器加載了某個類,子加載器就不加載了,全類名是唯一標(biāo)識
安全性考慮,比如String等Jre自帶的核心Api,由最父類的啟動類加載器加載,不由它的子類加載器惡意替換.
- 方法棧
堆棧先進(jìn)后出,后進(jìn)先出,像是一個彈簧,壓棧,彈棧概念,方法每次被調(diào)用,都會產(chǎn)生一個棧幀,方法內(nèi)的局部變量,方法釋放,變量也釋放
棧幀的結(jié)構(gòu):
局部變量表:方法執(zhí)行時的參數(shù)、方法體內(nèi)聲明的局部變量
操作數(shù)棧:存儲中間運(yùn)算結(jié)果,是一個臨時存儲空間
幀數(shù)據(jù)區(qū):保存訪問常量池指針,異常處理表
操作數(shù)棧的存在,保存一個臨時存儲空間,例如數(shù)值交換int a = 5 和int b = 6 交換
棧溢出異常(StackOverflowErro):
例如一個沒有退出機(jī)制的遞歸方法,只占用不結(jié)束,一直產(chǎn)生棧幀,一直在申請空間,把??臻g擠滿,??臻g一直不釋放,一直在堆積,把棧內(nèi)存中的空間耗盡
某一個線程拋出『棧溢出異常』,會導(dǎo)致其他線程也崩潰嗎?
不會,線程對棧內(nèi)存空間的使用方式是彼此隔離的。每個線程都是在自己獨(dú)享的空間內(nèi)運(yùn)行,反過來也可以說,這個空間是當(dāng)前線程私有的。
- 堆
工作機(jī)制:
新new出來的對象最先放在新生代的伊甸區(qū),
伊甸區(qū)的使用空間達(dá)到一定的值后觸發(fā)Minor GC垃圾回收,
沒有被這個小GC回收的會成為幸存者,去往幸存者區(qū),
幸存者區(qū)有from和to2個區(qū),(誰空誰為to區(qū),意味著這2個區(qū)是互相彼此交換的)
from區(qū)快滿了就去往了to區(qū),此時from變成了空成為了to區(qū),而to區(qū)就變成了from區(qū)
如果一個對象經(jīng)過了15次GC還沒有被垃圾回收,那么就會去往老年代區(qū)保存,
如果幸存者區(qū)滿了,即是沒有15次GC,這個對象也會去往老年代區(qū)保存
說明:
eden區(qū)存儲的主要是短暫臨時,生命周期很短的對象
幸存者區(qū)是一個中間地帶
老年代區(qū)是存放生命周期很長的對象,例如IOC管理的對象(Controller,Service,Mapper等等),線程池對象,數(shù)據(jù)庫連接池對象等等...
不被回收是因?yàn)闂V幸恢庇袀€變量指引著這個地址,相當(dāng)于有一根線連接起來了,如果這個線斷了,就是不指向這個對象的地址了,那么就會被GC垃圾回收機(jī)制回收,老年代區(qū)的對象就是一直有這根指引著
堆溢出異常(OutOfMemeory):
包括兩種Java.heap.space和PermGen space,前者是一直在new Object而且不回收,超出了堆內(nèi)存
后者PermGen space翻譯過來也很好理解,permanent generation永久代的問題,是永久代(方法去)加載的時候,類實(shí)在太多了,就導(dǎo)致方法區(qū)直接溢出了
GC的使用
為什么要GC:
程序運(yùn)行中,會產(chǎn)生很多對象,如果不處理垃圾對象,那么一直累加會導(dǎo)致內(nèi)存耗盡,程序崩潰,GC就是處理不使用的垃圾對象,釋放內(nèi)存
如何找出垃圾對象:
引用計數(shù)法(不靠譜,如果有循環(huán)引用的對象,不能夠標(biāo)記到)
可達(dá)性分析(從GC Roots對象出發(fā),不可達(dá)的對象就是要清理的對象)
GC的算法:
基本算法
引用計數(shù)法:使用引用標(biāo)記法標(biāo)記對象被引用的次數(shù),引用加1,刪除減1,當(dāng)引用計數(shù)為0 則GC
標(biāo)記清除法:當(dāng)堆的有效內(nèi)存即將耗盡的時候,暫時掛起程序(stop the world),從根對象開始遍歷所有的對象,然后再清楚所有沒有被標(biāo)記到的對象
標(biāo)記壓縮法:標(biāo)記清除法的升級,同樣當(dāng)堆有效內(nèi)存即將耗盡,暫時掛起,從根對象遍歷所有對象,然后中間多一步壓縮, 移動所有的可達(dá)對象到堆內(nèi)存的同一個區(qū)域中,使他們緊湊的排列在一起,從而將所有非可達(dá)對象釋放出來的空閑內(nèi)存都集中在一起,通過這樣的方式來達(dá)到減少內(nèi)存碎片的目的。
復(fù)制算法:復(fù)制算法的核心就是,將原有的內(nèi)存空間一分為二,每次只用其中的一塊,在垃圾回收時,將正在使用的對象復(fù)制到另一個內(nèi)存空間中,并依次排列,然后將該內(nèi)存空間清空,交換兩個內(nèi)存的角色,完成垃圾的回收。
綜合算法
分代算法:前面介紹了多種回收算法,每一種算法都有自己的優(yōu)點(diǎn)也有缺點(diǎn),誰都不能替代誰,所以根據(jù)垃圾回收對象的特點(diǎn)進(jìn)行選擇,才是明智的。分代算法其實(shí)就是這樣的,根據(jù)回收對象的特點(diǎn)進(jìn)行選擇新生代使用復(fù)制算法,老年代使用標(biāo)記清除或者標(biāo)記壓縮方法
分區(qū)算法:有點(diǎn)類似微服務(wù)那種分布式,把堆空間劃分為多個不同的小區(qū)間,每個小區(qū)間獨(dú)立使用獨(dú)立回收,這樣可以控制一次回收多個區(qū)間,并不像標(biāo)記清除和壓縮方法一樣,需要把程序全部掛起一段時間
JVM的常用參數(shù):
-Xms 堆內(nèi)存的初始化大小(建議初始化大小和最大值一樣,這樣就不需要多次提交申請)
-Xmx 堆內(nèi)存的最大值
-Xmn 新生代內(nèi)存的大小
-XX:PermSize 永久代的大小
-XX:MaxPermSize 永久代的初始化大小