位元組碼底層分析String
String
特性
不可變性:不可變性也就是 如果建立了一個String物件,進行改變那麼就是再建立了一個物件,而不是在原有的改變。
jdk8之前內部儲存定義的是char型別陣列
jdk9以後是定義的byte型別陣列儲存,可以更好的節省了空間。
字串常量池在jdk1.8(含8)以後都是儲存在堆空間中的。
字串常量池
String pool 底層也就是HashTable
所以字串常量池是不可重複的
StringMT4
如 String s = “abc” s2 =“abc”. 在棧中其實引用的是同一個地址(編譯最佳化)
jdk1.6以前 HashTable 長度預設值1009 ,jdk7預設為 60013, jdk8以後設定長度最低要求1009
引數設定
-XX:StringTableSize=*
連結串列短的話會影響效率
實戰
可編譯看位元組碼,一下都是基於jdk1.8以後進行說明
宣告:1.字面量建立String 那麼會在字串常量池中新增一個空間,但是堆空間不會有地址;
宣告:2.new 方式建立String 那麼會在字串常量池中新增一個空間,並且在堆中開闢一個空間,並且引用堆空間的地址
宣告:3.兩個字面量拼接,那麼會編譯階段就會進行編譯最佳化如String c = "a"+"b";編譯期間會預設為String c = "ab",不會再字串常量池中分別開闢空間
宣告:4.在String c = new String("a")+new String("b")相加是不會在字串常量池中開闢新的空間的,c只是引用
public void test(){
String s = "javaHeap";
String s1 = "java";
String s2 = "heap";
String s3 = s1 + s2;
/**
* 因為編譯時 s1 s2 相當於在字串常量池中建立物件,不知道其中的值,需要引用地址
* 當賦值時,那麼底層會使用 StringBuilder 的append 方法進行拼接,是引用了兩個地址,進行賦值,組成新的物件,並且字串常量池中是沒有"a、javaHeap"
*/
boolean result = s == s3; //false
}
public void test2(){
String s = "javaHeap";
final String s1 = "java";
final String s2 = "heap";
String s3 = s1 + s2;
/**
* final在編譯期就已經確定下來了值,在進行字串拼接時會進行 編譯最佳化 String s3 = "javaHeap"
*/
boolean result = s == s3; //true
}
public static void test3(){
String s = new String("javaHeap");
/**
* 在記憶體中建立了5個物件 細緻的說6個
* 1.new 在堆中
* 2.“字串”在字串常量池中
* 3. + 變數拼接 建立了StringBuilder
* 外加:StringBuilder 中的toString方法,底層也就是new String();但是不會在常量池中建立
*/
String s2 = new String("java") + new String("Heap");
}
public static void test4(){
/**
* 字面量和new 的區別
* 1. 字面量會在字串常量池中 放入,new物件也會在常量池中放入"javaHeap"
* 2. 字面量不會在堆中開闢空間,而new會在堆中開闢空間
*/
String b = new String("javaHeap");//返回的是堆空間中的地址
String a = "javaHeap";
System.out.println(a==b);//false
}
intern方法
簡介
Intern方法:檢視字串常量池中是否有 , 如果有,那麼返回字串常量池中的引用,如果沒有將會檢視堆中是否有資料,有將會引用物件地址,沒有那麼建立字串,放入常量池。
為什麼使用intern?
:降低記憶體的大小,提高的執行的速度,會自動釋放記憶體,會引用常量池中的資料
實戰詳解
public static void test(){
/**
* 記憶體結構詳解:
* new String 是在堆中建立了物件,並且在字串常量池中建立了字面量,
* 第二行:(在建立字面量時 先檢視了堆中是否有物件的引用,如果有那麼就不會進行建立,之間在字串 常量池中引用了堆中的地址【節省空間】)
* String s = "javaHeap";
* 字面量:只會在常量池中 建立常量。
*
* 所以在比較時 new 在常量池是物件引用地址,而字面量是值(或者說地址)所以false
*/
String s2 = new String("javaHeap");
s2.intern();
String s = "javaHeap";
System.out.println(s==s2);//false
}
public static void test2(){
/**
* 記憶體結構詳解:
* 這裡在進行第一行程式碼時 結構(建立了6個物件,最後在堆中生成了物件s ="javaHeap" 變數池中沒有生成)
* 第二行:在變數池中生成
* 第三行:直接引用變數池
*
*/
String s = new String("java") + new String("Heap");
s.intern();
String s2 = "javaHeap";
System.out.println(s==s2);//true
}
//-------------------------------------------------------------------------------------------------
public static void main(String[] args) {
String str2 = new String("xin") + new String("cen");
//String str2 = new String("a");
String intern = str2.intern();
String str = "xincen";
System.out.println(intern==str);//true 比較串常量池中的地址
System.out.println(str2==str);//false 比較了堆中的地址
}
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2694235/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 位元組碼層面深入分析Java列舉類Java
- 【Java】JVM位元組碼分析JavaJVM
- HasMap 底層原始碼分析ASM原始碼
- JVM 層對 jar 包位元組碼加密JVMJAR加密
- 位元組碼技術---------動態代理,lombok外掛底層原理。類載入器Lombok
- Java併發雜談(一):volatile的底層原理,從位元組碼到CPUJava
- 例項分析理解Java位元組碼Java
- Owin Katana 的底層原始碼分析原始碼
- JAVA ArrayList集合底層原始碼分析Java原始碼
- 位元組碼指令分析 ++i 和 i++
- JVM 位元組碼執行例項分析JVM
- ArrayList底層結構和原始碼分析原始碼
- String操作方法底層實現!!!
- RabbitMq底層原理分析MQ
- LinkedHashMap 底層分析HashMap
- 持久層Mybatis3底層原始碼分析,原理解析MyBatisS3原始碼
- JVM(三):深入分析Java位元組碼-上JVMJava
- JVM(四):深入分析Java位元組碼-下JVMJava
- Java 位元組碼Java
- 位元組碼指令
- Seata原始碼分析(一). AT模式底層實現原始碼模式
- iOS底層原理總結 -- 利用Runtime原始碼 分析Category的底層實現iOS原始碼Go
- 5道面試題,拿捏String底層原理!面試題
- 使用javap分析Java位元組碼的一個例子Java
- HashMap的底層原理分析HashMap
- 位元組碼基礎
- 位元組碼詳解
- Java位元組碼指令Java
- 什麼是位元組碼?python位元組碼詳細介紹!Python
- 位元組面試:什麼是讀寫分離?讀寫分離的底層如何實現?面試
- Redis原始碼分析-底層資料結構盤點Redis原始碼資料結構
- Android Compose 入門,深入底層原始碼分析Android原始碼
- JAVA動態位元組碼Java
- 位元組編碼轉換
- 理解 Python 位元組碼Python
- 字元,位元組和編碼字元
- 位元組碼檔案解剖
- Laravel 執行原理分析與原始碼分析,底層看這篇足矣Laravel原始碼