閱讀前思考
說一下 JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)吧,都有哪些區(qū)?分別是干什么的?
Java 8 的內(nèi)存分代改進(jìn)
舉例棧溢出的情況?
調(diào)整棧大小,就能保存不出現(xiàn)溢出嗎?
分配的棧內(nèi)存越大越好嗎?
垃圾回收是否會(huì)涉及到虛擬機(jī)棧?
方法中定義的局部變量是否線程安全?
運(yùn)行時(shí)數(shù)據(jù)區(qū)
內(nèi)存是非常重要的系統(tǒng)資源,是硬盤和 CPU 的中間倉(cāng)庫(kù)及橋梁,承載著操作系統(tǒng)和應(yīng)用程序的實(shí)時(shí)運(yùn)行。JVM 內(nèi)存布局規(guī)定了 Java 在運(yùn)行過程中內(nèi)存申請(qǐng)、分配、管理的策略,保證了 JVM 的高效穩(wěn)定運(yùn)行。不同的 JVM 對(duì)于內(nèi)存的劃分方式和管理機(jī)制存在著部分差異。
下圖是 JVM 整體架構(gòu),中間部分就是 Java 虛擬機(jī)定義的各種運(yùn)行時(shí)數(shù)據(jù)區(qū)域。
Java 虛擬機(jī)定義了若干種程序運(yùn)行期間會(huì)使用到的運(yùn)行時(shí)數(shù)據(jù)區(qū),其中有一些會(huì)隨著虛擬機(jī)啟動(dòng)而創(chuàng)建,隨著虛擬機(jī)退出而銷毀。另外一些則是與線程一一對(duì)應(yīng)的,這些與線程一一對(duì)應(yīng)的數(shù)據(jù)區(qū)域會(huì)隨著線程開始和結(jié)束而創(chuàng)建和銷毀。
線程私有:程序計(jì)數(shù)器、棧、本地棧
線程共享:堆、堆外內(nèi)存(永久代或元空間、代碼緩存)
四、堆內(nèi)存
內(nèi)存劃分
對(duì)于大多數(shù)應(yīng)用,Java 堆是 Java 虛擬機(jī)管理的內(nèi)存中最大的一塊,被所有線程共享。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例以及數(shù)據(jù)都在這里分配內(nèi)存。
為了進(jìn)行高效的垃圾回收,虛擬機(jī)把堆內(nèi)存邏輯上劃分成三塊區(qū)域(分代的唯一理由就是優(yōu)化 GC 性能):
新生帶(年輕代):新對(duì)象和沒達(dá)到一定年齡的對(duì)象都在新生代
老年代(養(yǎng)老區(qū)):被長(zhǎng)時(shí)間使用的對(duì)象,老年代的內(nèi)存空間應(yīng)該要比年輕代更大
元空間(JDK1.8 之前叫永久代):像一些方法中的操作臨時(shí)對(duì)象等,JDK1.8 之前是占用 JVM 內(nèi)存,JDK1.8 之后直接使用物理內(nèi)存
Java 虛擬機(jī)規(guī)范規(guī)定,Java 堆可以是處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可,像磁盤空間一樣。實(shí)現(xiàn)時(shí),既可以是固定大小,也可以是可擴(kuò)展的,主流虛擬機(jī)都是可擴(kuò)展的(通過-Xmx和-Xms控制),如果堆中沒有完成實(shí)例分配,并且堆無(wú)法再擴(kuò)展時(shí),就會(huì)拋出OutOfMemoryError異常。
年輕代 (Young Generation)
年輕代是所有新對(duì)象創(chuàng)建的地方。當(dāng)填充年輕代時(shí),執(zhí)行垃圾收集。這種垃圾收集稱為Minor GC。年輕一代被分為三個(gè)部分——伊甸園(Eden Memory)和兩個(gè)幸存區(qū)(Survivor Memory,被稱為from/to或s0/s1),默認(rèn)比例是81
大多數(shù)新創(chuàng)建的對(duì)象都位于 Eden 內(nèi)存空間中
當(dāng) Eden 空間被對(duì)象填充時(shí),執(zhí)行Minor GC,并將所有幸存者對(duì)象移動(dòng)到一個(gè)幸存者空間中
Minor GC 檢查幸存者對(duì)象,并將它們移動(dòng)到另一個(gè)幸存者空間。所以每次,一個(gè)幸存者空間總是空的
經(jīng)過多次 GC 循環(huán)后存活下來的對(duì)象被移動(dòng)到老年代。通常,這是通過設(shè)置年輕一代對(duì)象的年齡閾值來實(shí)現(xiàn)的,然后他們才有資格提升到老一代
老年代(Old Generation)
舊的一代內(nèi)存包含那些經(jīng)過許多輪小型 GC 后仍然存活的對(duì)象。通常,垃圾收集是在老年代內(nèi)存滿時(shí)執(zhí)行的。老年代垃圾收集稱為 主GC(Major GC),通常需要更長(zhǎng)的時(shí)間。
大對(duì)象直接進(jìn)入老年代(大對(duì)象是指需要大量連續(xù)內(nèi)存空間的對(duì)象)。這樣做的目的是避免在 Eden 區(qū)和兩個(gè)Survivor 區(qū)之間發(fā)生大量的內(nèi)存拷貝
元空間
不管是 JDK8 之前的永久代,還是 JDK8 及以后的元空間,都可以看作是 Java 虛擬機(jī)規(guī)范中方法區(qū)的實(shí)現(xiàn)。
雖然 Java 虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是它卻有一個(gè)別名叫 Non-Heap(非堆),目的應(yīng)該是與 Java 堆區(qū)分開。
所以元空間放在后邊的方法區(qū)再說。
設(shè)置堆內(nèi)存大小和 OOM
Java 堆用于存儲(chǔ) Java 對(duì)象實(shí)例,那么堆的大小在 JVM 啟動(dòng)的時(shí)候就確定了,我們可以通過-Xmx和-Xms來設(shè)定
-Xmx用來表示堆的起始內(nèi)存,等價(jià)于-XX:InitialHeapSize
-Xms用來表示堆的最大內(nèi)存,等價(jià)于-XX:MaxHeapSize
如果堆的內(nèi)存大小超過-Xms設(shè)定的最大內(nèi)存, 就會(huì)拋出OutOfMemoryError異常。
我們通常會(huì)將-Xmx和-Xms兩個(gè)參數(shù)配置為相同的值,其目的是為了能夠在垃圾回收機(jī)制清理完堆區(qū)后不再需要重新分隔計(jì)算堆的大小,從而提高性能
默認(rèn)情況下,初始堆內(nèi)存大小為:電腦內(nèi)存大小/64
默認(rèn)情況下,最大堆內(nèi)存大小為:電腦內(nèi)存大小/4
可以通過代碼獲取到我們的設(shè)置值,當(dāng)然也可以模擬 OOM:
public static void main(String[] args) { //返回 JVM 堆大小 long initalMemory = Runtime.getRuntime().totalMemory() / 1024 /1024; //返回 JVM 堆的最大內(nèi)存 long maxMemory = Runtime.getRuntime().maxMemory() / 1024 /1024; System.out.println("-Xms : "+initalMemory + "M"); System.out.println("-Xmx : "+maxMemory + "M"); System.out.println("系統(tǒng)內(nèi)存大?。? + initalMemory * 64 / 1024 + "G"); System.out.println("系統(tǒng)內(nèi)存大小:" + maxMemory * 4 / 1024 + "G"); }
查看 JVM 堆內(nèi)存分配
在默認(rèn)不配置 JVM 堆內(nèi)存大小的情況下,JVM 根據(jù)默認(rèn)值來配置當(dāng)前內(nèi)存大小
默認(rèn)情況下新生代和老年代的比例是 1:2,可以通過–XX:NewRatio來配置
新生代中的Eden:From Survivor:To Survivor的比例是81,可以通過-XX:SurvivorRatio來配置
若在 JDK 7 中開啟了-XX:+UseAdaptiveSizePolicy,JVM 會(huì)動(dòng)態(tài)調(diào)整 JVM 堆中各個(gè)區(qū)域的大小以及進(jìn)入老年代的年齡
此時(shí)–XX:NewRatio和-XX:SurvivorRatio將會(huì)失效,而 JDK 8 是默認(rèn)開啟-XX:+UseAdaptiveSizePolicy
在 JDK 8中,不要隨意關(guān)閉-XX:+UseAdaptiveSizePolicy,除非對(duì)堆內(nèi)存的劃分有明確的規(guī)劃
每次 GC 后都會(huì)重新計(jì)算 Eden、From Survivor、To Survivor 的大小
計(jì)算依據(jù)是GC過程中統(tǒng)計(jì)的GC時(shí)間、吞吐量、內(nèi)存占用量
java -XX:+PrintFlagsFinal -version | grep HeapSize uintx ErgoHeapSizeLimit = 0 {product} uintx HeapSizePerGCThread = 87241520 {product} uintx InitialHeapSize := 134217728 {product} uintx LargePageHeapSizeThreshold = 134217728 {product} uintx MaxHeapSize := 2147483648 {product} java version "1.8.0_211" Java(TM) SE Runtime Environment (build 1.8.0_211-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
jmap -heap 進(jìn)程號(hào)
對(duì)象在堆中的生命周期
在 JVM 內(nèi)存模型的堆中,堆被劃分為新生代和老年代
新生代又被進(jìn)一步劃分為Eden區(qū)和Survivor區(qū),Survivor 區(qū)由From Survivor和To Survivor組成
當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),對(duì)象會(huì)被優(yōu)先分配到新生代的 Eden 區(qū)
此時(shí) JVM 會(huì)給對(duì)象定義一個(gè)對(duì)象年輕計(jì)數(shù)器(-XX:MaxTenuringThreshold)
當(dāng) Eden 空間不足時(shí),JVM 將執(zhí)行新生代的垃圾回收(Minor GC)
JVM 會(huì)把存活的對(duì)象轉(zhuǎn)移到 Survivor 中,并且對(duì)象年齡 +1
對(duì)象在 Survivor 中同樣也會(huì)經(jīng)歷 Minor GC,每經(jīng)歷一次 Minor GC,對(duì)象年齡都會(huì)+1
如果分配的對(duì)象超過了-XX:PetenureSizeThreshold,對(duì)象會(huì)直接被分配到老年代
對(duì)象的分配過程
為對(duì)象分配內(nèi)存是一件非常嚴(yán)謹(jǐn)和復(fù)雜的任務(wù),JVM 的設(shè)計(jì)者們不僅需要考慮內(nèi)存如何分配、在哪里分配等問題,并且由于內(nèi)存分配算法和內(nèi)存回收算法密切相關(guān),所以還需要考慮 GC 執(zhí)行完內(nèi)存回收后是否會(huì)在內(nèi)存空間中產(chǎn)生內(nèi)存碎片。
new 的對(duì)象先放在伊甸園區(qū),此區(qū)有大小限制
當(dāng)伊甸園的空間填滿時(shí),程序又需要?jiǎng)?chuàng)建對(duì)象,JVM 的垃圾回收器將對(duì)伊甸園區(qū)進(jìn)行垃圾回收(Minor GC),將伊甸園區(qū)中的不再被其他對(duì)象所引用的對(duì)象進(jìn)行銷毀。再加載新的對(duì)象放到伊甸園區(qū)
然后將伊甸園中的剩余對(duì)象移動(dòng)到幸存者 0 區(qū)
如果再次觸發(fā)垃圾回收,此時(shí)上次幸存下來的放到幸存者 0 區(qū),如果沒有回收,就會(huì)放到幸存者 1 區(qū)
如果再次經(jīng)歷垃圾回收,此時(shí)會(huì)重新放回幸存者 0 區(qū),接著再去幸存者 1 區(qū)
什么時(shí)候才會(huì)去養(yǎng)老區(qū)呢?默認(rèn)是 15 次回收標(biāo)記
在養(yǎng)老區(qū),相對(duì)悠閑。當(dāng)養(yǎng)老區(qū)內(nèi)存不足時(shí),再次觸發(fā) Major GC,進(jìn)行養(yǎng)老區(qū)的內(nèi)存清理
若養(yǎng)老區(qū)執(zhí)行了 Major GC 之后發(fā)現(xiàn)依然無(wú)法進(jìn)行對(duì)象的保存,就會(huì)產(chǎn)生 OOM 異常
GC 垃圾回收簡(jiǎn)介
Minor GC、Major GC、Full GC
JVM 在進(jìn)行 GC 時(shí),并非每次都對(duì)堆內(nèi)存(新生代、老年代;方法區(qū))區(qū)域一起回收的,大部分時(shí)候回收的都是指新生代。
針對(duì) HotSpot VM 的實(shí)現(xiàn),它里面的 GC 按照回收區(qū)域又分為兩大類:部分收集(Partial GC),整堆收集(Full GC)
部分收集:不是完整收集整個(gè) Java 堆的垃圾收集。其中又分為:
目前只有 G1 GC 會(huì)有這種行為
目前,只有 CMS GC 會(huì)有單獨(dú)收集老年代的行為
很多時(shí)候 Major GC 會(huì)和 Full GC 混合使用,需要具體分辨是老年代回收還是整堆回收
新生代收集(Minor GC/Young GC):只是新生代的垃圾收集
老年代收集(Major GC/Old GC):只是老年代的垃圾收集
混合收集(Mixed GC):收集整個(gè)新生代以及部分老年代的垃圾收集
整堆收集(Full GC):收集整個(gè) Java 堆和方法區(qū)的垃圾
TLAB
什么是 TLAB (Thread Local Allocation Buffer)?
從內(nèi)存模型而不是垃圾回收的角度,對(duì) Eden 區(qū)域繼續(xù)進(jìn)行劃分,JVM 為每個(gè)線程分配了一個(gè)私有緩存區(qū)域,它包含在 Eden 空間內(nèi)
多線程同時(shí)分配內(nèi)存時(shí),使用 TLAB 可以避免一系列的非線程安全問題,同時(shí)還能提升內(nèi)存分配的吞吐量,因此我們可以將這種內(nèi)存分配方式稱為快速分配策略
OpenJDK 衍生出來的 JVM 大都提供了 TLAB 設(shè)計(jì)
為什么要有 TLAB ?
堆區(qū)是線程共享的,任何線程都可以訪問到堆區(qū)中的共享數(shù)據(jù)
由于對(duì)象實(shí)例的創(chuàng)建在 JVM 中非常頻繁,因此在并發(fā)環(huán)境下從堆區(qū)中劃分內(nèi)存空間是線程不安全的
為避免多個(gè)線程操作同一地址,需要使用加鎖等機(jī)制,進(jìn)而影響分配速度
盡管不是所有的對(duì)象實(shí)例都能夠在 TLAB 中成功分配內(nèi)存,但 JVM 確實(shí)是將 TLAB 作為內(nèi)存分配的首選。
在程序中,可以通過-XX:UseTLAB設(shè)置是否開啟 TLAB 空間。
默認(rèn)情況下,TLAB 空間的內(nèi)存非常小,僅占有整個(gè) Eden 空間的 1%,我們可以通過-XX:TLABWasteTargetPercent設(shè)置 TLAB 空間所占用 Eden 空間的百分比大小。
一旦對(duì)象在 TLAB 空間分配內(nèi)存失敗時(shí),JVM 就會(huì)嘗試著通過使用加鎖機(jī)制確保數(shù)據(jù)操作的原子性,從而直接在 Eden 空間中分配內(nèi)存。
堆是分配對(duì)象存儲(chǔ)的唯一選擇嗎
隨著 JIT 編譯期的發(fā)展和逃逸分析技術(shù)的逐漸成熟,棧上分配、標(biāo)量替換優(yōu)化技術(shù)將會(huì)導(dǎo)致一些微妙的變化,所有的對(duì)象都分配到堆上也漸漸變得不那么“絕對(duì)”了?!渡钊肜斫?Java 虛擬機(jī)》
逃逸分析
逃逸分析(Escape Analysis)是目前 Java 虛擬機(jī)中比較前沿的優(yōu)化技術(shù)。這是一種可以有效減少 Java 程序中同步負(fù)載和內(nèi)存堆分配壓力的跨函數(shù)全局?jǐn)?shù)據(jù)流分析算法。通過逃逸分析,Java Hotspot 編譯器能夠分析出一個(gè)新的對(duì)象的引用的使用范圍從而決定是否要將這個(gè)對(duì)象分配到堆上。
逃逸分析的基本行為就是分析對(duì)象動(dòng)態(tài)作用域:
當(dāng)一個(gè)對(duì)象在方法中被定義后,對(duì)象只在方法內(nèi)部使用,則認(rèn)為沒有發(fā)生逃逸。
當(dāng)一個(gè)對(duì)象在方法中被定義后,它被外部方法所引用,則認(rèn)為發(fā)生逃逸。例如作為調(diào)用參數(shù)傳遞到其他地方中,稱為方法逃逸。
例如:
public static StringBuffer craeteStringBuffer(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb; }
StringBuffer sb是一個(gè)方法內(nèi)部變量,上述代碼中直接將sb返回,這樣這個(gè) StringBuffer 有可能被其他方法所改變,這樣它的作用域就不只是在方法內(nèi)部,雖然它是一個(gè)局部變量,稱其逃逸到了方法外部。甚至還有可能被外部線程訪問到,譬如賦值給類變量或可以在其他線程中訪問的實(shí)例變量,稱為線程逃逸。
上述代碼如果想要StringBuffer sb不逃出方法,可以這樣寫:
public static String createStringBuffer(String s1, String s2) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); return sb.toString(); }
不直接返回 StringBuffer,那么 StringBuffer 將不會(huì)逃逸出方法。
參數(shù)設(shè)置:
在 JDK 6u23 版本之后,HotSpot 中默認(rèn)就已經(jīng)開啟了逃逸分析
如果使用較早版本,可以通過-XX"+DoEscapeAnalysis顯式開啟
開發(fā)中使用局部變量,就不要在方法外定義。
使用逃逸分析,編譯器可以對(duì)代碼做優(yōu)化:
棧上分配:將堆分配轉(zhuǎn)化為棧分配。如果一個(gè)對(duì)象在子程序中被分配,要使指向該對(duì)象的指針永遠(yuǎn)不會(huì)逃逸,對(duì)象可能是棧分配的候選,而不是堆分配
同步省略:如果一個(gè)對(duì)象被發(fā)現(xiàn)只能從一個(gè)線程被訪問到,那么對(duì)于這個(gè)對(duì)象的操作可以不考慮同步
分離對(duì)象或標(biāo)量替換:有的對(duì)象可能不需要作為一個(gè)連續(xù)的內(nèi)存結(jié)構(gòu)存在也可以被訪問到,那么對(duì)象的部分(或全部)可以不存儲(chǔ)在內(nèi)存,而存儲(chǔ)在 CPU 寄存器
JIT 編譯器在編譯期間根據(jù)逃逸分析的結(jié)果,發(fā)現(xiàn)如果一個(gè)對(duì)象并沒有逃逸出方法的話,就可能被優(yōu)化成棧上分配。分配完成后,繼續(xù)在調(diào)用棧內(nèi)執(zhí)行,最后線程結(jié)束,??臻g被回收,局部變量對(duì)象也被回收。這樣就無(wú)需進(jìn)行垃圾回收了。
常見棧上分配的場(chǎng)景:成員變量賦值、方法返回值、實(shí)例引用傳遞
代碼優(yōu)化之同步省略(消除)
線程同步的代價(jià)是相當(dāng)高的,同步的后果是降低并發(fā)性和性能
在動(dòng)態(tài)編譯同步塊的時(shí)候,JIT 編譯器可以借助逃逸分析來判斷同步塊所使用的鎖對(duì)象是否能夠被一個(gè)線程訪問而沒有被發(fā)布到其他線程。如果沒有,那么 JIT 編譯器在編譯這個(gè)同步塊的時(shí)候就會(huì)取消對(duì)這個(gè)代碼的同步。這樣就能大大提高并發(fā)性和性能。這個(gè)取消同步的過程就叫做同步省略,也叫鎖消除。
public void keep() { Object keeper = new Object(); synchronized(keeper) { System.out.println(keeper); } }
如上代碼,代碼中對(duì) keeper 這個(gè)對(duì)象進(jìn)行加鎖,但是 keeper 對(duì)象的生命周期只在keep()方法中,并不會(huì)被其他線程所訪問到,所以在 JIT編譯階段就會(huì)被優(yōu)化掉。優(yōu)化成:
public void keep() { Object keeper = new Object(); System.out.println(keeper); }
代碼優(yōu)化之標(biāo)量替換
標(biāo)量(Scalar)是指一個(gè)無(wú)法再分解成更小的數(shù)據(jù)的數(shù)據(jù)。Java 中的原始數(shù)據(jù)類型就是標(biāo)量。
相對(duì)的,那些的還可以分解的數(shù)據(jù)叫做聚合量(Aggregate),Java 中的對(duì)象就是聚合量,因?yàn)槠溥€可以分解成其他聚合量和標(biāo)量。
在 JIT 階段,通過逃逸分析確定該對(duì)象不會(huì)被外部訪問,并且對(duì)象可以被進(jìn)一步分解時(shí),JVM 不會(huì)創(chuàng)建該對(duì)象,而會(huì)將該對(duì)象成員變量分解若干個(gè)被這個(gè)方法使用的成員變量所代替。這些代替的成員變量在棧幀或寄存器上分配空間。這個(gè)過程就是標(biāo)量替換。
通過-XX:+EliminateAllocations可以開啟標(biāo)量替換,-XX:+PrintEliminateAllocations查看標(biāo)量替換情況。
public static void main(String[] args) { alloc(); } private static void alloc() { Point point = new Point(1,2); System.out.println("point.x="+point.x+"; point.y="+point.y); } class Point{ private int x; private int y; }
以上代碼中,point 對(duì)象并沒有逃逸出alloc()方法,并且 point 對(duì)象是可以拆解成標(biāo)量的。那么,JIT 就不會(huì)直接創(chuàng)建 Point 對(duì)象,而是直接使用兩個(gè)標(biāo)量 int x ,int y 來替代 Point 對(duì)象。
private static void alloc() { int x = 1; int y = 2; System.out.println("point.x="+x+"; point.y="+y); }
代碼優(yōu)化之棧上分配
我們通過 JVM 內(nèi)存分配可以知道 JAVA 中的對(duì)象都是在堆上進(jìn)行分配,當(dāng)對(duì)象沒有被引用的時(shí)候,需要依靠 GC 進(jìn)行回收內(nèi)存,如果對(duì)象數(shù)量較多的時(shí)候,會(huì)給 GC 帶來較大壓力,也間接影響了應(yīng)用的性能。為了減少臨時(shí)對(duì)象在堆內(nèi)分配的數(shù)量,JVM 通過逃逸分析確定該對(duì)象不會(huì)被外部訪問。那就通過標(biāo)量替換將該對(duì)象分解在棧上分配內(nèi)存,這樣該對(duì)象所占用的內(nèi)存空間就可以隨棧幀出棧而銷毀,就減輕了垃圾回收的壓力。
總結(jié):
關(guān)于逃逸分析的論文在1999年就已經(jīng)發(fā)表了,但直到JDK 1.6才有實(shí)現(xiàn),而且這項(xiàng)技術(shù)到如今也并不是十分成熟的。
其根本原因就是無(wú)法保證逃逸分析的性能消耗一定能高于他的消耗。雖然經(jīng)過逃逸分析可以做標(biāo)量替換、棧上分配、和鎖消除。但是逃逸分析自身也是需要進(jìn)行一系列復(fù)雜的分析的,這其實(shí)也是一個(gè)相對(duì)耗時(shí)的過程。
一個(gè)極端的例子,就是經(jīng)過逃逸分析之后,發(fā)現(xiàn)沒有一個(gè)對(duì)象是不逃逸的。那這個(gè)逃逸分析的過程就白白浪費(fèi)掉了。
雖然這項(xiàng)技術(shù)并不十分成熟,但是他也是即時(shí)編譯器優(yōu)化技術(shù)中一個(gè)十分重要的手段。
審核編輯:湯梓紅
-
cpu
+關(guān)注
關(guān)注
68文章
11031瀏覽量
215931 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
3108瀏覽量
74974 -
JVM
+關(guān)注
關(guān)注
0文章
160瀏覽量
12515 -
虛擬機(jī)
+關(guān)注
關(guān)注
1文章
962瀏覽量
29014 -
線程
+關(guān)注
關(guān)注
0文章
507瀏覽量
20068
原文標(biāo)題:運(yùn)行時(shí)數(shù)據(jù)區(qū)
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
Jvm的整體結(jié)構(gòu)和特點(diǎn)
java線程內(nèi)存模型

Java內(nèi)存模型及原理分析

jvm內(nèi)存溢出故障排查
jvm內(nèi)存模型和內(nèi)存結(jié)構(gòu)
jvm哪些區(qū)域會(huì)發(fā)生oom
jvm運(yùn)行時(shí)內(nèi)存區(qū)域劃分
jvm管理的內(nèi)存包括哪幾個(gè)運(yùn)行時(shí)數(shù)據(jù)內(nèi)存
jvm內(nèi)存區(qū)域由哪幾部分組成
jvm內(nèi)存區(qū)域中,哪一塊是屬于線程共享
jvm配置堆內(nèi)存初始值參數(shù)
weblogic jvm參數(shù)配置
weblogic設(shè)置jvm內(nèi)存大小
eclipse設(shè)置jvm內(nèi)存大小
從原理聊JVM(一):染色標(biāo)記和垃圾回收算法

評(píng)論