JVM
JVM的內(nèi)存區(qū)域
線程私有區(qū)域:程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法區(qū)
線程共享區(qū)域:方法區(qū)、堆
直接內(nèi)存
JDK的NIO模板提供基于channel與buffer的IO操作方式是基于堆外內(nèi)存的。
JVM運(yùn)行時(shí)內(nèi)存
JVM運(yùn)行時(shí)內(nèi)存又叫JVM堆。其中新生代默認(rèn)占1/3,老年代2/3,永久代占非常少的堆內(nèi)存空間。
新生代由分為eden區(qū)(8/10),SurvivorFrom(1/10),Survivor(1/10)。
老年代主要存在長生命周期的對(duì)象和大對(duì)象。老年代GC叫MajorGC。在進(jìn)行MajorGC之前,JVM會(huì)進(jìn)行一次MinorGC,如果GC后仍然出現(xiàn)老年代且老年代空間不足或無法找到足夠大的連續(xù)內(nèi)存分配給新的大對(duì)象,會(huì)觸發(fā)MajorGC。
MajorGC采用標(biāo)記清楚算法。因?yàn)橐獟呙枥夏甏袑?duì)象,MajorGC耗時(shí)較長,且容易產(chǎn)生內(nèi)存碎片。如果老年代沒有內(nèi)存空間可以分配時(shí),會(huì)跑出Out Of Memory異常。
永久代主要存放Class和Meta的信息。Class在被類加載時(shí)放入永久代。GC不會(huì)在程序運(yùn)行期間對(duì)永久代進(jìn)行清理,這也導(dǎo)致了永久代的內(nèi)存會(huì)隨著加載的class文件的增加而增加。在加載的Class文件過多時(shí),會(huì)拋出OOM。比如Tomcat引用JAR文件過多導(dǎo)致JVM內(nèi)存不足而無法啟動(dòng)。
JAVA8中,永久代已經(jīng)被元數(shù)據(jù)區(qū)(也叫作元空間)取代。元數(shù)據(jù)區(qū)和永久代類似,二者的最大區(qū)別是:元數(shù)據(jù)區(qū)并沒有使用虛擬機(jī)的內(nèi)存,而是直接使用操作系統(tǒng)的本地內(nèi)存。因此,元空間的大小不受JVM內(nèi)存的限制,只和操作系統(tǒng)的內(nèi)存有關(guān)。
常用的垃圾回收算法
1、標(biāo)記清除算法
標(biāo)記和清除兩個(gè)階段。清除后沒有重新整理可用空間,如果回收的小對(duì)象過多,則會(huì)引起內(nèi)存碎片化的問題。
2、復(fù)制算法
復(fù)制算法是為了解決內(nèi)存碎片化問題而設(shè)計(jì)的。內(nèi)存分為區(qū)域1、2、新生對(duì)象都在區(qū)域1內(nèi),區(qū)域1滿后對(duì)區(qū)域1進(jìn)行一次標(biāo)記,并將標(biāo)記后仍然存活的對(duì)象復(fù)制到區(qū)域2,再將區(qū)域1清理。
缺點(diǎn):1、內(nèi)存浪費(fèi) 2、長時(shí)間存活的對(duì)象頻繁在區(qū)域1、2之間來回復(fù)制,影響系統(tǒng)運(yùn)行效率。
3、標(biāo)記整理算法
標(biāo)記整理算法整合了標(biāo)記清楚、復(fù)制算法的優(yōu)點(diǎn),其標(biāo)記階段和標(biāo)記清楚算法相同,在標(biāo)記完成后將存活的對(duì)象移到內(nèi)存的另一端,然后清楚現(xiàn)在這段的對(duì)象并釋放內(nèi)存。
4、分代收集算法
前三個(gè)算法都無法對(duì)所有類型的對(duì)象進(jìn)行垃圾回收。因此,針對(duì)不同的對(duì)象類型,JVM采用了不同的垃圾回收算法。
JVM新生代對(duì)象數(shù)量多但是聲明周期短,每次存活的對(duì)象少,采用復(fù)制算法。JVM將新生代進(jìn)步分為Eden,Suvivor區(qū)。Survivor區(qū)又分為SurvivorFrom、SurvivorTo。JVM在運(yùn)行過程中主要使用Eden和SurvivorFrom,進(jìn)行垃圾回收是將存活對(duì)象復(fù)制到SurvivorTo,然后清理Eden和SruvivorFrom。
JVM老年代主要存放聲明周期較長的對(duì)象和大對(duì)象,因而每次只有少量非存活的對(duì)象被回收,因此采用標(biāo)記清楚算法。
在JVM中還有一個(gè)區(qū)域---方法區(qū)的永久代。永久代用來存儲(chǔ)class類、常量、方法描述等。在永久代主要回收廢棄的常量和無用的類。
在新生代的Eden和SurvivorFrom的內(nèi)存空間不足時(shí)會(huì)觸發(fā)一個(gè)GC,該過程被稱為MinorGC。如果此時(shí)SurvivorTo區(qū)無法找到連續(xù)的內(nèi)存空間存儲(chǔ)某個(gè)對(duì)象,則將這個(gè)對(duì)象直接存儲(chǔ)到老年代。若Survivor區(qū)的對(duì)象經(jīng)過一次GC后仍然存活,則將其年齡加1。默認(rèn)情況下,對(duì)象在年齡達(dá)到15時(shí),將被移到老年代。
5、分區(qū)收集算法
分區(qū)收集算法將整個(gè)堆空間劃分為連續(xù)的大小不同的小區(qū)域。在每個(gè)小區(qū)域單獨(dú)進(jìn)行內(nèi)存使用和垃圾回收。它可以根據(jù)系統(tǒng)可以接受的停頓時(shí)間,每次都快速回收若干個(gè)小區(qū)域中的內(nèi)存,以縮短回收時(shí)系統(tǒng)停頓的時(shí)間,最后以多次并行累加的方式逐步完成整個(gè)內(nèi)存區(qū)域的垃圾回收。
垃圾收集器
新生代
Serial 單線程復(fù)制算法
ParNew 多線程復(fù)制算法
Parallel Scavenge 多線程復(fù)制算法
老年代
CMS 多線程標(biāo)記清除算法
Serial Old 單線程標(biāo)記整理算法
Parallel Old 多線程標(biāo)記整理算法
G1 多線程標(biāo)記整理算法
G1
Java網(wǎng)絡(luò)模型編程
阻塞I/O模型
用戶線程發(fā)出I/O請(qǐng)求,內(nèi)核檢查數(shù)據(jù)是否就緒,此時(shí)用戶線程一致阻塞等待內(nèi)存數(shù)據(jù)就緒;在內(nèi)存數(shù)據(jù)就緒后,內(nèi)核將數(shù)據(jù)復(fù)制到用戶線程中,并返回I/0執(zhí)行結(jié)果到用戶線程,此時(shí)用戶線程將解除阻塞狀態(tài)并開始處理數(shù)據(jù)。
data = socket.read()
非阻塞I/O模型
非阻塞I/O模型指用戶線程在發(fā)起一個(gè)I/O操作后,無須阻塞便可以馬上得到內(nèi)核返回的一個(gè)結(jié)果。如果內(nèi)核返回的結(jié)果為false,則表示內(nèi)核數(shù)據(jù)還沒準(zhǔn)備好,需要稍后再發(fā)起I/O操作。準(zhǔn)備好了,用戶再請(qǐng)求就會(huì)正常返回。
在該模型中,用戶線程需要不斷詢問內(nèi)核數(shù)據(jù)是否就緒。
while(true){
? ? data = socket.read();
? ? if(data = true){
????????break;
? ? }}
多路復(fù)用I/O模型
Java NIO就是基于多路復(fù)用I/O模型實(shí)現(xiàn)的。在該模型中會(huì)有一個(gè)被稱為selector的線程不斷輪詢多個(gè)socket的狀態(tài),只有在socket有讀寫事件時(shí),才會(huì)用用戶線程進(jìn)行I/O操作。
對(duì)于該模型來說,在事件響應(yīng)體(消息體)很大時(shí),selector線程就會(huì)成為性能瓶頸,導(dǎo)致后續(xù)的事件遲遲得不到處理。在實(shí)際應(yīng)用中,一般不建議做復(fù)雜邏輯運(yùn)算,只做數(shù)據(jù)的接收和轉(zhuǎn)發(fā),將具體的業(yè)務(wù)操作轉(zhuǎn)發(fā)給后面的業(yè)務(wù)線程處理。
信號(hào)驅(qū)動(dòng)I/O模型
用戶線程發(fā)起一個(gè)I/O請(qǐng)求操作后,系統(tǒng)會(huì)為該請(qǐng)求對(duì)應(yīng)的socket注冊(cè)一個(gè)信號(hào)函數(shù),然后用戶線程就可以繼續(xù)執(zhí)行其他業(yè)務(wù)邏輯;在內(nèi)核數(shù)據(jù)就緒時(shí),系統(tǒng)會(huì)發(fā)送一個(gè)信號(hào)到用戶線程,用戶線程在接收到該信號(hào)后,就會(huì)在信號(hào)函數(shù)中調(diào)用對(duì)應(yīng)的I/O讀寫操作完成實(shí)際的I/O請(qǐng)求操作。
異步I/O模型
在異步I/O模型中,用戶線程會(huì)發(fā)起一個(gè)Asynchronous read操作到內(nèi)核,內(nèi)后收到后立刻返回一個(gè)狀態(tài),來說明請(qǐng)求是否成功發(fā)起。在此過程中,用戶線程不會(huì)阻塞。內(nèi)核等數(shù)據(jù)準(zhǔn)備完成并將數(shù)據(jù)復(fù)制到用戶線程,并發(fā)送一個(gè)信號(hào)到用戶線程,通知用戶線程Asynchronous讀操作已完成。用戶線程不需要關(guān)心整個(gè)I/O操作是如何進(jìn)行的,在接收到內(nèi)核返回的成功或失敗信號(hào)時(shí),說明I/O操作已經(jīng)完成,直接使用數(shù)據(jù)即可。
它和信號(hào)驅(qū)動(dòng)I/O的區(qū)別:
異步I/O模型中,I/O操作的兩個(gè)階段(請(qǐng)求發(fā)起,數(shù)據(jù)讀取)都是在內(nèi)核中完成的,最后發(fā)送一個(gè)信號(hào)通知用戶線程。用戶線程直接使用數(shù)據(jù),不需要再次調(diào)用I/O函數(shù)進(jìn)行具體的讀寫。因此用戶線程非阻塞。
信號(hào)驅(qū)動(dòng)模型中,用戶收到信號(hào)后,還需要調(diào)用I/O函數(shù)進(jìn)行實(shí)際的I/O讀寫操作。
JVM類加載機(jī)制
待填坑