JVM内存结构与GC
JVM內(nèi)存模型總體架構(gòu)圖
程序計(jì)數(shù)器
多線程時(shí),當(dāng)線程數(shù)超過CPU數(shù)量或CPU內(nèi)核數(shù)量,線程之間就要根據(jù)時(shí)間片輪詢搶奪CPU時(shí)間資源。因此每個(gè)線程有要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,記錄下一條要運(yùn)行的指令。線程私有的內(nèi)存區(qū)域。如果執(zhí)行的是JAVA方法,計(jì)數(shù)器記錄正在執(zhí)行的java字節(jié)碼地址,如果執(zhí)行的是native方法,則計(jì)數(shù)器為空。
虛擬機(jī)棧(棧內(nèi)存)
線程私有的,與線程在同一時(shí)間創(chuàng)建。管理JAVA方法執(zhí)行的內(nèi)存模型。每個(gè)方法執(zhí)行時(shí)都會創(chuàng)建一個(gè)楨棧來存儲方法的的變量表、操作數(shù)棧、動態(tài)鏈接方法、返回值、返回地址等信息。棧的大小決定了方法調(diào)用的可達(dá)深度(遞歸多少層次,或嵌套調(diào)用多少層其他方法,-Xss參數(shù)可以設(shè)置虛擬機(jī)棧大小)。棧的大小可以是固定的,或者是動態(tài)擴(kuò)展的。如果請求的棧深度大于最大可用深度,則拋出stackOverflowError;如果棧是可動態(tài)擴(kuò)展的,但沒有內(nèi)存空間支持?jǐn)U展,則拋出OutofMemoryError。
本地方法區(qū)
和虛擬機(jī)棧功能相似,但管理的不是JAVA方法,是本地方法,本地方法是用C實(shí)現(xiàn)的。
JAVA堆
線程共享的,存放所有對象實(shí)例和數(shù)組。垃圾回收的主要區(qū)域。可以分為新生代和老年代(tenured)。
新生代用于存放剛創(chuàng)建的對象以及年輕的對象,如果對象一直沒有被回收,生存得足夠長,老年對象就會被移入老年代。
新生代又可進(jìn)一步細(xì)分為eden、survivorSpace0(s0,from space)、survivorSpace1(s1,to space)。剛創(chuàng)建的對象都放入eden,s0和s1都至少經(jīng)過一次GC并幸存。如果幸存對象經(jīng)過一定時(shí)間仍存在,則進(jìn)入老年代(tenured)。
方法區(qū)(永久代)
線程共享的,用于存放被虛擬機(jī)加載的類的元數(shù)據(jù)信息:如常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼。也成為永久代。如果hotspot虛擬機(jī)確定一個(gè)類的定義信息不會被使用,也會將其回收。回收的基本條件至少有:所有該類的實(shí)例被回收,而且裝載該類的ClassLoader被回收。方法區(qū)存儲了下面兩種類型的數(shù)據(jù):
1.Class的節(jié)本信息
Package Name
Super class package name
Class or interface
Type modifiers
Super inferface package name
2.其它信息
The constant pool for the type
Field information
Method information
All class (static) variables declared
in the type, except constants
A reference to class ClassLoader
A reference to class Class
SUN設(shè)計(jì)的時(shí)候認(rèn)為這個(gè)區(qū)域在JVM啟動的時(shí)候就固定了,但他沒有想到現(xiàn)在動態(tài)會用得這么廣泛。而且這個(gè)區(qū)域有特殊的垃圾收回機(jī)制,現(xiàn)在的問題是動態(tài)加載類到這個(gè)區(qū)域后,gc根本沒辦法回收!
現(xiàn)在開發(fā)中最常見的錯(cuò)誤莫過于Permgen Space!
解決辦法:
1,把不必要的 jar 文件清理出 lib;
2,不要頻繁地進(jìn)行 reload;
3,增加 PermGen Space 內(nèi)存區(qū)域,默認(rèn)是 64MB,可以采用 -XX:MaxPermSize 這個(gè) JVM 參數(shù)改這塊區(qū)域改大一些,這個(gè)參數(shù)詳見:
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
如果用的是 Tomcat 的話,可以在 catalina.bat 開始處增加一行 set JAVA_OPTS=-XX:MaxPermSize=128m
垃圾回收算法
復(fù)制算法(copying)
將內(nèi)存分成兩塊,每次只使用其中一塊,垃圾回收時(shí),將標(biāo)記的對象拷貝到另外一塊中,然后完全清除原來使用的那塊內(nèi)存。復(fù)制后的空間是連續(xù)的。復(fù)制算法適用于新生代,因?yàn)槔鴮ο蠖嘤诖婊顚ο?#xff0c;復(fù)制算法更高效。在新生代串行垃圾回收算法中,將eden中標(biāo)記存活的對象拷貝未使用的s1中,s0中的年輕對象也進(jìn)入s1,如果s1空間已滿,則進(jìn)入老年代;這樣交替使用s0和s1。這種改進(jìn)的復(fù)制算法,既保證了空間的連續(xù)性,有避免了大量的內(nèi)存空間浪費(fèi)。
標(biāo)記-壓縮算法(Mark-compact)
適合用于老年代的算法(存活對象多于垃圾對象)。
標(biāo)記后不復(fù)制,而是將存活對象壓縮到內(nèi)存的一端,然后清理邊界外的所有對象。
注意:
1.增加Heap的大小雖然會降低GC的頻率,但也增加了每次GC的時(shí)間。并且GC運(yùn)行時(shí),所有的用戶線程將暫停,也就是GC期間,Java應(yīng)用程序不做任何工作
?
JVM參數(shù):
-XX:+PrintGCDetails? 打印垃圾回收信息
-Xms 為Heap區(qū)域的初始值,線上環(huán)境需要與-Xmx設(shè)置為一致,否則capacity的值會來回飄動
-Xmx 為Heap區(qū)域的最大值
-Xss(或-ss) 線程棧大小(指一個(gè)線程的native空間)1.5以后是1M的默認(rèn)大小
-XX:PermSize與-XX:MaxPermSize? 方法區(qū)(永久代)的初始大小和最大值(但不是本地方法區(qū))
-XX:NewRatio? 老年代與新生代比率
-XX:SurvivorRatio? Eden與Survivor的占用比例。例如8表示,一個(gè)survivor區(qū)占用 1/8 的Eden內(nèi)存,即1/10的新生代內(nèi)存,為什么不是1/9?因?yàn)槲覀兊男律?個(gè)survivor,即S1和S22。所以survivor總共是占用新生代內(nèi)存的 2/10,Eden與新生代的占比則為 8/10。
-XX:MaxHeapFreeRatio? GC后,如果發(fā)現(xiàn)空閑堆內(nèi)存占到整個(gè)預(yù)估的比例小于這個(gè)值,則減小堆空間。
-XX:MinHeapFreeRatio? GC后,如果發(fā)現(xiàn)空閑堆內(nèi)存占到整個(gè)預(yù)估的比例大于這個(gè)值,則增大堆空間。
-XX:NewSize??? 新生代大小
?
Jstat用法
http://my.oschina.net/skyline520/blog/304805
打出進(jìn)程堆棧信息
jmap -dump:format=b,file=2.dump pid
查看java運(yùn)行系統(tǒng)內(nèi)部信息
先用top -H -p pid 找到進(jìn)程下cpu最高的線程 比如是4571 (16進(jìn)賬是11DB)
然后用jstack pid > jstack.log
然后在log文件中查看11DB的信息
?
?本文轉(zhuǎn)載整理至:
http://blog.csdn.net/kingofworld/article/details/17718587
http://www.cnblogs.com/dingyingsi/p/3760447.html
http://blog.csdn.net/ji13921602232/article/details/51159142
http://blog.csdn.net/doc_sgl/article/details/46594975
http://my.oschina.net/skyline520/blog/304805
?
轉(zhuǎn)載于:https://www.cnblogs.com/jager/p/5679902.html
總結(jié)
以上是生活随笔為你收集整理的JVM内存结构与GC的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Selenium2Lib库之输入常用关键
- 下一篇: 插入锁