Java內存解析

public class TestSxtStu {
    int id;
    String sname;
    int age;
    computer comp;
    
    void study() {
        System.out.println("我在認真學習,使用電腦: " + comp.brand);
    }
    
    void play() {
        System.out.println("我在玩王者榮耀!");
    }
    
    TestSxtStu(){
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        TestSxtStu stu = new TestSxtStu();
        System.out.println(stu);
        stu.id = 1000;
        stu.sname = "Seconp";
        stu.age = 21;
        
        computer c1 = new computer();
        
        c1.brand = "聯想";
        
        stu.comp = c1;
        
        stu.play();
        stu.study();
    }

}

class computer{
    String brand;
}

上述代碼運行過程:

詳解棧幀:

棧幀由三部分組成:局部變量區、操作數棧、幀數據區。局部變量區和操作數棧的大小要視對應的方法而定,單位是字長。當調用一個方法時,它從類型信息中得到此方法局部變量區和操作數棧大小,並據此分配棧內存,然後壓入Java棧。

局部變量區 局部變量區被組織爲以一個字長爲單位、從0開始計數的數組,類型爲short、byte和char的值在存入數組前要被轉換成int值,而long和double在數組中佔據連續的兩項,在訪問局部變量中的long或double時,只需取出連續兩項的第一項的索引值即可,如某個long值在局部變量區中佔據的索引時3、4項,取值時,指令只需取索引爲3的long值即可。

 

public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) {  
        return 0;  
    }  
      
    public int runInstanceMethod(char c,double d,short s,boolean b) {  
        return 0;  
    } 

 

操作數棧 

和局部變量區一樣,操作數棧也被組織成一個以字長爲單位的數組。但和前者不同的是,它不是通過索引來訪問的,而是通過入棧和出棧來訪問的。可把操作數棧理解爲存儲計算時,臨時數據的存儲區域。下面我們通過一段簡短的程序片段外加一幅圖片來了解下操作數棧的作用。

int a = 1;
int b = 98;
int c = a+b;	

操作數棧其實就是個臨時數據存儲區域,它是通過入棧和出棧來進行操作的。

幀數據區   

除了局部變量區和操作數棧外,java棧幀還需要一些數據來支持常量池解析、正常方法返回以及異常派發機制。這些數據都保存在java棧幀的幀數據區中。

當JVM執行到需要常量池數據的指令時,它都會通過幀數據區中指向常量池的指針來訪問它。

 除了處理常量池解析外,幀裏的數據還要處理java方法的正常結束和異常終止。如果是通過return正常結束,則當前棧幀從Java棧中彈出,恢復發起調用的方法的棧。如果方法又返回值,JVM會把返回值壓入到發起調用方法的操作數棧。

爲了處理java方法中的異常情況,幀數據區還必須保存一個對此方法異常引用表的引用。當異常拋出時,JVM給catch塊中的代碼。如果沒發現,方法立即終止,然後JVM用幀區數據的信息恢復發起調用的方法的幀。然後再發起調用方法的上下文重新拋出同樣的異常。

棧的整個結構

在前面就描述過:棧是由棧幀組成,每當線程調用一個java方法時,JVM就會在該線程對應的棧中壓入一個幀,而幀是由局部變量區、操作數棧和幀數據區組成。那在一個代碼塊中,棧到底是什麼形式呢?下面是我從《深入JVM》中摘抄的一個例子,大家可以看看:

public class Main{    
	public static void addAndPrint(){      
    	double result = addTwoTypes(1,88.88);    
        System.out.println(result);    
    }   
         
    public static double addTwoTypes(int i,double d){  
    	return i + d;  
    }
}

 

注意:

    1. 只有在調用一個方法時,才爲當前棧分配一個幀,然後將該幀壓入棧
    2. 幀中存儲了對應方法的局部數據,方法執行完,對應的幀則從棧中彈出,並把返回結果存儲在調用 方法的幀的操作數棧中

Java中的基本數據類型一定存儲在棧中嗎?

不一定。棧內存用來存儲局部變量和方法調用。

如果該局部變量是基本數據類型例如

public void function(){
    int a = 1;
}

那麼直接將該值存儲在棧中。

如果該局部變量是一個對象如

public void function(){
    int[] array=new int[]{1,2};
}

那麼將引用存在棧中而對象({1,2})存儲在堆內。

棧的速度比堆快嗎?

Contrary to popular belief, there isn’t that much of a difference between stacks and heaps in a .NET process. Stacks and heaps are nothing more than ranges of addresses in virtual memory, and there is no inherent advantage in the range of addresses reserved to the stack of a particular thread compared to the range of addresses reserved for the managed heap. Accessing a memory location on the heap is neither faster nor slower than accessing a memory location on the stack. There are several considerations that might, in certain cases, support the claim that memory access to stack locations is faster, overall, than memory access to heap locations. Among them:

On the stack, temporal allocation locality (allocations made close together in time) implies spatial locality (storage that is close together in space). In turn, when temporal allocation locality implies temporal access locality (objects allocated together are accessed together), the sequential stack storage tends to perform better with respect to CPU caches and operating system paging systems.
Memory density on the stack tends to be higher than on the heap because of the reference type overhead (discussed later in this chapter). Higher memory density often leads to better performance, e.g., because more objects fit in the CPU cache.
Thread stacks tend to be fairly small – the default maximum stack size on Windows is 1MB, and most threads tend to actually use only a few stack pages. On modern systems, the stacks of all application threads can fit into the CPU cache, making typical stack object access extremely fast. (Entire heaps, on the other hand, rarely fit into CPU caches.)
With that said, you should not be moving all your allocations to the stack! Thread stacks on Windows are limited, and it is easy to exhaust the stack by applying injudicious recursion and large stack allocations.

參考資料:

Java堆和棧看這篇就夠

推薦閱讀:

Java虛擬機—棧幀、操作數棧和局部變量表​​​​​​​

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章