php使用漢字作為進位制轉換
阿新 • • 發佈:2022-03-23
2,String的記憶體分配
3,String基本操作
4,字串拼接操作
5,intern()的使用
6,StringTable的垃圾回收
7,G1中String去重操作
String的基本特性
String:字串,使用一對 “” 引起來表示
-
String s1 = “ylcandzy”;//字面量的定義方式
-
String s2 = new String("hello");
String宣告為final的,不可被繼承
String實現了Serializable介面;表示字串是支援序列化的,實現Comparable介面:表示String可以比較大小
String在jdk8及以前內部定義了final char[] value用於儲存字串資料。jdk9時改為byte[]
- String儲存結構變更
結論:String再也不用 char[] 來儲存,改為 byte[] 加上編碼標記,節約了一些空間
public final class String implements java.io.Serializable,Comparable<String>,CharSequence{ @Stable private final byte[] value; }
那StringBuffer 和 StringBuilder 是否仍然無動於衷?
String-related classes such as AbstractStringBuilder,StringBuilder,
and StringBuffer will be updated to use the same representation,as will the
HotSpot VM’s intrinsic(固有的,內建的)string operations
- String:代表不可變的字元序列。簡稱:不可變性。
- 當對字串重新賦值時,需要重寫指定記憶體區域賦值,不能使用原有的value進行賦值。
- 當對現在的字串進行連線操作時,也需要重新指定記憶體區域賦值,不能使用功能原有的value進行賦值。
- 當呼叫String的 replace() 方法修改指定字串或字串時,也需要重新指定記憶體區域賦值,不能使用原有的value進行賦值。
- 通過字面量的方式(區別於new)給一個字串賦值,此時的字串值宣告在字串常量池中。
package com.ylc.java05; import org.junit.Test; /** * * * String基本使用:驗證String的不可變性 * 並不會在原有的地址上進行修改,只會建立一個新的地址 * * @Author ylc * @Date 2022/3/25 16:02 * @Version 1.0 */ public class StringTest01 { @Test public void test01(){ String s1 = "abc";//字面量定義的方式,"abc"儲存在字串常量池中 String s2 = "abc"; s2 = "hello"; System.out.println(s1 == s2); //判斷地址 System.out.println(s1); } @Test public void test02(){ String s1 = "abc"; String s2 = "abc"; s2 += "ylc"; System.out.println(s1 == s2); System.out.println(s2); } @Test public void test03(){ String s1 = "abc"; String s2 = s1.replace("a","y"); System.out.println(s1 == s2); } }
字串常量池中是不會儲存相同內容的字串的。
- String的String Pool是一個固定大小的Hashtable,預設值大小長度是1009。如果放進String Pool的String非常多,就會造成Hash衝突嚴重,從而導致連結串列會很長,而連結串列長了後直接會造成的影響就是當呼叫String.intern時效能會大幅下降。
- 使用-XX:StringTableSize可以設定StringTable的長度
- 在jdk6中StringTable是固定的,就是1009的長度,所以如果常量池中的字串過多就會導致效率下降很快。StringTableSize設定沒有要求。
- 在jdk7中StringTable的長度預設值60013,1009是可以設定的最小值。
- jdk8開始,1009是可設定的最小值。
String記憶體分配
- 常量池就是類似一個Java系統級別提供的快取。8種基本資料型別的常量池都是系統協調的,String型別的常量池比較特殊。它是主要使用方法有兩種
- 直接使用雙引號宣告出來的String物件會直接儲存在常量池中。(比如:String info = “ylcandzy”);
- 如果不是用雙引號宣告的String物件,可以使用String提供的intern()方法。
- Java 6及以前,字串常量池存放在永久代。
- Java 7中字串常量池的位置調整到Java堆中。
- Java 8元空間,字串常量在堆。
- StringTable為什麼要調整?
- permSize預設比較小
- 永久代垃圾回收頻率低
package com.ylc.java05; import java.util.HashSet; import java.util.Set; /** * * jdk6: * -XX:MetaspaceSize=6m -XX:MaxMetaspaceSize=6m -Xms6m -Xmx6m * * jdk8: * -XX:MetaspaceSize=6m -XX:MaxMetaspaceSize=6m -Xms6m -Xmx6m * @Author ylc * @Date 2022/3/25 17:47 * @Version 1.0 */ public class StringTest02 { public static void main(String[] args) { //使用Set保持著常量池引用,避免full gc 回收常量池行為 Set<String> set = new HashSet<String>(); //在short可以取值的範圍內足以讓6MB的PermSize或heap產生OOM short i = 0; while (true){ set.add(String.valueOf(i++).intern()); } } }
String的基本操作
package com.ylc.java05; /** * @Author ylc * @Date 2022/3/25 18:23 * @Version 1.0 */ public class StringTest03 { public static void main(String[] args) { int i = 1; Object obj = new Object(); StringTest03 mem = new StringTest03(); mem.foo(obj); } private void foo(Object param){ String str = param.toString(); System.out.println(str); } }
字串拼接操作
- 常量與常量的拼接結果在常量池,原理是編譯期優化
- 常量池中不會存在相同內容的常量。
- 只要其中有一個變數,結果就是在堆中。變數拼接的原理是StringBuilder
- 如果拼接的結果呼叫intern()方法,則主動將常量池中還沒有的字串物件放入池中,並返回此物件地址。
@Test public void test02(){ String s1 = "ylc"; String s2 = "zy"; String s3 = "ylcandzy"; String s4 = "ylcand" + "zy";//編譯期間優化 //如果拼接符號的前後出現了變數,則相當於堆空間new String(),具體的內容為拼接的結果:ylcandzy String s5 = s1 + "zy"; String s6 = "ylc" +s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7); System.out.println(s5 == s6); System.out.println(s5 == s7); System.out.println(s6 == s7); //intern():判斷字串常量池中是否存在ylcandzy值,如果存在,則返回常量池中ylcandzy的地址; //如果字串常量池不存在ylcandzy,則在常量池中載入一份ylcandzy,並返回物件的地址 String s8 = s6.intern(); System.out.println(s3 == s8); } }