string str=new string("aaa");
這行**究竟建立了幾個string物件呢?答案是2個,而不是3個。由於new string("aaa")相當於"aaa"與乙個就是建立出來的放在堆時原例項物件,而另乙個就是放在常量池中的 "aaa" 物件,當然這裡的str本身只是乙個引用,放在棧裡,用來指向堆中建立出來的物件。
常量池(constant pool)指的是在編譯期被確定,並被儲存在已編譯的.class檔案中的一些資料。它包括了關於類、方法、介面等中的常量,也包括字串常量。
string str="aaa";
只建立1個物件。這裡涉及到字串常量池,在jvm中有乙個字串池,它用來儲存很多可以被共享的string物件,這樣如果我們在使用同樣字面字串時,它就使用字串池中同字面的字串。當然我們可以使用string物件的intern()方法來訪問string物件在字串池中所對應的常量物件。
上面這行**被執行的時候,jvm先到字串池中查詢,看是否已經存在值為"aaa"的物件,如果存在,則不再建立新的物件,直接返回已存在物件的引用;如果不存在,則先建立這個物件,然後把它加入到字串池中,再將它的引用返回。
string str1="aaa";
string str2="aaa";
也只建立1個物件。能過上面的解釋這個就更清楚了,在執行第二行**時,aaa字串物件在池中已存在,所以直接返回池中已存在的那個字串物件。
string str="aaa"+"bbb";
還是只建立1個物件。由於常量字串是在編譯的時候就也被確定的,又因"aaa"和"bbb"都是常量,因此變數str的值在編譯時就可以確定。這行**編譯後的與string str="aaabbb";是一樣的,這與我們平時好像不太一樣啊?一般使用「+」連線兩個字串都會產生另乙個新的字元物件。下面我們看一下例子就明白了:
string str1 = "aaa";
string str2 = "bbb";
string str3 = "aaabbb";
string str4 = "aaa" + "bbb";//不會產生新的字串物件
system.out.println(str3 == str4);//true
str4 = str1 + "bbb";//會產生新的字串物件
system.out.println(str3 == str4);//false
str4 = str1 + str2;//會產生新的字串物件
system.out.println(str3 == str4);//false
從上面例子我們就可以得出:使用「+」連線的兩個字串本身就是字面常量字串時,如果池中存在這樣連線後的字串,則是不會重新建立物件,而是直接引用池中的字串物件;如果「+」連線的兩字串中只要有乙個不是字面常量串(即定義過的),是會產生新的字串物件。
凡事也有例外,這個也不例外:如果「+」連線的字串中兩個或乙個不是「字面常量」,但如果定義成常量字串時,情況又有變化:
final string str1 = "aaa";
final string str2 = "bbb";
string str3 = "aaabbb";
/* * 因為str1與str2都定義成了常量,所以編譯時就能確定,編譯時就會將常量替換,等同於
* str4 = "aaa"+"bbb",因此不產生新物件
*/string str4 = str1 + str2;
system.out.println(str3 == str4);//true
但如果先定義final字串,但未在定義處初始化,而初始化在塊中,如下:
//此時str1與str2相當於變數,而不是常,因為塊是在執行時才能確定,在編譯時不能確定
final static string str1;
final static string str2;
static
public static void main(string args)
string str=" ";與string str=new string();
str=" "會放入池中,但new string()不會放入池中。
string的intern()方法
「當呼叫 intern 方法時,如果池已經包含乙個等於此 string 物件的字串(該物件由 equals(object) 方法確定),則返回池中的字串;否則,將此 string 物件新增到池中,並且返回此 string 物件的引用。它遵循對於任何兩個字串 s 和 t,當且僅當 s.equals(t) 為 true 時,s.intern() == t.intern() 才為 true」,這是jdk文件原文註解。
現在我們回到最開頭的那個例子,為什麼string str=new string("aaa");會產生2個物件?乙個是"aaa"又作為字串建構函式的引數,但"aaa"自己就是乙個字串,在傳進建構函式前就已建立了乙個字元物件,實質上與就好比是第二個例項:string str="aaa"; ,它建立的字串物件會放入到池中,並且引用的也是池中的那個字串物件;另乙個就是通過new string()建構函式建立的。所以new string("aaa")會產生兩個物件,也就是說通過此種方式建立字串物件時,會先將字串引數物件放入物件池,然後另外建立乙個字串物件。
理解完new string("aaa")為什麼會產生兩個物件,我們再來看看new string(char value)這樣的方式建立時,又會不會把字串物件放入到池中呢?答案是不會的。因為傳遞給建構函式的是乙個字元陣列,而不是像前面傳遞的是乙個字面常量字串引數那樣,將字串引數本身放入池中。那麼我們現在如果證明new string(char value)未將字串物件放入池中,我們可以寫乙個簡單的測試,執行時開啟xp的任務管理器,檢視作業系統的記憶體使用情況就可以初步確認:
int size = 10000000;
char c = new char[size];
for (int i = 0; i < size; i++)
//使用帶字元陣列引數建構函式建立字串時,字串物件不會放入字串池
string str1 = new string(c);
system.out.println("string字串物件建立完畢...");
thread.sleep(5000);
str1.intern();//到裡會看見記憶體增加
system.out.println("第一次呼叫intern()完畢...");
thread.sleep(5000);
str1.intern();//再過5秒將看不到記憶體增長,因為池中有了,不會再放入,所以記憶體無變化
system.out.println("第二次呼叫intern()完畢...");
thread.sleep(5000);
所以建立字串物件放入並放入池中有二種方式:第一種就是直接使用字面常量定義字串時,如 string str="aaa"; ,str會引用放入池中的物件;第二種就是使用帶字串引數的字串建構函式,並且此時傳入的引數值要是字串常量形式,而不能是變數的形式,也就是說只能是 string str=new string("aaa");形式,而不能是先定義 string s = "aaa",然後再使用 string str=new string(s);來建立物件,new string(s);此時只建立乙個物件,但如果池中不存在時我們可以使用intern方法將它放入池中。當然上面放入池中的前提是池中還不存在這些字串物件。
其實,當我們仔細研究時,發現放入池中只實質上只存在一種時機,那就是:直接使用字面常量字串時。上面所說的兩種時機實質上就是直接使用了字面常的字串而將其放入池中的。
上面多處提到了jvm中的堆疊,下面小結一下各自原作用:
棧用來儲存基本型別與物件的引用的,基本型在建立前會檢視stack中是否已經有, 有則指向, 沒有則建立。
string內部是以字串陣列來儲存字串的,因此可以認為與char等同, string a= "abc",首先在heap中創乙個物件,再到stack中找char是否存在,有則指向該位址, 無則在stack中建立陣列。
new出來的都是在heap中,堆是用於儲存物件的。
死磕併發之到底建立多少個執行緒合適
使用多執行緒的目的就是提公升程式效能,在併發程式設計領域,提公升效能本質上就是提公升硬體的利用率,再具體點來說,就是提公升 i o 的利用率和 cpu 的利用率。而解決的方案就是多執行緒。知道了解決方案,問題又來了,建立多少個執行緒合適呢?多了會造成cpu上下文切換,浪費資源不說效能還上不去,少了吧...
合理計算建立多少個執行緒
為什麼 實際執行緒數量 理論執行緒數量 1?計算 cpu 密集型的執行緒恰好在某時因為發生乙個頁錯誤或者因其他原因而暫停,剛好有乙個 額外 的執行緒,可以確保在這種情況下cpu週期不會中斷工作。問題1 假設要求乙個系統的 tps transaction per second 或者 task per ...
多執行緒,到底該設定多少個執行緒?
不好了,線上伺服器超時嚴重,請求非常慢,好像報連線數too many了,怎麼辦?小夥伴們在反饋。一般我們的技術老大的處理方式,把連線數和執行緒池調大點,重啟,再觀察。往往這個方式是應急措施,治標不治本,因為不知道問題的原因。有個嚴重誤區,以為執行緒池設定太小了,調大點請求就會快了。今天就帶著小夥伴們...