Java程序員把內存的控制權交給了Java虛擬機。在Java虛擬機內存管理機制的幫助下,程序員再也不須要爲每個new操做寫對應的delete/free代碼,並且不容易出現內存泄露和溢出。
虛擬機在執行Java程序過程當中,會把它所管理的內存劃分爲若干個不一樣的數據區域,各個區域有各自的用途,以及建立和銷燬時間。
《
Java虛擬機規範(第二版)》規定了Java虛擬機所管理的內存將包括如下幾個運行時數據區域:
線程共享級別:方法區(Method Area)、堆(Heap)
線程私有級別:虛擬機棧(VM Stack)、本地方法棧(Native Method Stack)、程序計數器(Program Counter Register)
深刻學習內存運行時數據區域:
程序計數器:
程序計數器是一塊較小的內存空間,它的做用能夠看作是當前線程所執行的字節碼的行號指示器。
Java虛擬機的多線程是經過線程輪流切換並分配處理器執行時間的方式來實現的,在任何是一個肯定的時刻,一個處理器(內核)只會執行一條線程中的指令。爲了線程切換後能恢復到正確的執行位置,每條線程都須要有一個獨立的程序計數器。
若是線程正在執行Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令地址;
若是線程正在執行Native方法,這個計數器值爲空(Undefined);
程序計數器區域是惟一沒有OutOfMemoryError的區域。
虛擬機棧:
虛擬機棧描述Java方法執行的內存模型:每一個方法被執行的時候都會建立一個棧幀(Stack Frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息。
局部變量表存放了編譯期可知的各類基本數據類型、對象引用和returnAddress類型。
虛擬機棧存在兩種異常:
StackOverflowError---線程請求的棧深度大於虛擬機所容許的深度。
OutOfMemoryError---若是虛擬機棧能夠動態擴展,當擴展時沒法申請到足夠的內存。
本地方法棧:
與虛擬機棧發回的做用很是類似,虛擬機棧爲虛擬機執行Java方法服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。
堆:
堆是虛擬機所管理的內存中最大的一塊,在虛擬機啓動時建立,用於存放對象實例。幾乎全部的對象實例都在這裏分配內存。
堆是垃圾收集器管理的主要區域。
若是堆中沒有內存完成實例分配,而且也沒法再擴展時,將會拋出OutOfMemoryError異常。
方法區:
用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
運行時常量池(Runtime Constant Pool):
Class文件中除了又類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放比哪一期生成的各類字面量和符號引用,這部份內容將在類加載後存放到方法區的運行時常量池。
Java虛擬機規範對Class文件的格式有嚴格規定,可是對運行時常量池沒有作任何細節要求。
Class文件的常量池不具備動態性,運行時常量池具備動態性(如:String類的intern()方法)。
當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError異常。
直接內存:
直接內存(Direct Memory)不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域,可是這部份內存也被頻繁的使用,也可能致使OutOfMemoryError異常。(Java NIO)
對象訪問機制:
句柄訪問方式:
Java堆中劃分出一塊內存來做爲句柄池,reference中存儲的就是對象的句柄,而句柄中包含了對象實例數據和類型數據各自的具體地址信息。
直接指針訪問方式:
reference中存儲的就是Java堆中的對象地址,可是對象的佈局中必須考慮如何放置類型數據的相關信息。
句柄訪問方式的最大
好處就是reference中存儲的是穩定的句柄地址,在對象唄移動時只會改變句柄中的實例數據指針,而reference自己不須要被修改。
直接指針訪問方式的最大好處就是速度更快,它節省了一次指針定位的時間開銷,因爲對象的訪問在Java中很是頻繁,所以這類開銷聚沙成塔後也是一項很是可觀的執行成本。