JVM,JAVA虛擬機,內(nèi)存機制,線程_第1頁
JVM,JAVA虛擬機,內(nèi)存機制,線程_第2頁
JVM,JAVA虛擬機,內(nèi)存機制,線程_第3頁
JVM,JAVA虛擬機,內(nèi)存機制,線程_第4頁
JVM,JAVA虛擬機,內(nèi)存機制,線程_第5頁
已閱讀5頁,還剩16頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

JVM的機制學習綱行理管存內(nèi)程線大執(zhí)的碼代代碼編譯為classSunjdk中的javac裝載class執(zhí)彳亍class內(nèi)存空間內(nèi)存回收ClassLoaderClientcompilerServercompiler方法區(qū),堆,JVM方法

棧,本地方法棧,PC寄存

器堆上分配棧上分配■-—TLAB分配.-算法Copying■,—內(nèi)存分配算*Mark-Sweep....Mark-Compact新生代可用GC分代回收JDK實現(xiàn)—GC參數(shù)內(nèi)存狀態(tài)分析G1Jconsole,jstat,jmap,MAT同步交互狀態(tài)并行回收GC并行GCMinorGC觸發(fā)機制/一及日志串行Mark-Sweep-Compact舊生代GCFullGC并行。。mpact-■-'并發(fā)Mark-SweepJVM標準結(jié)構(gòu)類加載子系統(tǒng)內(nèi)存空間Java代碼執(zhí)行機制源碼編譯機制JVM規(guī)范中定義了class文件的格式,JDK在編譯java源碼時,使用了javac,javac編譯的步驟:分析和輸入到符號表(ParseAndEnter)Parse做的是詞法和語法的分析。詞法分析:將代碼字符串轉(zhuǎn)變?yōu)閠oken序列語法分析:將根據(jù)語法由token序列生成抽象語法樹Enter將符號輸入到符號表,通常包括確定類的超類和接口,添加默認構(gòu)造器等。注解處理語義分析和生成class文件通常生成class文件不知包括字節(jié)碼,一般包括結(jié)構(gòu)信息,元數(shù)據(jù),方法信息。下面是一個例子:Compiledfrom"Foo.java"〃類/繼承的超類/實現(xiàn)的接口的聲明信息publicclassFooextendsjava.lang.ObjectSourceFile:"Foo.java"http://class文件格式版本號,majorversion50表示jdk6,49為jdk5只有高版本能執(zhí)行低版本的class文件minorversion:0

majorversion:50〃常量池,存放了所有的Field名稱,方法名,方法簽名,類型名,代碼及class文件中的常量值Constantpool:const#1=Method#7.#27;//java/lang/Object."<init>":()Vconst#2=Fieldconst#3=class#6.#28;//Foo.count:I#29;//java/lang/Exceptionconst#4=Stringconst#5=Methoding;)Vconst#6=class#30;//countoverflow#3.#31;//java/lang/Exception."<init>":(Ljava/lang/Str#32;//Fooconst#7=class#33;//java/lang/Objectconst#8=Ascizconst#9=AscizMAX_COUNT;I;const#10=Ascizconst#11=int1000;const#12=Ascizconst#13=AscizConstantValue;count;<init>;const#14=Ascizconst#15=Asciz()V;Code;const#16=Ascizconst#17=AscizLineNumberTable;LocalVariableTable;const#18=Ascizconst#19=Ascizthis;LFoo;;const#20=Ascizconst#21=Ascizbar;()I;const#22=Ascizconst#23=AscizStackMapTable;Exceptions;const#24=Ascizconst#25=Asciz<clinit>;SourceFile;const#26=AscizFoo.java;const#27=NameAndType#13:#14;//"<init>":()Vconst#28=NameAndType#12:#9;//count:Iconst#29=Ascizjava/lang/Exception;const#30=Ascizcountoverflow;const#31=NameAndType#13:#34;//"<init>":(Ljava/lang/String;)Vconst#32=Ascizconst#33=Ascizconst#34=AscizFoo;java/lang/Object;(Ljava/lang/String;)V;{//將符號輸入到符號表時生成的默認構(gòu)造器方法publicFoo();Signature:()VLineNumberTable:line1:0LocalVariableTable:StartLengthSlotNameSignature050thisLFoo;Code:Stack=1,Locals=1,Args_size=10:aload_01:invokespecial#1;//Methodjava/lang/Object."<init>":()V4:returnLineNumberTable:line1:0LocalVariableTable:StartLengthSlotNameSignature050thisLFoo;//bar方法的元數(shù)據(jù)信息publicintbar()throwsjava.lang.Exception;Signature:()I//對應字節(jié)碼的源碼行號信息,可在編譯的時候通過-g:none去掉行號信息,行號信息對于查找問題而言至關(guān)重要,因此最好保留LineNumberTable:line5:0line6:15line7:19line9:29//局部變量信息,如生成的class文件中無局部變量信息,則無法知道局部變量的名稱,并且局部變量信息是和方法綁定的,接口是沒有方法體的,所以ASM之類的在獲取接口方法時,是拿不到方法中參數(shù)的信息的LocalVariableTable:StartLengthSlotNameSignature0330thisLFoo;Code:Stack=3,Locals=1,Args_size=1〃對應方法的字節(jié)碼0:getstatic#2;//Fieldcount:I3:iconst_14:iadd5:dup6:putstatic#2;//Fieldcount:I9:sipush100012:if_icmplt2915:iconst_016:putstatic#2;//Fieldcount:I19:new#3;//classjava/lang/Exception22:dup23:ldc#4;//Stringcountoverflow25:invokespecial#5;//Methodjava/lang/Exception."<init>":(Ljava/lang/String;)V28:athrow29:getstatic#2;//Fieldcount:I32:ireturnLineNumberTable:line5:0line6:15line7:19line9:29LocalVariableTable:StartLengthSlotNameSignature0330thisLFoo;〃記錄有分支的情況(對應代碼中的if???for,while等)StackMapTable:number_of_entries=1frame_type=29/*same*/〃異常處理表Exceptions:throwsjava.lang.Exceptionstatic{};Signature:()VLineNumberTable:line3:0Code:Stack=1,Locals=0,Args_size=00:iconst_01:putstatic#2;//Fieldcount:I4:returnLineNumberTable:line3:0}類加載生成的class文件,可被JVM裝載,并形成Class對象的機制,稱為類加載機制之后就可對Class對像進行實例化并調(diào)用,類加載機制可在運行時動態(tài)加載外部的類。JVM的類加載分為三步:裝載,鏈接,初始化裝載和鏈接之后,即將二進制字節(jié)碼轉(zhuǎn)化成Class對象初始化則不是加載時,必須的步驟,最遲可在首次調(diào)用對象前執(zhí)行,行為包括給靜態(tài)變量賦值,調(diào)用<高血>()等裝載:裝載通過類的全限定名+ClassLoader實例ID對其進行標識鏈接:對二進制字節(jié)碼的格式進行校驗,初始化裝載類中的靜態(tài)變量及解析類中調(diào)用的接口,類初始化:執(zhí)行類中的靜態(tài)初始化代碼,構(gòu)造器代碼及靜態(tài)屬性的初始化,以下情況初始化會被觸發(fā):調(diào)用了new反射調(diào)用了類中的方法子類調(diào)用了初始化JVM啟動過程中指定的初始化類類執(zhí)行字節(jié)碼解釋執(zhí)行在源碼編譯階段將源碼編譯成JVM字節(jié)碼后,要由JVM在運行期對其進行解釋并執(zhí)行,稱為字節(jié)碼解釋執(zhí)行方式對于字節(jié)碼JVM有自己的執(zhí)行指令,invokestatic,invokevirtual,invokeinterface,ipvokespecial,分別對應static方法,對象實例方法,接口方法,private方法/編譯源碼后生成的<init>方法的調(diào)用。eg:publicclassDemo(publicvoidexecute()(A.execute();Aa=newA();bar();IFoob=newB();bar();}}classA(publicstaticintexecute()(return1+2;}publicintbar()(return1+2;}}classBimplementsIFoo(publicintbar()(return1+2;}}interfaceIFoo(publicintbar();}對上面的代碼編譯,然后javapcDemo查看其execute方法字節(jié)碼Compiledfrom"Demo.java"publicclassDemoextendsjava.lang.Object(publicDemo();Code:0:aload_01:invokespecial#1;//Methodjava/lang/Object."<init>":()V4:returnpublicvoidexecute();Code:0:invokestatic#2;//MethodA.execute:()I3:pop4:new#3;//classA7:dup8:invokespecial#4;//MethodA."<init>”:()V11:astore_112:aload_113:invokevirtual#5;//MethodA.bar:()I16:pop17:new#6;//classB20:dup21:invokespecial#7;//MethodB."<init>”:()V24:astore_225:aload_226:invokeinterface#8,1;//InterfaceMethodIFoo.bar:()I31:pop32:return}上面可以看到4個指令的使用情況JDK棧體系線程創(chuàng)建后,產(chǎn)生PC(程序計數(shù)器)和棧,PC:存放了下一條要執(zhí)行的指令在方法內(nèi)的便宜量;棧:棧中存放了棧幀(StackFrame),每個方法的每次調(diào)用都會產(chǎn)生棧幀棧幀主要分為局部變量去和操作數(shù)棧兩部分,局部變量區(qū)用于存放方法中局部變量和參數(shù)操作數(shù)棧用于存放方法執(zhí)行過程中產(chǎn)生的中間結(jié)果,棧幀中還有一些雜用控件,指向方法已解析的常量池的引用,其他一些VM內(nèi)部實現(xiàn)需要的數(shù)據(jù)eg:publicclassDemo1(publicstaticvoidfoo()(inta=1;intb=2;intc=(a+b)*5;}}編譯Demo1.java查看字節(jié)碼:Compiledfrom"Demo1.java"publicclassDemo1extendsjava.lang.Object(publicDemo1();Code:0:aload_01:invokespecial#1;//Methodjava/lang/Object."<init>":()V4:returnpublicstaticvoidfoo();Code:0:iconst_1//將類型為int,值為1的常量放入操作數(shù)棧1:istore_0//將操作數(shù)棧中棧頂?shù)闹祻棾龇湃刖植孔兞繀^(qū)2:iconst_2//將類型為int,值為2的常量放入操作數(shù)棧3:istore_1//將操作數(shù)棧中棧頂?shù)闹祻棾龇湃刖植孔兞繀^(qū)4:iload_0//裝載局部變量區(qū)中的第一個值到操作數(shù)棧5:iload_1//裝載局部變量區(qū)中的第一個值到操作數(shù)棧6:iadd〃執(zhí)行int類型的add指令,并將結(jié)果放入操作數(shù)棧7:iconst_5//將類型為int,值為5的常量放入操作數(shù)棧8:imul〃執(zhí)行int類型的mul指令,并將結(jié)果放入操作數(shù)棧9:istore_2//將操作數(shù)棧中棧頂?shù)闹祻棾龇湃刖植孔兞繀^(qū)10:return//返回}對于方法指令的解釋執(zhí)行,執(zhí)行方式為經(jīng)典馮。諾依曼體系中的FDX循環(huán)方式,即獲取下一條指令,解碼并分派,然后執(zhí)行。在實現(xiàn)FDX循環(huán)時有switch-threading,token-threading,direct-threading,subroutine-threading,inline-threading等多種方式switch-threading,最簡單:while(true)(intcode=fetchNextCode();switch(code)(caseIADD://doaddcase…://dosth}}〃每次執(zhí)行完后回到起點,重新獲取下一條,并繼續(xù)switch,大部分時間都花在跳轉(zhuǎn)和獲取下一條上token-threading:在switch-threading上稍作改進IADD:{//doadd;fetchNextCode();dispatch();}ICOUNT_0:{push(0);fetchNextCode();dispatch();}雖然冗余了fetchNextCode和dispatch,消耗內(nèi)存大一些,但由于去除了switch,性能會好一些SUNJDK的重點為編譯成機器碼,因此采用了token-threading,讓解釋執(zhí)行更高效,還做了其他的優(yōu)化:棧頂緩存(top-of-stackcaching)和部分棧幀共享棧頂緩存:因為在很多操作中要將值放入操作數(shù)棧,導致了寄存器和內(nèi)存的不斷交換數(shù)據(jù),SunJDK采用了一個棧頂緩存,將本來放在操作數(shù)棧頂?shù)闹稻彺嬖诩拇嫫魃希@樣對于大多只要一個值的操作而言,不需要將數(shù)據(jù)放入操作數(shù)棧,可在寄存器中計算,完了再放回操作數(shù)棧部分棧幀共享:當一個方法調(diào)用另一個方法時,傳入另一個方法的參數(shù)已經(jīng)放在了操作數(shù)棧中,SunJDK做了優(yōu)化,當調(diào)用方法時,后一方法可將前一方法的操作數(shù)棧作為當前方法的局部變量,從而節(jié)省數(shù)據(jù)copy帶來的消耗編譯執(zhí)行因為解釋執(zhí)行效率低,所以為了提升性能,SunJDK將字節(jié)碼編譯成機器碼,編譯在運行時進行,稱為JIT編譯器。SunJDK在執(zhí)行過程中對執(zhí)行頻率高的代碼進行編譯,對執(zhí)行不頻繁的代碼進行解釋,在編譯上SunJDK提供了兩種方式:clientcompiler(-client),servercompiler(-server)clientcompiler:又稱為C1,只做少量性能開銷比高的優(yōu)化,占用內(nèi)存少,適合于桌面交換式應用,其他方面的優(yōu)化有方法內(nèi)聯(lián),去虛擬化,冗余消除方法內(nèi)聯(lián):是在調(diào)用方法時,可直接將調(diào)用的方法的指令直接植入到當前方法中,通過-XX:MaxInlineSize=35可以配置當前允許內(nèi)聯(lián)的方法的大小,<=35字節(jié)的方法都可內(nèi)聯(lián)。去虛擬化:在裝載class文件后,進行類層次分析,如發(fā)現(xiàn)類中的方法只提供一個實現(xiàn)類時,那么對于調(diào)用方法的代碼,也可進行內(nèi)聯(lián)冗余消除:是指在編譯時,根據(jù)運行時狀況進行代碼的折疊或消除,eg:if(false){...}這樣的代碼就消除了servercompiler:又稱C2,是較重量級的,占用內(nèi)存較多,適合于服務器端,不同于C1的局部(方法塊)的優(yōu)化,C2更多在全局的優(yōu)化,收集的主要信息有:分支的跳軸不跳轉(zhuǎn)的頻率,某條指令出現(xiàn)過的類型,是否出現(xiàn)過空值,是否出現(xiàn)過異常逃逸分析是C2進行很多優(yōu)化的基礎,是指根據(jù)運行狀況判斷方法中的變量是否會被外部讀取,不會則認為是不逃逸的,基于逃逸分析C2編譯時會做標量替換,棧上分配和同步消除等標量替換:簡單說就是用標量替換聚合量eg:Pointpoint=newPoint();System.out.println("point.x=''+point.x+'';point.y="+point.y);在這里如果point沒有被后面的代碼調(diào)用到,則經(jīng)過編譯后,代碼變成:intx=1;inty=2;System.out.println("point.x=''+x+'';point.y="+y);之后,基于此可再進行冗余消除這種方式的好處是,如果創(chuàng)建的對象未用到全部的變量,則節(jié)省了內(nèi)存,代碼執(zhí)行時,不需要找對象的引用,快一點棧上分配:在上面的例子中,如果p沒有逃逸,則C2會選擇在棧上創(chuàng)建對象實例,而不是JVM堆中,好處是一方面更快,另一方面回收時隨著方法的結(jié)束,對象也被回收。同步削除:在變量沒有逃逸時,同步是不需要的,C2會直接去掉同步其他優(yōu)化:逆優(yōu)化:當運行后C1,C2編譯出來的機器碼不再符合優(yōu)化條件,則會進行逆優(yōu)化(deoptimization),也就是回到解釋執(zhí)行的方式,例如基于類層次分析編譯的代碼,在加入新的接口實現(xiàn)類時,就執(zhí)行逆優(yōu)化其他編譯方式:OSR:OSR和C1,C2的不同在于,C1,C2基于方法入口進行優(yōu)化,替換方法調(diào)用的入口,而OSR是替換循環(huán)代碼的入口,會出現(xiàn)編譯后方法的整段代碼被編譯了,只有循環(huán)部分是機器碼,其他仍是解釋執(zhí)行可以通過-client,-server進行自主選擇編譯方式,默認JDK會根據(jù)機器的狀況選擇合適的方式為什么不在啟動時將代碼編譯成機器碼?靜態(tài)編譯不能根據(jù)運行狀況來優(yōu)化執(zhí)行的代碼,C2是根據(jù)運行狀況來進行動態(tài)編譯的,C2收集數(shù)據(jù)的時間越長,編譯出來的代碼會越優(yōu)解釋執(zhí)行比編譯更節(jié)省內(nèi)存啟動時解釋執(zhí)行啟動速度比編譯再啟動更快但是未編譯時,解釋執(zhí)行慢,因此需要一個閥值,用來給JVM判斷JDK中主要有兩個計數(shù)器,一個為調(diào)用計數(shù)器,計算方法調(diào)用次數(shù);另一個是回邊計數(shù)器,計算循環(huán)次數(shù)CompileThreshold:可通過-XX:CompileThreshold=10000來設置,在超過閥值時就編譯成機器碼,在client模式下為1500,server模式下為10000OnStackReplacePercentage:該值用于判斷是否啟動OSR編譯,閥值client默認為933,server為140,可通過-XX:OnStackReplacePercentage=140來設置,閥值:在client模式下為公式CompileThreshold*(OnStackReplacePercentage/100),在server模式下(CompileThreshold*(OnStackReplacePercentage-InterpreterProfilePercentage))/100,InterpreterProfilePercentage默認值為33,當觸發(fā)了OSR后,調(diào)用計數(shù)器設置為CompileThreshold的值,回邊計數(shù)器設置為CompileThreshold/2的值

JVM內(nèi)存管理內(nèi)存管理PC寄存器局部變量區(qū)操作數(shù)棧-棧幀JVM方法棧JVM內(nèi)存結(jié)構(gòu)JVM內(nèi)存管理內(nèi)存管理PC寄存器1.方法區(qū)存放了類的信息(名稱,修飾符等),包括類的靜態(tài)屬性,final類型的常量,F(xiàn)ield信息,方法信息,這些是全局共享的,是會被GC的在JDK中這塊區(qū)域?qū)狿ermanetGeneration,又成為持久代,默認大小16MB,最大值64MB,可通過-XX:PermSize和-XX:MaxPermSize來指定。當所需的內(nèi)存大小超出其允許范圍時,拋出OutOfMemoryError2.堆用于存放對象實例和數(shù)組值??梢哉J為所有通過new創(chuàng)建的對象的內(nèi)存都在此分配,Heep對象所占內(nèi)存由GC回收??赏ㄟ^-Xms和-Xmx來設置大小,當Heep的空內(nèi)存不到40%時,會增大內(nèi)存到-Xmx設置值,可通過-XX:MinHeapFreeRatio來改變比例,當空內(nèi)存超過70%時,減小到到-Xms設置值,可通過-XX:MaxHeapFreeRatio來改變比例,對于運行的系統(tǒng),為了避免頻繁的更改Heep的大小,通常將-Xms和-Xmx設成一樣為了讓內(nèi)存的回收更有效,SunJDK采用了分代管理的方式......NewGenerationEdenS0S1OldGeneration-HeepJVM堆的分代NewGeneration:在Java中大部分new的對象都在NewGeneration中分配,由EdenSpace和兩塊大小相同的SurvivorSpace組成OldGeneration:當在NewGeneration經(jīng)過多次GC還存活的對象將移到Old中,如緩存對象,新對象也可能在Old上分配,主要有兩種狀況:1.大對象,超過了-XX:PretenureSizeThreshold=1024設置值,2.大數(shù)組對象,沒有引用外部對象本地方法棧用于支持native方法的執(zhí)行,存放native方法的信息,在SunJDK中本地方法棧和JVM方法棧是同一個PC寄存器和JVM方法棧每個線程都會創(chuàng)建PC寄存器和JVM方法棧,PC寄存器占用CPU寄存器和操作系統(tǒng)內(nèi)存,JVM方法棧占用內(nèi)存,JVM方法棧為線程私有,當方法執(zhí)行完畢后,其棧幀占用的內(nèi)存也會釋放。當JVM方法棧空間不足時,拋出StackOverflowError,在SunJDK中通過-Xss來設置內(nèi)存分配Java對象占用的內(nèi)存主要在堆上進行分配,堆為所有線程共享的,因此分配內(nèi)存時需要加鎖,因此創(chuàng)建對象開銷較大。SunJDK為了提高內(nèi)存分配效率,為每個新線程在EdenSpace中分配一塊獨立的空間,叫做TLAB,其大小由JVM運行時計算而得,通過配置-XX:TLABWasteTargetPercent來設置占用EdenSpace的比例,默認1%,JVM將根據(jù)這個比率,線程數(shù)量及線程是否頻繁分配對象來給每個線程分配合適的大小。因為在TLAB中分配內(nèi)存不需要加鎖,因此JVM中給線程中的對象分配內(nèi)存都盡量在TLAB中,如果對象過大或TLAB不夠,則仍在堆上分配。當然,還有Java對象在棧上分配的情況,前面已經(jīng)提過了。內(nèi)存回收收集器GC的一般實現(xiàn),是通過收集器,收集器主要有引用計數(shù)收集器和跟蹤收集器。引用計數(shù)收集器:采用分散式的管理方式,記錄對象是否被引用,當計數(shù)器為0時,可GC。缺點:每次對象賦值時,要進行計數(shù)器加減,有消耗,而且對于循環(huán)賦值的情況,不能使用。跟蹤計數(shù)器:采用了集中式的管理方式,全局記錄數(shù)據(jù)的引用狀態(tài)。當一定條件時啟動GC(如空間不足,定時等),執(zhí)行時從根集合來掃描對象的引用關(guān)系。采用的算法有:復制,標記-清除,標記-壓縮復制:掃描,找到所有存活的對象,提供一塊空間,復制所有的對象進去。缺點:需要額外的空間,需要移動對象。適用情況:當存活對象較少時。標記-清除:掃描,對所有存活對象進行標記,然后清除所有未標記對象,不需要額外的移動對象。缺點:會產(chǎn)生空間碎片適用:當存活對象較多時。標記-壓縮:標記過程與標記-清除一樣,只不過清除后,將像左移動所有存活對象,缺點:額外的移動優(yōu)點:不會產(chǎn)生空間碎片SunJDK中可用的GCGC方式新生代可用GC:SunJDK認為大部分的新生代中對象存活時間較短,因此使用Copying算法來對新生代進行GC,新生代分為三部分:Eden用于存放新建的對象,S0和S1其中一塊在MinorGC觸發(fā)時作為Copying的目標空間,另一塊清空。因此通常將S0和S1稱為,F(xiàn)romSpace和ToSpace,SunJDK提供了串行GC,并行回收GC,并行GC三種方式來GC,對新生代對象所占內(nèi)存進行的GC又通常稱為MinorGC。1.串行GC:(NG代表NewGeneration,OG代表OldGeneration)Ng的內(nèi)存分配采用了空閑指針的方式,指針保持最后一個分配對象在NG中的位置,當有新的對象要分配時,如果有剩余空間則分配,更新指針,如果沒有,則MinorGCMinorGC用了Copying算法,要查找所有根集合中的存活對象,SunJDK認為根集合為:當前運行的線程棧上引用的對象,常量和靜態(tài)變量,傳到本地方法中,但是沒有被釋放的對象。MinorGC僅從根集合上得到對象是不夠的,當OG上的對象引用了NG的對象,則出現(xiàn)問題,OG較大,不可能每次MinorGC都去掃描OG,因此SunJDK使用了remembersetrememberset:當對象進行賦值時,如果賦值的是一個對象引用,則檢查要賦值的對象是否在OG,而賦值對象是否在NG,如果是則,在rememberset中記錄。所以對MinorGC而言,完整的根集合是SunJDK認為的根集合加上rememberset中的標記對象。同時為了避免在GC時,對象關(guān)系的變化,SunJDK在編譯時設置了SafePoint,即在循環(huán)和方法執(zhí)行結(jié)束處,如果要執(zhí)行MinorGC,要等所有線程進入SafePoint。前面提到,當NG中的多次GC,還存活的對像放入OG中,這個次數(shù)在MinorGC下是有參數(shù)-XX:PretenureSizeThreshold來決定,同時可以在運行時,加上-XX+UseSerialGC來強制指定GC方式。對象的引用關(guān)系:默認是強引用:Aa=newA();只有在主動釋放了引用后,才會GC。其他引用方式:軟引用:SoftReference弱引用:WeakReference虛引用:PhantomReferenceMinorGC:對于單CPU系統(tǒng),比較適用。2并行回收GCEden,S0,S1的大?。耗J采用的是InitialSurvivorRatio來進行配置:此值的大小是NG/survivorspace,在SunJDK1.6之后,加入了SurvivorRatio參數(shù),此值大小為edenspace/survivorspace在并行回收時,默認采用InitialSurvivorRatio,如果只設了SurvivorRatio則并行回收GC時,將SurvivorRatio值+2賦給InitialSurvivorRatio,同時配置的話,已InitialSurvivorRatio為準,因此采用并行回收時,如果-Xmn為16MB,則默認edenspace為12MB,survivorspace各2MB,如果SurvivorRatio配置為8,則denspace為12。8MB,survivorspace各1.6MB。對于并行回收GC,在啟動時Eden,S0,S1按照上面的方式進行分配,但在運行一段時間后,并行回收GC會根據(jù)MinorGC的頻率、消耗時間等來動態(tài)調(diào)整Eden,S0,S1的大小,可通過-XX:-UseAdaptiveSizePolicy來固定Eden,S0,S1的大小。PSGC不是根據(jù)-XX:PretenureSizeThreshold來決定是否在OG上存放新對象,而是在分配時,如果Eden不夠大,此對象的大小〉=Edenspace大小的一半,就在OG上分配.eg:pufalicclassPSGCDirect01dDeino{pufalicstaticvoidmain(String[]args)throwsException{byte[]bytes=newbyte[1024*1024*2];byte[]bytes2=newbyte[1024*1024*2];byte[]bytes3=newbyte[1024*1024*2];System,out.printIn(,rreadytodirectallocatetoold");Thread,sleep(30000);//jstat^WSystem,out.println(,rdoit1);byte[]]oytes4=newbyte[1024*1024*4];PSGC(并行回收GC)測試,以參數(shù)java-Xms20M-Xmn10M-Xmx20M-XX:SurvivorRatio=8-XX:+UseParallelGCPSGCDirectOldDemo運行例子,并以jstat來查看兩次,看到bytes4數(shù)組分配在OG(OldGeneration)上jstat-gcpid;查看結(jié)果:

YGCYGCTFGCFGCTGCT00.00000.0000.000YGCTFGCYGCYGCTFGCFGCTGCT00.00000.0000.000YGCTFGCFGCTGCT0.00000.0000.00011024.0102^.00.00.08192.06471.910240.00.012288.02036.^第二次:|SueSICSUUSIUECEUOCOUPCPUYGC1024.01024.00.00.08192.06471.910240.04096.012288.02036.50分析:第一次時OU為0,OG上的占用空間為0,EC為Edenspace的空間大小,要分配bytes4時,bytes4的大小為4096=EC/2,所以分配在了OG上,因此第二次查看時,OU=4096.0.3.并行GC(ParNew)并行GC在EdenS0,S1的大小分配上和串行GC是一樣的。和PSGC的區(qū)別在于并行GC配合OG使用CMSGC,CMSGC在進行OGGC時,有些過程是并發(fā)的,如此時發(fā)生MinorGC,需要做一些處理,而PSGC沒有這些處理。在Eden上分配內(nèi)存不足時,JVM即觸發(fā)MinorGC,也可在程序中手動System.gc調(diào)用示例MinorGC:publicclassMinorGCDenio{publicstaticvoidmain(String[]args)throwsException(Thread,sleep(10000);F/1給啟動jmt■日仁肘|可MenioryObjectobject=newMeinoryObject(1024*1024);for(inti=0;i:2;i+4){happenMinorGC(11);Thread.sleep(10000);privatestaticvoidhappenMinorGC(inthappenMinorGCIndex)throwsException(for(inti=0;i<happenMinorGCIndex;i-l-+){if(i==happenMinorGCIndex-1){Thread.sleep(10000);Systein.out.printIn(,ritiinorgcshouldhappenFF);newMenioryObject(1024*1024);classMemoryObject(privatebyte[]bytes;pub1icMenioryObject(intobjectSize)(this.bytes=newbytejectSize];}}javacMinorGCDemo.javajava-Xms40M-Xmx40M-Xmn16M-verbose:gc-XX:+PrintGCDetails這里分配整個JVM方法棧大小為40M,其中NewGeneration為16M,Eden為12M,兩個survivorspace各2M,OG為24M通過jstat-gcutilpid100010(間隔1s,查看10次)查看Eden,S0,S1,old在minorGC時的變化結(jié)果:Console:minorgcshouldhappen[GC[DefNew:1255lK->1159K(14784K),□.0025361secs]1255lK->1159K(39360K),0.0028512secs][Times:user=0.□□sys=0.00^real=0.00secs]minorgcshouldhappenHeapdefnewgeneration000)edenspacefromspacetospacetotal14784K,used12821K[Ox241eOOOO,Ox251eOOOO,Ox251eO13184K,Console:minorgcshouldhappen[GC[DefNew:1255lK->1159K(14784K),□.0025361secs]1255lK->1159K(39360K),0.0028512secs][Times:user=0.□□sys=0.00^real=0.00secs]minorgcshouldhappenHeapdefnewgeneration000)edenspacefromspacetospacetotal14784K,used12821K[Ox241eOOOO,Ox251eOOOO,Ox251eO13184K,1600K,1600K,tenuredgeneration88%used[0\24160000,0x24d43a50/Ox24ecOOOO)72%used[0X25050000,0x25171c48,Ox251eOOOO)0%used[0x24ec0000^Ox24ecOOOO^0x25050000)total24576K,usedOK[0x251e0000,OxaeSeOOOO,0x269e0000)Ithespace24576K^compactingpermgen□)thespacerospacerwspace12288K,8192K,12288K,jstat顯示:soS1E0PYGCYGCTFGCFGCTGCT0.00□.0087.442.96□0.000□0.000□.□□00.000.0095.212.97□0.000□0.000□.□□00.00□.0095.212.97□0.000□0.000□.□□00.0072.4478.692.9710.003□0.000□.□□30.0072.4478.692.9710.003□0.000□.□□30.0072.4486.462.9710.003□0.000□.□□3OG和PG可用的GCJDK提供了串行,并行,并發(fā)三種GC來對OG和PG占的內(nèi)存進行GC。1.串行:串行基于標記-清除-壓縮(Mark-Sweep-Compact)方式,在使用串行時,OG的內(nèi)存分配和NG是一樣的。串行分三步:a)從根集合開始掃描對象b)對未標記的對象進行清除c)執(zhí)行滑動壓縮,將存活的對象向OG空間開始處移動,最終在OG中留出一塊連續(xù)的空間到結(jié)尾處的空間。整個過程是單線程的,需要暫停應用并行采用了Mark-Compact的方式,內(nèi)存分配上和串行相同并行GC分三步:a)根據(jù)CPU的數(shù)量,將OG分為多個region,并發(fā)掃描這些region,進行標記b)一般經(jīng)過多次GC,OG最左邊存放多是活躍的對象,從左往右掃描,找到第一個值得Compact的region,這個region左邊的區(qū)域認為是denseprefix不進行操作,然后繼續(xù)往右掃描,標記region的源和目標,切換這些對象的指針,并在region上做標志,同時清除regions中其他不存活對象的空間,此過程是單線程的c)根據(jù)上面的分析,找到目標region,和完全沒有存活對象的region,并行進行對象移動和region回收并發(fā)(CMSConcurrentMark-SweepGC)因為Mark-Sweep掃描整個OG需要較長時間,SunJDK提供了CMSGC的方式,使大部分動作與應用并發(fā)的進行。CMS用了Mark-Sweep的方式,在清除后,會產(chǎn)生很多個空白空間,它將這些空間在一個freelist中標記,當NG的新對象請求OG空間,在freelist中可以找到夠用的空間使用。同時,因為CMS與應用并發(fā),會導致空間的分配和回收的同時進行,導致freelist競爭激烈,CMS為了避免這個現(xiàn)象,引入了Mutualexclusionlocks,以JVM分配內(nèi)存為先。整個CMS過程中要進行三次的掃描,標記。FullGC當OG,PG觸發(fā)GC時,出現(xiàn)對NG,OGPG都進行GC的,稱為FullGC。FullGC被觸發(fā)時,NG先按配置進行NG的GC,然后OG,PG進行GC。但其中有一種情況,當MinorGC前,NG移到OG的對象大于OG的剩余空間,則不進行MinorGC,直接采用OG的GC方式對NG,OG,PG進行GC。除了直接調(diào)用System.gc外,觸發(fā)FullGC的四種情況1.OG空間不足上面說到的,轉(zhuǎn)入OG的對象太大,OG不足,則觸發(fā)FullGC,如果FullGC之后還不足,則拋出java.lang.OutOfMemoryError:Javaheapspace這種情況調(diào)優(yōu):不要創(chuàng)建過大對象和數(shù)組PG空間滿PG存放了class信息,當系統(tǒng)要加載的類,反射的類和調(diào)用的方法過多時,PG滿,而且沒使用CMS的情況下,F(xiàn)ullGC使用。如果還不行,則拋出java.lang.OutOfMemoryError:PermGenspaceCMSGC出現(xiàn)promotionfailed和concurrentmodefailurepromotionfailed:MinorGC時,survivorspace放不下,轉(zhuǎn)入OG,但是OG不足concurrentmodefailure:在CMSGC時,同時有對象要放入OG,OG不足統(tǒng)計得到的MinorGC晉升到OG的平均大小大于OG的剩余空間小結(jié):client,server模式默認GCNGOG&PGClient串行GC串行GCServer并行回收GC并行GCSunJDKGC組合方式NGOGPG-XX:+UseSerialGC串行GC串行GC-XX:+UseParallelGC并行回收GC并行GC-XX:+UseConcMarkSweepGC并行GC并發(fā)GC當出現(xiàn)concurrentmodefailure時采用串行GC-XX:+UseParallelOldGC-XX:+UseConcMarkSweepGC-XX:+UseParNewGC并行回收GC串行GC并行GC并發(fā)GC當出現(xiàn)concurrentmodefailure或promotionfailed時采用串行GC不支持的組合1.-XX:+UseParNewGC-XX:+UseParallelOldGC2.-XX:+UseParNewGC-XX:+UseSerialGC不支持的組合JVM內(nèi)存狀況查看方法和分析工具輸出GC日志將JVM支持的日志輸出到控制臺或文件,到Console:JVM啟動參數(shù)加入:-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintGCApplicationStoppedTime,按照參數(shù)的順序分別輸出GC的簡要信息,GC的詳細信息,GC的時間信息及GC造成的應用暫停的時間到文件:在上面的啟動參數(shù)中加入:-Xloggc:gc.log可指定gc信息輸出到gc.log可用于GC分析的參數(shù)還有:-verbose:gc,-XX:+PrintTenuringDistribution等。GCPortalGCPortal提供了對GC日志的圖標分析,需要運行在tomcat上,還有數(shù)據(jù)庫的支持,配置有點麻煩。GCPortal對于JDK6的日志,只能分析6M以下的。GCPortal提供了吞吐量分析,耗費CPU時間,造成的應用暫停時間,每秒從NG到OG的數(shù)量,minorGC的狀況及FullGC的狀況等Jconsole是JDK5以上版本自帶工具,圖形化,直接運行Jconsole.exe或Jconsole.sh,查到JAVA進程的pid,就可查看相應進程的狀況。Jconsole顯示JVM的很多信息:內(nèi)存,線程,類和Mbean等。JVisualVM是JDK6update7之后推出的,類似Jprofiler的工具JMapJDK自帶的用于分析JVM內(nèi)存狀況的工具查

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論