Java的內存 -JVM 內存管理

一.綜述

若是你學過C或者C++,那麼你應該感覺過它們對內存那種強大的掌控力。可是強大的能力每每須要更強大的控制力才能保證能力不被濫用,若是濫用C/C++的內存管理那麼很容易出現指針滿天飛的狀況,不出問題還好,一出問題debug起來簡直讓人頭疼得不要不要的。借用一句話,「指針一時爽,重構火葬場」。java

而對java程序員來講,則沒有這樣的煩惱,由於java直接將內存管理交由jvm來管理,這樣程序員在編寫程序的時候就不用擔憂內存的使用狀況而能夠專一內容的實現。但這其實也形成了一點隱患,若是你不瞭解jvm內存管理的機制,極可能會因一些錯誤的代碼寫法而致使內存泄漏或內存溢出。程序員

注:所述內容取自Jdk1.6。多線程

二.jvm內存結構

三.每部分存儲了哪些數據

1.程序計數器

程序計數器是一塊較小的內存空間,能夠看做當前線程所執行字節碼的行號指示器,即指向正在執行的字節碼。在概念模型中,字節碼解釋器的工做就是經過改變這個程序計數器的值來選取下一條字節碼的指令。app

值得一提的是,由於java的多線程是經過線程輪流切換並分配處理器執行時間來實現的(即一個小的時間段內仍然只有一個線程處於運行狀態),每一個線程的執行指令都不同,爲了使線程切換後能正確執行到該線程的下一指令,每一個線程都須要一個獨立的程序計數器,因此程序計數器使線程私有的。less

2.虛擬機棧

虛擬機堆和虛擬機棧能夠說是jvm內存中最值得咱們關注的兩塊內存區域。虛擬機棧是內存私有的,每一個方法在執行的同時會建立一個棧幀。用於存儲局部變量表,操做數棧,動態連接等信息。每個方法調用到執行完成的過程,其實就是對應一個棧幀在虛擬機棧中入棧到出棧的過程。jvm

在這個區域可能出現的異常狀況有兩種,分別是StackOverflowError和OutOfMemoryError。當棧動態拓展過深,好比無限遞歸時會出現StackOverflowError,而當沒法申請到足夠內存時,則發生OutOfMemoryError。ui

3.堆

對大部分應用來講,堆是jvm管理的內存中最大的一塊。與虛擬機棧不一樣,堆是被全部線程共享的。它的做用是存放對象的實例,幾乎全部的對象實例都在這裏分配內存。
堆同時也是垃圾收集器管理的主要區域,從內存回收的角度看,java堆能夠分爲「新生代」和「老年帶」。this

java堆能夠處於物理上不連續的內存空間中,只須要其是邏輯上連續的便可,如咱們的磁盤空間。當在堆中沒法申請到足夠的內存空間時,會拋出OutOfMemoryError。spa

在JDK1.6以前,字符串常量池一直放在方法區中,可是到了jdk1.7的時候,常量池便被移出方法區,而轉到Java堆中區了。線程

在HotSpot虛擬機裏實現的字符串常量池功能的是一個StringTable類,它是一個Hash表。這個哈希表在每一個HotSpot虛擬機的實例只有一份,被全部的類共享。字符串常量由一個一個字符組成,而且相同字符串只保留一份。

HotSpot虛擬機的說明以下:

Area: HotSpot 
Synopsis: In JDK 7, interned strings are no longer allocated in the permanent generation of the Java heap, but are instead allocated in the main part of the Java heap (known as the young and old generations), along with the other objects created by the application. This change will result in more data residing in the main Java heap, and less data in the permanent generation, and thus may require heap sizes to be adjusted. Most applications will see only relatively small differences in heap usage due to this change, but larger applications that load many classes or make heavy use of the String.intern() method will see more significant differences. 
RFE: 6962931 

大意即是說JDK1.7中的字符串不會再分配到Java的永久代中,而是分配到Java堆中。這意味着更多的數據將存於堆中而更少的數據存於方法區,這致使堆大小須要調整以作適配。因爲此更改,大多數應用程序只會看到堆使用中的相對較小的差別,較大型的應用可能會發現其顯著差別

4.方法區

與java堆同樣,方法區也是全部線程共享的。它主要的功能時存儲虛擬機加載的類信息,常量,靜態變量,編譯後的代碼數據等。能夠明顯發現,方法區存放的這些數據都是比較難以被回收的,因此這個區的垃圾回收行爲較少發生。

若在方法區中沒法申請到足夠的內存時,將會拋出OutOfMemoryError。

另外方法區中有一個運行時常量池。注意這裏不是字符串常量池,它存儲的是類編譯時期生成的各類字面量和符號引用,而且每一個類都有一個。

這裏介紹一下什麼是字面量和符號引用:

  • 字面量包括:1.文本字符串 2.八種基本類型的值 3.被聲明爲final的常量等;
  • 符號引用包括:1.類和方法的全限定名 2.字段的名稱和描述符 3.方法的名稱和描述符。

 

四.內存溢出和內存泄漏

內存溢出很好理解,就是發生OutOfMemoryError,好比當Java堆中新建了太多實例,耗完內存後就會發生內存溢出。好比以下實例代碼:

 public class HeapOOM{ static class OOMObject{} public static void main(String[] args){ List<OOMObject> list = new ArrayList<OOMObject>(); while(true){ list.add(new OOMObject()); } } }

因爲無限循環不斷新建對象,最終會致使內存溢出。

那麼內存泄漏呢?

內存泄漏的緣由主要是一個對象已經再也不須要使用,但被另外一個長對象持有時,就有可能發生內存泄漏。好比在方法內一個對象被全局的HashMap持有,方法執行結束沒有釋放就會致使內存泄漏。

再有就是當一個對象被存儲進HashSet後,其hashcode計算相關的變量被修改了,這也有可能致使內存泄漏,由於這時候這個對應基本已經不可達了。

相關文章
相關標籤/搜索