前言
這些算法,都是小編一點一點看的大佬們的方法,自己積累的.
如果有什麼描述的不對的地方還望大佬賜教
多交流才能進步,加油,沖沖沖!!!
直擊面試
反正我是帶着這些問題往下讀的
- 說一下 JVM 運作時資料區吧,都有哪些區?分别是幹什麼的?
- Java 8 的記憶體分代改進
- 舉例棧溢出的情況?
- 調整棧大小,就能儲存不出現溢出嗎?
- 配置設定的棧記憶體越大越好嗎?
- 垃圾回收是否會涉及到虛拟機棧?
- 方法中定義的局部變量是否線程安全?
運作時資料區
記憶體是非常重要的系統資源,是硬碟和 CPU 的中間倉庫及橋梁,承載着作業系統和應用程式的實時運作。JVM 記憶體布局規定了 Java 在運作過程中記憶體申請、配置設定、管理的政策,保證了 JVM 的高效穩定運作。不同的 JVM 對于記憶體的劃分方式和管理機制存在着部分差異。
下圖是 JVM 整體架構,中間部分就是 Java 虛拟機定義的各種運作時資料區域。

jvm-framework
Java 虛拟機定義了若幹種程式運作期間會使用到的運作時資料區,其中有一些會随着虛拟機啟動而建立,随着虛拟機退出而銷毀。另外一些則是與線程一一對應的,這些與線程一一對應的資料區域會随着線程開始和結束而建立和銷毀。
- 線程私有:程式計數器、棧、本地棧
- 線程共享:堆、堆外記憶體(永久代或元空間、代碼緩存)
下面我們就來一一解毒下這些記憶體區域,先從最簡單的入手
一、程式計數器
程式計數寄存器(Program Counter Register),Register 的命名源于 CPU 的寄存器,寄存器存儲指令相關的線程資訊,CPU 隻有把資料裝載到寄存器才能夠運作。
這裡,并非是廣義上所指的實體寄存器,叫程式計數器(或PC計數器或指令計數器)會更加貼切,并且也不容易引起一些不必要的誤會。JVM 中的 PC 寄存器是對實體 PC 寄存器的一種抽象模拟。
程式計數器是一塊較小的記憶體空間,可以看作是目前線程所執行的位元組碼的行号訓示器。
1.1 作用
PC 寄存器用來存儲指向下一條指令的位址,即将要執行的指令代碼。由執行引擎讀取下一條指令。
jvm-pc-counter
(分析:進入class檔案所在目錄,執行javap -v xx.class反解析(或者通過IDEA插件Jclasslib直接檢視,上圖),可以看到目前類對應的Code區(彙編指令)、本地變量表、異常表和代碼行偏移量映射表、常量池等資訊。)
1.2 概述
- 它是一塊很小的記憶體空間,幾乎可以忽略不計。也是運作速度最快的存儲區域
- 在 JVM 規範中,每個線程都有它自己的程式計數器,是線程私有的,生命周期與線程的生命周期一緻
- 任何時間一個線程都隻有一個方法在執行,也就是所謂的目前方法。如果目前線程正在執行的是 Java 方法,程式計數器記錄的是 JVM 位元組碼指令位址,如果是執行 natice 方法,則是未指定值(undefined)
- 它是程式控制流的訓示器,分支、循環、跳轉、異常處理、線程恢複等基礎功能都需要依賴這個計數器來完成
- 位元組碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令
- 它是唯一一個在 JVM 規範中沒有規定任何 OutOfMemoryError 情況的區域
:使用PC寄存器存儲位元組碼指令位址有什麼用呢?為什麼使用PC寄存器記錄目前線程的執行位址呢?
♂️:因為CPU需要不停的切換各個線程,這時候切換回來以後,就得知道接着從哪開始繼續執行。JVM的位元組碼解釋器就需要通過改變PC寄存器的值來明确下一條應該執行什麼樣的位元組碼指令。
:PC寄存器為什麼會被設定為線程私有的?
♂️:多線程在一個特定的時間段内隻會執行其中某一個線程方法,CPU會不停的做任務切換,這樣必然會導緻經常中斷或恢複。為了能夠準确的記錄各個線程正在執行的目前位元組碼指令位址,是以為每個線程都配置設定了一個PC寄存器,每個線程都獨立計算,不會互相影響。
二、虛拟機棧
2.1 概述
Java 虛拟機棧(Java Virtual Machine Stacks),早期也叫 Java 棧。每個線程在建立的時候都會建立一個虛拟機棧,其内部儲存一個個的棧幀(Stack Frame),對應着一次次 Java 方法調用,是線程私有的,生命周期和線程一緻。
作用:主管 Java 程式的運作,它儲存方法的局部變量、部分結果,并參與方法的調用和傳回。
特點:
- 棧是一種快速有效的配置設定存儲方式,通路速度僅次于程式計數器
- JVM 直接對虛拟機棧的操作隻有兩個:每個方法執行,伴随着入棧(進棧/壓棧),方法執行結束出棧
- 棧不存在垃圾回收問題
棧中可能出現的異常:
Java 虛拟機規範允許 Java虛拟機棧的大小是動态的或者是固定不變的
- 如果采用固定大小的 Java 虛拟機棧,那每個線程的 Java 虛拟機棧容量可以線上程建立的時候獨立標明。如果線程請求配置設定的棧容量超過 Java 虛拟機棧允許的最大容量,Java 虛拟機将會抛出一個 StackOverflowError 異常
- 如果 Java 虛拟機棧可以動态擴充,并且在嘗試擴充的時候無法申請到足夠的記憶體,或者在建立新的線程時沒有足夠的記憶體去建立對應的虛拟機棧,那 Java 虛拟機将會抛出一個OutOfMemoryError異常
可以通過參數-Xss來設定線程的最大棧空間,棧的大小直接決定了函數調用的最大可達深度。
2.2 棧的存儲機關
棧中存儲什麼?
- 每個線程都有自己的棧,棧中的資料都是以棧幀(Stack Frame)的格式存在
- 在這個線程上正在執行的每個方法都各自有對應的一個棧幀
- 棧幀是一個記憶體區塊,是一個資料集,維系着方法執行過程中的各種資料資訊
2.3 棧運作原理
- JVM 直接對 Java 棧的操作隻有兩個,對棧幀的壓棧和出棧,遵循“先進後出/後進先出”原則
- 在一條活動線程中,一個時間點上,隻會有一個活動的棧幀。即隻有目前正在執行的方法的棧幀(棧頂棧幀)是有效的,這個棧幀被稱為目前棧幀(Current Frame),與目前棧幀對應的方法就是目前方法(Current Method),定義這個方法的類就是目前類(Current Class)
- 執行引擎運作的所有位元組碼指令隻針對目前棧幀進行操作
- 如果在該方法中調用了其他方法,對應的新的棧幀會被建立出來,放在棧的頂端,稱為新的目前棧幀
- 不同線程中所包含的棧幀是不允許存在互相引用的,即不可能在一個棧幀中引用另外一個線程的棧幀
- 如果目前方法調用了其他方法,方法傳回之際,目前棧幀會傳回此方法的執行結果給前一個棧幀,接着,虛拟機會丢棄目前棧幀,使得前一個棧幀重新成為目前棧幀
- Java 方法有兩種傳回函數的方式,一種是正常的函數傳回,使用 return 指令,另一種是抛出異常,不管用哪種方式,都會導緻棧幀被彈出
最後
本人也收藏了一份Java面試核心知識點來應付面試,借着這次機會可以免費送給我的讀者朋友們:
目錄:
Java面試核心知識點
一共有30個專題,足夠讀者朋友們應付面試啦,也節省朋友們去到處搜刮資料自己整理的時間!
Java面試核心知識點
326)]
Java面試核心知識點
一共有30個專題,足夠讀者朋友們應付面試啦,也節省朋友們去到處搜刮資料自己整理的時間!
[外鍊圖檔轉存中…(img-SMmR2JZy-1618150462328)]
Java面試核心知識點
資料的領取方式:點贊後【戳面試資料】即可免費擷取哦!