記Java中有關記憶體的簡單認識

余月七發表於2020-08-05

一、Java記憶體劃分

分為五個部分,可以參考這篇筆記簡單認識一下:

https://www.cnblogs.com/unleashed/p/13268027.html

方法區 本地方法棧 暫存器

二、從陣列的記憶體說起


一個 陣列的記憶體圖

首先,我們有這樣一組程式碼:

1 public class HelloWorld{ 
2  public static void main(String[] args){ 
3     int[] array = new int[2]; 
4     System.out.println(array); 
5     System.out.println(array[0]); 
6     System.out.println(array[1]);     
7     array[0]= 1; 
8     array[1]= 2; 
9     System.out.println(array); 
10     System.out.println(array[0]); 
11    System.out.println(array[1]); 
  } 
} 

看這張圖:

“.class”檔案裡面主要儲存的就是main方法,
而圖中過程就是 “進棧”的過程,並且為main方法開闢了一個新的空間。

繼續來看

“int[] array” 左邊其實是堆當中陣列的地址值
所以“array”這個變數儲存的其實是陣列的地址值
然後根據地址進行尋找陣列

輸出的時候,會自動找到陣列所有相關資訊

當程式執行到賦值語句時
根據陣列的地址值找到陣列
並且找到索引位置進行修改數值
然後列印輸出的時候,又會重複此前的步驟,進行定址,取值

兩個陣列的記憶體圖

1、新建陣列的情況

如果在剛才的main方法中新增一個這樣的語句

int[] array2 = new int[10]; 

此時,需要我們記住只要 new 了,它就會在堆當中 開闢出一個新的空間 ,也可以說是 新的記憶體空間

2、傳遞地址

如果新增的是這種語句呢?

int[] array2 = array; 

此時,堆當中還是隻有那一個陣列,只是將 array 的地址值傳遞給 array2 ,,因為它們的地址值相等,當給 array2 賦值時,更改的內容就是原來 array 裡面的內容,也就是 : 兩個引用指向同一個陣列的情況

三、來看物件的記憶體

一個物件的記憶體圖

首先,還是得有一段程式碼

public class student{ 
  String name; 
  String ssex; 
  int age; 
  public void study(){ 
    System.out.println("正在學習。。。。"); 
  } 
  public void eat(){ 
    System.out.println("正在吃飯。。。。"); 
  } 
} 

既然是物件,那就還得有一段程式碼,來使用這個student類

 public class TestStudent{ 
   public static void main(String[] args){ 
     Student stu = new Student(); 
     System.out.println(stu.name); 
     System.out.println(stu.ssex); 
     System.out.println(stu.age); 
      
     stu.name = "小杜"; 
     stu.age = 20; 
     stu.ssex = "男"; 
      
     System.out.println(stu.name); 
     System.out.println(stu.ssex); 
     System.out.println(stu.age); 
    stu.study(); 
    stu.eat(); 
   } 
 } 

那就從圖看起來

java中執行程式,首先是從main方法開始執行的
所以它必須第一個進棧

此處要 注意 !!!
當new Student()時
Student.class中的成員方法地址值會儲存在堆當中
所以要記住,對於引用型別,都是地址在傳遞

所謂的stu.name
就是在呼叫成員變數,所以通過地址值來進行定址
找到之後就進行更改
比如後面的語句
stu.name = "小杜" ;

物件.成員方法
通過地址值找到所要找的內容
然後開始進棧
當方法執行完畢後就會出現 “彈棧”
然後執行下一條語句
在我們這段程式碼中,下一條語句還是呼叫成員方法
所以study方法執行玩之後就會被彈出
進行下一條指令
呼叫eat方法


兩個物件使用同一個方法時的記憶體圖

比如: 
  Student stu = new Student(); 
  Student stu2 = new Student(); 
  …… 

都是指向方法區的同一塊方法的同一塊地址空間


兩個引用指向同一個物件的記憶體圖

Student stu = new Student(); 
Student stu2 = stu; 
…… 

尋找stu的地址值,然後根據stu的地址值進行呼叫方法


使用物件型別作為方法的引數

比如程式碼中有這麼兩三行 
  public static void method(Student stu){ 
    System.out.println(stu.eat); 
    …… 
  } 

當一個物件作為方法的引數時,傳遞到方法中時,實際傳遞進去的是物件的地址值


使用物件型別作為方法的返回值

public static Student eat(){ 
  Student stu =new Student(); 
  stu.study(); 
  stu.name = "小杜"; 
  return stu; 
} 

當使用一個物件型別作為方法的返回值時
返回值其實就是物件的地址值


四、字串常量池

字串常量池:程式當中直接寫上的雙引號字串,就在字串常量池當中

所以:

對於基本型別來說,== 比較的是數值

對於引用型別來說 ,== 比較的是地址值

因為內容不可變性,所以可以共享的

而且字串的效果相當於char[]陣列,但是底層原理是byte[]陣列,所以它會在儲存的過程中自動轉換成byte[]陣列

借用一張網上的圖

順便提一下static關鍵字

根據類名稱訪問靜態成員變數的時候,全程和物件是沒有關係的,只和類有關係。


五、繼承中的記憶體圖

圖片來源網路:

也就是父類空間優先於子類物件的產生,在每次建立子類物件時,先初始化父類空間,再建立子類物件本身。

相關文章