Java內存劃分:運行時數據區域
1. 程序計數器(線程私有)
程序計數器是一塊比較小的內存空間,可以看做是當前線程所執行的字節碼的行號指示器(切換線程後,能恢復到正確的執行位置).
2. Java虛擬機棧(線程私有)
(1) 概念
虛擬機棧描述的是Java方法執行的內存模型 : 每個方法執行的同時都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口 等信息。每一個方法從調用直至執行完成的過程,就對應一個棧幀在虛擬機棧中入棧和出棧的過程。聲明周期與線程相同。
關於棧幀的理解: 創建啟動一個線程,就創建瞭一個虛擬機棧空間(存放多個節點,先進後出).可能出現兩種異常:
①StackOverflowError: 方法調用鏈太深(如遞歸);
②OOM
(2) 下面我們來分析一段代碼
public class VMStackTest { public static void main(String[] args) { int m = 1; int n = 2; swap1(1, 2); System.out.printf("main:m=%s, n=%s%n", m, n); } private static void swap1(int m, int n) { int tmp = m; m = n; n = tmp; Person p1 = new Person("p1"); Person p2 = new Person("p2"); swap2(p1, p2); System.out.printf("swap1:p1=%s, p2=%s%n", p1.name, p2.name); } private static void swap2(Person p1, Person p2) { Person tmp = p1; p1 = p2; p2 = tmp; System.out.printf("swap2:p1=%s, p2=%s%n", p1.name, p2.name); } static class Person{ String name; public Person(String name) { this.name = name; } } }
這段代碼的執行結果是什麼?
我們知道,Java中隻有值傳遞,當為基本數據類型時,傳的是字面值常量,當為引用類型,傳的是地址.
swap1中,隻是修改瞭方法棧幀中的局部變量,對外面的局部變量不起作用,所以swap1中的交換是無效的.
swap2中也一樣,隻是修改瞭方法棧幀中的局部變量,對外面的局部變量交換也是無效的.
所以最終的打印結果為:
如果要把p1,p2對象的名稱調換,p1.name=”p2″, p2.name=”p1″(作用在堆裡邊的對象)
3. 本地方法棧(線程私有)
本地方法棧與虛擬機棧的作用完全一樣,他倆的區別無非是本地方法棧為虛擬機使用的Native方法服務,而虛擬機棧為JVM執行的Java方法服務。
4. Java堆(線程共享)
Java堆(Java Heap)是JVM所管理的最大內存區域。Java堆是所有線程共享的一塊區域,在JVM啟動時創建。此內存區域存放的都是對象實例。JVM規范中說到:”所有的對象實例以及數組都要在堆上分配”。
5. 方法區(線程共享)
方法區與Java堆一樣,是各個線程共享的內存區域。它用於存儲已被虛擬機加載的類信息、常量、靜態變量(jdk1.7在方法區中;1.8在堆中)、即時編譯器編譯後的代碼等數據。
6. 運行時常量池(方法區的一部分)
(1) 概念
運行時常量池是方法區的一部分,存放字面量與符號引用。 字面量 : 字符串(JDK1.7後移動到堆中) 、final常量、基本數據類型的值。 符號引用 : 類和結構的完全限定名、字段的名稱和描述符、方法的名稱和描述符。
(2) 補充: 其他常量池
- ① class文件常量池: Java文件編譯為class字節碼文件,存在的常量池,包含字面量和符號引用.
- ② Java進程運行後,即使沒有執行到某行代碼,也已經把class文件常量池中的內容放在運行時常量池,如下圖:
- ③ 字符串常量池: 1.7之前是在運行時常量池裡面,1.7之後,是在堆裡面.
總結
本篇文章就到這裡瞭,希望能夠給你一些幫助,也希望你能夠多多關註WalkonNet的更多內容!