日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入理解java虚拟机 (周志明)JVM个人总结

發(fā)布時間:2023/12/8 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解java虚拟机 (周志明)JVM个人总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

JIT:即時編譯器,把class中的字節(jié)碼翻譯成CPU上可以直接執(zhí)行的二進制指令。新的JIT不僅是編譯,可以分析字節(jié)碼是否可以優(yōu)化,它可以將那些經(jīng)常執(zhí)行的字節(jié)碼片段(熱點代碼)進行緩存。

java虛擬機規(guī)范 周志明
JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用于計算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的。Java語言的一個非常重要的特點就是與平臺的無關(guān)性。而使用Java虛擬機是實現(xiàn)這一特點的關(guān)鍵。一般的高級語言如果要在不同的平臺上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機后,Java語言在不同平臺上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與具體平臺相關(guān)的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節(jié)碼),就可以在多種平臺上不加修改地運行。Java虛擬機在執(zhí)行字節(jié)碼時,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行。這就是Java的能夠一次編譯,到處運行 的原因。

* 執(zhí)行引擎處于JVM的核心位置,在Java虛擬機規(guī)范中,它的行為是由指令集所決定的。盡管對于每條指令,規(guī)范很詳細地說明了當JVM執(zhí)行字節(jié)碼遇到指令時,它的實現(xiàn)應(yīng)該做什么,但對于怎么做卻言之甚少。Java虛擬機支持大約248個字節(jié)碼。每個字節(jié)碼執(zhí)行一種基本的CPU運算,例如,把一個整數(shù)加到寄存器,子程序轉(zhuǎn)移等。Java指令集相當于Java程序的匯編語言。Java指令集中的指令包含一個單字節(jié)的操作符,用于指定要執(zhí)行的操作,還有0個或多個操作數(shù),提供操作所需的參數(shù)或數(shù)據(jù)。許多指令沒有操作數(shù),僅由一個單字節(jié)的操作符構(gòu)成。 * java的字節(jié)碼是由javac所編譯的,

Java中,字節(jié)碼是CPU構(gòu)架(JVM)的具有可移植性的機器語言

Java中,字節(jié)碼是CPU構(gòu)架(JVM)的具有可移植性的機器語言第一章 走近java
* 因為程序員把內(nèi)存控制的權(quán)力交給了java虛擬機,編碼的時候享自動內(nèi)存管理的諸多優(yōu)勢。
* 提供了一個相對安全的內(nèi)存管理和訪問機制,避免了絕大部分的內(nèi)存泄漏和指針越界問題
* 但是也是會出現(xiàn)內(nèi)存泄漏。
* Jdk進化史
* jdk 1.1 jdbc jar文件格式 jdk javabeans 語法的內(nèi)部類 反射
* 1.2 java分為三個方向 j2ee(企業(yè)) j2se(桌面開發(fā)) j2me(手機移動終端)
* collections集合 math TimerAPI
* 1.3 類庫
* 1.4 正則表達式 異常鏈 nio xml 等
* 1.5 自動裝箱 泛型 動態(tài)注解 枚舉 可變長參數(shù) 遍歷(foreach) concurrent 并發(fā)包
* 1.6 鎖 垃圾收集 類加載 算法
* 普通對象指針壓縮功能 (-XX:+ userCompressedOops)不建議開啟 jvm自動管理開啟
* 開啟壓縮指針會增加執(zhí)行代碼質(zhì)量,java 堆 指向java堆內(nèi)對象的指針都會被壓縮
* 1.7
* 1.8 lambda 表達式 map


* 第二章
* java 內(nèi)存區(qū)域與內(nèi)存溢出異常
* 方法區(qū)和堆 線程共享
* 剩下的線程隔離
* 程序計數(shù)器(program counter register)只占用了一塊比較小的內(nèi)存空間{可以忽略不計}
* 可以看作是當前線程所執(zhí)行的字節(jié)碼文件(class)的行號指示器。在虛擬機的世界中,字節(jié)碼解釋器就是通過改變計數(shù)器的值來選取下一條執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)都需要這玩意來實現(xiàn)的
* 多線程是通過線程輪流切換,并分配處理器執(zhí)行時間的方式來實現(xiàn)的。
* 1個處理器執(zhí)行一個線程 多核同時多個
* 每條線程都需要有一個獨立的程序計數(shù)器。各條線程之間計數(shù)器互不影響?yīng)毚鎯Α>€程私有的內(nèi)存。

java虛擬機棧
* 每個方法執(zhí)行都會創(chuàng)建一個棧幀,用于存放局部變量表,操作棧,動態(tài)鏈接,方法出口等。每個方法從被調(diào)用,直到被執(zhí)行完。對應(yīng)著一個棧幀在虛擬機中從入棧到出棧的過程。
* 會有兩種異常StackOverFlowError和 OutOfMemoneyError。當線程請求棧深度大于虛擬機所允許的深度就會拋出StackOverFlowError錯誤;虛擬機棧動態(tài)擴展,當擴展無法申請到足夠的內(nèi)存空間時候,拋出OutOfMemoneyError

* 每當一個java方法被執(zhí)行時都會在虛擬機中新創(chuàng)建一個棧幀,方法調(diào)用結(jié)束后即被銷毀。 * 局部變量表中的變量作用域是當前調(diào)用的函數(shù)。函數(shù)調(diào)用結(jié)束后,隨著函數(shù)棧幀的銷毀。局部變量表也會隨之銷毀,釋放空間。 * 棧幀存儲空間為虛擬機棧,每一個棧幀都有自己的局部變量表、操作數(shù)棧和指向當前方法所屬的類引用。 * 當然方法調(diào)用其他的方法 新的棧幀 就會創(chuàng)建且控制權(quán)交給新的棧幀 * 而 JVM 的字節(jié)碼指令是這樣的: * iconst_1 //把整數(shù) 1 壓入操作數(shù)棧 * iconst_2 //把整數(shù) 2 壓入操作數(shù)棧 * iadd //棧頂?shù)膬蓚€數(shù)相加后出棧,結(jié)果入棧 * 局部變量表所需內(nèi)存空間在編譯期間完成分配 當進入一個方法時,這個方法需要在棧幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。 * 局部變量區(qū)被組織為以一個字長為單位、從0開始計數(shù)的數(shù)組,類型為short、byte和char的值在存入數(shù)組前要被轉(zhuǎn)換成int值,而long和 double在數(shù)組中占據(jù)連續(xù)的兩項,在訪問局部變量中的long或double時,只需取出連續(xù)兩項的第一項的索引值即可,如某個long值在局部變量 區(qū)中占據(jù)的索引時3、4項,取值時,指令只需取索引為3的long值即可。 * 單位slot 8個數(shù)據(jù)類型基本上都是占用一個slot long duble 占用2個 加引用類型的數(shù)據(jù)指向一條虛擬機指令的操作碼 引用指針 或者對象句柄 * 虛擬機規(guī)范 boolean 虛擬機中int代替 boolean 數(shù)組 oracle 中為byte 數(shù)組 * true 為1 false 為0 * 局部變量表使用索引來進行訪問 首個局部變量的索引值為0 * 操作數(shù)棧是后進先出的棧


* 本地方法棧
* 什么是Native Method
* 簡單地講,一個Native Method就是一個java調(diào)用非java代碼的接口。一個Native Method是這樣一個java的方法:該方法的實現(xiàn)由非java語言實現(xiàn),比如C。這個特征并非java所特有,很多其它的編程語言都有這一機制
* 與java環(huán)境外交互:
* 有時java應(yīng)用需要與java外面的環(huán)境交互。這是本地方法存在的主要原因,你可以想想java需要與一些底層系統(tǒng)如操作系統(tǒng)或某些硬件交換信息時的情況。本地方法正是這樣一種交流機制:它為我們提供了一個非常簡潔的接口,而且我們無需去了解java應(yīng)用之外的繁瑣的細節(jié)。
* 堆
* 是虛擬機中最大的一塊共享區(qū)域 在虛擬機啟動的時候創(chuàng)建 它存儲了自動內(nèi)存管理系統(tǒng) (gc垃圾收集器) 虛擬機實現(xiàn)者根據(jù)系統(tǒng)的實際需要來選擇自動內(nèi)存管理技術(shù)
* 所有類實例和數(shù)組分配內(nèi)存的區(qū)域
* 基本上采用分代收集算法

* 方法區(qū) * 各個線程共享的運行區(qū)域 * 存儲了每個類的結(jié)構(gòu)信息 運行時常量池 字段 方法數(shù)據(jù) 構(gòu)造函數(shù) 普通方法的字節(jié)碼內(nèi)存 還有特殊方法 * oom * 方法區(qū)并不等于永久代 * hotspot 把gc 分代收集擴展到方法區(qū) 使用永久代來實現(xiàn)方法區(qū) 跟堆一樣管理內(nèi)存 * 運行時常量池是方法區(qū)的一部分 具備動態(tài)性 可以編譯時候產(chǎn)生 class 文件常量池內(nèi)容 運行區(qū)間產(chǎn)生新的 * 虛擬機指令不依賴類 接口 類實例 數(shù)組的布局 而是依賴常量池表中符號信息 * 在HotSpot虛擬機中,用永久代來實現(xiàn)方法區(qū),將GC分代收集擴展至方法區(qū),但是這樣容易遇到內(nèi)存溢出的問題。 *   JDK1.7中,字符串常量池native() *   JDK1.8撤銷永久代,引入元空間。 * 直接內(nèi)存(堆外內(nèi)存)并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是Java 虛擬機規(guī)范中農(nóng)定義的內(nèi)存區(qū)域。在JDK1.4 中新加入了NIO(New Input/Output)類,引入了一種基于通道(Channel)與緩沖區(qū)(Buffer)的I/O 方式,它可以使用native 函數(shù)庫直接分配堆外內(nèi)存,然后通脫一個存儲在Java堆中的DirectByteBuffer 對象作為這塊內(nèi)存的引用進行操作。這樣能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆中來回復(fù)制數(shù)據(jù)。 * 好處: 這樣做有兩方面的好處: * 減少GC管理內(nèi)存:由于GCIH會從Old區(qū)“切出”一塊, 因此導(dǎo)致GC管理區(qū)域變小, 可以明顯降低GC工作量, 提高GC效率, 降低Full GC STW時間(且由于這部分內(nèi)存仍屬于堆, 因此其訪問方式/速度不變- 不必付出序列化/反序列化的開銷). * GCIH內(nèi)容進程間共享:由于這部分區(qū)域不再是JVM運行時數(shù)據(jù)的一部分, 因此GCIH內(nèi)的對象可供對個JVM實例所共享(如一臺Server跑多個MR-Job可共享同一份Cache數(shù)據(jù)), 這樣一臺Server也就可以跑更多的VM實例. * 3、堆外內(nèi)存的好處是: * (1)可以擴展至更大的內(nèi)存空間。比如超過1TB甚至比主存還大的空間; * (2)理論上能減少GC暫停時間; * (3)可以在進程間共享,減少JVM間的對象復(fù)制,使得JVM的分割部署更容易實現(xiàn); * (4)它的持久化存儲可以支持快速重啟,同時還能夠在測試環(huán)境中重現(xiàn)生產(chǎn)數(shù)據(jù) * 本機直接內(nèi)存的分配不會受到Java 堆大小的限制,受到本機總內(nèi)存大小限制 * 配置虛擬機參數(shù)時,不要忽略直接內(nèi)存 防止出現(xiàn)OutOfMemoryError異常 * Java內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存中。每條線程中還有自己的工作內(nèi)存,線程的工作內(nèi)存中保存了被該線程所使用到的變量(這些變量是從主內(nèi)存中拷貝而來)。線程對變量的所有操作(讀取,賦值)都必須在工作內(nèi)存中進行。不同線程之間也無法直接訪問對方工作內(nèi)存中的變量,線程間變量值的傳遞均需要通過主內(nèi)存來完成。 * 基于此種內(nèi)存模型,便產(chǎn)生了多線程編程中的數(shù)據(jù)“臟讀”等問題。

volatile變量是一種稍弱的同步機制在訪問volatile變量時不會執(zhí)行加鎖操作,因此也就不會使執(zhí)行線程阻塞,因此volatile變量是一種比synchronized關(guān)鍵字更輕量級的同步機制。讀取快 修改慢
* 1.volatile保證可見性
* 1)保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的。
* 2)禁止進行指令重排序。
* 編譯出來的只有一條字節(jié)碼指令,也不意味執(zhí)行這條指令就是一個原子操作 一條字節(jié)碼指令在解釋
* 執(zhí)行時,解釋器將要運行許多行代碼才能實現(xiàn)。
* 什么是指令重排?
* 指令重排是指JVM在編譯Java代碼的時候,或者CPU在執(zhí)行JVM字節(jié)碼的時候,對現(xiàn)有的指令順序進行重新排序。
* 指令重排的目的是為了在不改變程序執(zhí)行結(jié)果的前提下,優(yōu)化程序的運行效率。需要注意的是,這里所說的不改變執(zhí)行結(jié)果,指的是不改變單線程下的程序執(zhí)行結(jié)果。
* 一個項目一個接口,每天調(diào)用1次,1s 1s 1s 以后都會是1s嗎?
* 如何使用volatile呢
* 運算結(jié)果并不依賴變量的當前值,后者能夠確保只有單一的線程修改變量的值

非原子操作加鎖 ++ 不能保證原子性 需要加synchronized 或者lock 第七章虛擬機類加載機制 * Java源代碼被編譯成class字節(jié)碼,最終需要加載到虛擬機中才能運行。整個生命周期包括:加載、驗證、準備、解析、初始化、使用和卸載7個階段。 * * 加載 驗證 準備 初始化 卸載5個階段的順序是確定的 * 加載 * 1、通過一個類的全限定名獲取描述此類的二進制字節(jié)流; * 2、將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)保存為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu); * 3、在java堆中生成一個代表這個類的java.lang.Class對象,作為訪問方法區(qū)的入口; * 類加載器 * 虛擬機設(shè)計團隊把加載動作放到JVM外部實現(xiàn),以便讓應(yīng)用程序決定如何獲取所需的類,實現(xiàn)這個動作的代碼稱為?°類加載器?±,JVM提供了3種類加載器: * 1、啟動類加載器(Bootstrap ClassLoader):負責加載 JAVAHOME\lib 目錄中的,或通過-Xbootclasspath參數(shù)指定路徑中的,且被虛擬機認可(按文件名識別,如rt.jar)的類。 * 2、擴展類加載器(Extension ClassLoader):負責加載 JAVAHOME\lib\ext 目錄中的,或通過java.ext.dirs系統(tǒng)變量指定路徑中的類庫。 * 3、應(yīng)用程序類加載器(Application ClassLoader):負責加載用戶路徑(classpath)上的類庫。 * JVM基于上述類加載器,通過雙親委派模型進行類的加載,當然我們也可以通過繼承java.lang.ClassLoader實現(xiàn)自定義的類加載器。

* 雙親委派模型工作過程:當一個類加載器收到類加載任務(wù),優(yōu)先交給其父類加載器去完成,因此最終加載任務(wù)都會傳遞到頂層的啟動類加載器,只有當父類加載器無法完成加載任務(wù)時,才會嘗試執(zhí)行加載任務(wù)。 * 雙親委派模型有什么好處? 比如位于rt.jar包中的類java.lang.Object,無論哪個加載器加載這個類,最終都是委托給頂層的啟動類加載器進行加載,確保了Object類在各種加載器環(huán)境中都是同一個類。 * 驗證 * 為了確保Class文件符合當前虛擬機要求,需要對其字節(jié)流數(shù)據(jù)進行驗證,主要包括格式驗證、元數(shù)據(jù)驗證、字節(jié)碼驗證和符號引用驗證。 * 格式驗證:驗證字節(jié)流是否符合class文件格式的規(guī)范,并且能被當前虛擬機處理,如是否以魔數(shù)0xCAFEBABE開頭、主次版本號是否在當前虛擬機處理范圍內(nèi)、常量池是否有不支持的常量類型等。只有經(jīng)過格式驗證的字節(jié)流,才會存儲到方法區(qū)的數(shù)據(jù)結(jié)構(gòu),剩余3個驗證都基于方法區(qū)的數(shù)據(jù)進行。 * 元數(shù)據(jù)驗證:對字節(jié)碼描述的數(shù)據(jù)進行語義分析,以保證符合Java語言規(guī)范,如是否繼承了final修飾的類、是否實現(xiàn)了父類的抽象方法、是否覆蓋了父類的final方法或final字段等。 * 字節(jié)碼驗證:對類的方法體進行分析,確保在方法運行時不會有危害虛擬機的事件發(fā)生,如保證操作數(shù)棧的數(shù)據(jù)類型和指令代碼序列的匹配、保證跳轉(zhuǎn)指令的正確性、保證類型轉(zhuǎn)換的有效性等。 * 符號引用驗證:為了確保后續(xù)的解析動作能夠正常執(zhí)行,對符號引用進行驗證,如通過字符串描述的全限定名是都能找到對應(yīng)的類、在指定類中是否存在符合方法的字段描述符等。

準備準備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值得階段,這些變量所使用的內(nèi)存都講在方法區(qū)中進行分配。這時候進行內(nèi)存分配的僅包括類變量(被static修飾的變量),而不包括實例變量,實例變量將會在對象實例化時隨著對象一起分配在Java堆中。
* 在準備階段,為類變量(static修飾)在方法區(qū)中分配內(nèi)存并設(shè)置初始值。
* private static int var = 100;
* 準備階段完成后,var 值為0,而不是100。在初始化階段,才會把100賦值給val,但是有個特殊情況:
* private static final int VAL= 100;
* 在編譯階段會為VAL生成ConstantValue屬性,在準備階段虛擬機會根據(jù)ConstantValue屬性將VAL賦值為100。
* 初始化
* 初始化階段是執(zhí)行類構(gòu)造器方法的過程,方法由類變量的賦值動作和靜態(tài)語句塊按照在源文件出現(xiàn)的順序合并而成,該合并操作由編譯器完成。
* 開始執(zhí)行java代碼(或者說字節(jié)碼)


查看JVM使用的默認的垃圾收集器

查看步驟
cmd執(zhí)行命令:
java -XX:+PrintCommandLineFlags -version

jdk1.7 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.8 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk1.9 默認垃圾收集器G1

上邊新生代 下邊老年代

標記-清除算法
復(fù)制算法
標記-整理算法
新生代 單線程
ParNew
新生代多線程
Parallel ScaVenge
新生代 復(fù)制算法 可控制的吞吐量 吞吐量優(yōu)先
Serial old
老年代 單線程標記-整理算法
Parallel Old 多線程 標記-整理算法
cms
最短停頓時間為目標(快)
標記-清除 再整理
Serial old是cms的后備方案

算法過程:

  • Eden+S0可分配新生對象;
  • 對Eden+S0進行垃圾收集,存活對象復(fù)制到S1。清理Eden+S0。一次新生代GC結(jié)束。
  • Eden+S1可分配新生對象;
  • 對Eden+S1進行垃圾收集,存活對象復(fù)制到S0。清理Eden+S1。二次新生代GC結(jié)束。
  • goto 1。
    1.S0與S1的區(qū)間明顯較小,有效新生代空間為Eden+S0/S1,因此有效空間就大,增加了內(nèi)存使用率
    2.有利于對象代的計算,當一個對象在S0/S1中達到設(shè)置的XX:MaxTenuringThreshold值后,會將其分到老年代中,設(shè)想一下,如果沒有S0/S1,直接分成兩個區(qū),該如何計算對象經(jīng)過了多少次GC還沒被釋放,你可能會說,在對象里加一個計數(shù)器記錄經(jīng)過的GC次數(shù),或者存在一張映射表記錄對象和GC次數(shù)的關(guān)系,是的,可以,但是這樣的話,會掃描整個新生代中的對象, 有了S0/S1我們就可以只掃描S0/S1區(qū)了
  • 在年輕代中經(jīng)歷了N次(可配置)垃圾回收后仍然存活的對象,就會被復(fù)制到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。
    針對年老代的垃圾回收即Full GC。

    Jvm查看
    對象的內(nèi)布局
    對象頭 實例數(shù)據(jù) 對象填充
    對象頭 mark word 存儲 64位或者32位指針
    25 哈希嗎
    4 分代年齡
    2 鎖標志
    輕量級 重量級 gc標志 可偏向
    top
    ps -ef | grep java
    jps 條件 pid -q –m –l -v
    jstat 條件 pid
    -class -gc –gccapacity –gcutil –gccause -gcnew
    -gcnewcapacity –gcold –gcoldcapacity -gcpermcapacity
    -compiler -printcompilation
    jmap 條件 pid
    -dump –finalizerinfo –heap –histo –permstat -F

    jvm dump 分析

    mat 英文 memory analyzer tool
    jvm dump 分析工具 (MAT)
    Memory Analyzer tool
    Histogram 所有實例的分配情況
    Dominator Tree 堆的最大對象
    Leak Suspecks 列出懷疑的內(nèi)存泄漏處

    -gc (jstat -gc pid 1000 5 )
    S0C: Survivor0(幸存區(qū)0)大小(KB)
    S1C: Survivor1(幸存區(qū)1)1大小(KB)
    S0U: Survivor0(幸存區(qū)0)已使用大小(KB)
    S1U: Survivor1(幸存區(qū)1)已使用大小(KB)
    EC : Eden(伊甸區(qū))大小(KB)
    EU : Eden(伊甸區(qū))已使用大小(KB)
    OC :老年代大小(KB)
    OU : 老年代已使用大小(KB)
    PC : Perm永久代大小(KB)
    PU : Perm永久代已使用大小(KB)
    YGC:新生代GC個數(shù)
    YGCT:新生代GC的耗時(秒)
    FGC :Full GC次數(shù)
    FGCT:Full GC耗時(秒)
    GCT :GC總耗時(秒)

    常用 jvm tomcat 配置參數(shù)

  • 與串行回收器相關(guān)的參數(shù)
    -XX:+UseSerialGC:在新生代和老年代使用串行回收器。
    -XX:+SuivivorRatio:設(shè)置 eden 區(qū)大小和 survivor 區(qū)大小的比例。
    -XX:+PretenureSize :設(shè)置大對象直接進入老年代的閾值。當對象的大小超過這個值時,將直接在老年代分配 默認15。
    -XX:MaxTenuringThreshold:設(shè)置對象進入老年代的年齡的最大值。每一次 Minor GC 后,對象年齡就加 1。任何大于這個年齡的對象,一定會進入老年代。
  • 與并行 GC 相關(guān)的參數(shù)
    -XX:+UseParNewGC: 在新生代使用并行收集器。
    -XX:+UseParallelOldGC: 老年代使用并行回收收集器。
    -XX:ParallelGCThreads:設(shè)置用于垃圾回收的線程數(shù)。通常情況下可以和 CPU 數(shù)量相等。但在 CPU 數(shù)量比較多的情況下,設(shè)置相對較小的數(shù)值也是合理的。
    -XX:MaxGCPauseMills:設(shè)置最大垃圾收集停頓時間。它的值是一個大于 0 的整數(shù)。收集器在工作時,會調(diào)整 Java 堆大小或者其他一些參數(shù),盡可能地把停頓時間控制在 MaxGCPauseMills 以內(nèi)。
    -XX:GCTimeRatio:設(shè)置吞吐量大小,它的值是一個 0-100 之間的整數(shù)。假設(shè) GCTimeRatio 的值為 n,那么系統(tǒng)將花費不超過 1/(1+n) 的時間用于垃圾收集。
    -XX:+UseAdaptiveSizePolicy:打開自適應(yīng) GC 策略。在這種模式下,新生代的大小,eden 和 survivor 的比例、晉升老年代的對象年齡等參數(shù)會被自動調(diào)整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。
  • 與 CMS 回收器相關(guān)的參數(shù)
    -XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。
    -XX:+ParallelCMSThreads: 設(shè)定 CMS 的線程數(shù)量。
  • 滴滴面試問到的 68 是什么的比例

    XX:+CMSInitiatingOccupancyFraction:設(shè)置 CMS 收集器在老年代空間被使用多少后觸發(fā),默認為 68%。

    XX:+UseFullGCsBeforeCompaction:設(shè)定進行多少次 CMS 垃圾回收后,進行一次內(nèi)存壓縮。
    -XX:+CMSClassUnloadingEnabled:允許對類元數(shù)據(jù)進行回收。
    -XX:+CMSParallelRemarkEndable:啟用并行重標記。
    -XX:CMSInitatingPermOccupancyFraction:當永久區(qū)占用率達到這一百分比后,啟動 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。
    -XX:UseCMSInitatingOccupancyOnly:表示只在到達閾值的時候,才進行 CMS 回收。
    -XX:+CMSIncrementalMode:使用增量模式,比較適合單 CPU。
    4. 與 G1 回收器相關(guān)的參數(shù)
    -XX:+UseG1GC:使用 G1 回收器。
    -XX:+UnlockExperimentalVMOptions:允許使用實驗性參數(shù)。
    -XX:+MaxGCPauseMills:設(shè)置最大垃圾收集停頓時間。
    -XX:+GCPauseIntervalMills:設(shè)置停頓間隔時間。

    1.手動調(diào)整

    Xmn
    -Xms
    -XX:NewRatio=N
    手動指定堆內(nèi)存大小和代空間比例,一般要多次試驗

    2.自動參數(shù)調(diào)整

    XX:MaxGCPauseMillis=N 可接受最大停頓時間
    -XX:GCTimeRatio=N 可接受GC時間占比(目標吞吐量) 吞吐量=1-1/(1+N)
    步驟:
    1.MaxGCPauseMillis優(yōu)先級高,JVM會自動調(diào)整堆大小和代空間值,以期滿足MaxGCPauseMillis
    2.當MaxGCPauseMillis滿足后,JVM會增大堆大小,直到滿足GCTimeRatio
    3.當MaxGCPauseMillis和GCTimeRadio都滿足后,JVM會盡可能以最小堆大小來實現(xiàn)這兩個指標參數(shù)

    Full GC原因:

    并發(fā)模式失效:新生代發(fā)生GC時,老年代沒有足夠內(nèi)存容納晉升對象
    晉升失敗:老年代雖然有足夠容納晉升對象的內(nèi)存,但內(nèi)存都是碎片,導(dǎo)致晉升失敗
    *參數(shù)調(diào)整:避免并發(fā)模式失效和晉升失敗
    -XX:+UseCMSInitiatingOccupancyOnly 根據(jù)Old內(nèi)存使用閾值決定何時CMS, 默認是false,會用更復(fù)雜的算法決定何時CMS
    -XX:CMSInitingOccupancyFraction=N default N=70,老年代內(nèi)存使用70%時就發(fā)生CMS
    N設(shè)置太大,容易并發(fā)模式失效;N太小,CMS過于頻繁,而CMS也會導(dǎo)致stop-the-world
    -XX:ConGCThreads=N GC的線程會100%占用CPU,如果發(fā)生并發(fā)模式失敗,而N還小于CPU核心數(shù),此時可以增加N。
    如果沒有發(fā)生并發(fā)模式失敗,此時可以減少N,以讓應(yīng)用程序有更多CPU執(zhí)行
    Perm持久代GC調(diào)優(yōu)
    持久代內(nèi)存滿了會引發(fā)Full GC
    持久代GC調(diào)優(yōu)主要是讓持久代也進行CMS收集
    -XX:+CMSPermGenSweepingEnable 使持久代使用CMS收集器
    -XX:+CMSClassUnloadingEnable 使持久代能真正釋放不再被使用的類。默認是不會釋放類的元數(shù)據(jù)的
    增量式CMS:普通CMS線程會占用100%的cpu負載,增量式CMS會讓出一定CPU負載給應(yīng)用線程
    這適合在單核CPU使用,顯然已經(jīng)沒啥用處了

    1. -Xms128m 2. -Xmx128m 3. -XX:NewSize=64m 4. -XX:PermSize=64m 5. -XX:+UseConcMarkSweepGC 6. -XX:CMSInitiatingOccupancyFraction=78 7. -XX:ThreadStackSize=128-Xloggc:logs/gc.log 8. -Dsun.rmi.dgc.server.gcInterval=3600000 9. -Dsun.rmi.dgc.client.gcInterval=3600000 10. -Dsun.rmi.server.exceptionTrace=true

    老年代(Old Generation)

    老年代(Old Generation)老年代的GC實現(xiàn)要復(fù)雜得多。老年代內(nèi)存空間通常會更大,里面的對象是垃圾的概率也更小。
    老年代GC發(fā)生的頻率比年輕代小很多。同時, 因為預(yù)期老年代中的對象大部分是存活的, 所以不再使用標記和復(fù)制(Mark and Copy)算法。而是采用移動對象的方式來實現(xiàn)最小化內(nèi)存碎片。老年代空間的清理算法通常是建立在不同的基礎(chǔ)上的。原則上,會執(zhí)行以下這些步驟:

    通過標志位(marked bit),標記所有通過 GC roots 可達的對象.
    刪除所有不可達對象
    整理老年代空間中的內(nèi)容,方法是將所有的存活對象復(fù)制,從老年代空間開始的地方,依次存放。

    Minor GC vs Major GC vs Full GC

    垃圾收集事件(Garbage Collection events)通常分為: 小型GC(Minor GC) - 大型GC(Major GC) - 和完全GC(Full GC) 。

    小型GC(Minor GC)

    年輕代內(nèi)存的垃圾收集事件稱為小型GC。這個定義既清晰又得到廣泛共識。對于小型GC事件,有一些有趣的事情你應(yīng)該了解一下:
    1. 當JVM無法為新對象分配內(nèi)存空間時總會觸發(fā) Minor GC,比如 Eden 區(qū)占滿時。所以(新對象)分配頻率越高, Minor GC 的頻率就越高。
    2. Minor GC 事件實際上忽略了老年代。從老年代指向年輕代的引用都被認為是GC Root。而從年輕代指向老年代的引用在標記階段全部被忽略。
    3. 與一般的認識相反, Minor GC 每次都會引起全線停頓(stop-the-world ), 暫停所有的應(yīng)用線程。對大多數(shù)程序而言,暫停時長基本上是可以忽略不計的, 因為 Eden 區(qū)的對象基本上都是垃圾, 也不怎么復(fù)制到存活區(qū)/老年代。如果情況不是這樣, 大部分新創(chuàng)建的對象不能被垃圾回收清理掉, 則 Minor GC的停頓就會持續(xù)更長的時間。
    所以 Minor GC 的定義很簡單 —— Minor GC 清理的就是年輕代。

    Major GC vs Full GC

    沒有明確的定義
    Major GC(大型GC) 清理的是老年代空間(Old space)。
    Full GC(完全GC)清理的是整個堆, 包括年輕代和老年代空間。

    很多 Major GC 是由 Minor GC 觸發(fā)的, 所以很多情況下這兩者是不可分離的。另一方面, 像G1這樣的垃圾收集算法執(zhí)行的是部分區(qū)域垃圾回收 回收區(qū)分也不是很明確
    這也讓我們認識到,不應(yīng)該去操心是叫 Major GC 呢還是叫 Full GC, 我們應(yīng)該關(guān)注的是: 某次GC事件 是否停止所有線程,或者是與其他線程并發(fā)執(zhí)行。

    一個線程OOM,進程里其他線程還能運行么?

    正常linux 項目啟動 oom error 就會造成項目停止
    這問題正常也是回答 其他線程也會停止,總感覺oom 出問題都會停止

    但是這個線程有時候真是特殊,一個線程oom并不一定其他的線程也停止。
    既然一個線程oom,那它就會觸發(fā)gc,gc回收后如果有足夠的空間,并不會造成其他的線程停止。

    https://cloud.tencent.com/developer/article/1614156
    https://blog.csdn.net/qq_40298351/article/details/121256296

    總結(jié)

    以上是生活随笔為你收集整理的深入理解java虚拟机 (周志明)JVM个人总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。