日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

元空间和直接内存_JVM探秘:Java内存区域

發(fā)布時(shí)間:2024/9/19 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 元空间和直接内存_JVM探秘:Java内存区域 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本系列筆記目前主要基于《深入理解Java虛擬機(jī):JVM高級特性與最佳實(shí)踐 第2版》,后續(xù)還會(huì)加入《實(shí)戰(zhàn)Java虛擬機(jī):JVM故障診斷與性能優(yōu)化》、《Java性能權(quán)威指南》、《Java性能優(yōu)化權(quán)威指南》的閱讀筆記。

概述

Java 虛擬機(jī)為程序員分擔(dān)了很多內(nèi)存管理的工作,不再像 C/C++ 那樣容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出問題了,也正是這樣,導(dǎo)致一旦出現(xiàn)了內(nèi)存泄漏和溢出方面的問題,就難以排查。因此一個(gè)優(yōu)秀的 Java 程序員應(yīng)該對 Java 虛擬機(jī)有充足的了解,JVM 是你的必修課。

運(yùn)行時(shí)數(shù)據(jù)區(qū)域

根據(jù)《Java虛擬機(jī)規(guī)范(Java SE 7版)》,JVM 所管理的內(nèi)存區(qū)域的劃分如下:

image

每個(gè)內(nèi)存區(qū)域都有各自的用途,以及創(chuàng)建和銷毀時(shí)間,有的區(qū)域會(huì)隨著虛擬機(jī)的啟動(dòng)而存在,有的區(qū)域依賴用戶線程而建立和銷毀,接下來一次介紹這些內(nèi)存區(qū)域。程序計(jì)數(shù)器

程序計(jì)數(shù)器(Program Counter Register)是線程私有的內(nèi)存區(qū)域,是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器,是一塊較小的內(nèi)存空間。

字節(jié)碼解釋器(java源碼編譯成字節(jié)碼,運(yùn)行時(shí)解釋成機(jī)器碼執(zhí)行)工作時(shí),就是通過改變程序計(jì)數(shù)器的值,來選取下一條要執(zhí)行的字節(jié)碼。分支、循環(huán)、跳轉(zhuǎn)等都依賴程序計(jì)數(shù)器執(zhí)行。

當(dāng)線程執(zhí)行 Java 方法時(shí),程序計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址;當(dāng)線程執(zhí)行 Native 方法時(shí),程序計(jì)數(shù)器的值為空(Undefined)。程序計(jì)數(shù)器是唯一一個(gè)在 Java 虛擬機(jī)規(guī)范中沒有規(guī)定 OutOfMemoryError 的內(nèi)存區(qū)域。

Java虛擬機(jī)棧

Java 虛擬機(jī)棧(Java Stack)也是線程私有的內(nèi)存區(qū)域,它的生命周期與線程相同。虛擬機(jī)棧描述的是 Java 方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)用以存儲局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。每一個(gè)方法從開始調(diào)用到執(zhí)行完成的過程,就對應(yīng)一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過程。

局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean byte char short int long double float)、對象引用,以及 returnAddress 類型(指向一條字節(jié)碼指令的地址)。局部變量表所需內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí),需要在棧幀中分配多大的局部變量空間是完全確定的,在方法運(yùn)行期間不會(huì)改變局部變量表大小。

在 Java 虛擬機(jī)規(guī)范中,Java 虛擬機(jī)棧有可能會(huì)出現(xiàn)兩種異常:StackOverflowError 和 OutOfMemoryError。如果線程請求的棧深度大于虛擬機(jī)棧的深度,則會(huì) StackOverflowError。如果虛擬機(jī)棧動(dòng)態(tài)擴(kuò)展時(shí)申請不到足夠的內(nèi)存,則會(huì) OutOfMemoryError。

本地方法棧

本地方法棧(Native Method Stack)與 Java 虛擬機(jī)棧的作用一樣,是線程私有的,區(qū)別就是 Java 虛擬機(jī)棧為執(zhí)行 Java 方法服務(wù),本地方法棧為 Native 方法服務(wù),虛擬機(jī)規(guī)范并沒有對本地方法棧做強(qiáng)制規(guī)定,在 HotSpot 虛擬機(jī)中把本地方法棧和虛擬機(jī)棧合二為一了。此內(nèi)存區(qū)域也會(huì)拋出 StackOverflowError 和 OutOfMemoryError。

Java堆

Java 堆(Java Heap)是一塊被所有線程共享的內(nèi)存區(qū)域,同時(shí)也是 Java 虛擬機(jī)所管理的內(nèi)存中最大的一塊,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例,幾乎所有的對象實(shí)例都在這里分配內(nèi)存。

Java 堆是垃圾收集器管理堆主要區(qū)域,也被稱做“GC堆”。現(xiàn)在垃圾收集器基本都采用分代收集算法,所以從內(nèi)存回收角度看,Java 堆還可以細(xì)分為:新生代和老年代;新生代可以再細(xì)分為 Eden 空間、From Survivor 空間、To Survivor空間。

如下圖所示:

image

從內(nèi)存分配角度看,線程共享的 Java 堆中可能劃分出多個(gè)線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB)。虛擬機(jī)為新生對象分配內(nèi)存時(shí),為每個(gè)線程在 Java 堆 中預(yù)先分配一小塊內(nèi)存,稱做本地線程分配緩沖(TLAB)。哪個(gè)線程要分配內(nèi)存,就在哪個(gè)線程的 TLAB 上分配,只有 TLAB 用完并分配新的 TLAB 時(shí),才會(huì)同步鎖定(為了并發(fā)情況下的線程安全)。

Java 堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可。如果堆中沒有足夠內(nèi)存完成實(shí)例分配,并且也無法擴(kuò)展時(shí),會(huì)拋出 OutOfMemoryError。

方法區(qū)

方法區(qū)(Method Aera)與 Java 堆一樣,也是線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。在虛擬機(jī)規(guī)范中,它被描述為堆的一個(gè)邏輯部分,但實(shí)際它應(yīng)該要與 Java 堆區(qū)分開來。

從分代收集的角度看,方法區(qū)也被稱做永久代(Permanent Generation),實(shí)際上兩者并不等價(jià),只是因?yàn)?HotSpot 虛擬機(jī)使用永久代實(shí)現(xiàn)了方法區(qū),對于其他虛擬機(jī)(JRockit、IBM J9)是不存在永久代概念的。

使用永久代實(shí)現(xiàn)方法區(qū),更容易出現(xiàn)內(nèi)存溢出問題(永久代有 -XX:MaxPermSize 的上限),所以在 JDK1.8 中,HotSpot 就取消了永久代(JEP122),取而代之的是元空間(MetaSpace),元空間是方法區(qū)新的實(shí)現(xiàn),而且使用的是本地內(nèi)存不是虛擬機(jī)內(nèi)存。原先永久代中類的元信息會(huì)放入元空間,類的靜態(tài)變量和常量會(huì)放入 Java 堆。

永久代也并不是指真的“永久”存在,只是說這部分內(nèi)存回收(常量池回收和對類型的卸載)的成績難以令人滿意,條件也非常苛刻。

方法區(qū)會(huì)有 OutOfMemoryError 異常。

運(yùn)行時(shí)常量池

運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分。Class文件中除了有類的版本信息、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池,用于存放編譯期間生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中,另外翻譯出來的直接引用也會(huì)存儲在這個(gè)區(qū)域中。

這個(gè)區(qū)域另外一個(gè)特點(diǎn)就是動(dòng)態(tài)性,Java并不要求常量就一定要在編譯期間才能產(chǎn)生,運(yùn)行期間也可以在這個(gè)區(qū)域放入新的內(nèi)容,String.intern()方法就是這個(gè)特性的應(yīng)用。此區(qū)域有 OutOfMemoryError 異常。

在JDK1.6及之前,常量池是位于方法區(qū)中的,但在JDK1.7的時(shí)候常量池挪到了堆內(nèi)存,也就是常量池和對象共享 Java 堆,所以在 Java7 以后,常量池就不在方法區(qū)分配了,而是在 Java 堆 中分配。

直接內(nèi)存

直接內(nèi)存(Direct Memory)并不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是 Java 虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域。但這部分區(qū)域被頻繁的使用,也可能導(dǎo)致 OutOfMemory 異常出現(xiàn)。

JDK1.4 中加入了 NIO,這是一種基于通道(Channel)和緩沖區(qū)的 I/O 方式,它可以使用 Native 函數(shù)庫直接分配堆外內(nèi)存,然后通過一個(gè)存儲在 Java 堆中的 DirectByteBuffer 對象作為這塊內(nèi)存的引用進(jìn)行操作。這樣顯著提高了性能,因?yàn)楸苊饬嗽?Java 堆和 Native 堆中來回復(fù)制數(shù)據(jù)。顯然,本機(jī)直接內(nèi)存的分配不會(huì)受到 Java 堆大小的限制,但是,既然是內(nèi)存,肯定還是會(huì)受到本機(jī)總內(nèi)存(包括RAM、SWAP區(qū))大小以及處理器尋址空間的限制。在配置虛擬機(jī)參數(shù)時(shí),會(huì)根據(jù)實(shí)際內(nèi)存配置 -Xmx 等,但經(jīng)常忽略直接內(nèi)存,使得各個(gè)內(nèi)存區(qū)域總和大于了物理內(nèi)存,從而導(dǎo)致動(dòng)態(tài)擴(kuò)展時(shí)出現(xiàn) OutOfMemoryError。

總結(jié)

以上是生活随笔為你收集整理的元空间和直接内存_JVM探秘:Java内存区域的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。