1. 程式人生 > >【Java】記憶體分析

【Java】記憶體分析

程式與記憶體

  程式要想執行,一般需要以下三個步驟,如圖1-1所示:

1、將程式載入到記憶體區。
2、作業系統程式碼找到程式的main方法開始執行。
3、執行過程中進行記憶體管理。

這裡寫圖片描述
圖1-1 程式執行過程

一個程式的記憶體分析

  比如我們寫了一個求空間中兩點之間距離的程式。很簡單,只要一個點物件就可以完成了。程式碼如下:

class Point {
    double x, y, z;

    Point(double _x, double _y, double _z) {
        x = _x;
        y = _y;
        z = _z;
    }

    void setX(double
_x)
{ x = _x; } double getDistance(Point p) { return (x - p.x)*(x - p.x) + (y - p.y)*(y - p.y) + (z - p.z)*(z - p.z); } }

  同樣,也需要一個類來使用Point類,我們將這個使用過程寫在了main函式中,程式碼如下:

public class TestPoint {
    public static void main(String[] args) {
        Point p = new Point(1.0
, 2.0, 3.0); Point p1 = new Point(0.0, 0.0, 0.0); //計算點p到原點的距離 System.out.println(p.getDistance(p1)); //設定點的x座標為5 p.setX(5.0); //計算點p到點(1.0,1.0,1.0)的距離 System.out.println(p.getDistance(new Point(1.0, 1.0, 1.0))); } }

  我們開始看第一句程式碼:Point p = new Point(1.0, 2.0, 3.0);

,分析其執行過程和相應的記憶體變化。它首先可以分成兩個部分:①呼叫構造方法生成Point物件;②宣告Point物件併為其賦值。new Point(1.0, 2.0, 3.0)的過程其實就是在堆記憶體中新建了一個Point物件,裡面有三個成員變數(x = 1.0 ,y = 2.0 ,z = 3.0)。然後再執行宣告和賦值,Point p即Point物件的宣告過程,在記憶體的棧空間中分配一塊兒記憶體,通過名字p呼叫,為其賦值的過程即:將堆中new出來的物件的引用儲存到這塊棧空間中。記憶體狀態如圖2-1所示:

這裡寫圖片描述
圖2-1 第一句程式碼執行完成後記憶體圖

  第二句程式碼的分析過程同一類似,不再贅述,執行完成後記憶體空間狀態如圖2-2所示:
這裡寫圖片描述
圖2-2 第二句程式碼執行完成後記憶體圖

  第三句程式碼:System.out.println(p.getDistance(p1)),其中println是字串的列印輸出方法,而括號中是點物件方法的呼叫,它們的過程是一樣的,那我們直接分析括號中的方法呼叫過程,即:p.getDistance(p1)。
  
  我們可以看到getDistance(Point p)方法中有一個引數Point p,p也叫區域性變數。當我們呼叫這個方法的時候,在棧記憶體中會分配一直臨時變數p,指向p1的引用物件。計算完成後,會有一個返回值,記憶體中的棧空間會分配一塊兒空間來儲存這個返回值。這時方法呼叫完畢,為其區域性變數分配的記憶體空間收回,即區域性變數p消失。當System.out.println()執行完畢,將返回值列印在介面上,為返回值分配的記憶體消失。所以第三句程式碼執行完成之後,記憶體空間無變化。(它曾經來過,它現在沒了)
  
  第四句程式碼:p.setX(5.0);,呼叫setX()方法將p引用的物件的x值修改為5.0,此時記憶體空間狀態如圖2-3所示:
這裡寫圖片描述
圖2-3 第三句程式碼執行完成後記憶體圖

  最後一句程式碼:System.out.println(p.getDistance(new Point(1.0, 1.0, 1.0)));。遇到稍複雜的表示式,我們應從裡向外分析。new Point(1.0, 1.0, 1.0)也就是在堆中新建一個點物件,裡面有三個成員變數(x=1.0 , y=1.0 ,z=1.0)。然後呼叫物件p的getDistance()方法,新建一個臨時變數p(引數),指向剛new出來的這物件,計算結果,返回並列印計算結果,方法執行完畢,為區域性變數p和返回值分配的記憶體空間消失。堆中的點物件(1.0,1.0,1.0)沒有引用指向它,等待垃圾收集器回收。此時記憶體空間狀態如圖2-4所示:
這裡寫圖片描述
圖2-4 最後一句程式碼執行完成後記憶體圖

  main方法執行完成後,區域性變數p和p1也就消失了,其引用的物件等待垃圾收集器回收。

總結

  1. 基本資料型別只在棧中分配一塊空間。
  2. 引用型別在棧中和堆中各有一塊記憶體空間。
  3. 方法執行完畢,為其分配的區域性變數消失,棧中的返回值如果沒有用到也會默默的消失。
  4. 垃圾收集器判斷一個物件是否需要回收的標準是:有沒有引用指向它。如果沒有,則回收。
  5. 字串常量在DataSegment和棧中分配記憶體空間,相同字串常量的引用相同(java優化機制)。
  6. 字串變數在堆疊中分配記憶體,相同字串的引用不同,並重寫了equals()方法,以方便比較字串內容是否一致。