自動(dòng)內(nèi)存管理機(jī)制
Java虛擬機(jī)原理
所謂虛擬機(jī)揉忘,就是一臺(tái)虛擬的機(jī)器破停。他是一款軟件陷嘴,用來執(zhí)行一系列虛擬計(jì)算指令,大體上虛擬機(jī)可以分為
系統(tǒng)虛擬機(jī)和程序虛擬機(jī)摔竿, 大名鼎鼎的Visual Box面粮、Vmare就屬于系統(tǒng)虛擬機(jī),他們完全是對物理計(jì)算的仿真继低,
提供了一個(gè)可以運(yùn)行完整操作系統(tǒng)的軟件平臺(tái)熬苍。
程序虛擬機(jī)典型代碼就是Java虛擬機(jī),它專門為執(zhí)行單個(gè)計(jì)算程序而計(jì)算袁翁,在Java虛擬機(jī)中執(zhí)行的指令我們成為Java
自己碼指令柴底。無論是系統(tǒng)虛擬機(jī)還是程序虛擬機(jī),在上面運(yùn)行的軟件都被限制于虛擬機(jī)提供的資源中粱胜。
Java發(fā)展至今柄驻,出現(xiàn)過很多虛擬機(jī),做初Sun使用的一款叫ClassIc的Java虛擬機(jī)焙压,到現(xiàn)在引用最廣泛的是HotSpot虛擬
機(jī)凿歼,除了Sum意外,還有BEA的Jrockit冗恨,目前Jrockit和HostSopt都被oralce收入旗下,大有整合的趨勢味赃。
Java內(nèi)存結(jié)構(gòu)
1掀抹、 類加載子系統(tǒng):負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)加載Class信息,加載的信息存放在一塊稱之方法區(qū)的內(nèi)存空間心俗。
2傲武、 方法區(qū):就是存放類的信息、常量信息城榛、常量池信息揪利、包括字符串字面量和數(shù)字常量等。
3狠持、 Java堆:在Java虛擬機(jī)啟動(dòng)的時(shí)候建立Java堆疟位,它是Java程序最主要的內(nèi)存工作區(qū)域,幾乎所有的對象實(shí)例都存放到
Java堆中喘垂,堆空間是所有線程共享甜刻。
4、 直接內(nèi)存:JavaNio庫允許Java程序直接內(nèi)存正勒,從而提高性能得院,通常直接內(nèi)存速度會(huì)優(yōu)于Java堆。讀寫頻繁的場合可能會(huì)考慮使用章贞。
5祥绞、 每個(gè)虛擬機(jī)線程都有一個(gè)私有棧,一個(gè)線程的Java棧在線程創(chuàng)建的時(shí)候被創(chuàng)建,Java棧保存著局部變量蜕径、方法參數(shù)两踏、同事Java的方法調(diào)用、
返回值等丧荐。
6缆瓣、 本地方法棧,最大不同為本地方法棧用于本地方法調(diào)用虹统。Java虛擬機(jī)允許Java直接調(diào)用本地方法(通過使用C語言寫)
7弓坞、 垃圾收集系統(tǒng)是Java的核心,也是不可少的车荔,Java有一套自己進(jìn)行垃圾清理的機(jī)制渡冻,開發(fā)人員無需手工清理,下一節(jié)課詳細(xì)講忧便。
8族吻、 PC(Program Couneter)寄存器也是每個(gè)線程私有的空間, Java虛擬機(jī)會(huì)為每個(gè)線程創(chuàng)建PC寄存器珠增,在任意時(shí)刻超歌,
一個(gè)Java線程總是在執(zhí)行一個(gè)方法,這個(gè)方法稱為當(dāng)前方法蒂教,如果當(dāng)前方法不是本地方法巍举,PC寄存器總會(huì)執(zhí)行當(dāng)前正在被執(zhí)行的指令,
如果是本地方法凝垛,則PC寄存器值為Underfined懊悯,寄存器存放如果當(dāng)前執(zhí)行環(huán)境指針、程序技術(shù)器梦皮、操作棧指針炭分、計(jì)算的變量指針等信息。
9剑肯、 虛擬機(jī)核心的組件就是執(zhí)行引擎捧毛,它負(fù)責(zé)執(zhí)行虛擬機(jī)的字節(jié)碼,一般戶先進(jìn)行編譯成機(jī)器碼后執(zhí)行退子。
堆岖妄、棧、方法區(qū)概念區(qū)別
Java堆
堆內(nèi)存用于存放由new創(chuàng)建的對象和數(shù)組寂祥。在堆中分配的內(nèi)存荐虐,由java虛擬機(jī)自動(dòng)垃圾回收器來管理。在堆中產(chǎn)生了一個(gè)數(shù)組或者對象后丸凭,還可以在棧中定義一個(gè)特殊的變量福扬,這個(gè)變量的取值等于數(shù)組或者對象在堆內(nèi)存中的首地址腕铸,在棧中的這個(gè)特殊的變量就變成了數(shù)組或者對象的引用變量,以后就可以在程序中使用棧內(nèi)存中的引用變量來訪問堆中的數(shù)組或者對象铛碑,引用變量相當(dāng)于為數(shù)組或者對象起的一個(gè)別名狠裹,或者代號。
根據(jù)垃圾回收機(jī)制的不同汽烦,Java堆有可能擁有不同的結(jié)構(gòu)涛菠,最為常見的就是將整個(gè)Java堆分為
新生代和老年代。其中新聲帶存放新生的對象或者年齡不大的對象撇吞,老年代則存放老年對象俗冻。
新生代分為den區(qū)、s0區(qū)牍颈、s1區(qū)迄薄,s0和s1也被稱為from和to區(qū)域,他們是兩塊大小相等并且可以互相角色的空間煮岁。
絕大多數(shù)情況下讥蔽,對象首先分配在eden區(qū),在新生代回收后画机,如果對象還存活冶伞,則進(jìn)入s0或s1區(qū),之后每經(jīng)過一次
新生代回收步氏,如果對象存活則它的年齡就加1碰缔,對象達(dá)到一定的年齡后,則進(jìn)入老年代戳护。
Java棧
Java棧是一塊線程私有的空間,一個(gè)棧瀑焦,一般由三部分組成:局部變量表腌且、操作數(shù)據(jù)棧和幀數(shù)據(jù)區(qū)
局部變量表:用于報(bào)錯(cuò)函數(shù)的參數(shù)及局部變量
操作數(shù)棧:主要保存計(jì)算過程的中間結(jié)果,同時(shí)作為計(jì)算過程中的變量臨時(shí)的存儲(chǔ)空間榛瓮。
幀數(shù)據(jù)區(qū):除了局部變量表和操作數(shù)據(jù)棧以外铺董,棧還需要一些數(shù)據(jù)來支持常量池的解析,這里幀數(shù)據(jù)區(qū)保存著
訪問常量池的指針禀晓,方便計(jì)程序訪問常量池精续,另外當(dāng)函數(shù)返回或出現(xiàn)異常時(shí)賣虛擬機(jī)子必須有一個(gè)異常處理表,方便發(fā)送異常
的時(shí)候找到異常的代碼粹懒,因此異常處理表也是幀數(shù)據(jù)區(qū)的一部分重付。
Java方法區(qū)
Java方法區(qū)和堆一樣,方法區(qū)是一塊所有線程共享的內(nèi)存區(qū)域凫乖,他保存系統(tǒng)的類信息确垫。
比如類的字段弓颈、方法、常量池等删掀。方法區(qū)的大小決定系統(tǒng)可以保存多少個(gè)類翔冀。如果系統(tǒng)
定義太多的類,導(dǎo)致方法區(qū)溢出披泪。虛擬機(jī)同樣會(huì)拋出內(nèi)存溢出的錯(cuò)誤纤子。方法區(qū)可以理解
為永久區(qū)。
虛擬機(jī)參數(shù)配置
什么是虛擬機(jī)參數(shù)配置
在虛擬機(jī)運(yùn)行的過程中款票,如果可以跟蹤系統(tǒng)的運(yùn)行狀態(tài)控硼,那么對于問題的故障
排查會(huì)有一定的幫助,為此徽职,在虛擬機(jī)提供了一些跟蹤系統(tǒng)狀態(tài)的參數(shù)象颖,使用
給定的參數(shù)執(zhí)行Java虛擬機(jī),就可以在系統(tǒng)運(yùn)行時(shí)打印相關(guān)日志姆钉,用于分析實(shí)際
問題说订。我們進(jìn)行虛擬機(jī)參數(shù)配置,其實(shí)就是圍繞著堆潮瓶、棧陶冷、方法區(qū)、進(jìn)行配置毯辅。
你說下 你熟悉那些jvm參數(shù)調(diào)優(yōu)
堆的參數(shù)配置
-XX:+PrintGC 每次觸發(fā)GC的時(shí)候打印相關(guān)日志
-XX:+UseSerialGC 串行回收
-XX:+PrintGCDetails 更詳細(xì)的GC日志
-Xms 堆初始值
-Xmx 堆最大可用值
-Xmn 新生代堆最大可用值
-XX:SurvivorRatio 用來設(shè)置新生代中eden空間和from/to空間的比例.
含以-XX:SurvivorRatio=eden/from=den/to
總結(jié):在實(shí)際工作中埂伦,我們可以直接將初始的堆大小與最大堆大小相等,
這樣的好處是可以減少程序運(yùn)行時(shí)垃圾回收次數(shù)思恐,從而提高效率沾谜。
-XX:SurvivorRatio 用來設(shè)置新生代中eden空間和from/to空間的比例.
設(shè)置最大堆內(nèi)存
參數(shù): -Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
設(shè)置新生代與老年代優(yōu)化參數(shù)
-Xmn 新生代大小,一般設(shè)為整個(gè)堆的1/3到1/4左右
-XX:SurvivorRatio 設(shè)置新生代中eden區(qū)和from/to空間的比例關(guān)系n/1
設(shè)置新生代比例參數(shù)
參數(shù): -Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
設(shè)置新生與老年代代參數(shù)
-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
-XX:NewRatio=2
總結(jié):不同的堆分布情況胀莹,對系統(tǒng)執(zhí)行會(huì)產(chǎn)生一定的影響基跑,在實(shí)際工作中,
應(yīng)該根據(jù)系統(tǒng)的特點(diǎn)做出合理的配置描焰,基本策略:盡可能將對象預(yù)留在新生代媳否,
減少老年代的GC次數(shù)。
除了可以設(shè)置新生代的絕對大小(-Xmn),可以使用(-XX:NewRatio)設(shè)置新生代和老年
代的比例:-XX:NewRatio=老年代/新生代
內(nèi)存溢出解決辦法
設(shè)置堆內(nèi)存大小
錯(cuò)誤原因: java.lang.OutOfMemoryError: Java heap space
解決辦法:設(shè)置堆內(nèi)存大小 -Xms1m -Xmx70m -XX:+HeapDumpOnOutOfMemoryError
設(shè)置棧內(nèi)存大小
錯(cuò)誤原因: java.lang.StackOverflowError
棧溢出 產(chǎn)生于遞歸調(diào)用荆秦,循環(huán)遍歷是不會(huì)的篱竭,但是循環(huán)方法里面產(chǎn)生遞歸調(diào)用, 也會(huì)發(fā)生棧溢出步绸。
解決辦法:設(shè)置線程最大調(diào)用深度
-Xss5m 設(shè)置最大調(diào)用深度