String顾复、StringBufer班挖、StringBuilder
String 它是典型的Immutable(不可改變)類,被聲明成為fnal class芯砸,所有屬性也都是fnal的萧芙。也由于它的不可變性,類似拼接假丧、裁剪字符串等動(dòng)作双揪,都會(huì)產(chǎn)生新的String對(duì)象。在循環(huán)場(chǎng)景下不建議使用這些功能包帚。
StringBufer是為解決上面提到拼接產(chǎn)生太多中間對(duì)象的問(wèn)題而提供的一個(gè)類渔期,可以用append或者add方法,把字符串添加到已有序列的末尾或者指定位置。StringBufer本質(zhì)是一個(gè)線程安全的可修改字符序列疯趟,它保證了線程安全(簡(jiǎn)單粗暴各種修改數(shù)據(jù)的方法都加上synchronized關(guān)鍵字實(shí)現(xiàn)的)拘哨,也隨之帶來(lái)了額外的性能開銷,所以除非有線程安全的需要信峻,不然還是推薦使用它的后繼者宅静,也就是StringBuilder。
StringBuilder 在能力上和StringBufer沒(méi)有本質(zhì)區(qū)別站欺,但是它去掉了線程安全的部分姨夹,有效減小了開銷,是絕大部分情況下進(jìn)行字符串拼接的首選矾策。從 builder 命名來(lái)看這個(gè)類專門就是為創(chuàng)建而生磷账。
- Immutable對(duì)象在拷貝時(shí)不需要額外復(fù)制數(shù)據(jù)。
字符串的內(nèi)部數(shù)組
為了實(shí)現(xiàn)修改字符序列的目的贾虽,StringBufer和StringBuilder底層都是利用可修改的char數(shù)組(JDK 9以后是byte)逃糟,都繼承了AbstractStringBuilder。
這個(gè)內(nèi)部數(shù)組應(yīng)該創(chuàng)建成多大的呢蓬豁?如果太小绰咽,拼接的時(shí)候可能要重新創(chuàng)建足夠大的數(shù)組;如果太大地粪,又會(huì)浪費(fèi)空間取募。目前的實(shí)現(xiàn)是,構(gòu)建時(shí)初始字符串長(zhǎng)度加16(這意味著蟆技,如果沒(méi)有構(gòu)建對(duì)象時(shí)輸入最初的字符串玩敏,那么初始值就是16)。我們?nèi)绻_定拼接會(huì)發(fā)生非常多次质礼,而且大概是可預(yù)計(jì)的旺聚,那么就可以指定合適的大小,避免很多次擴(kuò)容的開銷眶蕉。擴(kuò)容會(huì)產(chǎn)生多重開銷砰粹,因?yàn)橐獟仐壴袛?shù)組,創(chuàng)建新的(可以簡(jiǎn)單認(rèn)為是倍數(shù))數(shù)組造挽,還要進(jìn)行arraycopy碱璃。
字符串拼接
- 如果在代碼拼接說(shuō) String 拼接效率低下,但是在 jdk8即使你使用 String 拼接編譯器還是會(huì)把你優(yōu)化成 StringBuilder刽宪,jdk9優(yōu)化成StringConcatFactory(暫時(shí)沒(méi)用了解過(guò))
字符串緩存厘贼、內(nèi)存
String在jdk6以后提供了intern()方法界酒,目的是提示JVM把相應(yīng)字符串緩存起來(lái)圣拄,以備重復(fù)使用。但是不建議使用毁欣。
jdk6被緩存的字符串是存在所謂PermGen里的庇谆,也就是臭名昭著的“永久代”岳掐,這個(gè)空間是很有限的,也基本不會(huì)被FullGC之外的垃圾收集照顧到饭耳。所以串述,如果使用不當(dāng),OOM就會(huì)光顧寞肖。
jdk7存放在堆中纲酗。
jdk8出現(xiàn)了MetaSpace(元數(shù)據(jù)區(qū)),就存在元空間中。
intern是一種顯式地排重機(jī)制新蟆,但是它也有一定的副作用觅赊,因?yàn)樾枰_發(fā)者寫代碼時(shí)明確調(diào)用,一是不方便琼稻,每一個(gè)都顯式調(diào)用是非常麻煩的吮螺;另外就是我們很難保證效率,應(yīng)用開發(fā)階段很難清楚地預(yù)計(jì)字符串的重復(fù)情況帕翻,有人認(rèn)為這是一種污染代碼的實(shí)踐鸠补。
幸好在Oracle JDK 8u20之后,推出了一個(gè)新的特性嘀掸,也就是G1 GC下的字符串排重紫岩。它是通過(guò)將相同數(shù)據(jù)的字符串指向同一份數(shù)據(jù)來(lái)做到的,是JVM底層的改變睬塌,并不需要Java類庫(kù)做什么修改被因。
開啟 G1 GC 后可加入這個(gè)參數(shù)開啟-XX:+UseStringDeduplication
動(dòng)態(tài)代理
深入理解RPC之動(dòng)態(tài)代理篇這篇文章針對(duì)集中動(dòng)態(tài)代理有比較詳細(xì)的分析
動(dòng)態(tài)代理的應(yīng)用場(chǎng)景比如:RPC、安全衫仑、日志梨与、事務(wù)
靜態(tài)代理
事先寫好代理類,可以手工編寫文狱,也可以用工具生成粥鞋。缺點(diǎn)是每個(gè)業(yè)務(wù)類都要對(duì)應(yīng)一個(gè)代理類,非常不靈活瞄崇。
動(dòng)態(tài)代理
運(yùn)行時(shí)自動(dòng)生成代理對(duì)象呻粹。缺點(diǎn)是生成代理代理對(duì)象和調(diào)用代理方法都要額外花費(fèi)時(shí)間。
JDK動(dòng)態(tài)代理:基于Java反射機(jī)制實(shí)現(xiàn)苏研,必須要實(shí)現(xiàn)了接口的業(yè)務(wù)類才能用這種辦法生成代理對(duì)象等浊。新版本也開始結(jié)合ASM機(jī)制。
cglib動(dòng)態(tài)代理:基于ASM機(jī)制實(shí)現(xiàn)摹蘑,通過(guò)生成業(yè)務(wù)類的子類作為代理類筹燕。
基本數(shù)據(jù)類型和引用數(shù)據(jù)類型
緩存機(jī)制
緩存機(jī)制并不是只有Integer才有,同樣存在于其他的一些包裝類,比如:
- Boolean撒踪,緩存了true/ false對(duì)應(yīng)實(shí)例过咬,確切說(shuō),只會(huì)返回兩個(gè)常量實(shí)例Boolean.TRUE/ FALSE制妄。
- Short掸绞,同樣是緩存了-128到127之間的數(shù)值。
- Byte耕捞,數(shù)值有限衔掸,所以全部都被緩存。
- Character俺抽,緩存范圍'\ u0000' 到 '\ u007F'具篇。
繼續(xù)深挖緩存,I nteger的緩存范圍雖然默認(rèn)是-128到127凌埂,但是在特別的應(yīng)用場(chǎng)景驱显,比如我們明確知道應(yīng)用會(huì)頻繁使用更大的數(shù)值,這時(shí)候應(yīng)該怎么辦呢瞳抓?
-XX:AutoBoxCacheMax=N
緩存上限值實(shí)際是可以根據(jù)需要調(diào)整的埃疫,JVM提供了參數(shù)設(shè)置:
自動(dòng)拆裝箱的注意點(diǎn)
建議避免無(wú)意中的裝箱逛犹、拆箱行為锤窑,尤其是在性能敏感的場(chǎng)合,創(chuàng)建10萬(wàn)個(gè)Java對(duì)象和10萬(wàn)個(gè)整數(shù)的開銷可不是一個(gè)數(shù)量級(jí)的苟蹈,不管是內(nèi)存使用還是處理速度横蜒,光是對(duì)象頭的空間占用就已經(jīng)是數(shù)量級(jí)的差距了胳蛮。
Integer 內(nèi)存結(jié)構(gòu)組成
- Mark Word: 標(biāo)記位 4字節(jié),類似輕量級(jí)鎖標(biāo)記位丛晌,偏向鎖標(biāo)記位等仅炊。
- Class對(duì)象指針: 4字節(jié),指向?qū)ο髮?duì)應(yīng)class對(duì)象的內(nèi)存地址澎蛛。
- 對(duì)象實(shí)際數(shù)據(jù):對(duì)象所有成員變量抚垄。
- 對(duì)齊:對(duì)齊填充字節(jié),按照8個(gè)字節(jié)填充谋逻。
Integer占用內(nèi)存大小呆馁,4+ 4+ 4+ 4= 16字節(jié)。
集合
Map
LinkedHashMap
LinkedHashMap通常提供的是遍歷順序符合插入順序毁兆,它的實(shí)現(xiàn)是通過(guò)為條目(鍵值對(duì))維護(hù)一個(gè)雙向鏈表浙滤。注意,通過(guò)特定構(gòu)造函數(shù)气堕,我們可以創(chuàng)建反映訪問(wèn)順序的實(shí)例纺腊,所謂的put畔咧、get、compute等摹菠,都算作“訪問(wèn)”盒卸。