java基礎教程第三版耿祥義,后臺開發JAVA崗

Java虛擬機內存模型

Java虛擬機內存模型中定義的訪問操作與物理計算機處理的基本一致!

Java中通過多線程機制使得多個任務同時執行處理,所有的線程共享JVM內存區域main memory,而每個線程又單獨的有自己的工作內存,當線程與內存區域進行交互時,數據從主存拷貝到工作內存,進而交由線程處理(操作碼+操作數)。更多信息我們會在后面的《深入JVM—JVM類執行機制中詳細解說》。

在之前,我們也已經提到,JVM的邏輯內存模型如下:

我們現在來逐個的看下每個到底是做什么的!

1、程序計數器

程序計數器(Program Counter Register)是一塊較小的內存空間,它的作用可以看做是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型里(僅是概念模型,各種虛擬機可能會通過一些更高效的方式去實現),字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。

由于Java 虛擬機的多線程是通過線程輪流切換并分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(對于多核處理器來說是一個內核)只會執行一條線程中的指令。因此,為了線程切換后能恢復到正確的執行位置,每條線程都需要有一個獨立的程序計數器,各條線程之間的計數器互不影響,獨立存儲,我們稱這類內存區域為“線程私有”的內存。

如果線程正在執行的是一個Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是Natvie 方法,這個計數器值則為空(Undefined)。此內存區域是唯一一個在Java 虛擬機規范中沒有規定任何OutOfMemoryError 情況的區域。

2、Java 虛擬機棧

與程序計數器一樣,Java 虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機棧描述的是Java 方法執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀(Stack Frame ①)用于存儲局部變量表、操作棧、動態鏈接、方法出口等信息。每一個方法被調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。

經常有人把Java 內存區分為堆內存(Heap)和棧內存(Stack),這種分法比較粗糙,Java 內存區域的劃分實際上遠比這復雜。這種劃分方式的流行只能說明大多數程序員最關注的、與對象內存分配關系最密切的內存區域是這兩塊。其中所指的“堆”在后面會專門講述,而所指的“棧”就是現在講的虛擬機棧,或者說是虛擬機棧中的局部變量表部分。

局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference 類型,它不等同于對象本身,根據不同的虛擬機實現,它可能是一個指向對象起始地址的引用指針,也可能指向一個代表對象的句柄或者其他與此對象相關的位置)和returnAddress 類型(指向了一條字節碼指令的地址)。

其中64 位長度的long 和double 類型的數據會占用2 個局部變量空間(Slot),其余的數據類型只占用1 個。局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。

在Java 虛擬機規范中,對這個區域規定了兩種異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError 異常;如果虛擬機棧可以動態擴展(當前大部分的Java 虛擬機都可動態擴展,只不過Java 虛擬機規范中也允許固定長度的虛擬機棧),當擴展時無法申請到足夠的內存時會拋出OutOfMemoryError 異常。

3、本地方法棧

本地方法棧(Native Method Stacks)與虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧為虛擬機執行Java 方法(也就是字節碼)服務,而本地方法棧則是為虛擬機使用到的Native 方法服務。虛擬機規范中對本地方法棧中的方法使用的語言、使用方式與數據結構并沒有強制規定,因此具體的虛擬機可以自由實現它。甚至有的虛擬機(譬如Sun HotSpot 虛擬機)直接就把本地方法棧和虛擬機棧合二為一。

與虛擬機棧一樣,本地方法棧區域也會拋出StackOverflowError 和OutOfMemoryError異常。

4、Java 堆

對于大多數應用來說,Java 堆(Java Heap)是Java 虛擬機所管理的內存中最大的一塊。Java 堆是被所有線程共享的一塊內存區域,在虛擬機啟動時創建。此內存區域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內存。這一點在Java 虛擬機規范中的描述是:所有的對象實例以及數組都要在堆上分配①,但是隨著JIT 編譯器的發展與逃逸分析技術的逐漸成熟,棧上分配、標量替換②優化技術將會導致一些微妙的變化發生,所有的對象都分配在堆上也漸漸變得不是那么“絕對”了。

Java 堆是垃圾收集器管理的主要區域,因此很多時候也被稱做“GC 堆”(GarbageCollected Heap,幸好國內沒翻譯成“垃圾堆”)。如果從內存回收的角度看,由于現在收集器基本都是采用的分代收集算法,所以Java 堆中還可以細分為:新生代和老年代;再細致一點的有Eden 空間、From Survivor 空間、To Survivor 空間等。如果從內存分配的角度看,線程共享的Java 堆中可能劃分出多個線程私有的分配緩沖區(Thread LocalAllocation Buffer,TLAB)。不過,無論如何劃分,都與存放內容無關,無論哪個區域,存儲的都仍然是對象實例,進一步劃分的目的是為了更好地回收內存,或者更快地分配內存。在本章中,我們僅僅針對內存區域的作用進行討論,Java 堆中的上述各個區域的分配和回收等細節將會是下一章的主題。

根據Java 虛擬機規范的規定,Java 堆可以處于物理上不連續的內存空間中,只要邏輯上是連續的即可,就像我們的磁盤空間一樣。在實現時,既可以實現成固定大小的,也可以是可擴展的,不過當前主流的虛擬機都是按照可擴展來實現的(通過-Xmx和-Xms 控制)。如果在堆中沒有內存完成實例分配,并且堆也無法再擴展時,將會拋出OutOfMemoryError 異常。

4、方法區

方法區(Method Area)與Java 堆一樣,是各個線程共享的內存區域,它用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。雖然Java 虛擬機規范把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆),目的應該是與Java 堆區分開來。

對于習慣在HotSpot 虛擬機上開發和部署程序的開發者來說,很多人愿意把方法區稱為“永久代”(Permanent Generation),本質上兩者并不等價,僅僅是因為HotSpot 虛擬機的設計團隊選擇把GC 分代收集擴展至方法區,或者說使用永久代來實現方法區而已。對于其他虛擬機(如BEA JRockit、IBM J9 等)來說是不存在永久代的概念的。即使是HotSpot 虛擬機本身,根據官方發布的路線圖信息,現在也有放棄永久代并“搬家”至Native Memory 來實現方法區的規劃了。

Java 虛擬機規范對這個區域的限制非常寬松,除了和Java 堆一樣不需要連續的內存和可以選擇固定大小或者可擴展外,還可以選擇不實現垃圾收集。相對而言,垃圾收集行為在這個區域是比較少出現的,但并非數據進入了方法區就如永久代的名字一樣“永久”存在了。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載,一般來說這個區域的回收“成績”比較難以令人滿意,尤其是類型的卸載,條件相當苛刻,但是這部分區域的回收確實是有必要的。在Sun 公司的BUG 列表中,曾出現過的若干個嚴重的BUG 就是由于低版本的HotSpot 虛擬機對此區域未完全回收而導致內存泄漏。

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

5、運行時常量池

運行時常量池(Runtime Constant Pool)是方法區的一部分。Class 文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量池(Constant PoolTable),用于存放編譯期生成的各種字面量和符號引用,這部分內容將在類加載后存放到方法區的運行時常量池中。

Java 虛擬機對Class 文件的每一部分(自然也包括常量池)的格式都有嚴格的規定,每一個字節用于存儲哪種數據都必須符合規范上的要求,這樣才會被虛擬機認可、裝載和執行。但對于運行時常量池,Java 虛擬機規范沒有做任何細節的要求,不同的提供商實現的虛擬機可以按照自己的需要來實現這個內存區域。不過,一般來說,除了保存Class 文件中描述的符號引用外,還會把翻譯出來的直接引用也存儲在運行時常量池中①。

運行時常量池相對于Class 文件常量池的另外一個重要特征是具備動態性,Java 語言并不要求常量一定只能在編譯期產生,也就是并非預置入Class 文件中常量池的內容才能進入方法區運行時常量池,運行期間也可能將新的常量放入池中,這種特性被開發人員利用得比較多的便是String 類的intern() 方法。

既然運行時常量池是方法區的一部分,自然會受到方法區內存的限制,當常量池無法再申請到內存時會拋出OutOfMemoryError 異常

6、直接內存

直接內存(Direct Memory)并不是虛擬機運行時數據區的一部分,也不是Java虛擬機規范中定義的內存區域,但是這部分內存也被頻繁地使用,而且也可能導致OutOfMemoryError 異常出現,所以我們放到這里一起講解。

在JDK 1.4 中新加入了NIO(New Input/Output)類,引入了一種基于通道(Channel)與緩沖區(Buffer)的I/O 方式,它可以使用Native 函數庫直接分配堆外內存,然后通過一個存儲在Java 堆里面的DirectByteBuffer 對象作為這塊內存的引用進行操作。這樣能在一些場景中顯著提高性能,因為避免了在Java 堆和Native 堆中來回復制數據。

顯然,本機直接內存的分配不會受到Java 堆大小的限制,但是,既然是內存,則肯定還是會受到本機總內存(包括RAM 及SWAP 區或者分頁文件)的大小及處理器尋址空間的限制。服務器管理員配置虛擬機參數時,一般會根據實際內存設置-Xmx等參數信息,但經常會忽略掉直接內存,使得各個內存區域的總和大于物理內存限制(包括物理上的和操作系統級的限制),從而導致動態擴展時出現OutOfMemoryError異常

邏輯內存模型我們已經看到了,那當我們建立一個對象的時候是怎么進行訪問的呢?在Java 語言中,對象訪問是如何進行的?對象訪問在Java 語言中無處不在,是最普通的程序行為,但即使是最簡單的訪問,也會卻涉及Java 棧、Java 堆、方法區這三個最重要內存區域之間的關聯關系,如下面的這句代碼:

Object obj = new Object();

假設這句代碼出現在方法體中,那“Object obj”這部分的語義將會反映到Java 棧的本地變量表中,作為一個reference 類型數據出現。而“new Object()”這部分的語義將會反映到Java 堆中,形成一塊存儲了Object 類型所有實例數據值(Instance Data,對象中各個實例字段的數據)的結構化內存,根據具體類型以及虛擬機實現的對象內存布局(Object Memory Layout)的不同,這塊內存的長度是不固定的。另外,在Java 堆中還必須包含能查找到此對象類型數據(如對象類型、父類、實現的接口、方法等)的地址信息,這些類型數據則存儲在方法區中。

由于reference 類型在Java 虛擬機規范里面只規定了一個指向對象的引用,并沒有定義這個引用應該通過哪種方式去定位,以及訪問到Java 堆中的對象的具體位置,因此不同虛擬機實現的對象訪問方式會有所不同,主流的訪問方式有兩種:使用句柄和直接指針。

句柄池

如果使用句柄訪問方式,Java 堆中將會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據和類型數據各自的具體地址信息,如下圖所示。

如果使用直接指針訪問方式,Java 堆對象的布局中就必須考慮如何放置訪問類型數據的相關信息,reference 中直接存儲的就是對象地址,如下圖所示

這兩種對象的訪問方式各有優勢,使用句柄訪問方式的最大好處就是reference 中存儲的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數據指針,而reference 本身不需要被修改。

使用直接指針訪問方式的最大好處就是速度更快,它節省了一次指針定位的時間開銷,由于對象的訪問在Java 中非常頻繁,因此這類開銷積少成多后也是一項非常可觀的執行成本。就本書討論的主要虛擬機Sun HotSpot 而言,它是使用第二種方式進行對象訪問的,但從整個軟件開發的范圍來看,各種語言和框架使用句柄來訪問的情況也十分常見。

下面我們來看幾個示例

1、Java 堆溢出

下面的程中我們限制Java 堆的大小為20MB,不可擴展(將堆的最小值-Xms 參數與最大值-Xmx 參數設置為一樣即可避免堆自動擴展),通過參數-XX:+HeapDumpOnOutOfMemoryError 可以讓虛擬機在出現內存溢出異常時Dump 出當前的內存堆轉儲快照以便事后進行分析。

參數設置如下

最后

金三銀四馬上就到了,希望大家能好好學習一下這些技術點,需要領取這些學習資料和面試筆記的朋友請**趕緊點擊這里免費獲取!**

學習視頻:

大廠面試真題:

料和面試筆記的朋友請**趕緊點擊這里免費獲取!**

學習視頻:

[外鏈圖片轉存中…(img-Ix5wi90h-1626019499188)]

大廠面試真題:

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385649.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385649.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385649.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

501. 二叉搜索樹中的眾數

給定一個有相同值的二叉搜索樹(BST),找出 BST 中的所有眾數(出現頻率最高的元素)。 假定 BST 有如下定義: 結點左子樹中所含結點的值小于等于當前結點的值結點右子樹中所含結點的值大于等于當前結點的值左…

java基礎案例教程黑馬程序員案例答案,真香

掌握核心知識 1、90%幾率面試被問,吃透原理,面試不慌(Spring原理) 2、大廠必問Redis,趕緊碼起來(Redis核心原理) 3、MySQL從入門到實戰都在這篇,面試笑談優化 當然核心知識不止這三…

java基礎的三個框架,進階學習資料!

阿里巴巴一面 自我介紹這個就不說了,開頭必問的說一下StringBuilder 和 StringBufferSpring bean加載,實例化的過程Spring AOP源碼看過嗎java內存模型說一下如果給你一個map,里面有很多很多對象,那么這個map存放在哪了解GC算法嗎…

實驗3 | 由遍歷序列構造二叉樹

二叉樹構造定理: 定理7.1:任何n(n>0)個不同結點的二又樹,都可由它的中序序列和先序序列唯一地確定。 定理7.2:任何n(n>0)個不同結點的二又樹,都可由它的…

萬字總結!java讓字符串反轉

Java基礎 JDK 和 JRE 有什么區別? 和 equals 的區別是什么?兩個對象的 hashCode()相同,則 equals()也一定為 true,對嗎?final 在 java 中有什么作用?java 中的 Math.round(-1.5) 等于多少?Stri…

String | 344. Reverse String

題目&#xff1a;反轉字符串 方法1&#xff1a; class Solution { public:string reverseString(string s) {int m (s.size() -1) / 2;for(int i 0; i < m; i){char c s[i];s[i] s[s.size() -1 - i];s[s.size() -1 - i] c;}return s; } }; 方法2&#xff1a;…

萬字總結!springcloud分布式限流

正文 作為后端開發&#xff0c;日常操作數據庫最常用的是寫操作和讀操作。讀操作我們下邊會講&#xff0c;這個分類里我們主要來看看寫操作時為什么會導致 SQL 變慢。 刷臟頁 臟頁的定義是這樣的&#xff1a;內存數據頁和磁盤數據頁不一致時&#xff0c;那么稱這個內存數據頁…

String | 263. Ugly Number

題目&#xff1a;丑數 方法1 class Solution { public:bool isUgly ( int num ) {if (num 0)return false;while (num % 5 0)num / 5;while (num % 3 0)num / 3;while (num % 2 0)num / 2;return num 1;} };

萬字長文!java讀取json文件數據給對象

Java基礎核心筆記總結 由于篇幅限制&#xff0c;我就只以截圖展示目錄內容以及部分筆記內容&#xff0c;獲取完整版王者級核心寶典只需要點擊點贊關注即可獲取領取方式&#xff01; 在這個部分我們總結了Java的基礎知識&#xff0c;涵蓋了&#xff1a;概述、開發環境、開發環境…

三年Java開發,java基礎常問面試題

一、首先本職工作一定要做好做精 本人之前在干兼職的時候&#xff0c;也忽視過本職工作&#xff0c;從而導致自己落后平均技術水平&#xff0c;雖然之后迎頭趕上&#xff0c;但這不能不算是個遺憾。前在接一些活的時候就感覺技術的重要性了&#xff0c;如果當年我技術再好些&a…

Array | 867. Transpose Matrix

題目&#xff1a;轉置矩陣 方法1&#xff1a; class Solution { public:vector<vector<int>> transpose(vector<vector<int>>& A) {vector<vector<int>> num(A[0].size(), vector<int>(A.size(), 0));for(int i 0; i < A.…

三年Java開發,尚學堂java馬士兵全套

基于 Servlet 容器的 Web MVC 身為 Java 開發者&#xff0c;對于 Spring 框架并不陌生。它起源于 2002 年、Rod Johnson 著作《Expert One-on-One J2EE Design and Development》中的 Interface 21 框架&#xff0c;到了 2004 年&#xff0c;推出 Spring 1.0&#xff0c;從 XM…

Array | 74. Search a 2D Matrix

題目&#xff1a;搜索二維矩陣 方法1&#xff1a; class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {if(!matrix.size())return false;if(!matrix[0].size())return false;for(int i 0; i < matrix[matrix.size() …

三年經驗java工資,含愛奇藝,小米,騰訊,阿里

1、PTP模型 Point-to-Point&#xff0c;點對點通信模型。PTP是基于隊列(Queue)的&#xff0c;一個隊列可以有多個生產者&#xff0c;和多個消費者。消息服務器按照收到消息的先后順序&#xff0c;將消息放到隊列中。隊列中的每一條消息&#xff0c;只能由一個消費者進行消費&a…

三面美團Java崗,java多線程匿名內部類

Part 1微服務架構設計概述 1.1 傳統應用架構的問題 1.2 微服務架構是什么 1.3 微服務架構有哪些特點和挑戰 1.4 如何搭建微服務架構 Part 2微服務開發框架 2.1 Spring Boot 是什么 2.2 如何使用Spring Boot框架 2.3 Spring Boot生產級特性 Part 3微服務網關 3.1 Node.js 是什…

函數signal

1. 函數signal #include <signal.h> void (*signal(int sig,void (*func)(int)))(int)typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 分析&#xff1a; signal參數信號名&#xff0c;func的值是SIG_ING、SIG_DFL或接到…

三面美團Java崗,java架構師線下培訓

性能優化專欄 1.Tomcat性能優化整理 2.JVM性能優化專題 3.Mysql性能優化整理 微服務架構面試專欄 1.SpringCloud面試整理 2.SpringBoot面試整理 3.Dubbo面試整理 并發編程高級面試專欄 開源框架面試題專欄 1.Spring面試整理 2.SpringMVC面試整理 3.MyBatis面試整理 分布式面…

信號 09 | SIGCLD語義

1. SIGCLD信號 SIG_DFL &#xff1a;默認的處理方式是不理會這個信號&#xff0c;但是也不會丟棄子進程狀態&#xff0c;所以如果不用wait&#xff0c;waitpid對其子進行進行狀態信息回收&#xff0c;會產生僵尸進程。SIG_IGN &#xff1a;忽略的處理方式&#xff0c;這個方式…

三面美團Java崗,java電子書下載百度云

Spring 全家桶&#xff1a; Spring 原理Spring面試題思維導圖面試題Spring視頻 Spring 原理 Spring特點Spring 核心組件Spring常用模塊Spring主要包Spring常用注解Sping第三方結合Spring 10C原理Spring APO原理Spring MVC原理Spring Boot原理JPA原理Mybatis緩存Tomcat架構 Sp…

【信號】函數kill、raise、abort、alarm

一、函數kill 1. kill函數原型&#xff1a; int kill(pid_t pid, int signo); //signo:信號名分析&#xff1a; pid > 0&#xff1a; 發送信號給指定的進程pid 0&#xff1a; 發送信號給調用kill函數進程屬于同一個進程組的所有進程pid < 0&#xff1a;信號signo將…