java同步:
背景:因為這個設備的存儲讀寫速度跟處理器的讀寫速度差異比較大,所以為了保證效率,會有個高速緩存蛾娶,高速緩存的讀寫速度與處理器差不多佣谐,這個高速緩存就用來作為存儲設備和
1:lock和synchronized實現(xiàn):有兩個準則,第一個是同一個變量在同一時刻只允許一個線程對其lock刷允;第二個準則是在線程對一個變量進行unlock之前,必須把這個變量刷回住內存。這樣就保證了一個線程在操作變量時兔毒,別的線程沒法將這個變量load到線程的工作區(qū)域,而線程操作完成之后甸箱,變量已經(jīng)會刷新到主內存育叁,這個時候其它線程才能lock,再去load的時候肯定是最新的值芍殖,不會是過期的豪嗽,所以能達到自己想要的線程安全
2:volatile關鍵字的作用是:修改了變量的值,要馬上刷回到主內存中去豌骏,而線程在使用變量時必須從主內存中l(wèi)oad,這樣保證單個簡單操作時能比較線程安全
java運行時數(shù)據(jù)區(qū)域:
1:程序計數(shù)器(PC寄存器):用于存放執(zhí)行的字節(jié)碼行號龟梦,這個的用途在于多線程模型下,CPU會調度程序的運行窃躲,就會有這種情況计贰,A線程執(zhí)行到A方法的第十個字節(jié)指令后,CPU切換到B線程執(zhí)行B方法蒂窒,然后再切換會A之后躁倒,需要讓程序恢復到繼續(xù)執(zhí)行第十一個字節(jié)碼指令荞怒,程序計數(shù)器就用來記錄這個,所以它是線程私有的秧秉。
2:java虛擬機棧:與線程的生命周期保持一致褐桌,一個方法對應了虛擬機中的一個棧幀,一個方法的調用對應的就是棧幀出棧入棧的過程福贞,棧幀中存儲的是局部變量撩嚼,操作棧,傳入的參數(shù)和返回值挖帘。局部變量包括int,byte,double之類這種基本變量也包括對象引用(這個兩種完丽,一種就是指向對象在堆中的地址,堆中的對象會存儲著類在方法區(qū)中的指針拇舀;另一種是指向的是句柄池中的地址逻族,句柄池中的地址由指向堆和方法區(qū)中類的地址)和returnAddress
3:本地方法棧和2差不多,知識用來用于native的方法
4:堆:是專賣用來存放對象實例的地方骄崩,并且基本所有的對象都在這分配聘鳞,除了JIT技術和一些亂七八糟的技術導致一丟丟實例不在這。因此是GC進行垃圾收集的主要地方也稱作GC堆要拂,再就是因為當前垃圾回收主要是分代進行回收抠璃,所以堆里會按代進行分配。java堆會隨著虛擬機啟動而建立脱惰,內存由各個線程共享搏嗡。
5:方法區(qū):方法區(qū)也是線程共享的,存儲著加載的類信息拉一,靜態(tài)變量采盒,常量和即時編譯器編譯的代碼。近乎是永久代蔚润。
6:運行時常量池:是方法區(qū)的一部分磅氨,在class文件中除了記錄類型的版本、方法嫡纠、字段烦租、接口等描述信息外,還有一項信息是常量池除盏,常量池里存的是各種字面常量叉橱,符號引用,這些信息加載后會放在常量池里痴颊。與java文件記錄的常量池相比赏迟,這個不規(guī)定只在編譯期產(chǎn)生屡贺,因此可以后期運行時候加蠢棱。
7:直接內存:這個不是運行時數(shù)據(jù)區(qū)域存儲的锌杀,用于干的是natvie和java搞得,可以看下nio之后再理解這個
對象訪問:涉及到堆泻仙,棧糕再,方法區(qū),例如Object object=new Object();new Object()這個涉及到的是在堆上分配內存玉转,存儲來Object類型對象所有實例數(shù)據(jù)值突想,然后object的一些類型,比方說父類究抓,實現(xiàn)的接口猾担,對象類型、方法等在方法區(qū)記錄刺下。object變量作為一個reference在棧上記錄
一直在堆上分配內存的話绑嘹,會造成oom,這個的話橘茉,后邊會跟著后邊會跟著heap相關的提示
GC:
程序計數(shù)器工腋,java虛擬機棧,本地棧都是隨著線程結束而消亡畅卓,所以不用太考慮垃圾回收的問題擅腰。棧中的棧幀隨著方法調用而出棧入棧,并且在類的結構定了之后翁潘,棧的大小也就知道了趁冈。所以這幾部分的內存都是隨著方法結束或者隨著線程結束而被自動回收掉。不用考慮垃圾收集的事情
如何判斷對象已死:
1:引用計數(shù)法唐础,有地方引用就加一箱歧,引用失效時候減一,當技術為0時候一膨,就給回收了呀邢,但是一般商用的都不用這個,因為不好解決循環(huán)引用的情況
2:GC Root:就是通過一系列GC Root對象作為基礎豹绪,然后以這些對象作為頭向下搜索价淌,如果有對象不能跟GC ROOT對象鏈在一起,就可以回收瞒津。作為GC Root的來源是:1棧中引用的對象 2:方法區(qū)中的靜態(tài)變量 3方法區(qū)中的常量 4:native方法引用的對象
引用:java中引用有四種蝉衣,強引用,軟引用巷蚪,弱引用病毡,虛引用
垃圾收集算法:
1:標記-清除法:這個簡單理解就是先把需要收集的對象標記出來,然后再清理掉屁柏,問題有兩個啦膜,效率不夠高有送,標記和清理的效率都不高,而是清除之后存活的對象不是挨著的僧家,比較碎片化雀摘,導致分配連續(xù)大量內存的話 需要再進行垃圾收集
2:復制算法:這個很簡單就是把內存分成兩份,A份滿了之后八拱,把存貨的對象復制到B阵赠,然后把A刪了即可,然后進行來回的循環(huán)即可肌稻,問題在于這樣會浪費百分之五十內存清蚀,而大多數(shù)對象生命周期又比較短,所以根據(jù)這個爹谭,再進行分配內存時候不是百分之五十這么分的轧铁,會一個Eden,兩個Survivor,比例是8:1旦棉,這樣每次會用掉E+一個S齿风,滿了之后,會復制到另一個S绑洛。救斑。。如果S不夠用就復制到老年代≌嫱停現(xiàn)代虛擬機的新年代一半用的是這種算法
3:標記-整理算法:基本思路跟標記-清除算法類似脸候,差別在于在清理時候,存活的對象會向頭部移動绑蔫,這樣保證內存是連續(xù)的
4:分代收集算法:沒有啥新思路运沦,就是按照對象的生命周期,分成新年代和老年代配深,然后根據(jù)這兩個不同的表現(xiàn)携添,新年代采用復制算法,老年代采用標記清理或者標記整理算法篓叶。
垃圾收集器:基于上邊方法烈掠,然后虛擬機實現(xiàn)的具體的垃圾收集器。不每一個都說來缸托,基本的差別主要在于采用的什么算法左敌,適合在什么代用;在垃圾收集時候是單線程還是多線程俐镐;在垃圾收集時候是一直停頓將垃圾完全收集結束矫限,用戶再開始,還是過程中可以停一會,收集一會
內存分配和回收策略:
1:內存優(yōu)先在eden上分配叼风,eden滿了的話會有一次minor GC
2:大對象直接進入老年代幸斥,虛擬機比較怕一群生命周期比較短的大對象,因此盡量避免這種用法
3:存活周期比較長的對象進入老年代:虛擬機會給每一個對象做一個age的標記咬扇,沒經(jīng)過一次minor GC值就會加一,當加到一定閥值后會放到老年代
4:動態(tài)選擇放入老年代的算法:如果survivor中的對象某一個年齡段的對象大于一半廊勃,那么比他老的和他都放到老年代懈贺,不用等到閥值
當放入老年代的對象大于老年代可以容納的時候就會調用一次full GC
Class文件格式:
java class問價實以8位字節(jié)為基礎單位的二進制流,前后一致相鄰沒有間隔坡垫,如果字段過長梭灿,8個字節(jié)裝不下,就以高位在前的方式分成多個字節(jié)進行存儲冰悠。根據(jù)java虛擬機規(guī)范的規(guī)定堡妒,class文件是以類似C語言結構體的偽結構進行存儲。這種偽結構只有兩種數(shù)據(jù)類型溉卓,無符號數(shù)和表皮迟。
無符號數(shù),屬于基本的數(shù)據(jù)類型桑寨,包括u1,u2,u4,u8這些不同的數(shù)據(jù)類型伏尼,用于描述數(shù)字,索引尉尾,數(shù)量值爆阶,或者按照UTF-8進行編碼構成字符串的值
表,是以無符號數(shù)和表一起組成的復合復雜結構沙咏。
因為class文件格式這種的非結構話定義方式辨图,使得每一位和順序都是嚴格定義的
class具體格式定義:
1:u4 魔數(shù)
2:u2 最小版本號
3:u2 主版本號
4:接下來是常量池,是class出現(xiàn)的第一個表類型數(shù)據(jù)結構的數(shù)據(jù)肢藐,是與其它項目關聯(lián)最多的數(shù)據(jù)故河,也是最大的數(shù)據(jù)量結構之一,常量池由于大小不一致所以會在開頭記錄長度吆豹,常量池中主要存著兩類數(shù)據(jù):字面量和符號引用忧勿。字面量比較接近于java層面的常量概念:例如,文本字符串瞻讽,被聲明為final的變量鸳吸。符號引用是編譯層面的概念:1:類或接口的全限定名 2:方法名和描述符 3:字段名和描述符
4.1: u2 常量池容量計數(shù)(從1開始)
4.2: 常量池中的每一項都是表,第一個字段是u1類型標識符速勇,用于標識數(shù)據(jù)類型晌砾,比方說int;字符串烦磁;方法养匈、字段或類的引用哼勇,然后每種類型都會有特定的存儲方式,具體可以看一下class的文件呕乎。
5: 常量池之后緊跟的是訪問修飾符积担,用于表明是不是public,是不是接口猬仁,是不是abstract帝璧,是不是final
6:類索引,父索引湿刽,接口索引:類索引的烁,父索引都是u2類型數(shù)據(jù),接口索引是u2類型數(shù)據(jù)集合诈闺,由這三項數(shù)據(jù)可以確定類的繼承關系類索引和父索引不用解釋了渴庆,就挨著,接口索引開始會有一個u2數(shù)據(jù)來描述數(shù)量雅镊,然后后邊跟著接口索引襟雷,
7:后邊跟著的是字段表:第一個字段是u2類型,記錄字段的訪問修身符仁烹,類級字段還是實例級字段嗤军、可變性、并發(fā)可見性晃危、可否序列化等叙赚;第二個字段也是u2類型,用來記錄字段的name—index僚饭;第三個字段是字段的description_index字段震叮;這兩個字段用來記錄,字段和方法的名稱鳍鸵,和數(shù)據(jù)類型苇瓣、返回值、輸入?yún)?shù)等偿乖;字段表不會列出從父類繼承的字段击罪,只會出現(xiàn)java代碼中從來沒出現(xiàn)過的字段
8:方法表:和字段表基本一致。編譯之后的java代碼會放到方法表中屬性表里的Code屬性中贪薪。在class的文件中對方法的簽名會包括返回值媳禁,所以返回值不同也可以重載,但是在eclipse里會編譯不過去画切,基于的編譯器不是JavaC
9:屬性表竣稽,屬性表是在class文件、字段表、方法表中都可以攜帶自己屬性的集合毫别。
9.1:Code屬性娃弓,主要會記錄代碼的長度(code_length是u4類型,所以方法中最大不能夠超過65535個字節(jié)碼指令)岛宦,代碼的內容台丛;操作棧的最大深度;局部變量所需的空間(實例級別的函數(shù)砾肺,局部變量從1開始挽霉,0默認存著的是this);接下來是異常表,異常其實就是把各種形式編譯成不同的分支债沮,有異常走到哪,沒一場走到哪本鸣。
疫衩。。荣德。闷煤。。涮瞻。鲤拿。。署咽。近顷。。宁否。
虛擬機類加載機制:
java虛擬機類加載和連接都發(fā)生在運行期窒升,這樣會導致運行期開始有一些性能開銷,但是增加了靈活性慕匠。加載機制是雙親委托模型饱须,意思是啥呢,如果一個classloader繼承了上層的classloader之后呢台谊,當用這個classloader加載一個類時候蓉媳,會優(yōu)先去它父類中看看是不是能加載這樣依次找上去,找到最頂層的classloader锅铅,如果它可以加載就用它加載酪呻,如果加載不了,就向下再找盐须,這樣個人感覺保障了安全性号杠,使得主要的尤其是系統(tǒng)級的類庫加載的是java制定的那些,而不是自己偷偷模仿改的版本.
Class文件從被加載到虛擬機內存到被卸載為止,總共需要經(jīng)過加載姨蟋、驗證屉凯、準備、解析眼溶、初始化悠砚、使用、卸載七個生命周期堂飞。其中驗證灌旧、準備、解析可以稱作是連接過程绰筛。
虛擬機規(guī)范對于加載過程何時執(zhí)行枢泰,沒有嚴格的限制,但是對于合適初始化嚴格定義了四種情況(加載铝噩、驗證衡蚂、準備、解析肯定要在此之前發(fā)生)
1:遇到new骏庸,getstatic,putstatic,invoklestatic時候毛甲,如果沒有初始化類時候需要立刻初始化類,對應的就是實例化一個對象具被、讀取或者設置一個靜態(tài)字段(final的除外)玻募、調用靜態(tài)方法
2:通過反射對類進行反射調用時候
3:當初始化一個類時候,父類還沒有初始化的情況
4:指定的main方法的類
加載過程:
1:通過類的權限定名加載class文件二進制流
2:將class的二進制的結構轉換為方法區(qū)的運行時數(shù)據(jù)結構
3:在堆中分配類的一個實例一姿,作為方法區(qū)數(shù)據(jù)的訪問的入口
方法區(qū)中的數(shù)據(jù)結構又虛擬機自己進行定義七咧,虛擬機并沒有嚴格規(guī)范其定義方式
驗證階段:
驗證階段是為了保證虛擬機的安全性,避免class的二進制流文件不合法把虛擬機搞崩了
1:文件格式驗證叮叹,魔數(shù)坑雅,主次版本,常量池的類型是不是有未定意的衬横,索引是否有指向不存在的常量或不匹配的常量裹粤,utf-8是否合理
2:元數(shù)據(jù)驗證,對語意進行驗證蜂林,包括是否有父對象遥诉,是否繼承了不允許繼承的類,類中的字段是否和父對象的有矛盾噪叙,是不是抽象類矮锈,有沒有未實現(xiàn)的接口或抽象方法
3:字節(jié)碼驗證:保證任意時刻操作數(shù)棧的數(shù)據(jù)類型與指令碼序列都能配合工作,保證跳轉指令不會跳轉到字節(jié)碼意外的范圍睁蕾,保證方法中的類型轉換時合法的苞笨,
4:符號引用驗證:根據(jù)類名是否有類债朵,是否有方法和字段,訪問性是否可以
準備階段:
準備階段時為類變量分配內存和設置初始值的過程瀑凝,內存分配在方法區(qū)序芦,初始值是各種值的默認值,對應實際的賦值要在 cinit方法時候執(zhí)行的粤咪,實例變量是在初始化時候在堆上分配的
解析階段:
將虛擬機常量池中的符號引用(一般是用來表征目標的字面量)轉換為直接引用(指針或者偏移谚中,句柄之類的)的過程,
類或接口的解析:主要就是把全限定名交給類加載器去加載
字段或者方法解析:
初始化階段:
除了類加載使用自定義的類加載器之外寥枝,其余的情況都是由虛擬機進行控制宪塔,這一過程也是java代碼開始執(zhí)行的地方。
準備階段已經(jīng)將類變量設置為初始值囊拜,現(xiàn)在就有初始化階段對其進行賦值某筐,最終會形成<cinit>方法,收集類類變量冠跷,靜態(tài)語塊南誊,他們執(zhí)行的順序是在源文件出現(xiàn)的前后順序,虛擬機保證子類的<cinit>方法調用之前會調用到父類的<cinit>蔽莱,方法不是必須生成的弟疆,如果一個類沒有靜態(tài)變量的話戚长,就不會生成這個方法盗冷。虛擬機會保證<cinit>方法的線程安全。
類加載器:
使用類加載器的一個潛在的坑或者問題是同廉,如果同一個類的加載器不同仪糖,那么這兩個類則不相等,這樣就會導致instanceof迫肖,equals锅劝,isinstance之類的方法都失效了
字節(jié)碼執(zhí)行引擎:
棧幀:是用于支持虛擬機方法調用和方法執(zhí)行的數(shù)據(jù)結構,棧幀時在虛擬機棧中的元素蟆湖,在其中存儲了局部變量表故爵、操作數(shù)棧、動態(tài)鏈接和返回地址等信息隅津。每一個方法的調用對應的都是進棧出棧的過程诬垂。局部變量表大小和操作數(shù)棧的深度,在代碼編譯后已經(jīng)確定伦仍,將會存儲在code屬性中结窘,
動態(tài)連接:棧幀中都會包含一個指向運行時常量池中該棧幀所屬方法的引用,持有這個引用是為了動態(tài)連接充蓝。字節(jié)碼中的方法調用是通過符號索引調用的隧枫,這個符號索引有的在類加載階段變?yōu)橹苯右煤泶牛@個稱作是靜態(tài)解析。另一部分在每一次運行時轉換為引用官脓,稱作是動態(tài)連接协怒。
方法調用:
方法調用不等同于方法執(zhí)行,方法調用時確定調用方法的版本确买。在方法調用時候主要是用來確定方法調用的版本斤讥,而不涉及調用方法內部的具體運行過程。
這給java帶來動態(tài)擴展的能力湾趾,但也增加了java在方法調用過程的復雜度芭商。方法的調用需要再類加載后,甚至是運行期才能確定版本
解析:
所有方法調用中的目標方法搀缠,都是class文件中的常量池中的一個符號引用铛楣,類加載解析階段會把一部分符號引用轉換為直接引用。這部分解析是編譯器可知艺普,運行期不變的方法簸州,主要是靜態(tài)方法,私有方法歧譬。
聲明變量方法的變量時靜態(tài)類型岸浑,new出來的變量是動態(tài)類型。靜態(tài)變量是編譯期瑰步、可知的矢洲,動態(tài)類型是運行期可知的。在方法調用時缩焦,確定重載的定義版本時候读虏,根據(jù)的是變量的靜態(tài)類型(編譯器確定),所有依賴靜態(tài)類型進行定位方法執(zhí)行版本的分派動作稱為是靜態(tài)分派袁滥。靜態(tài)分派的典型應用就是方法重載盖桥。當然對于重載方法的運行時候,靜態(tài)分派找重載方法的時候题翻,策略是找到最合適的版本揩徊。想個例子就是test(1),有int,char,double的版本嵌赠,肯定選int版本塑荒,沒有int版本,選char版本猾普,這個沒有選double的版本袜炕。
動態(tài)分派是實現(xiàn)多態(tài)的另一個形式,復寫實現(xiàn)的根本初家。在調用方法時候偎窘,其實根據(jù)反射的調用乌助,應該知道是需要把對象傳進去,在調用方法時候陌知,先看他有沒有實現(xiàn)他托,沒有的話找父,這樣覆寫實現(xiàn)了的話仆葡,肯定搞子的實現(xiàn)赏参。
字節(jié)碼執(zhí)行:
字節(jié)碼執(zhí)行是基于棧進行操作,而不是基于地址沿盅,簡單的概述流程就是把篓,方法在調用的時候,在遇到變量分配的時候腰涧,這些變量會放在局部變量表里韧掩,并且知道它在局部變量表中的索引,而方法在執(zhí)行過程中的時候窖铡,需要操作這些個臨時變量時候疗锐,就根據(jù)索引把這個索引的局部變量load到操作數(shù)棧里邊,這樣新load的會放在棧頂费彼,完成操作時候滑臊,就把棧頂?shù)膹棾霾僮鳎僮魍瓿珊笤賶喝霔m敗?br>
編譯優(yōu)化箍铲,前期編譯雇卷,JavaC,Eclipse這種虹钮;
JIT聋庵,即時編譯膘融,這個是在運行期把一些調用頻次高的函數(shù)芙粱,即時編譯成本地碼運行,提高之后的運行效率氧映;
AOT:提前編譯春畔,在運行之前,預先編譯岛都,將字節(jié)碼編譯為本地碼律姨,這種預編譯使得連接變?yōu)殪o態(tài)連接而不是動態(tài)連接,這樣使java動態(tài)擴展的特性喪失了臼疫,如果是這樣的話择份,需要考慮art下怎么進行動態(tài)擴展,插件話之類的操作烫堤。
JVM-讀書筆記
最后編輯于 :
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
- 文/潘曉璐 我一進店門灭红,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侣滩,“玉大人,你說我怎么就攤上這事变擒∈ぢ保” “怎么了?”我有些...
- 文/不壞的土叔 我叫張陵赁项,是天一觀的道長葛躏。 經(jīng)常有香客問我,道長悠菜,這世上最難降的妖魔是什么舰攒? 我笑而不...
- 正文 為了忘掉前任,我火速辦了婚禮悔醋,結果婚禮上摩窃,老公的妹妹穿的比我還像新娘。我一直安慰自己芬骄,他們只是感情好猾愿,可當我...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著账阻,像睡著了一般蒂秘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淘太,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挎扰!你這毒婦竟也來了翠订?” 一聲冷哼從身側響起缓升,我...
- 正文 年R本政府宣布屈梁,位于F島的核電站嗤练,受9級特大地震影響,放射性物質發(fā)生泄漏在讶。R本人自食惡果不足惜煞抬,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望构哺。 院中可真熱鬧革答,春花似錦、人聲如沸曙强。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽碟嘴。三九已至溪食,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間臀防,已是汗流浹背眠菇。 一陣腳步聲響...
推薦閱讀更多精彩內容
- 虛擬機把Class文件加載到內存嫌蚤,并對數(shù)據(jù)進行校驗辐益、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型脱吱,...
- 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡上收集的一些資料的整理智政,因此不免有一些不準確的地方,同時不同JDK版本的...
- JVM內存模型Java虛擬機(Java Virtual Machine=JVM)的內存空間分為五個部分箱蝠,分別是: ...
- 從三月份找實習到現(xiàn)在续捂,面了一些公司,掛了不少宦搬,但最終還是拿到小米牙瓢、百度、阿里间校、京東矾克、新浪、CVTE憔足、樂視家的研發(fā)崗...