Java棧與棧上分配
一. java棧:
java棧是一塊執行緒私有的記憶體空間。如果說,java堆和程式資料密切相關,那麼java棧就是和執行緒執行密切相關的。執行緒執行的基本行為是函式呼叫,每次函式呼叫的資料都是通過Java棧傳遞的。
java heap,java stack 與Javametaspace之間的關係:
特點:
- 執行緒私有
- 棧由一系列幀組成(因此Java棧也叫做幀棧)
- 幀儲存一個方法的區域性變數、運算元棧、常量池指標
- 每一次方法呼叫建立一個幀,並壓棧
1.棧的結構和組成:
1)棧的結構:
- 這是一塊先進後出的資料結構,只支援出棧和入棧兩種操作。在java棧中儲存的主要內容是棧幀。每一次函式呼叫都會有一個相應的棧幀入棧,每個函式呼叫結束,都有一個棧幀彈出java棧。當前正在執行的函式對應的棧就是當前的幀(位於棧頂)。
- 每個棧幀中,至少包含區域性變數表,運算元棧和幀資料區幾個部分。
- 注意由於每次函式呼叫都會生成棧幀並佔有一定的棧空間。因此如果棧空間不足,函式呼叫就無法進行下去。系統就會丟擲StackOverflowOver棧溢位的錯誤。例如遞迴時,會有很多棧幀入棧。jvm提供了-Xss來指定執行緒的最大棧空間,這個引數決定了函式呼叫的深度。
2)棧組成:
棧由棧幀組成,棧幀由區域性變數表,運算元棧,幀資料區組成。
區域性變數表:
用於儲存函式的引數(實參)變數和區域性變數。區域性變數表中的變數只在當前函式呼叫中有效,當函式呼叫結束後,隨著函式棧幀的銷燬,區域性變數表也會隨之銷燬。運算元棧:
棧幀的一部分,也是個先入先出的資料結構。用於計算過程的中間結果,同時作為計算過程中變數臨時的儲存空間。
public static int add(int a,int b){
int c=0;
c=a+b;
return c;
}
呼叫函式的過程:
0: iconst_0 // 0壓棧
1: istore_2 // 彈出int,存放於區域性變數2
2: iload_0 // 把區域性變數0壓棧
3: iload_1 // 區域性變數1壓棧
4: iadd //彈出2個變數,求和,結果壓棧
5: istore_2 //彈出結果,放於區域性變數2
6: iload_2 //區域性變數2壓棧
7: ireturn //返回
a,b變數的值分別是100和98,以下是運算元棧的工作原理以及和區域性變數表的關係:
- 幀資料區:
棧幀需要資料開支援常量池解析,正常方法返回和異常處理等
以下的例子是個遞迴,沒有遞迴的出口,會出現棧溢位,並列印遞迴的深度。
public class TestStackDeep {
private static int count=0;
public static void recursion(long a,long b,long c){
long e=1,f=2,g=3,h=4,i=5,k=6,q=7,x=8,y=9,z=10;
count++;
System.out.println(count);
recursion(a, b, c);
}
public static void recursion(){
count++;
System.out.println(count);
recursion();
}
public static void main(String[] args) {
try {
// recursion(0L,0L,0L);
recursion();
}catch (Exception e){
System.out.println("deep of calling="+count);
e.fillInStackTrace();
}
}
}
影響棧空間使用的因素:
1.闡述列表的引數多。
2.遞迴的深度過深了。
-Xss256k:
deep of calling=568
遞迴呼叫了568次-Xss512k:
deep of calling=3030
遞迴呼叫了568次Exception in thread "main" java.lang.StackOverflowError
棧溢位,棧的空間滿了。可以通過減少引數或區域性變數的個數,減少棧空間的佔用,達到函式多呼叫幾次的目的。呼叫recursion(a, b, c);-Xss256k:
最大深度716呼叫呼叫recursion(),-Xss256k:
最大深度1963可以看到在相同的棧容量下,區域性變數少的函式可以支援更深的函 數呼叫。
二.棧上分配:
棧上分配是jvm的一個優化技術,對於那些執行緒私有的物件,可以將它們分配在棧上,而不是堆上。棧上分配的好處是可以在函式呼叫後自行銷燬,而不是GC介入,從而提升了系統的效能。
棧上分配的基礎是逃逸分析,逃逸分析的目的是判斷物件的作用域是否有可能逃逸出函式體。
函式alloc()內的變數b是執行緒私有的區域性變數,
public class OnStackTest {
public static void alloc(){
byte[] b=new byte[2];
b[0]=1;
}
public static void main(String[] args) {
long b=System.currentTimeMillis();
for(int i=0;i<100000000;i++){
alloc();
}
long e=System.currentTimeMillis();
System.out.println(e-b);
}
}
第一種執行方式:-server -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC
這種方式new物件在棧上分配,gc不參與回收,因為變數僅僅在棧上分配空間,降低gc的工作量,同時防止堆上的空間被佔用
輸出結果 5 效率很高。第二種執行方式:-server -Xmx10m -Xms10m -XX:-DoEscapeAnalysis -XX:+PrintGC
這種方式new物件在java堆上分配,gc參與釋放
輸出結果:
……
[GC 3550K->478K(10240K), 0.0000977 secs]
[GC 3550K->478K(10240K), 0.0001361 secs]
[GC 3550K->478K(10240K), 0.0000963 secs]
564
GC的效率明顯低於棧上分配對棧幀的銷燬的效率。小物件(一般幾十個bytes),在沒有逃逸的情況下,可以直接分配在棧上
直接分配在棧上,可以自動回收,減輕GC壓力
大物件或者逃逸物件無法棧上分配
逃逸分析:
下面的程式碼顯示了一個逃逸的物件:因為程式碼中的User的作用域是整個Main Class,所以user物件是可以逃逸出函式體的。
public class PartionOnStack {
static class User{
private int id;
private String name;
public User(){}
}
private static User user;//在這裡逃逸
public static void foo() {
user=new User();
user.id=1;
user.name="sixtrees";
}
public static void main(String[] args) {
foo();
}
}
下面的程式碼展示的則是一個不能逃逸的程式碼段。(不能逃逸的才能棧上分配)
public class PartionOnStack {
class User{
private int id;
private String name;
public User(){}
}
public void foo() {
User user=new User();
user.id=1;
user.name="sixtrees";
}
public static void main(String[] args) {
PartionOnStack pos=new PartionOnStack();
pos.foo();
}
}
總結:
*小物件(一般幾十個bytes),在沒有逃逸的情況下,可以直接分配在棧上
*直接分配在棧上,可以自動回收,減輕GC壓力
*大物件或者逃逸物件無法棧上分配
相關文章
- JVM的棧上分配JVM
- Java是否可以棧上分配物件記憶體? 為什麼?Java物件記憶體
- java虛擬機器 jvm 出入java棧 棧空間記憶體分配Java虛擬機JVM記憶體
- java 棧與佇列Java佇列
- AArch64函式棧的分配,指令生成與GCC實現(上)函式GC
- 關於Java棧與堆的思考Java
- .NET面試題系列(24)值型別總是分配在棧上嗎?面試題型別
- Java之Stack --- 棧Java
- java中的棧Java
- 【LWJGL3】LWJGL3的記憶體分配設計,第一篇,棧上分配記憶體
- 順序棧與鏈式棧的圖解與實現圖解
- Tarjan中棧的分析與SLT棧的實現
- java:模擬棧操作Java
- java集合類——Stack棧類與Queue佇列Java佇列
- java全棧工程師:從java後端到全棧,高階電商全棧系統大課Java全棧工程師後端
- 順序棧————遍歷、出棧、入棧
- 全棧化與效率全棧
- JAVA的堆和棧(轉)Java
- 記憶體分配策略中,堆和棧的區別記憶體
- 值棧中root棧和context棧詳解Context
- 軟體測試是走Python棧還是Java棧好?PythonJava
- 棧(2)--棧的應用
- 棧與佇列簡介佇列
- 棧的原理與應用
- The Stack and the Heap棧與堆__RustRust
- 6.13-棧與佇列佇列
- 資料結構:棧的基本概念、順序棧、共享棧以及鏈棧資料結構
- 資料結構(筆試題-棧(入棧出棧)資料結構筆試
- 順序棧、鏈棧基本操作
- Java中棧和堆講解Java
- Java版-資料結構-棧Java資料結構
- JAVA堆區棧區方法區Java
- java堆和棧的區別Java
- 走在JS上的全棧之路(一)JS全棧
- 資料結構與演算法——棧(一)【棧的快速入門】資料結構演算法
- 演算法與資料結構-棧(Stack)-Java實現演算法資料結構Java
- 惡補基礎知識:Java 棧與佇列詳解Java佇列
- 程式的記憶體分配:棧區(stack)堆區(heap)。。。(轉載)記憶體