Java內存區域與內存溢出異常

Java的內存管理是一個老生常談的問題,雖然Java號稱能夠自動管理本身的內存,使程序員從內存管理的圍牆解放出來,可是一連串的內存泄漏和溢出方面的問題,使得咱們不得不去深刻了解Java的內存管理機制。本篇文章將從Java的內存區域開始剖析Jvm的內存機制,闡述內存溢出異常產生的緣由以及解決辦法。程序員

運行時數據區域

衆說周知,Java程序是運行在Java虛擬機中的,虛擬機顧名思義,就是一個虛擬的計算機。因此Java虛擬機也擁有一些與真實計算機相近的概念,好比棧,堆,程序計數器等,一般咱們在這些概念面前加上虛擬機,以代表特指Java虛擬機的棧。數組

Java程序運行時,Java虛擬機會對內存進行管理,劃分爲若干個不一樣的數據區域,每一個數據區域都有其不一樣的功能。根據《Java虛擬機規範(Java SE 7版)》的規定,Java虛擬機所管理的區域會包括如下幾個運行時區域,以下圖所示。多線程

下面咱們一一介紹每一個區域的不一樣功能。架構

程序計數器

程序計數器,即PC。學過計算機組成原理的同窗必定對這個概念不陌生,在計算機組成原理中PC指的是PC寄存器,用來存放計算機執行的指令的所在內存區域的地址。而在Java虛擬機中,PC也有相似的做用,它的做用是存儲當前線程所執行的字節碼的行號指示器,經過改變這個計數器的值來選取下一條須要執行的字節碼指令。與計算機PC不一樣的是,在Java虛擬機中,PC只是一塊較小的內存空間,而不是寄存器。框架

因爲Java虛擬機是多線程的,爲了在線程之間進行隔離,每個線程都會擁有一個獨立的程序計數器。所以,在進行線程調度的時候,每一個線程的執行互不影響。咱們稱這類內存區域爲「線程私有」的內存。性能

程序計數器記錄的只是正在執行的虛擬機字節碼指令的地址,若是執行的是Native方法,那麼計數器的值則爲空。該內存區域是惟一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域。學習

Java虛擬機棧

Java虛擬機棧與系統棧也有些相似,都用來存儲程序運行過程當中建立的棧幀,不過Java虛擬機棧存儲的是方法的棧幀而已,它與程序計數器同樣,都是線程私有的。spa

在Java方法執行時建立的棧幀是用來存儲局部變量表、操做數棧、動態連接,方法出口等信息,咱們日常所說的方法的入棧和出棧就是一個方法從執行到結束的過程。虛擬機棧的特性與通常的棧同樣,一樣是後進先出,遞歸調用的原理就是基於此。操作系統

通常來講,對於Java虛擬機棧,咱們主要關心的部分是它的局部變量表的存儲。在咱們定義一個變量的時候,變量到底被存放在哪裏是咱們常常遇到的問題。對於基本數據類型,如boolean、byte等以及對象的引用(reference類型,一個指向對象的指針或者是一個句柄,不是對象自己)和returnAddress類型(指向了一條字節碼指令的地址)。線程

局部變量表的大小是在編譯期就已經徹底肯定下來的,在方法運行期間不會改變局部變量表的大小。同時,對於64位長度的long和double類型的數據會佔用兩個局部變量空間,其他的只佔用一個。

在Java虛擬機規範中,對這個區域規定了兩種異常狀況:StackOverflowError異常和OutOfMemoryError異常。

本地方法棧

本地方法棧與虛擬機棧相似,惟一的區別是本地方法棧是用來執行Native方法的。

Java堆

Java堆是咱們在編寫Java程序中所能使用的最大的一塊的內存區域了,也是咱們常常須要調整的區域。這個區域的惟一目的就是存放對象實例,幾乎全部的對象實例都在這裏分配內存,包括數組(由於數組也是引用數據類型)。Java堆與虛擬機棧不一樣,它是被全部線程共享的區域,在虛擬機啓動的時候建立。

Java堆還能夠進一步分爲:新生代和老年代;再細緻一點的有Eden空間、From Survivor空間、To Survivor空間等,這些更細緻的分區是在Java堆垃圾收集器進行垃圾管理的時候須要考慮的。

Java堆的大小能夠是固定的,也能夠是不固定的,能夠經過-Xmx和-Xms控制,前者是最大值,後者是最小值,在二者相同時,堆的大小就是固定的。在內存中若是沒有足夠的空間來分配,將會拋出OutOfMemoryError異常。

方法區

方法區和Java堆同樣,是各個線程共享的內存區域,用來存儲已經被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。

這個區域也是屬於須要進行垃圾回收的區域,主要是回收常量池和對類型的卸載,通常來講,回收的效果不會太理想,可是倒是必須的。

在此我向你們推薦一個架構學習交流圈:830478757  幫助突破瓶頸 提高思惟能力 

根據Java虛擬機的規範規定,當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError異常。

運行時常量池

該區域是方法區的一部分,用於存放編譯期生成的各類字面量和符號引用,有時候直接引用也會放入,這部份內容將在類加載後進入方法區的運行時常量池中存放。

運行時常量池有必定的動態性,對String類有所瞭解的同窗應該明白,在運行期間經過String類的intern()方法能夠動態往常量池裏動態添加常量。

直接內存

直接內存不屬於Java虛擬機運行時數據區的一部分,而是屬於操做系統管理的區域。這部分的使用很頻繁,利用的好,能夠大大提高程序的運行效率,比較優秀的使用例如基於NIO的Netty框架等。

爲何使用直接內存能夠提高性能呢,由於能夠避免在Java堆和Native堆中來回複製數據的開銷。

這部分的內存使用不會收到Java堆大小的限制,但會收到本機的內存大小限制。所以,在操做這部份內存時須要謹慎,一旦出問題,可能會影響到本機的其它服務。 當各個內存區域總和大於物理內存限制,拋出OutOfMemoryError異常。

相關文章
相關標籤/搜索