JVM內(nèi)存分配及String常用方法解析
一,JVM內(nèi)存分配和常量池
在介紹String類之前,先來簡單分析一下在JVM中,對內(nèi)存的使用是如何進行分配的。如下圖所示(注意:在jdk1.8之后便沒有方法區(qū)了):
如上JVM將內(nèi)存分為多個不同的區(qū)域,這些區(qū)域都有各自的用途、創(chuàng)建和銷毀的時間,有些區(qū)域隨虛擬機進程的啟動而存在,有些區(qū)域則是依賴用戶線程的啟動和結束來建立和銷毀。
區(qū)域名稱的說明:
1.1,方法區(qū):
屬于數(shù)據(jù)共享內(nèi)存區(qū)域,存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。
1.2,虛擬機棧
虛擬機棧就是我們通常說的棧,是Java執(zhí)行方法的內(nèi)存模型,每當執(zhí)行一次方法時,都會創(chuàng)建一個棧幀。把棧幀壓入棧,當Java方法調(diào)用時返回正常的結果或者捕獲異常時,棧幀出棧。
棧幀:棧幀存儲方法的相關信息,包含局部變量數(shù)表、返回值、操作數(shù)棧、動態(tài)鏈接。
1.3,本地方法棧
從功能上來說與虛擬機棧類似,但是虛擬機棧執(zhí)行的是字節(jié)碼,而本地方法棧調(diào)用的是Native方法,并且它是線程獨享的。
1.4,程序計數(shù)器
程序計數(shù)器是線程獨享的,它是記錄當前線程執(zhí)行的字節(jié)碼行號。在多線程執(zhí)行時,CPU會來回在線程之間進行切換,那么當再次回到一條線程時,是如何得知線程的存儲單元及執(zhí)行指令。而程序計數(shù)器便會進行存儲下一條存儲單元的地址,執(zhí)行完畢后程序計數(shù)器自動加 1 ,以此循環(huán)直到程序結束為止。
1.5,堆
說到堆這個概念想必都不陌生,它是內(nèi)存中的重要角色。它主要是用來存儲被創(chuàng)建出來的對象,通過關鍵字new實例出來的,是所有線程共享的一塊最大的區(qū)域。
==特別注意:在JDK1.7及以后,常量池移動到堆內(nèi)存中。==
堆還包括一個==常量池==,用來存儲編譯期間生成的==字面量和符號==引用。這部分內(nèi)容在類被加載后,都會存儲到方法區(qū)中。同時,運行時產(chǎn)生的新常量也可以被放入常量池中,比如 String 類中的 intern() 方法產(chǎn)生的常量。
常量池就是這個類型用到的常量的一個有序集合。包括直接常量(基本類型,String)和對其他類型、方法、字段的符號引用。
二,常量池
2.1,什么是常量:
常量是指被final修飾的變量,值一旦確定就無法改變。
final可以修飾靜態(tài)變量、方法、實例變量和局部變量。
常量池分為兩種形式:靜態(tài)常量池和運行時常量池
2.2,靜態(tài)常量池
即*.class文件中的常量池,class文件中的常量池不僅僅包含字符串(數(shù)字)字面量,還包含類、方法的信息,占用class文件絕大部分空間。這種常量池用于存放字面量和符號引用量。
2.3,運行時常量池
指JVM虛擬機在完成類裝載操作后,將class文件中的常量池載入到內(nèi)存中,并保存在方法區(qū)中,我們常說的常量池,就是指方法區(qū)中的運行時常量池。同樣運行時常量池一個重要的特征就是具有動態(tài)性,指并不需要常量只有在編譯期才會產(chǎn)生,在運行期也會將新的常量保存到常量池中,如String類中的intern()方法。
三,== 和equals
3.1,兩者之間區(qū)邊
==:
對于基本類型來說:==表示數(shù)值的比較
對于引用類型來說:==表示地址值的比較
equals:
比較的是兩者之間值是否相等,但是Java中的類都是直接或者間接繼承Object類,而equals不也例外。其實在equals源碼中也是使用==進行比較的,如下源碼:

那么問題來了,這和==又有什么區(qū)別呢?
上面說到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出來的示例對象,在堆內(nèi)存中會開辟一塊新的內(nèi)存地址,它并不在常量池中。所以返回結果為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("轉(zhuǎn)為數(shù)組輸出:" + 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("點最后出現(xiàn)的位置為:" + a3);
4.5,字符串大小寫轉(zhuǎn)換
toUpperCase(); 轉(zhuǎn)換成大寫
toLowerCase();轉(zhuǎn)換成小寫
String str = "hello world"; String str1 = "HELLO WORD"; System.out.println("將字符串轉(zhuǎn)大寫為:" + str.toUpperCase()); System.out.println("將字符串轉(zhuǎn)換成小寫為:" + 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)
將某個內(nèi)容全部替換成指定內(nèi)容
String repalceFirst(String,String)
將第一次出現(xiàn)的某個內(nèi)容替換成指定的內(nèi)容
String str = "abcdefgabdc"; System.out.println("替換:" + str.replace("abc", "123")); System.out.println("替換全部:" + str.replaceAll("ab", "12")); System.out.println("替換第一次出現(xiàn):" + str.repalceFirst("a", "a"));
4.10,String substring(int beginIndex,int endIndex)
String str = "abcdefg"; // 截取0-3個位置的內(nèi)容, 不含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內(nèi)容中包含ABC"); } else{ System.out.println("str內(nèi)容中不包含ABC"); }
五,總結
1,對于JVM內(nèi)存的分配,在jdk6中存在方法區(qū),jdk8中便沒有方法區(qū),改成元區(qū)域。
2,jdk6中常量池存在方法區(qū)中,jdk7以后常量池移動到堆中。
本篇文章只是簡單介紹了JVM內(nèi)存分配和常量池的概念,仍有很多更深入的原理沒有記錄。因此在后期會繼續(xù)更新關于JVM相關的原理知識,以上博客中如有不適之處還請留言(郵箱)指教。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
SpringBoot3+ShardingJDBC5.5.0 讀寫分離配置的實現(xiàn)
本文主要介紹了SpringBoot3+ShardingJDBC5.5.0 讀寫分離配置的實現(xiàn),最新版5.5.0支持SpringBoot3x現(xiàn)分享給大家,具有一定的參考價值,感興趣的可以了解一下2024-08-08Spring核心容器之ApplicationContext上下文啟動準備詳解
這篇文章主要介紹了Spring核心容器之ApplicationContext上下文啟動準備詳解,ApplicationContext 繼承自 BeanFactory ,其不僅包含 BeanFactory 所有功能,還擴展了容器功能,需要的朋友可以參考下2023-11-11Spring Date jpa 獲取最新一條數(shù)據(jù)的實例代碼
這篇文章主要介紹了Spring Date jpa 獲取最新一條數(shù)據(jù)的實例代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Spring-boot JMS 發(fā)送消息慢的解決方法
這篇文章主要為大家詳細介紹了Spring-boot JMS 發(fā)送消息慢的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08