您好,登錄后才能下訂單哦!
相應的名詞解釋:
- 類加載器:在jvm啟動或者類運行時將需要的class加載到jvm內存中
- 執行引擎:負責執行class文件中包含的字節指令
- 內存區:是在jvm運行的時候操作鎖分配的內存區。運行時內存區分五個部分:堆、方法區、棧、本地方法棧、程序計數器,
- 本地方法接口:主要是調用c或者c++實現的本地方法及返回結果。
jvm內存結構主要由三大塊:堆內存、方法區和棧。
堆內存:是jvm中最大的一塊由年輕代和老年代組成,而年輕代內存又被分成三個部分:Eden 空間(對相關剛剛被創建時存放的位置)、From Survivor() 空間、To Survivor(存貨下來的對象的存放區域) 空間,默認情況下年輕代按照 8:1:1 的比例來分配。
方法區(永久代):存儲信息、常量、靜態變量等數據,是線程共享的區域,為與java堆區分,方法區還有一個別名Non-Heap(非堆) jvm各區域的作用。
棧:又分為java虛擬機棧和本地方法棧和程序計數器,主要用于方法的執行。
堆內存的設置:
-Xms:設置堆的最小空間大小
-Xmx:設置堆的最大空間大小
-XX:NewSize 設置新生代最小空間大小
-XX:MaxNewSize:設置新生代最大空間大小
-XX:PermSize 設置永久代最小空間大小
-XX:MaxPermSize 設置永久代最大空間大小
-Xss 設置每個線程的堆棧大小
沒有直接設置老年代的參數,但是可以設置堆空間大小和新生代空間大小兩個參數來間接控 制:老年代空間大小=堆空間大小-年輕代大空間大小
對于大多數應用來說,Java 堆(Java Heap)是 Java 虛擬機所管理的內存中最大的一塊。Java 堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。
java堆是垃圾收集器管理的主要區域,因此很多時候也被成為GC堆。如果從內存回收的角度看,由于現在收集器基本都是采用分代收集算法,所以java堆中還可以劃分:新生代和老年代;在細一點的劃分:新生代又包括:Eden 空間、From Survivor 空間、To Survivor 空間。
如果在堆中沒有內存完成實例分配(堆中沒有內存裝得下對象),并且堆也無法再擴展時,將會拋出 OutOfMemoryError 異常。
補充(新生代到老年的過程):大多數情況下,對象在新生代Eden區中分配。當Eden區沒有足夠空間進行分配時,虛擬機將發起一次Minor GC,此時對象會進入survivor區,當對象滿足一些條件后會進入老年代。
- 長期存活的對象將進入老年代:虛擬機給每個對象定義了一個對象年齡(Age)計數器。如果對象在Eden出生并經過第一次Minor GC后仍然存活,并且能被Survivor容納的話,將被移動到Survivor空間中,并且對象年齡設為1。對象在Survivor區中每“熬過”一次Minor GC,年齡就增加1歲,當它的年齡增加到一定程度(默認為15歲),就將會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數-XX:MaxTenuringThreshold設置。
- 如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡。
- 虛擬機提供了一個-XX:PretenureSizeThreshold參數,令大于這個設置值的對象直接在老年代分配。這樣做的目的是避免在Eden區及兩個Survivor區之間發生大量的內存復制。
-方法區與java一樣,是各個線程的共享的內存區域,它用于存儲被jvm加載的類信息、常量、靜態變量、及時編譯器編譯后的代碼等數據。
jvm規范對這個區域的限制非常寬松,除了和java堆一樣不需要連續的內存和可以選擇固定大小,還可以選擇不實現垃圾收集。相對而言,垃圾收集行為在這個區域是比較少出現的,到哪并非數據進入的方法區就“永遠”存在了。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載,一般來說這個區域的回收 “成績”比較難以令人滿意,尤其是類型的卸載,條件相當苛刻,但是這部分區域的回收確實是有必要的。
根據 Java 虛擬機規范的規定,當方法區無法滿足內存分配需求時,將拋出 OutOfMemoryError 異常。
程序計數器時一小塊的內存空間,它的作用可以看做是當前程序所執行的字節碼的行號指示器,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器完成。
由于 Java 虛擬機的多線程是通過線程輪流切換并分配處理器執行時間的方式來實現的,在 任何一個確定的時刻,一個處理器(對于多核處理器來說是一個內核)只會執行一條線程 中的指令。因此,為了線程切換后能恢復到正確的執行位置,每條線程都需要有一個獨立的程序計數器,各條線程之間的計數器互不影響,獨立存儲,我們稱這類內存區域為“線程私有”的內存。
如果線程正在執行的是一個 Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是 native 方法(調用時使用底層的指令),這個計數器值則為空(Undefined)。
此內存區域是唯一一個在 Java 虛擬機規范中沒有規定任何 OutOfMemoryError 情況的區域。
Java 虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是 Java 方法執行的內存模型:每個方法被執行的時候 都會同時創建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
局部變量表存放了編譯期可知的各種基本數據類型,局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。
關于java虛擬機棧的異常:1) 如果線程請求的棧深度大于虛擬機所允許的深度,將拋出 StackOverflowError 異常 2) 如果虛擬機棧可以動態擴展,當擴展時無法申請到足夠的內存時會拋出 OutOfMemoryError 異常。
使用native修飾的方法,則存儲在本地方法棧中。與虛擬機棧一樣,本地方法棧 區域也會拋出 StackOverflowError 和 OutOfMemoryError 異常。
垃圾收集Garbage Collection通常被稱為“GC”,它誕生于1960年MT的Lisp語言。Jvm中,程序計數器、虛擬機棧、本地方法棧都是隨線程而生隨線程而滅,棧幀隨著方法的 進入和退出做入棧和出棧操作,實現了自動的內存清理,因此,我們的內存垃圾回收主要集中于 Java 堆和方法區中,在程序運行期間,這部分內存的分配和使用都是動態的。
GC其實是一種自動的內存管理工具器行為主要包括2個步驟:在和Java堆中,為新的創建的對象分配空間、回收沒有用的對象的內存空間
Java 平臺被部署在各種各樣的硬件資源上,其次,在 Java 平臺上部署和運行著各種 各樣的應用,并且用戶對不同的應用的性能指標(吞吐率和延遲)預期也不同,為了滿足不同 應用的對內存管理的不同需求,JVM 提供了多種 GC 以供選擇。
GC的性能指標主要包括:
- 最大停頓時長:圾回收導致的應用停頓時間的最大值
- 吞吐率:垃圾回收停頓時長和應用運行總時長的比例
例:一次應用程序運行了60s,然后GC的時長為2s(進行了4次GC:0.5,0.8,0.2,0.5),那么最大的停頓時長為:0.8,吞吐率為:(60-2)/60
GC的種類大概分為:
- 序列化GC:適合占用內存少的應用
- 并行GC或者吞吐率GC,適合占用內存較多,多 CPU,追求高吞吐率的應用。
- 并發GC:適合占用內存較多,多 CPU 的應用,對延遲有要求的應用。
并行:指多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待狀態。
并發:指用戶線程與垃圾收集線程同時執行(但不一定是并行的,可能會交 替執行)。
- 引用計數:每個對象有一個引用計數屬性,新增一個引用時計數加 1,引用釋放時計數減 1,計數為 0 時可以回收。此方法簡單,缺點是無法解決對象相互循環引用的問題。
- 可達性分析:從 GC Roots 開始向下搜索,搜索所走過的路徑稱為 引用鏈。當一個對象到 GC Roots 沒有任何引用鏈相連時,則證明此對象是不可用的不可達對象。
可達對象:通過根對象進行引用搜索,最終可以達到的對象。
不可達對象:通過根對象進行引用搜索,最終沒有被引用到的對象。
Java 語言中,GC Roots 包括:
新生代GC(Minor GC):指發生在新生代的垃圾收集動作,因為 Java 對象大多都具備朝生 夕滅的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。
老年代GC(Full GC):是清理整個堆空間—包括年輕代和老年代或者永久代。。Full GC 的速度一般會比 Minor GC 慢 10 倍以上。
“標記-清除”(Mark-Sweep)算法,算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統一回收掉所有被標記的對象。
缺點:
- 一個是效率問題,標記和清除過程的效率都不高;
- 一個是空間問題,標記清除之后會產生大量不連續的內存碎片,空間碎片太多可能 會導致,當程序在以后的運行過程中需要分配較大對象時無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。
復制”(Copying)的收集算法,它將可用內存按容量劃分為大小相等的兩塊,每次只使用 其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉。
缺點:
- 這種算法的代價 是將內存縮小為原來的一半,持續復制長生存期的對象則導致效率降低。
標記-整理”(Mark-Compact)算法,標記過程仍 然與“標記-清除”算法一樣,但后續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存。
分代收集”(Generational Collection)算法,把 Java 堆分為新生代和老年代,這樣就可以 根據各個年代的特點采用最適當的收集算法。
年輕代(生存周期短,大量對象都是垃圾對象) 使用復制算法。
年老代(生存周期長,少量對象時垃圾對象) 使用標記整理,或者標記清除。
如果說垃圾收集算法是內存回收的方法論,那么垃圾收集器就是內存回收的具體實現。不同廠商、不 同版本的虛擬機實現差別很大,HotSpot 中包含的收集器如下:
串行收集器是最古老,最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個線 程去回收。新生代、老年代使用串行回收;新生代復制算法、老年代標記-壓縮;垃圾收集 的過程中會 Stop The World(服務暫停)
這種算法的缺點就是:在垃圾回收的時候,會停止其他的線程
ParNew 收集器其實就是 Serial 收集器的多線程版本。
新生代并行,老年代串行;新生代復制算法、老年代標記-整理
Parallel Scavenge 收集器類似 ParNew 收集器,Parallel 關注吞吐量的多線程 ParNew 進化版。新生代復制算法、老年代標記-整理算法。
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器。目 前很大一部分的 Java 應用都集中在互聯網站或 B/S 系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗。
優點:并發收集、低停頓
缺點:產生大量的空間碎片,并發節點吞吐率低。
參數配置:
-XX:+UseConcMarkSweepGC 使用 CMS 收集器
-XX:+ UseCMSCompactAtFullCollection Full GC 后,進行一次碎片整理;整理過程是獨占的,會引起停頓時間變長
-XX:+CMSFullGCsBeforeCompaction 設置進行幾次 Full GC 后,進行一次碎片整理
-XX:ParallelCMSThreads 設定 CMS 的線程數量(一般情況約等于可用 CPU 數量)
特點:
- 空間整合:G1 收集器采用標記整理算法,不會產生內存空間碎片。分配大對象時不會因為無法找到連續空間而提前觸發下一次 GC。
- 可預測的停頓:,降低停頓時間是 G1 和 CMS 的共同關注點,但 G1 除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度為 N 毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒,這幾乎已經是實時Java(RTSJ) 的垃圾收集器的特征了。
- G1搜集器,Java 堆的內存布局與其他收集器有很大差別,它將整個 Java 堆劃分為多個大 小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了,它們都是一部分(可以不連續)Region 的集合。
G1 的新生代收集跟 ParNew 類似,當新生代占用達到一定比例的時候,開始觸發收集。這樣一來可以做到,當達到一定的比例時,觸發垃圾回收,那些沒有使用region仍然可以對外提供使用,就有效的避免了stop the world。
與CMS的比較:
- 分代:CMS 中,堆被分為 PermGen,YoungGen,OldGen;而 YoungGen 又分了兩個 survivor 區域。在 G1 中,堆被平均分成幾個區域(region),在每個區域中,雖然也保留了新老代的概 念,但是收集器是以整個區域為單位收集的。
- 算法:相對于 CMS 的“標記—清除”算法,G1 會使用“標記--整理”算法,保證不產生 多余的碎片。
- 停頓時間可控制:了縮短停頓時間,G1 建立可預存停頓模型,這樣在用戶設置的停頓時 間范圍內,G1 會選擇適當的區域進行收集,確保停頓時間不超過用戶指定時間。
各個內存大小分配:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-Xmx3550m:最大堆內存為 3550M
-Xms3550m:初始堆內存為 3550m
一般情況:-Xmx與-Xms值設置為相同的,以避免每次垃圾回收完成后 JVM 重新分配內存。
-Xmn2g:設置年輕代大小為 2G
-Xss1m:設置每個線程的堆棧大小
-XX:NewRatio=4:設置年輕代(包括 Eden 和兩個 Survivor 區)與年老代的比值(除去持久 代)。設置為 4,則年輕代與年老代所占比值為 4:1,年輕代占整個堆棧的 4/5
-XX:SurvivorRatio=4:設置年輕代中 Eden 區與 Survivor 區的大小比值。
-XX:MaxPermSize=16m:設置持久代大小為 16m。
-XX:MaxTenuringThreshold=15:設置垃圾最大年齡。
收集器設置:
-XX:+UseSerialGC 設置串行收集器
-XX:+UseParallelGC 設置并行收集器
-XX:+UseParalledlOldGC 設置并行年老代收集器
-XX:+UseConcMarkSweepGC 設置并發收集器
垃圾回收統計信息:
-XX:+PrintGC
–XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器設置:
-XX:ParallelGCThreads=n 設置并行收集器收集時使用的 CPU 數。并行收集線程數
-XX:MaxGCPauseMillis=n 設置并行收集最大暫停時間
-XX:GCTimeRatio=n 設置垃圾回收時間占程序運行時間的百分比。公式為 1/(1+n)
并發收集器設置:
-XX:+CMSIncrementalMode 設置為增量模式。適用于單 CPU 情況
-XX:ParallelGCThreads=n 設置并發收集器年輕代收集方式為并行收集時,使用的 CPU 數并行收集線程數
jps:查看java進程
-q 只顯示pid,不顯示class名稱,jar文件名和傳遞給main 方法的參數
-m 輸出傳遞給main 方法的參數,在嵌入式jvm上可能是null
-l 輸出應用程序main class的完整package名 或者 應用程序的jar文件完整路徑名
-v 輸出傳遞給JVM的參數
jps host 查看host的jps情況(前提:host提供jstatd服務)
jstatd:啟動jvm監控服務。它是一個基于rmi(遠程接口調用)的應用,向遠程機器提供本機jvm應用程序的信息。默認端口1099。-p指定端口。
jmap1:觀察運行中的jvm物理內存的占用情況
-heap:打印jvm heap的情況(垃圾收集器類型)
-histo:打印jvm heap的直方圖。其輸出信息包括類名,對象數量,對象占用大小。
-histo:live :同上,但是只打印存活對象的情況
-permstat:打印permanent generation heap(方法區)情況
-finalizerinfo:打印正等候回收的對象信息
jmap2:用jmap把進程內存使用情況dump到文件中,再用jhat分析查看。
jmap -dump:format=b,file=dumpFileName pid
jmap -dump:format=b,file=4574.heap20151215 4574
jinfo:打印命令行參數和系統屬性
-flags 打印命令行參數
-sysprops 打印系統屬性
jstack1:能得到運行java程序的java stack和native stack的信息。可以輕松得知當前線程的運行情況
-l長列表. 打印關于鎖的附加信息,例如屬于java.util.concurrent的ownable synchronizers列表
-m打印java和native c/c++框架的所有棧信息
jstat:
Options — 選項,我們一般使用 -gcutil /-gc 查看gc情況
pid — VM的進程號,即當前運行的java進程號
interval[s|ms] —— 間隔時間,單位為秒或者毫秒,默認為ms。必須是正整型
count — 打印次數,如果缺省則打印無數次
例:jstat -gc 4645 500 10 表示查看進程為4645的gc每個 500ms打印一次,一共打印10次
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。