Java內存區(qū)域與內存溢出異常
運行時數(shù)據(jù)區(qū)域
程序計數(shù)器:字節(jié)碼的行號指示器勺像;每條線程都需要有一個獨立的程序計數(shù)器济瓢;“線程私有”;唯一沒有OOMError情況的區(qū)域括袒。
Java虛擬機棧(Hot Spot本地方法棧):Java方法執(zhí)行的內存模型厦滤;“線程私有”援岩;局部變量表存放了各種基本類型、對象引用掏导、returnAddress類型享怀;拋出SOFError和OOMError;單線程下只拋出SOFError趟咆,不斷建立線程可以產(chǎn)生OOMError添瓷。
Java堆:存放對象實例;拋出OOMError異常值纱。
方法區(qū)(1.7):存儲已被虛擬機加載的類信息鳞贷、常量、靜態(tài)常量虐唠、即時編譯器變異后的代碼等數(shù)據(jù)悄晃;1.8后類元數(shù)據(jù)放到本地內存、常量池和靜態(tài)變量放到 Java 堆凿滤;拋出OOMError異常妈橄。
HotSpot虛擬機對象
對象的創(chuàng)建:對象規(guī)整內存分配方式“指針碰撞”;對象不規(guī)整內存分配方式:“空閑列表”翁脆;內存分配方式取決于垃圾收集器回收內存時是否有整理功能眷蚓。
保障對象內存分配線程安全兩種方法:分配時采用循環(huán)CAS保證原子性;本地線程分配緩沖(TLAB)反番。
對象的內存結構:對象頭沙热、實例數(shù)據(jù)和對齊填充叉钥;對象頭一部分用于存儲對象自身的運行時數(shù)據(jù)(哈希碼、GC年代篙贸、鎖相關信息)投队、一部分為類型指針;實例數(shù)據(jù)存儲字段內容爵川;對齊填充保證對象大小是8字節(jié)的整數(shù)倍敷鸦。
對象的訪問定位:通過句柄訪問對象的棧里存儲句柄地址,句柄池里存儲到對象實例的指針(指向堆)和到對象類型數(shù)據(jù)的指針(指向方法區(qū)或本地內存)寝贡;通過直接指針訪問對象的棧里存儲的實例對象的指針扒披,實例對象對象頭里存儲到對象類型數(shù)據(jù)的指針。
垃圾收集器與內存分配策略
回收對象判斷
引用計數(shù)法:計算引用數(shù)量圃泡,有引用時就不回收碟案;實現(xiàn)簡單;難以解決循環(huán)引用颇蜡。
可達性分析算法:從GC Roots節(jié)點開始搜索价说,對象不可達則回收;GC Roots包括虛擬機棧中應用的對象风秤、方法區(qū)中類靜態(tài)屬性引用的對象熔任、方法區(qū)中常量引用的對象、本地方法棧JNI引用的對象唁情。
強引用:GC永不回收疑苔。
軟引用:SoftReference;系統(tǒng)將要發(fā)生內存溢出異常之前進行二次回收甸鸟。
弱引用:WeakReference惦费;下一次GC回收。
虛引用:PhantomReference抢韭;對象被GC時收到一個系統(tǒng)通知薪贫。
回收方法區(qū):回收廢棄常量和無用的類;該類無實例刻恭、ClassLoader被回收瞧省、對應的java.lang.Class對象無引用的類可以被回收。
垃圾收集算法
標記清除算法:效率問題鳍贾;空間問題鞍匾。
復制算法(新生代):解決效率問題;Eden和Survivor的大小比例是8:1骑科;分配擔保橡淑。
標記整理(老年代):解決空間問題。
HotSpot算法實現(xiàn)
枚舉根節(jié)點:OopMap存放對象引用位置幫助可達性分析咆爽;
安全點:安全點才能暫停開始GC梁棠;安全點太少會使得GC等待時間太長置森;太長會生成大量OopMap增大運行負荷。
安全區(qū)域:在一段代碼中引用關系沒有變化符糊,擴展的安全點凫海。
搶先式中斷:GC停止所有線程,不在安全點的線程恢復并跑到安全點再停止男娄。
主動式中斷:各線程在安全點或創(chuàng)建對象需要分配內存的地方輪詢中斷標志位行贪,自己主動中斷。
垃圾收集器
Serial\Serial Old收集器:新生代復制沪伙;老年代標記-整理;都需要“STW”县好;對于Client模式下的虛機是個好選擇围橡。
ParNew\ParNew Old收集器:多線程版Serial;Server模式下的首選新生代GC收集器(唯一可以與CMS配合的)缕贡。
Parallel Scavenge收集器:復制算法翁授;CMS等收集器的關注點是盡可能地縮短“STW”時間(適合交互程序),Parallel Scavenge收集器的目的是達到一個可控制的吞吐量(適合后臺程序)晾咪。
CMS收集器:初始標記收擦、并發(fā)標記、重新標記谍倦、并發(fā)清除四個步驟塞赂;初始標記和重新標記需要“STW”;初始標記只標記能和GC Roots直接關聯(lián)的對象昼蛀;并發(fā)標記一邊全局搜索一邊記錄引用變化宴猾;重新標記將全局搜索與引用變化的對象做并集;并發(fā)清除進行GC叼旋;CMS占用CPU較高仇哆;無法處理浮動垃圾;需要碎片整理夫植。
G1收集器:并行與并發(fā)讹剔;分代收集;空間整合详民;可預測的停頓延欠;將整個Java堆劃分為多個大小相等的獨立區(qū)域,優(yōu)先回收價值最大的區(qū)域沈跨;初始標記衫冻、并發(fā)標記、最終標記谒出、篩選回收四個步驟(與CMS類似)隅俘;
Remembered Set:用以存放堆的其他部分對本部分的引用以減少對其他部分GC Roots的搜索(新生代存放老年代對其的引用)(G1區(qū)域存放其他區(qū)域的引用)邻奠;以空間換時間。
內存分配與回收策略
對象優(yōu)先在Eden分配为居;大對象直接進入老年代碌宴;長期存活的對象進入老年代。
動態(tài)對象年齡判定:如果Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半蒙畴,年齡大于或等于該年齡的對象直接進入老年代贰镣。
空間分配擔保:Minor GC之前,虛機先檢查老年代最大可用空間是否大于新生代所有對象總空間膳凝,若大于則Minor GC,若不大于檢查參數(shù)是否允許擔保失敗碑隆,如果允許則檢查老年代最大可用空間是否大于新生代晉級老年代的平均大小,若大于則Minor GC蹬音,否則Full GC上煤。
類文件與類加載機制
類文件
類文件:平臺無關性;語言無關性著淆;8位字節(jié)為基礎單位的二進制流劫狠。
類加載的時機
類的生命周期:加載、驗證永部、準備独泞、解析、初始化苔埋、使用懦砂、卸載。
觸發(fā)類初始化:遇到new组橄、getstatic孕惜、putstatic、invokestatic這4條字節(jié)碼命令時晨炕;reflect方法反射調用時衫画;初始化子類的時候父類還沒初始化(接口在被使用時才會初始化);虛擬機啟動時初始化主類瓮栗;動態(tài)語言支持解析出REF_getStatic削罩、REF_putStatic、REF_invokeStatic的句柄并且對應的類沒有初始化時费奸。
類加載的過程
加載:加載器通過類的全限定名獲取定義此類的二進制字節(jié)流(從ZIP包弥激、網(wǎng)絡、運行時計算生成愿阐、其他文件生成微服、數(shù)據(jù)庫中讀取)缨历;將這個字節(jié)流所代表的靜態(tài)存儲結構轉化為方法區(qū)的運行時數(shù)據(jù)結構以蕴;在內存中生成一個代表這個類的Class對象糙麦,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口。
數(shù)組加載:不通過類加載器創(chuàng)建丛肮、由虛擬機直接創(chuàng)建赡磅;引用類型數(shù)組遞歸加載組件類型、在加載該組件類型的類加載器的類名稱空間上被標識宝与;基本類型數(shù)組與引導類加載器關聯(lián)焚廊;數(shù)組可見性與組件可見性一致(基本類型為public)。
驗證:虛擬機對自身保護的一項重要工作习劫;文件格式驗證(驗證字節(jié)流是否符合Class文件格式的規(guī)范)咆瘟、元數(shù)據(jù)驗證(對類的元數(shù)據(jù)信息進行語義校驗)、字節(jié)碼驗證(通過數(shù)據(jù)流和控制流分析確定程序語義是合法符合邏輯的诽里、通過StackMapTable優(yōu)化)袒餐、符號引用驗證(確保解析動作能正常執(zhí)行);-Xverify:none關閉大部分類驗證措施须肆、縮短虛擬機類加載時間匿乃。
準備:正式為類變量在方法區(qū)中分配內存并設置類變量初始值(static)桩皿。
解析:將常量池內的符號引用替換為直接引用豌汇;符號引用的字面量形式明確定義在Java虛擬機規(guī)范的Class文件格式中;直接引用可以是直接指向目標的指針泄隔、相對偏移量或是一個能間接定位到目標的句柄拒贱;虛擬機可以根據(jù)需要判斷在類被加載器加載時就對常量池中的符號引用進行解析還是等到一個符號引用將要被使用前才去解析。
初始化:執(zhí)行類構造器方法(編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊中的語句合并產(chǎn)生的)的過程佛嬉;編譯器收集的順序是由語句在源文件中出現(xiàn)的順序決定的逻澳;虛擬機會保證子類類構造器方法執(zhí)行之前父類的方法已經(jīng)執(zhí)行完畢;類構造器方法不是必須的暖呕;執(zhí)行接口的類構造器方法不需要先執(zhí)行父接口的類構造器方法斜做,只有當靜態(tài)變量被使用時父接口才會初始化;虛擬機會保證類構造器方法在多線程環(huán)境中被正確地加鎖湾揽、同步(單例的線程安全實現(xiàn)方法之一)瓤逼。
類加載器
類加載器:實現(xiàn)通過一個類的全限定名來獲取描述此類的二進制流這一動作的代碼模塊;由加載類的類加載器和這個類本身一同確立類在Java虛擬機中的唯一性库物;啟動類加載器(Bootstrap ClassLoader)由C++實現(xiàn)霸旗,虛擬機的一部分;其他類加載器由Java語言實現(xiàn)戚揭,獨立于虛擬機外部诱告,全部繼承自抽象類java.lang.ClassLoader;其他類加載器包括擴展類加載器(負責加載<JAVA_HOME>\lib\ext目錄中的或java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類庫民晒、開發(fā)者可直接使用)精居、應用程序類加載器(負責加載用戶類路徑上所指的類庫锄禽,開發(fā)者可直接使用)。
雙親委派模型:除了頂層的啟動類加載器外箱蟆,其余的類加載器都應當有自己的父類加載器沟绪,父子關系使用組合實現(xiàn);如果一個類加載器收到了類加載請求空猜,它首先不會自己嘗試加載這個類绽慈,而是把這個請求委派給父類加載器去完成;線程上下文加載器辈毯、OSGi模塊化熱部署不遵守雙親委派模型坝疼。
虛擬機字節(jié)碼執(zhí)行引擎
運行時棧幀結構
棧幀:用于支持虛擬機進行方法調用和方法執(zhí)行的數(shù)據(jù)結構,虛擬機運行時數(shù)據(jù)區(qū)中的虛擬機棧中的棧元素谆沃;每一個方法從調用開始至執(zhí)行完成的過程钝凶,都對應著一個棧幀在虛擬機棧里面從入棧到出棧的過程;棧幀包括局部變量表唁影、操作數(shù)棧耕陷、動態(tài)連接、方法返回地址和一些額外的附加信息据沈;在活動線程中位于棧頂?shù)臈攀怯行У挠茨蔀楫斍皸划斍皸嚓P聯(lián)的方法稱為當前方法锌介。
局部變量表:用于存放方法參數(shù)和方法內部定義的局部變量嗜诀;實例方法局部變量表的第0位索引Slot默認是用于傳遞方法所屬對象實例的引用(this)。
操作數(shù)棧:先入后出棧孔祸;方法的執(zhí)行過程中會有各種字節(jié)碼指令往操作數(shù)棧中寫入和提取內容(出棧隆敢、入棧)。
動態(tài)連接:每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用崔慧,持有這個引用是為了支持方法調用過程中的動態(tài)連接拂蝎。
方法返回地址:正常完成出口中PC計數(shù)器的值作為返回地址;異常完成出口(拋異常)返回地址通過異常處理器表來確定惶室;方法退出的過程等同于把當前棧幀出棧温自。
附加信息:一些規(guī)范里沒有描述的信息;動態(tài)連接拇涤、方法返回地址與其他附加信息全部歸為棧幀信息捣作。
方法調用
解析:調用目標在程序代碼寫好、編譯器進行編譯時就必須確定下來鹅士;靜態(tài)方法券躁、私有方法、實例構造器、父類方法在類加載的時候就會把符號引用解析為該方法的直接引用也拜。
靜態(tài)分派:靜態(tài)類型決定重載版本以舒;所有依賴靜態(tài)類型來定位方法執(zhí)行版本的分派動作稱為靜態(tài)分派;靜態(tài)分派發(fā)生在編譯階段慢哈。
動態(tài)分派:在運行期根據(jù)實際類型確定方法執(zhí)行版本的分派過程蔓钟。
單分派多分派:Java是一門靜態(tài)多分派、多態(tài)單分派的語言卵贱。
虛擬機動態(tài)分派的實現(xiàn):為類在方法區(qū)建立一個虛方法表滥沫,虛方法表中存放著各個方法的實際入口地址。
基于棧的字節(jié)碼解釋執(zhí)行引擎
基于棧的指令集架構:指令流中的指令大部分都是零地址指令键俱,依賴操作數(shù)棧進行工作兰绣;可移植;執(zhí)行速度稍慢编振。
編譯期優(yōu)化
Javac編譯器
編譯過程:解析與填充符號表過程缀辩、插入式注解處理器的注解處理過程、語義分析與字節(jié)碼生成過程踪央。
解析:包括詞法分析和語法分析兩個過程臀玄;詞法分析是將源代碼的字符流轉變?yōu)闃擞浖希徽Z法分析是根據(jù)Token序列構造抽象語法樹的過程畅蹂;抽象語法樹是一種用來描述程序代碼語法結構的樹形表示方式健无,語法樹的每一個節(jié)點都代表這程序代碼中的一個語法結構。
填充符號表:是一組符號地址和符號信息構成的表格魁莉;符號表中所登記的信息在編譯的不同階段都要用到睬涧。
注解處理器:在編譯期間對注解進行處理募胃;可以讀取旗唁、修改、添加抽象語法樹中的任意元素痹束。
語義分析:語義分析分為標注檢查以及數(shù)據(jù)及控制流分析检疫;標注檢查步驟檢查的內容包括諸如變量使用前是否已被聲明、變量與賦值之間的數(shù)據(jù)類型是否能夠匹配等祷嘶;常量折疊屎媳;數(shù)據(jù)及控制流分析是對程序上下文邏輯更進一步的驗證;數(shù)據(jù)及控制流分析可以檢查出諸如程序局部變量在使用前是否賦值论巍、方法的每條路徑是否都有返回值烛谊、是否所有的受查異常都被正常處理等問題。
解語法糖:語法糖指在計算機語言中添加的某種語法嘉汰,這種語法對語言的功能并沒有影響丹禀,但是更方便程序員使用;在編譯階段將語法糖還原簡單的基礎語法結構稱為解語法糖。
字節(jié)碼生成:不僅是把前面各個步驟所生成的信息轉化為字節(jié)碼寫到磁盤中双泪,編譯器還進行了少量的代碼添加和轉換工作持搜;生成類構造器;代碼替換優(yōu)化程序焙矛。
語法糖
泛型與類型擦除:參數(shù)化類型葫盼;Java實現(xiàn)為偽泛型。
自動裝箱村斟、拆箱與遍歷循環(huán):使用最多的語法糖贫导。
條件編譯:根據(jù)布爾常量值的真假,編譯器將會把分支中不成立的代碼塊消除蟆盹。
運行期優(yōu)化
HotSpot虛擬機內的即時編譯器
解釋器與編譯器:當程序需要迅速啟動和執(zhí)行的時候脱盲,解釋器可以首先發(fā)揮作用,省去編譯的時間日缨,立即執(zhí)行钱反;解釋器把越來越多的代碼編譯成本地代碼可以獲取更高地效率;HotSpot虛擬機中內置了兩個即時編譯器分別為Client Compiler(C1)和Server Compiler(C2)匣距;混合模式面哥、編譯模式、解釋模式毅待、分層編譯模式(C1和C2同時工作)尚卫。
編譯對象及觸發(fā)條件:編譯對象為被多次調用的方法、被多次執(zhí)行的循環(huán)體尸红。熱點檢測方法分別為基于采樣的熱點探測(檢查棧頂)和HotSpot采用的基于計數(shù)器的熱點探測(為每個方法建立計數(shù)器統(tǒng)計執(zhí)行次數(shù))吱涉。
基于計數(shù)器的熱點探測方法:方法調用計數(shù)器和回邊計數(shù)器;方法調用計數(shù)器在方法被調用后計數(shù)器加1外里、和回邊計數(shù)器計數(shù)和超過閾值后進行編譯怎爵、過一段時間后計數(shù)減半;回邊計數(shù)器的作用是統(tǒng)計一個方法中循環(huán)體代碼執(zhí)行的次數(shù)盅蝗、和方法調用計數(shù)器計數(shù)和超過閾值后進行編譯鳖链、計數(shù)不會衰減。
C1編譯過程:簡單快速墩莫、局部優(yōu)化芙委;一個平臺獨立的前端在完成一部分基礎優(yōu)化(如方法內聯(lián)、常量傳播)后將字節(jié)碼構造成一種高級中間代碼表示狂秦;一個平臺相關的后端在高級中間代碼上完成另外一下優(yōu)化(如空值檢查消除灌侣、范圍檢查消除)后生成低級中間代碼表示;在平臺相關的后端使用線性掃描算法在低級中間代碼上分配寄存器并做窺孔優(yōu)化裂问,然后產(chǎn)生機器碼侧啼。
c2編譯過程:充分優(yōu)化過的高級編譯器玖姑;會執(zhí)行所有經(jīng)典的優(yōu)化動作和一些與Java語言特性密切相關的優(yōu)化技術。
編譯優(yōu)化技術
公共子表達式消除:語言無關的經(jīng)典優(yōu)化技術之一慨菱;用計算結果直接代替重復的公共子表達式焰络,避免重復計算。
數(shù)組邊界檢查消除:語言相關的經(jīng)典優(yōu)化技術之一符喝;省略一些不必要的數(shù)組邊界檢查操作闪彼。
方法內聯(lián):最重要的優(yōu)化技術之一;消除方法調用的成本协饲、為其他優(yōu)化手段建立良好的基礎畏腕。
逃逸分析:最前沿的優(yōu)化技術之一;為其他優(yōu)化手段提供依據(jù)的分析技術茉稠;證明一個對象不會逃逸到方法或線程之外描馅,則可以進行棧上分配、同步消除而线、標量替換等優(yōu)化铭污。
Java內存模型與線程
Java內存模型
主內存與工作內存:所有的變量都存儲在主內存中;每條線程有自己的工作內存膀篮;工作內存中保存了被該線程使用到的變量的主內存副本拷貝嘹狞;線程對變量的所有操作都必須在工作內存中進行。
內存間交互操作:Java內存模型定義了8種操作來完成主內存與工作內存的交互誓竿。
volatile型變量的特殊規(guī)則:可見性磅网、有序性;讀前加內存屏障筷屡,寫后加內存屏障涧偷;
原子性:synchronized保證大范圍的原子性。
可見性:volatile毙死、synchronized燎潮、final保證可見性。
有序性:volatile规哲、synchronized保證有序性跟啤。
happens-before:Java內存模型定義的兩項操作之間的偏序關系诽表;程序次序規(guī)則唉锌、管程鎖定規(guī)則、volatile變量規(guī)則竿奏、線程啟動規(guī)則袄简、線程終止規(guī)則、線程中斷規(guī)則泛啸、對象終結規(guī)則绿语、傳遞性。
Java與線程
線程的實現(xiàn):內核線程實現(xiàn)、使用用戶線程實現(xiàn)和使用用戶線程加輕量級進程混合實現(xiàn)吕粹。
線程狀態(tài):新建种柑;運行;無限期等待匹耕;限期等待聚请;阻塞;結束稳其。
線程安全與鎖優(yōu)化
線程安全
線程安全:當多個線程訪問一個對象時驶赏,如果不用考慮這些線程在運行時環(huán)境下的調度和交替執(zhí)行,也不需要進行額外的同步既鞠,或者在調用方進行任何其他的協(xié)調操作煤傍,調用這個對象的行為都可以獲得正確的結果,那這個對象就是線程安全的嘱蛋。
線程安全程度:不可變蚯姆、絕對線程安全、相對線程安全洒敏、線程兼容蒋失、線程對立。
互斥同步:synchronized桐玻、ReentrantLock篙挽。
非阻塞同步:CAS;Atomic镊靴;
無同步方案:可重入代碼铣卡;線程本地存儲。
鎖優(yōu)化
自旋鎖與自適應鎖:讓線程不掛起偏竟,等待釋放鎖從而節(jié)約掛起恢復線程的資源煮落;由前一次在同一個鎖上的自旋時間及鎖的擁有者來決定本次自旋的等待時間。
鎖消除:即時編譯器運行時對一些代碼上要求同步踊谋,但是被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進行消除蝉仇;鎖消除的主要判定依據(jù)來源于逃逸分析的數(shù)據(jù)分析。
鎖粗化:虛擬機探測到有一串零碎的操作都對同一個對象加鎖殖蚕,將會把加鎖同步的范圍擴展到整個操作序列的外部轿衔。
輕量級鎖:使用CAS在沒有多線程競爭的前提下減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。
偏向鎖:消除數(shù)據(jù)在無競爭情況下的同步原語睦疫,進一步提高程序的運行性能害驹。