1. 程式人生 > >JVM記憶體分配及String常用方法

JVM記憶體分配及String常用方法

一,JVM記憶體分配和常量池

​ 在介紹String類之前,先來簡單分析一下在JVM中,對記憶體的使用是如何進行分配的。如下圖所示(注意:在jdk1.8之後便沒有方法區了):

​ 如上JVM將記憶體分為多個不同的區域,這些區域都有各自的用途、建立和銷燬的時間,有些區域隨虛擬機器程序的啟動而存在,有些區域則是依賴使用者執行緒的啟動和結束來建立和銷燬。

​ 區域名稱的說明:

1.1,方法區:

​ 屬於資料共享記憶體區域,儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。

1.2,虛擬機器棧

​ 虛擬機器棧就是我們通常說的棧,是Java執行方法的記憶體模型,每當執行一次方法時,都會建立一個棧幀。把棧幀壓入棧,當Java方法呼叫時返回正常的結果或者捕獲異常時,棧幀出棧。

​ 棧幀:棧幀儲存方法的相關資訊,包含區域性變數數表、返回值、運算元棧、動態連結。

1.3,本地方法棧

​ 從功能上來說與虛擬機器棧類似,但是虛擬機器棧執行的是位元組碼,而本地方法棧呼叫的是Native方法,並且它是執行緒獨享的。

1.4,程式計數器

​ 程式計數器是執行緒獨享的,它是記錄當前執行緒執行的位元組碼行號。在多執行緒執行時,CPU會來回線上程之間進行切換,那麼當再次回到一條執行緒時,是如何得知執行緒的儲存單元及執行指令。而程式計數器便會進行儲存下一條儲存單元的地址,執行完畢後程序計數器自動加 1 ,以此迴圈直到程式結束為止。

1.5,堆

​ 說到堆這個概念想必都不陌生,它是記憶體中的重要角色。它主要是用來儲存被創建出來的物件,通過關鍵字new例項出來的,是所有執行緒共享的一塊最大的區域。

​ ==特別注意:在JDK1.7及以後,常量池移動到堆記憶體中。==

​ 堆還包括一個==常量池==,用來儲存編譯期間生成的==字面量和符號==引用。這部分內容在類被載入後,都會儲存到方法區中。同時,執行時產生的新常量也可以被放入常量池中,比如 String 類中的 intern() 方法產生的常量。

​ 常量池就是這個型別用到的常量的一個有序集合。包括直接常量(基本型別,String)和對其他型別、方法、欄位的符號引用。

二,常量池

2.1,什麼是常量:

​ 常量是指被final修飾的變數,值一旦確定就無法改變。

​ final可以修飾靜態變數、方法、例項變數和區域性變數。

​ 常量池分為兩種形式:靜態常量池和執行時常量池

2.2,靜態常量池

​ 即*.class檔案中的常量池,class檔案中的常量池不僅僅包含字串(數字)字面量,還包含類、方法的資訊,佔用class檔案絕大部分空間。這種常量池用於存放字面量和符號引用量。

2.3,執行時常量池

​ 指JVM虛擬機器在完成類裝載操作後,將class檔案中的常量池載入到記憶體中,並儲存在方法區中,我們常說的常量池,就是指方法區中的執行時常量池。同樣執行時常量池一個重要的特徵就是具有動態性,指並不需要常量只有在編譯期才會產生,在執行期也會將新的常量儲存到常量池中,如String類中的intern()方法。

三,== 和equals

3.1,兩者之間區邊

​ ==:

​ 對於基本型別來說:==表示數值的比較

​ 對於引用型別來說:==表示地址值的比較

​ equals:

​ 比較的是兩者之間值是否相等,但是Java中的類都是直接或者間接繼承Object類,而equals不也例外。其實在equals原始碼中也是使用==進行比較的,如下原始碼:

        ![](https://img2018.cnblogs.com/blog/1655301/201909/1655301-20190902223856542-1095893842.png)

​ 那麼問題來了,這和==又有什麼區別呢?

​ 上面說到equals也是繼承自java.lang.Object,因此可以對equals進行重寫來定義我們自己的比較方式。

​ 請參看以下程式碼:

        String str1 = "abc";
        String str2 = "abc";

        char[] strArray = {'a','b','c'};
        String str3 = new String(strArray);

        String str4 = "abc";

        System.out.println(str1 == str2);   
        System.out.println(str1 == str3);   
        System.out.println(str2 == str3);   
        System.out.println(str4.equals(str1)); 

​ 以上執行結果為:

true
false
false
true

​ 接下來我們依次分析上面的結果:

​ 1,str1與str2比較的是字串物件地址,因為它們的值是相同的,所以地址值也是相同的。

​ 2,str3是new出來的示例物件,在堆記憶體中會開闢一塊新的記憶體地址,它並不在常量池中。所以返回結果為false。

​ 3,同理str2與str3比較也是一樣的結果。

​ 4,equals比較的是值是否相同,所以返回的結果為true。

如圖所示:

四,String常用方法

​ 首先宣告字串:

 String str1 = "abc";

4.1,int length()

 int length = str1.length();
 System.out.println(length);

4.2,char charAt(值)

String str= "abc";
char c = str.charAt(1);  
System.out.println(c); 

4.3,char toCharArray()

String str= "abc";
char c[] = str.toCharArray();  
for (int i = 0; i < c.length; i++) {
      System.out.println("轉為陣列輸出:" + c[i]);
}

4.4,int indexOf("字元"); int lastIndexOf("字元")

String str="axcdefgabc";
int a1 = str.indexOf("a"); 
int a2 = str.indexOf("x",  2);
int a3 = str.lastIndexOf("c"); 
System.out.println("你的位置為:" + a1); 
System.out.println("為的位置為:" + a2);
System.out.println("點最後出現的位置為:" + a3);

4.5,字串大小寫轉換

​ toUpperCase(); 轉換成大寫
​ toLowerCase();轉換成小寫

String str = "hello world";
String str1 = "HELLO WORD";
System.out.println("將字串轉大寫為:" + str.toUpperCase());
System.out.println("將字串轉換成小寫為:" + str1.toLowerCase());

4.6,String[] split("字元")

String str = "abc,def,123";
String[] arr1 = str.split(",");

4.7,boolean equals(Object anObject)

 String str = "abc";
 String str1= "123";  
 if(str.equals(str1)) {  
      System.out.println("相等"); 
 }  
 else{
      System.out.println("不相等");  
 }

4.8,String trim()

String str = "       abc         ";  
System.out.println("去掉左右空格後:" + str.trim());

4.9,字串替換

String replace(char oldChar,char newChar)
String replaceAll(String,String)將某個內容全部替換成指定內容
String repalceFirst(String,String)將第一次出現的某個內容替換成指定的內容

String str = "abcdefgabdc";  
System.out.println("替換:" + str.replace("abc", "123")); 
System.out.println("替換全部:" + str.replaceAll("ab", "12")); 
System.out.println("替換第一次出現:" + str.repalceFirst("a", "a")); 

4.10,String substring(int beginIndex,int endIndex)

 String str = "abcdefg";  
 // 擷取0-3個位置的內容, 不含3 
 System.out.println("擷取後的字元為:" + str.substring(0, 3));
 // 從第3個位置開始擷取, 含2
 System.out.println("擷取後字元為:" + str.substring(2));

4.11,boolean equalsIgnoreCase(String)

String str = "ABC";
String str1 = "abc";
if(str.equalsIgnoreCase(str1)){
       System.out.println("相等");
}
else{
       System.out.println("不相等");
}

4.12,boolean contains(String)

String str = "ABCDEF";
String str1 = "ABC";
if(str.contains(str1)){
       System.out.println("str內容中包含ABC");
}
else{
       System.out.println("str內容中不包含ABC");
}

五,總結

​ 1,對於JVM記憶體的分配,在jdk6中存在方法區,jdk8中便沒有方法區,改成元區域。

​ 2,jdk6中常量池存在方法區中,jdk7以後常量池移動到堆中。

​ 本篇部落格只是簡單介紹了JVM記憶體分配和常量池的概念,仍有很多更深入的原理沒有記錄。因此在後期會繼續更新關於JVM相關的原理知識,以上部落格中如有不適之處還請留言(郵箱)指教。

感謝閱讀!