1 Java基礎
1.1 編譯型語言VS解釋型語言
編譯型語言:程序在執(zhí)行之前需要一個專門的編譯過程,把程序編譯成為機器語言的文件崇堰,運行時不需要重新翻譯,直接使用編譯的結(jié)果就行了婉商。因此效率比較高龄减。比如 C 語言项钮。
解釋型語言:程序不需要編譯,程序在運行時才翻譯成機器語言希停,每執(zhí)行一次都要翻譯一次烁巫。因此效率比較低。比如Basic語言脖苏,專門有一個解釋器能夠直接執(zhí)行Basic程序程拭,每個語句都是執(zhí)行的時候才翻譯。
C語言是編譯型的棍潘。C程序——>機器語言(編譯)
Java比較特殊恃鞋,Java程序也需要編譯,但是沒有直接編譯成機器語言亦歉,而是編譯成字節(jié)碼恤浪,然后用解釋方式執(zhí)行字節(jié)碼。 Java程序—— >字節(jié)碼(編譯)—— >機器語言(解釋)
1.2 JVM工作原理
JVM 主要由 ClassLoader 和 執(zhí)行引擎 兩子系統(tǒng)組成肴楷,運行數(shù)據(jù)區(qū)分為五個部分: 方法區(qū)水由、堆、棧赛蔫、程序計數(shù)器砂客、本地方法棧泥张。其中的方法區(qū)和堆是所有線程共享的,JVM將臨時變量放在棧中鞠值,每個線程都有自己獨立的椕拇矗空間和程序計數(shù)器。
任何一個Java類的main函數(shù)運行都會創(chuàng)建一個JVM實例彤恶,JVM實例啟動時默認啟動幾個守護線程钞钙,比如:垃圾回收的線程,而 main 方法的執(zhí)行是在一個單獨的非守護線程中執(zhí)行的声离。只要非守護線程結(jié)束JVM實例就銷毀了芒炼。
那么在Java類main函數(shù)運行過程中,JVM的工作原理如下:
- 根據(jù)系統(tǒng)環(huán)境變量术徊,創(chuàng)建裝載JVM的環(huán)境與配置本刽;
- 尋找JRE目錄,尋找jvm.dll赠涮,并裝載jvm.dll盅安;
- 根據(jù)JVM的參數(shù)配置,如:內(nèi)存參數(shù)世囊,初始化jvm實例;
- JVM實例產(chǎn)生一個 引導類加載器實例(Bootstrap Loader)窿祥,加載Java核心庫株憾,然后引導類加載器自動加載 擴展類加載器(Extended Loader),加載Java擴展庫晒衩,最后擴展類加載器自動加載 系統(tǒng)類加載器(AppClass Loader)嗤瞎,加載當前的Java類;
- 當前Java類加載至內(nèi)存后听系,會經(jīng)過 驗證贝奇、準備、解析 三步靠胜,將Java類中的 類型信息掉瞳、屬性信息、常量池 存放在方法區(qū)內(nèi)存中浪漠,方法指令直接保存到棧內(nèi)存中陕习,如:main函數(shù);
- 執(zhí)行引擎開始執(zhí)行棧內(nèi)存中指令址愿,由于main函數(shù)是靜態(tài)方法该镣,所以不需要傳入實例,在類加載完畢之后响谓,直接執(zhí)行main方法指令损合;
- main函數(shù)執(zhí)行主線程結(jié)束省艳,隨之守護線程銷毀,最后JVM實例被銷毀嫁审;
1.3 JVM類加載機制
類加載是Java程序運行的第一步跋炕,在java.lang包里有個ClassLoader類,ClassLoader 的基本目標是 對類的請求提供服務土居,按需動態(tài)裝載類和資源 枣购,只有當一個類要使用 (1. Class.forName();2. 調(diào)用類的靜態(tài)方法擦耀;3. 使用 new 關鍵字來實例化一個類) 的時候棉圈,類加載器才會加載這個類并初始化。當我們自定義類加載器加載類文件時(繼承自ClassLoader類眷蜓,只需覆蓋 findClass方法分瘾,即可),其類加載機制如下:
當自定義類加載器加載類時吁系,會調(diào)用loadClass方法加載類德召,而由于類加載的雙親委托模式,會將類的加載代理給父類加載器:系統(tǒng)類加載器來完成汽纤,依次類推至最頂層引導類加載器加載上岗,如果父類加載器沒有加載到類,則最終返回由自定義類加載器加載類蕴坪,通過雙親委托模式肴掷,對于 Java 核心庫的類的加載工作由引導類加載器來統(tǒng)一完成,保證了 Java 應用所使用的都是同一個版本的 Java 核心庫的類背传。
需要說明一下Java虛擬機是如何判定兩個Java 類是相同的呆瞻。Java 虛擬機不僅要看類的全名是否相同,還要看加載此類的類加載器是否一樣径玖。只有兩者都相同的情況痴脾,才認為兩個類是相同的。即便是同樣的字節(jié)代碼梳星,被不同的類加載器加載之后所得到的類赞赖,也是不同的。然而冤灾,類加載器又分初始類加載器和定義類加載器薯定,由于類加載的代理模式,初始類加載器并不一定是定義類加載器瞳购,所以確切的說话侄,判定兩個 Java 類是否相同的, 哪個類加載器啟動類的加載過程并不重要,重要的是最終定義這個類的加載器年堆。
1.4 JVM內(nèi)存分配策略
JVM內(nèi)存主要是指運行數(shù)據(jù)區(qū)吞杭,粗略的分為:堆、棧变丧,細致區(qū)分五部分:方法區(qū)芽狗、堆、棧痒蓬、程序計數(shù)器童擎、本地方法棧。
程序計數(shù)器:
線程私有攻晒,記錄線程所執(zhí)行的虛擬機字節(jié)碼指令的地址顾复;如果正在執(zhí)行的是native方法,這個計數(shù)值則為空鲁捏。唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域芯砸。Java虛擬機棧:
線程私有,描述Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀给梅,用于存儲 局部變量表假丧,操作數(shù)棧,動態(tài)鏈接动羽,方法出口 等信息包帚。每一個方法從調(diào)用直至執(zhí)行完成的過程,就對應這一個棧幀在虛擬機棧中入棧到出棧的過程运吓。棧幀是方法運行時的基礎數(shù)據(jù)結(jié)構(gòu)婴噩。如果線程請求棧深度大于虛擬機所允許的深度,拋出StackOverflowError異常羽德;如果虛擬機棧動態(tài)擴展時無法申請到足夠的內(nèi)存,拋出 OutOfMemoryError異常迅办。本地方法棧:
線程私有宅静,描述native方法執(zhí)行的內(nèi)存模型。有的虛擬機(如Sun HotSpot虛擬機)直接就把 本地方法棧和虛擬機棧 合二為一站欺。Java堆:
線程共享姨夹,存放對象實例及數(shù)組,是垃圾收集器管理的主要區(qū)域矾策,采用分代收集策略磷账,所以Java堆會細分為新生代和老年代。根據(jù)Java虛擬機規(guī)范的規(guī)定贾虽,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中逃糟,只要邏輯上連續(xù)的即可。如果堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴展時绰咽,拋出OutOfMemoryError異常菇肃。-
方法區(qū):
線程共享,存儲已被虛擬機加載的類信息取募、常量池琐谤、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)玩敏。雖然Java虛擬機規(guī)范把方法區(qū)描述為堆的一個邏輯部分斗忌,但是它卻有一個別名叫Non-Heap(非堆),目的應該是與Java堆區(qū)分開來旺聚。如果方法區(qū)無法滿足內(nèi)存分配需求织阳,拋出OutOfMemoryError異常翻屈。對于HotSpot虛擬機,方法區(qū)被稱為永久代伸眶,本質(zhì)上兩者不等價,僅僅是因為HotSpot虛擬機設計團隊選擇把GC分代收集擴至方法區(qū)厘贼,或者說使用永久代來實現(xiàn)方法區(qū)而已界酒。JVM堆一般又可以分為以下三部分:Young 新生代嘴秸、Tenured 老年代、Perm 永久代岳掐;
Perm永久代主要保存class,method,filed對象凭疮,這部分的空間一般不會溢出串述,除非一次性加載了很多的類,不過在涉及到熱部署的應用服務器的時候纲酗,有時候會遇到java.lang.OutOfMemoryError : PermGen space 的錯誤。
Tenured老年代主要保存生命周期長的對象右蕊,多次未被GC掉的對象吮螺;
Young新生代主要保存新生成對象帕翻,根據(jù)JVM的策略坯约,在經(jīng)過幾次垃圾收集后,而沒有被垃圾回收的對象將被移動到Tenured區(qū)間横殴。有時候該區(qū)經(jīng)常會遇到java.lang.OutOfMemoryError :Java heap space的錯誤卿拴。
-Xms:指定了JVM初始啟動以后 初始化內(nèi)存;
-Xmx:指定JVM堆得 最大內(nèi)存文狱,在JVM啟動以后缘挽,會分配-Xmx參數(shù)指定大小的內(nèi)存給JVM,但是不一定全部使用苏研,JVM會根據(jù)-Xms參數(shù)來調(diào)節(jié)真正用于JVM的內(nèi)存腮郊;
-Xmn:參數(shù)設置了新生代的大小衅鹿;老年代等于-Xmx減去-Xmn过咬;
-XX:Xss:參數(shù)設置了永久代的大小掸绞;
直接內(nèi)存:
不是虛擬機運行時數(shù)據(jù)區(qū)的一部分泵三,也不是Java虛擬機規(guī)范中定義的內(nèi)存區(qū)域集漾。但是這部分內(nèi)存也被頻繁的使用砸脊,而且也可能導致OutOfMemoryError異常出現(xiàn)。例如:NIO類驱显,引入了一種基于通道(Channel)與緩沖區(qū)(Buffer)的IO方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存埃疫,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作伏恐。這樣能在一些場景中顯著提高性能栓霜,因為避免了Java堆和Native堆中來回復制數(shù)據(jù)。直接內(nèi)存雖然不會受到Java堆大小的限制销凑,但是仅炊,既然是內(nèi)存,仍會受到本機總內(nèi)存大小及處理器尋址空間的限制抚垄。-
JVM參數(shù):
-XX:+HeapDumpOnOutOfMemoryError:可讓虛擬機在內(nèi)存溢出異常時Dump出當前的內(nèi)存堆轉(zhuǎn)儲快照以便事后分析;
-Xss:設置線程棧大型┚智哀;
-XX:PermSize:設置永久代初始大小屯吊;
-XX:MaxPermSize:設置永久代最大大心〔ぁ次氨;
-XX:MaxDirectMemorySize:設置直接內(nèi)存大小,默認與Java堆最大值一樣虹蓄;
1.5 對象是否可回收
-
引用計數(shù)算法
存儲對特定對象的所有引用數(shù)幸撕,也就是說,當應用程序創(chuàng)建引用以及引用超出范圍時律胀,JVM必須適當增減引用數(shù)炭菌。當某對象的引用數(shù)為0時,便可以進行垃圾收集赘艳。優(yōu)點:實現(xiàn)簡單投储、效率高;
缺點:很難解決對象之間相互引用問題娇掏;
可達性分析算法
通過一系列的稱為“GC Roots”的對象作為起始點勋眯,從這些節(jié)點開始向下搜索客蹋,搜索所走的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時番电,則證明此對象是不可用的辆琅。
可作為GC Roots的對象包括:
- 虛擬機棧(棧幀的本地變量表)中引用的對象婉烟;
- 方法區(qū)中類靜態(tài)屬性引用的對象;
- 方法區(qū)中常量引用的對象洞辣;
- 本地方法棧中JNI(即一般說的是native方法)引用的對象昙衅;
1.6 四種引用
- 強引用:只要強引用還存在而涉,垃圾收集器永遠不會回收掉被引用的對象。
- 軟引用:對軟引用關聯(lián)著的對象蟹但,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前谭羔,將會把這些對象列進回收范圍之中進行第二次回收瘟裸。
- 弱引用:對弱引用關聯(lián)著的對象,只能生存到下一次垃圾收集發(fā)生之前兼搏。
- 虛引用:對象是否有虛引用沙郭,完全不會對其生存時間構(gòu)成影響病线,也無法通過虛引用來取得對象實例。關聯(lián)虛引用唯一目的就是能在對象被收集器回收時收到系統(tǒng)通知绑莺。
1.7 finalize()方法
任何一個對象的finalize()方法都僅會被系統(tǒng)自動調(diào)用一次惕耕。如果對象面臨下一次回收司澎,它的finalize()方法不會被再次執(zhí)行。建議避免使用該方法浪南。
1.8 JVM垃圾回收策略
GC即垃圾收集機制是指JVM用于釋放那些不再使用的對象所占用的內(nèi)存漱受。常用機制:
-
標記-清除算法【適用于老年代】
首先根據(jù)可達性分析算法昂羡,標記出所有需要回收的對象,在標記完成后統(tǒng)一回收所有標記的對象怨愤。缺點:效率問題:標記蛹批、清除兩個過程效率都低;空間問題:標記清除之后產(chǎn)生大量不連續(xù)的內(nèi)存碎片差导,空間碎片太多可能會導致以后在程序運行過程中设褐,需要分配大對象時,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作犀被。
-
復制算法【空間換時間外冀,適用于對象存活率低的新生代】
將可用內(nèi)存按容量劃分為大小相等的兩塊雪隧,每次只使用其中的一塊。當一塊的內(nèi)存用完了遭商,就將還存活著的對象復制到另一塊上面捅伤,然后再把已使用過的內(nèi)存空間一次清理掉丛忆。優(yōu)點:實現(xiàn)簡單、效率高可很、無內(nèi)存碎片我抠;
缺點:內(nèi)存使用率低袜茧,太浪費。
由于新生代中的對象98%是朝生夕死的纳鼎,所以 將內(nèi)存分為一塊較大的內(nèi)存贱鄙、兩塊較小的內(nèi)存。每次使用一塊大內(nèi)存和一塊小內(nèi)存悬荣,當垃圾回收時疙剑,會將該大內(nèi)存和小內(nèi)存中存活的對象一次性的復制到另外一塊小內(nèi)存中言缤,最后清理掉該大內(nèi)存禁灼、小內(nèi)存弄捕。但是如果對象的存活率較高,那么當復制對象至另一塊小內(nèi)存時穿铆,該小內(nèi)存空間會不夠用斋荞,則需要依賴其他內(nèi)存(老年代)進行分配擔保平酿。
當對象的存活率較高時,復制算法要進行較多的復制操作筑辨,效率會變低。
-
標記-整理算法【適用于老年代】
標記過程與“標記-清除”算法一樣搔涝,唯一區(qū)別是在后續(xù)步驟不是直接對內(nèi)存進行清除衙熔,而是先讓所有活著的對象都向一端移動,然后直接清除掉端邊界以外的內(nèi)存。
-
分代收集策略
一般是把Java堆分成 新生代和老年代凭舶,這樣就可以根據(jù)各個年代的特點采用最適當?shù)氖占惴ā?/p>新生代中對象存活率低,采用復制算法匆背,有老年代對它進行分配擔保钝尸。
老年代中對象存活率高搂根,無額外空間對它進行分配擔保剩愧,必須采用 “標記-清除”或“標記-整理”算法。
-
回收方法區(qū)(永久代)
很多人認為方法區(qū)(永久代)是沒有垃圾收集的穴翩,Java虛擬機規(guī)范中確實說過可以不要求虛擬機在方法區(qū)實現(xiàn)垃圾收集锦积,況且在方法區(qū)中進行垃圾收集性價比很低丰介;永久代的垃圾收集主要回收兩方面: 廢棄常量和無用的類;
廢棄常量:判斷常量池的對象是否還存在任何引用淆储;
無用的類:(1)該類的所有實例都被回收本砰;(2)該類的ClassLoader已被回收钢悲;(3)該類的Class對象沒有任何引用莺琳;
1.9 垃圾收集器
JDK1.7 Update14之后的HotSpot虛擬機正式提供了G1收集器;
Serial收集器:
單線程珍手、Stop The Wold琳要、Client模式默認新生代收集器、 復制算法童叠;-
ParNew收集器:
Serial多線程版厦坛、并行乍惊、Stop The Wold、Server模式默認新生代收集器、只有該收集器能與CMS收集器配合凡橱、 復制算法稼钩;ParNew收集器也是使用:
-XX:+UseConcMarkSweepGC 選項后的默認新生代收集器达罗;
-XX:+UserParNewGC 選項來強制指定它粮揉;
-XX:ParallelGCThreads 參數(shù)來限制垃圾收集的線程數(shù),默認開啟的線程數(shù)與CPU的數(shù)量相等侨拦; -
Parallel Scavenge收集器:
多線程狱从、并行叠纹、Stop The Wold誉察、 新生代收集器、Server模式递沪、 復制算法款慨; CMS等收集器的關注點是 盡可能地縮短垃圾收集時用戶線程的停頓時間,而 該收集器關注的是達到一個可控制的吞吐量桩了;所謂吞吐量就是CPU用于運行用戶代碼的時間與CPU總消耗時間的比值井誉,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)整胃,吞吐量與垃圾收集時間成反比屁使;
停頓時間越短就越適合需要與用戶交互的程序,而高吞吐量則可以高效率的利用CPU蔽午,盡快完成程序的運算任務及老,主要適合在后臺運算而不需要太多交互的任務范抓;
-XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間匕垫;
-XX:GCTimeRatio:設置吞吐量大小,大于0且小于100的整數(shù)悔捶,默認為99蜕该;
-XX:UseAdaptiveSizePolicy:設置打開GC自適應的調(diào)節(jié)策略堂淡,以達到最大的吞吐量; Serial Old收集器:
單線程萤悴、Stop The Wold皆的、 老年代费薄、 標記-整理算法、Client模式伟众;作為CMS收集器的備選方案凳厢,在并發(fā)收集發(fā)生Concurrent Mode Failure時使用竞慢;Parallel Old收集器:
多線程梗顺、Stop The Wold寺谤、 老年代吮播、 標記-整理算法、Server模式意狠、并行粟关;適合與Parallel Scavenge配合使用;-
CMS收集器:
多線程环戈、Stop The Wold闷板、 老年代、 標記-清除算法院塞、Server模式遮晚、并發(fā)拦止; 缺點:內(nèi)存碎片县遣;-XX:+UseCMSCompactAtFullCollection:默認開啟糜颠,用于在CMS收集器頂不住進行Full GC時開啟內(nèi)存碎片的合并整理過程,內(nèi)存整理的過程是無法并發(fā)萧求,會導致停頓時間變長其兴;
-XX:CMSFullGCsBeforeCompaction:用于設置執(zhí)行多少次不壓縮的Full GC后,跟著來一次帶壓縮的GC夸政,默認為0元旬,表示每次進行Full GC時都進行碎片整理;
-
G1收集器:
多線程秒梳、新老年代法绵、復制+標記-整理算法、并發(fā)酪碘、Server模式朋譬、GC整個堆; 特點:并行與并發(fā)兴垦、分代收集徙赢、空間整理、可預測的停頓探越;G1雖然保存了新老年代的概念狡赐,但已經(jīng)不是物理分割了,他們都是一部分Region(不需要連續(xù))的集合钦幔;
args="-Dfile.encoding=UTF-8 -J-server -J-Xss128k -J-XX:ThreadStackSize=128 -J-XX:PermSize=64m -J-XX:MaxPermSize=256m -J-verbose:gc -J-XX:+PrintGCDetails -J-XX:+PrintGCTimeStamps -Djava.library.path=${RESIN_HOME}/libexec:/opt/j2sdk/lib:/usr/lib64 -Djmagick.systemclassloader=false -DNO_TIMEOUT" args="$args -Xdebug - Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9090" args="$args -Xmn5g -Xms10g -Xmx10g" args="$args -J-XX:+UseParNewGC -J-XX:+UseConcMarkSweepGC"
Client模式:默認-XX:+UseSerialGC枕屉,Serial + Serial Old;
Server模式:默認-XX:+UseParallelGC鲤氢,Parallel Scavenge + Serial Old搀擂;-XX:+PrintGC 打印GC信息 -XX:+PrintGCDetails 打印較為詳細的GC信息 -XX:+PrintGCTimeStamps 打印GC時間戳,相對于應用程序啟動的時間
Serial:串行收集器卷玉,當進行垃圾收集時哨颂,會暫停所有線程;
Parallel:并行收集器相种,是串行收集器的多線程版本威恼,多CPU下;
ParallelOld:老年代的Parallel版本寝并;
ConcMarkSweep:簡稱CMS箫措,是并發(fā)收集器,將部分操作與用戶線程并發(fā)執(zhí)行衬潦;
CMSIncrementalMode:CMS收集器變種斤蔓,屬增量式垃圾收集器,在并發(fā)標記和并發(fā)清理時交替運行垃圾收集器和用戶線程别渔;
G1:面向服務器端應用的垃圾收集器附迷,計劃未來替代CMS收集器惧互;
1.10 JVM參數(shù)配置
-
跟 Java 堆大小相關的 JVM 內(nèi)存參數(shù)
下面三個 JVM 參數(shù)用來指定堆的初始大小和最大值以及堆棧大小:
-Xms 設置 Java 堆的初始化大小
-Xmx 設置最大的 Java 堆大小
-Xss 設置Java線程棧大小 -
關于打印垃圾收集器詳情的 JVM 參數(shù)
-verbose:gc 記錄 GC 運行以及運行時間喇伯,一般用來查看 GC 是否是應用的瓶頸
-XX:+PrintGCDetails 記錄 GC 運行時的詳細數(shù)據(jù)信息喊儡,包括新生成對象的占用內(nèi)存大小以及耗費時間等
-XX:+PrintGCTimeStamps 打印垃圾收集的時間戳 -
設置 Java 垃圾收集器行為的 JVM 參數(shù)
-XX:+UseParallelGC 使用并行垃圾收集
-XX:+UseConcMarkSweepGC 使用并發(fā)標志掃描收集 (Introduced in 1.4.1)
-XX:+UseSerialGC 使用串行垃圾收集 (Introduced in 5.0.)需要提醒的是,但你的應用是非常關鍵的稻据、交易非常頻繁應用時艾猜,應該謹慎使用 GC 參數(shù),因為 GC 操作是耗時的捻悯,你需要在這之中找到平衡點匆赃。
-
JVM調(diào)試參數(shù),用于遠程調(diào)試
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
-
用于修改 Perm Gen 大小的 JVM 參數(shù)
下面的這三個參數(shù)主要用來解決 JVM 錯誤:java.lang.OutOfMemoryError:Perm Gen Space
-XX:PermSize and MaxPermSize -XX:NewRatio=2 Ratio of new/old generation sizes. -XX:MaxPermSize=64m Size of the Permanent Generation.
-
用來跟蹤類加載和卸載的信息
-XX:+TraceClassLoading 和 -XX:+TraceClassUnloading 用來打印類被加載和卸載的過程信息今缚,這個用來診斷應用的內(nèi)存泄漏問題非常有用算柳。
-
用于調(diào)試目的的 JVM 開關參數(shù)
-XX:HeapDumpPath=./java_pid.hprof Path to directory or file name for heap dump. -XX:+PrintConcurrentLocks Print java.util.concurrent locks in Ctrl-Break thread dump. -XX:+PrintCommandLineFlags Print flags that appeared on the command line.
如果你的應用追求低停頓,那G1現(xiàn)在已經(jīng)可以作為一個可嘗試的選擇姓言;如果你的應用追求吞吐量瞬项,那G1并不會為你帶來什么特別的好處;
1.11 Stop The Wold
可達性分析必須在一個能確保一致性的快照中進行何荚,一致性是指在整個分析過程中整個執(zhí)行系統(tǒng)必須凍結(jié)囱淋,不可以出現(xiàn)分析過程中對象引用關系還在不斷變化的情況,該點不滿足的話可達性分析結(jié)果不準確餐塘。所以這點是導致GC進行時必須停頓所有線程的原因妥衣。
1.12 新生代GC、老年代GC
新生代GC:Minor GC戒傻,指發(fā)生在新生代的垃圾收集動作税手;
老年代GC:Major GC/Full GC,指發(fā)生在老年代垃圾收集動作稠鼻,出現(xiàn)了Major GC冈止,經(jīng)常會伴隨至少一次的Minor GC狂票。會發(fā)生Stop The Wold候齿。
1.13 對象進入老年代
大對象:通過參數(shù) -XX:PretenureSizeThreshold=3145287,令大于這個設置值的對象直接在老年代分配闺属。以避免大對象在新生代分配慌盯,從而觸發(fā)新生代GC。
多次GC仍存活的對象:當對象每”熬過“一次Minor GC掂器,對象年齡就增加1歲亚皂,當對象年齡增加到一定程度(默認15歲),就會晉升到老年代中国瓮。對象晉升老年代的年齡閥值灭必,可以通過參數(shù)-XX:MaxTenurigThreshold設置狞谱。
動態(tài)對象年齡判定:虛擬機并不是永遠地要求對象年齡必須達到MaxTenurigThreshold閥值才能晉升到老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半禁漓,年齡大于或等于該年齡的對象就可以直接進入老年代跟衅,無須等到MaxTenurigThreshold閥值。
新生代—分配擔保:由于新生代采用復制收集算法播歼,當新生代Minor GC 時伶跷,如果存活對象的大小大于Survivor,依據(jù)分配擔保策略會將Survivor無法容納的對象直接存入老年代秘狞。如果老年代仍不能夠存放剩余的對象叭莫,則會發(fā)生Major GC/Full GC,就會Stop The Wold烁试。
1.14 JVM常量池機制
常量池其實就是方法區(qū)一個內(nèi)存空間雇初,虛擬機必須為每個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集和减响。以字符串為例抵皱,在Java源代碼中的每一個字面值字符串,都會在編譯成class文件階段辩蛋,形成標志號為 8(CONSTANT_String_info)的常量表 呻畸。當JVM加載 class文件的時候,會為對應的常量池建立一個內(nèi)存數(shù)據(jù)結(jié)構(gòu)悼院,并存放在方法區(qū)中伤为。
如下代碼:
public class Test{
private String str="我們"。
}
將Test編譯之后形成class文件据途,那么在class文件中"我們"會以一種
CONSTANT_UTF8_info 表的形式存在绞愚,字節(jié)序列如下:1 0 6 230 136 145 228 187 172 1表示常量表的類型,0 6表示有6個字節(jié)的長度颖医。后面6個字節(jié)是UTF-8編碼的“我們”位衩。當JVM運行的時候會將這些常量池的信息加載進方法區(qū)。也就是說在運行過程中內(nèi)存存儲的"我們"是UTF-8編碼的熔萧。
1.15 為什么使用JVM
Java語言的一個非常重要的特點就是 平臺無關性糖驴。而使用JVM是實現(xiàn)這一特點的關鍵。一般的高級語言如果要在不同的平臺上運行佛致,至少需要編譯成不同的目標代碼贮缕。而引入JVM后,Java語言在不同平臺上運行時不需要重新編譯俺榆。Java語言使用JVM屏蔽了與具體平臺相關的信息感昼,使得Java語言編譯程序只需生成在JVM虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行罐脊。JVM在執(zhí)行字節(jié)碼時定嗓,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行蜕琴。
1.16 java.lang.OutOfMemoryError: unable to create new native thread
這個異常問題本質(zhì)原因是我們 創(chuàng)建了太多的線程,而能創(chuàng)建的線程數(shù)是有限制的宵溅,導致了異常的發(fā)生奸绷。能創(chuàng)建的線程數(shù)的具體計算公式如下:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
MaxProcessMemory:指的是一個進程的最大內(nèi)存
JVMMemory:JVM內(nèi)存
ReservedOsMemory:保留的操作系統(tǒng)內(nèi)存
ThreadStackSize:線程棧的大小
在java語言里, 當你創(chuàng)建一個線程的時候层玲,虛擬機會在JVM內(nèi)存創(chuàng)建一個Thread對象同時創(chuàng)建一個操作系統(tǒng)線程号醉,而這個系統(tǒng)線程的內(nèi)存用的不是JVMMemory,而是系統(tǒng)中剩下的內(nèi)存(MaxProcessMemory - JVMMemory - ReservedOsMemory)辛块,可以下面方法解決問題:
(1) 通過設置 -Xmx512m 減少JVM Heap size畔派;
(2) 通過設置 -Xss64k 減少線程占用的Stack size;
(3) 增加操作系統(tǒng)內(nèi)存润绵;