☆技術問答集錦(三)

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的工作原理如下:

  1. 根據(jù)系統(tǒng)環(huán)境變量术徊,創(chuàng)建裝載JVM的環(huán)境與配置本刽;
  2. 尋找JRE目錄,尋找jvm.dll赠涮,并裝載jvm.dll盅安;
  3. 根據(jù)JVM的參數(shù)配置,如:內(nèi)存參數(shù)世囊,初始化jvm實例;
  4. JVM實例產(chǎn)生一個 引導類加載器實例(Bootstrap Loader)窿祥,加載Java核心庫株憾,然后引導類加載器自動加載 擴展類加載器(Extended Loader),加載Java擴展庫晒衩,最后擴展類加載器自動加載 系統(tǒng)類加載器(AppClass Loader)嗤瞎,加載當前的Java類;
  5. 當前Java類加載至內(nèi)存后听系,會經(jīng)過 驗證贝奇、準備、解析 三步靠胜,將Java類中的 類型信息掉瞳、屬性信息、常量池 存放在方法區(qū)內(nèi)存中浪漠,方法指令直接保存到棧內(nèi)存中陕习,如:main函數(shù);
  6. 執(zhí)行引擎開始執(zhí)行棧內(nèi)存中指令址愿,由于main函數(shù)是靜態(tài)方法该镣,所以不需要傳入實例,在類加載完畢之后响谓,直接執(zhí)行main方法指令损合;
  7. 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ù)器童擎、本地方法棧

  1. 程序計數(shù)器:
    線程私有攻晒,記錄線程所執(zhí)行的虛擬機字節(jié)碼指令的地址顾复;如果正在執(zhí)行的是native方法,這個計數(shù)值則為空鲁捏。唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域芯砸。

  2. 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異常迅办。

  3. 本地方法棧:
    線程私有宅静,描述native方法執(zhí)行的內(nèi)存模型。有的虛擬機(如Sun HotSpot虛擬機)直接就把 本地方法棧和虛擬機棧 合二為一站欺。

  4. Java堆:
    線程共享姨夹,存放對象實例及數(shù)組,是垃圾收集器管理的主要區(qū)域矾策,采用分代收集策略磷账,所以Java堆會細分為新生代和老年代。根據(jù)Java虛擬機規(guī)范的規(guī)定贾虽,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中逃糟,只要邏輯上連續(xù)的即可。如果堆中沒有內(nèi)存完成實例分配,并且堆也無法再擴展時绰咽,拋出OutOfMemoryError異常菇肃。

  5. 方法區(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ù)設置了永久代的大小掸绞;

  6. 直接內(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)存大小及處理器尋址空間的限制抚垄。

  7. JVM參數(shù):

    -XX:+HeapDumpOnOutOfMemoryError:可讓虛擬機在內(nèi)存溢出異常時Dump出當前的內(nèi)存堆轉(zhuǎn)儲快照以便事后分析;

    -Xss:設置線程棧大型┚智哀;

    -XX:PermSize:設置永久代初始大小屯吊;

    -XX:MaxPermSize:設置永久代最大大心〔ぁ次氨;

    -XX:MaxDirectMemorySize:設置直接內(nèi)存大小,默認與Java堆最大值一樣虹蓄;

1.5 對象是否可回收

  1. 引用計數(shù)算法
    存儲對特定對象的所有引用數(shù)幸撕,也就是說,當應用程序創(chuàng)建引用以及引用超出范圍時律胀,JVM必須適當增減引用數(shù)炭菌。當某對象的引用數(shù)為0時,便可以進行垃圾收集赘艳。

    優(yōu)點:實現(xiàn)簡單投储、效率高;

    缺點:很難解決對象之間相互引用問題娇掏;

  2. 可達性分析算法
    通過一系列的稱為“GC Roots”的對象作為起始點勋眯,從這些節(jié)點開始向下搜索客蹋,搜索所走的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時番电,則證明此對象是不可用的辆琅。

可作為GC Roots的對象包括:

  1. 虛擬機棧(棧幀的本地變量表)中引用的對象婉烟;
  2. 方法區(qū)中類靜態(tài)屬性引用的對象;
  3. 方法區(qū)中常量引用的對象洞辣;
  4. 本地方法棧中JNI(即一般說的是native方法)引用的對象昙衅;

1.6 四種引用

  1. 強引用:只要強引用還存在而涉,垃圾收集器永遠不會回收掉被引用的對象
  2. 軟引用:對軟引用關聯(lián)著的對象蟹但,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前谭羔,將會把這些對象列進回收范圍之中進行第二次回收瘟裸。
  3. 弱引用:對弱引用關聯(lián)著的對象,只能生存到下一次垃圾收集發(fā)生之前兼搏。
  4. 虛引用:對象是否有虛引用沙郭,完全不會對其生存時間構(gòu)成影響病线,也無法通過虛引用來取得對象實例。關聯(lián)虛引用唯一目的就是能在對象被收集器回收時收到系統(tǒng)通知绑莺。

1.7 finalize()方法

任何一個對象的finalize()方法都僅會被系統(tǒng)自動調(diào)用一次惕耕。如果對象面臨下一次回收司澎,它的finalize()方法不會被再次執(zhí)行。建議避免使用該方法浪南。

1.8 JVM垃圾回收策略

GC即垃圾收集機制是指JVM用于釋放那些不再使用的對象所占用的內(nèi)存漱受。常用機制:

  1. 標記-清除算法【適用于老年代】
    首先根據(jù)可達性分析算法昂羡,標記出所有需要回收的對象,在標記完成后統(tǒng)一回收所有標記的對象怨愤。

    缺點:效率問題:標記蛹批、清除兩個過程效率都低;空間問題:標記清除之后產(chǎn)生大量不連續(xù)的內(nèi)存碎片差导,空間碎片太多可能會導致以后在程序運行過程中设褐,需要分配大對象時,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作犀被。

  2. 復制算法【空間換時間外冀,適用于對象存活率低的新生代】
    將可用內(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)存(老年代)進行分配擔保平酿。

    當對象的存活率較高時,復制算法要進行較多的復制操作筑辨,效率會變低。

  3. 標記-整理算法【適用于老年代】

    標記過程與“標記-清除”算法一樣搔涝,唯一區(qū)別是在后續(xù)步驟不是直接對內(nèi)存進行清除衙熔,而是先讓所有活著的對象都向一端移動,然后直接清除掉端邊界以外的內(nèi)存

  4. 分代收集策略
    一般是把Java堆分成 新生代和老年代凭舶,這樣就可以根據(jù)各個年代的特點采用最適當?shù)氖占惴ā?/p>

    新生代中對象存活率低,采用復制算法匆背,有老年代對它進行分配擔保钝尸。

    老年代中對象存活率高搂根,無額外空間對它進行分配擔保剩愧,必須采用 “標記-清除”或“標記-整理”算法。

  5. 回收方法區(qū)(永久代)
    很多人認為方法區(qū)(永久代)是沒有垃圾收集的穴翩,Java虛擬機規(guī)范中確實說過可以不要求虛擬機在方法區(qū)實現(xiàn)垃圾收集锦积,況且在方法區(qū)中進行垃圾收集性價比很低丰介;

    永久代的垃圾收集主要回收兩方面: 廢棄常量和無用的類

    廢棄常量:判斷常量池的對象是否還存在任何引用淆储;
    無用的類:(1)該類的所有實例都被回收本砰;(2)該類的ClassLoader已被回收钢悲;(3)該類的Class對象沒有任何引用莺琳;

1.9 垃圾收集器

JDK1.7 Update14之后的HotSpot虛擬機正式提供了G1收集器;

  1. Serial收集器:
    單線程珍手、Stop The Wold琳要、Client模式默認新生代收集器復制算法童叠;

  2. ParNew收集器:
    Serial多線程版厦坛、并行乍惊、Stop The Wold、Server模式默認新生代收集器、只有該收集器能與CMS收集器配合凡橱、 復制算法稼钩;

    ParNew收集器也是使用:

    -XX:+UseConcMarkSweepGC 選項后的默認新生代收集器达罗;
    -XX:+UserParNewGC 選項來強制指定它粮揉;
    -XX:ParallelGCThreads 參數(shù)來限制垃圾收集的線程數(shù),默認開啟的線程數(shù)與CPU的數(shù)量相等侨拦;

  3. Parallel Scavenge收集器:
    多線程狱从、并行叠纹、Stop The Wold誉察、 新生代收集器、Server模式递沪、 復制算法款慨; CMS等收集器的關注點是 盡可能地縮短垃圾收集時用戶線程的停頓時間,而 該收集器關注的是達到一個可控制的吞吐量桩了;

    所謂吞吐量就是CPU用于運行用戶代碼的時間與CPU總消耗時間的比值井誉,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)整胃,吞吐量與垃圾收集時間成反比屁使;

    停頓時間越短就越適合需要與用戶交互的程序,而高吞吐量則可以高效率的利用CPU蔽午,盡快完成程序的運算任務及老,主要適合在后臺運算而不需要太多交互的任務范抓;

    -XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間匕垫;
    -XX:GCTimeRatio:設置吞吐量大小,大于0且小于100的整數(shù)悔捶,默認為99蜕该;
    -XX:UseAdaptiveSizePolicy:設置打開GC自適應的調(diào)節(jié)策略堂淡,以達到最大的吞吐量;

  4. Serial Old收集器:
    單線程萤悴、Stop The Wold皆的、 老年代费薄、 標記-整理算法、Client模式伟众;作為CMS收集器的備選方案凳厢,在并發(fā)收集發(fā)生Concurrent Mode Failure時使用竞慢;

  5. Parallel Old收集器:
    多線程梗顺、Stop The Wold寺谤、 老年代吮播、 標記-整理算法、Server模式意狠、并行粟关;適合與Parallel Scavenge配合使用

  6. 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時都進行碎片整理;

  7. 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收集器惧互;

GC收集器

1.10 JVM參數(shù)配置

  1. 跟 Java 堆大小相關的 JVM 內(nèi)存參數(shù)

    下面三個 JVM 參數(shù)用來指定堆的初始大小和最大值以及堆棧大小:

    -Xms 設置 Java 堆的初始化大小
    -Xmx 設置最大的 Java 堆大小
    -Xss 設置Java線程棧大小

  2. 關于打印垃圾收集器詳情的 JVM 參數(shù)

    -verbose:gc 記錄 GC 運行以及運行時間喇伯,一般用來查看 GC 是否是應用的瓶頸
    -XX:+PrintGCDetails 記錄 GC 運行時的詳細數(shù)據(jù)信息喊儡,包括新生成對象的占用內(nèi)存大小以及耗費時間等
    -XX:+PrintGCTimeStamps 打印垃圾收集的時間戳

  3. 設置 Java 垃圾收集器行為的 JVM 參數(shù)

    -XX:+UseParallelGC 使用并行垃圾收集
    -XX:+UseConcMarkSweepGC 使用并發(fā)標志掃描收集 (Introduced in 1.4.1)
    -XX:+UseSerialGC 使用串行垃圾收集 (Introduced in 5.0.)

    需要提醒的是,但你的應用是非常關鍵的稻据、交易非常頻繁應用時艾猜,應該謹慎使用 GC 參數(shù),因為 GC 操作是耗時的捻悯,你需要在這之中找到平衡點匆赃。

  4. JVM調(diào)試參數(shù),用于遠程調(diào)試

    -Xdebug -Xnoagent 
    -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
    
  5. 用于修改 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.
    
  6. 用來跟蹤類加載和卸載的信息

    -XX:+TraceClassLoading-XX:+TraceClassUnloading 用來打印類被加載和卸載的過程信息今缚,這個用來診斷應用的內(nèi)存泄漏問題非常有用算柳。

  7. 用于調(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 對象進入老年代

  1. 大對象:通過參數(shù) -XX:PretenureSizeThreshold=3145287,令大于這個設置值的對象直接在老年代分配闺属。以避免大對象在新生代分配慌盯,從而觸發(fā)新生代GC。

  2. 多次GC仍存活的對象:當對象每”熬過“一次Minor GC掂器,對象年齡就增加1歲亚皂,當對象年齡增加到一定程度(默認15歲),就會晉升到老年代中国瓮。對象晉升老年代的年齡閥值灭必,可以通過參數(shù)-XX:MaxTenurigThreshold設置狞谱。

  3. 動態(tài)對象年齡判定:虛擬機并不是永遠地要求對象年齡必須達到MaxTenurigThreshold閥值才能晉升到老年代,如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半禁漓,年齡大于或等于該年齡的對象就可以直接進入老年代跟衅,無須等到MaxTenurigThreshold閥值

  4. 新生代—分配擔保:由于新生代采用復制收集算法播歼,當新生代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)存润绵;

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末线椰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子尘盼,更是在濱河造成了極大的恐慌憨愉,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卿捎,死亡現(xiàn)場離奇詭異配紫,居然都是意外死亡,警方通過查閱死者的電腦和手機午阵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門躺孝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人底桂,你說我怎么就攤上這事植袍。” “怎么了籽懦?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵于个,是天一觀的道長。 經(jīng)常有香客問我暮顺,道長厅篓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任拖云,我火速辦了婚禮贷笛,結(jié)果婚禮上应又,老公的妹妹穿的比我還像新娘宙项。我一直安慰自己,他們只是感情好株扛,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布尤筐。 她就那樣靜靜地躺著汇荐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盆繁。 梳的紋絲不亂的頭發(fā)上掀淘,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音油昂,去河邊找鬼革娄。 笑死,一個胖子當著我的面吹牛冕碟,可吹牛的內(nèi)容都是我干的拦惋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼安寺,長吁一口氣:“原來是場噩夢啊……” “哼厕妖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挑庶,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤言秸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后迎捺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體举畸,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年凳枝,在試婚紗的時候發(fā)現(xiàn)自己被綠了俱恶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡范舀,死狀恐怖合是,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锭环,我是刑警寧澤聪全,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站辅辩,受9級特大地震影響难礼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜玫锋,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一蛾茉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撩鹿,春花似錦谦炬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽础爬。三九已至,卻和暖如春吼鳞,著一層夾襖步出監(jiān)牢的瞬間看蚜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工赔桌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留供炎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓疾党,卻偏偏與公主長得像碱茁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仿贬,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 《深入理解Java虛擬機》筆記_第一遍 先取看完這本書(JVM)后必須掌握的部分纽竣。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,062評論 1 34
  • 第二部分 自動內(nèi)存管理機制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運行數(shù)據(jù)區(qū)域 程序計數(shù)器:當前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,130評論 0 2
  • 內(nèi)存溢出和內(nèi)存泄漏的區(qū)別 內(nèi)存溢出:out of memory,是指程序在申請內(nèi)存時茧泪,沒有足夠的內(nèi)存空間供其使用蜓氨,...
    Aimerwhy閱讀 730評論 0 1
  • 工作之余,想總結(jié)一下JVM相關知識队伟。 Java運行時數(shù)據(jù)區(qū): Java虛擬機在執(zhí)行Java程序的過程中會將其管理的...
    Huang遠閱讀 629評論 0 2
  • 介紹JVM中7個區(qū)域穴吹,然后把每個區(qū)域可能造成內(nèi)存的溢出的情況說明 程序計數(shù)器:看做當前線程所執(zhí)行的字節(jié)碼行號指示器...
    jemmm閱讀 2,223評論 0 9