這篇文章主要講解了“如何理解字符串常量池”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何理解字符串常量池”吧!
創(chuàng)新互聯(lián)建站2013年開創(chuàng)至今,先為承德等服務(wù)建站,承德等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為承德企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
從平均情況來看,應(yīng)用程序中的String對象會消耗大量的內(nèi)存。這里面有一部分是冗余的——同樣的字符串會存在多個不同的實(shí)例(a != b, 但a.equals(b))。在實(shí)踐中,有許多字符串會出于不同的原因造成冗余。
最初JDK提供了一個String.intern()方法來解決字符串冗余的問題。這個方法的缺點(diǎn)在于你必須得去找出哪些字符串需要進(jìn)行駐留(interned)。通常都需要具備冗余字符串查找功能的堆分析的工具才行,比如Youkit profiler。如果使用得當(dāng)?shù)脑挘址v留會是一個非常有效的節(jié)省內(nèi)存的工具——它讓你可以重用整個字符串對象(每個字符串對象在底層char[]的基礎(chǔ)上會增加24字節(jié)的額外開銷)。
Java 7 update 6開始,每個String對象都有一個自己專屬的私有char[] 。這樣JVM才可以自動進(jìn)行優(yōu)化——既然底層的char[]沒有暴露給外部的客戶端的話,那么JVM就能去判斷兩個字符串的內(nèi)容是否是一致的,進(jìn)而將一個字符串底層的char[]替換成另一個字符串的底層char[]數(shù)組。
字符串去重這個特性就是用來做這個的,它在Java 8 update 20中被引入。下面是它的工作原理:
1.你得使用G1垃圾回收器并啟用這一特性: -XX:+UseG1GC -XX:+UseStringDeduplication
這一特性作為G1垃圾回收器的一個可選的步驟來實(shí)現(xiàn)的,如果你用的是別的回收器是無法使用這一特性的。
2.這個特性會在G1回收器的minor GC階段中執(zhí)行。根據(jù)我的觀察來看,它是否會執(zhí)行取決于有多少空閑的CPU周期。因此,你不要指望它會在一個處理本地?cái)?shù)據(jù)的數(shù)據(jù)分析器中會被執(zhí)行,也就是說,WEB服務(wù)器中倒是很可能會執(zhí)行這個優(yōu)化。
3.字符串去重會去查找那些未被處理的字符串,計(jì)算它們的hash值(如果它沒在應(yīng)用的代碼中被計(jì)算過的話),然后再看是否有別的字符串的hash值和底層的char[]都是一樣的。如果找到的話——它會用一個新字符串的char[]來替換掉現(xiàn)有的這個char[]。
4.字符串去重只會去處理那些歷經(jīng)數(shù)次GC仍然存活的那些字符串。這樣能確保大多數(shù)的那些短生命周期的字符串不會被處理。字符串的這個最小的存活年齡可以通過 -XX:StringDeduplicationAgeThreshold=3的JVM參數(shù)來指定(3是這個參數(shù)的默認(rèn)值)。
下面是這個實(shí)現(xiàn)的一些重要的結(jié)論:
沒錯,如果你想享受字符串去重特性的這份免費(fèi)午餐的話,你得使用G1回收器。
使用parellel GC的話是無法使用它的,而對那些對吞吐量要求比延遲時期高的應(yīng)用而言,parellel GC應(yīng)該是個更好的選擇。
字符串去重是無法在一個已加載完的系統(tǒng)中運(yùn)行的。要想知道它是否被執(zhí)行了,可以通過 -XX:+PrintStringDeduplicationStatistics參數(shù)來運(yùn)行JVM,并查看控制臺的輸出。
如果你希望節(jié)省內(nèi)存的話,你可以在應(yīng)用程序中將字符串進(jìn)行駐留(interned)——那么放手去做吧,不要依賴于字符串去重的功能。
你需要時刻注意的是字符串去重是要處理你所有的字符串的(至少是大部分吧)——也就是說盡管你知道某個指定的字符串的內(nèi)容是唯一的(比如說GUID),但JVM并不知道這些,它還是會嘗試將這個字符串和其它的字符串進(jìn)行匹配。這樣的結(jié)果就是,字符串去重所產(chǎn)生的CPU開銷既取決于堆中字符串的數(shù)量(將新的字符串和別的字符串進(jìn)行比較),也取決于你在字符串去重的間隔中所創(chuàng)建的字符串的數(shù)量(這些字符串會和堆中的字符串進(jìn)行比較)。
在一個擁有好幾個G的堆的JVM上,可以通過- XX:+PrintStringDeduplicationStatistics選項(xiàng)來看下這個特性所產(chǎn)生的影響究竟有多大。
另一方面,它基本是以一種非阻塞的方式來完成的,如果你的服務(wù)器有足夠多的空閑CPU的話,那為什么不用呢?
最后,請記住,String.intern可以讓你只針對你的應(yīng)用程序中指定的某一部分已知會產(chǎn)生冗余的字符串。通常來說,它只需要比較一個較小的駐留字符串的池就可以了,也就是說你可以更高效地使用你的CPU。不僅如此,你還可以將整個字符串對象進(jìn)行駐留,這樣每個字符串你還多節(jié)省了24個字節(jié)。
這里是我用來試驗(yàn)這一特性的一個測試類。這三個測試都會一直運(yùn)行到JVM拋出OOM為止,因此你得分別去單獨(dú)地運(yùn)行它們。
第一個測試會創(chuàng)建內(nèi)容一樣的字符串,如果你想知道當(dāng)堆中字符串很多的時候,字符串去重會花掉多少時間的話,這個測試就變得非常有用了。盡量給第一個測試分配盡可能多的內(nèi)存——它創(chuàng)建的字符串越多,優(yōu)化的效果就越好。
第二三個測試會比較去重(第二個測試)及駐留(interning, 第三個測試)間的差別。你得用一個相同的Xmx設(shè)置來運(yùn)行它們。在程序中我把這個常量設(shè)置成了Xmx256M,但是當(dāng)然了,你可以分配得多點(diǎn)。然而,你會發(fā)現(xiàn),和interning測試相比,去重測試會更早地掛掉。這是為什么?因?yàn)槲覀冊谶@組測試中只有100個不同的字符串,因此對它們進(jìn)行駐留就意味著你用到的內(nèi)存就只是存儲這些字符串所需要的空間。而字符串去重的話,會產(chǎn)生不同的字符串對象,它僅會共享底層的char[]數(shù)組。
/** * String deduplication vs interning test */ public class StringDedupTest { private static final int MAX_EXPECTED_ITERS = 300; private static final int FULL_ITER_SIZE = 100 * 1000; //30M entries = 120M RAM (for 300 iters) private static List<String> LIST = new ArrayList<>( MAX_EXPECTED_ITERS * FULL_ITER_SIZE ); public static void main(String[] args) throws InterruptedException { //24+24 bytes per String (24 String shallow, 24 char[]) //136M left for Strings //Unique, dedup //136M / 2.9M strings = 48 bytes (exactly String size) //Non unique, dedup //4.9M Strings, 100 char[] //136M / 4.9M strings = 27.75 bytes (close to 24 bytes per String + small overhead //Non unique, intern //We use 120M (+small overhead for 100 strings) until very late, but can't extend ArrayList 3 times - we don't have 360M /* Run it with: -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics Give as much Xmx as you can on your box. This test will show you how long does it take to run a single deduplication and if it is run at all. To test when deduplication is run, try changing a parameter of Thread.sleep or comment it out. You may want to print garbage collection information using -XX:+PrintGCDetails -XX:+PrintGCTimestamps */ //Xmx256M - 29 iterations fillUnique(); /* This couple of tests compare string deduplication (first test) with string interning. Both tests should be run with the identical Xmx setting. I have tuned the constants in the program for Xmx256M, but any higher value is also good enough. The point of this tests is to show that string deduplication still leaves you with distinct String objects, each of those requiring 24 bytes. Interning, on the other hand, return you existing String objects, so the only memory you spend is for the LIST object. */ //Xmx256M - 49 iterations (100 unique strings) //fillNonUnique( false ); //Xmx256M - 299 iterations (100 unique strings) //fillNonUnique( true ); } private static void fillUnique() throws InterruptedException { int iters = 0; final UniqueStringGenerator gen = new UniqueStringGenerator(); while ( true ) { for ( int i = 0; i < FULL_ITER_SIZE; ++i ) LIST.add( gen.nextUnique() ); Thread.sleep( 300 ); System.out.println( "Iteration " + (iters++) + " finished" ); } } private static void fillNonUnique( final boolean intern ) throws InterruptedException { int iters = 0; final UniqueStringGenerator gen = new UniqueStringGenerator(); while ( true ) { for ( int i = 0; i < FULL_ITER_SIZE; ++i ) LIST.add( intern ? gen.nextNonUnique().intern() : gen.nextNonUnique() ); Thread.sleep( 300 ); System.out.println( "Iteration " + (iters++) + " finished" ); } } private static class UniqueStringGenerator { private char upper = 0; private char lower = 0; public String nextUnique() { final String res = String.valueOf( upper ) + lower; if ( lower < Character.MAX_VALUE ) lower++; else { upper++; lower = 0; } return res; } public String nextNonUnique() { final String res = "a" + lower; if ( lower < 100 ) lower++; else lower = 0; return res; } } }
Java 8 update 20中添加了字符串去重的特性。它是G1垃圾回收器的一部分,因此你必須使用G1回收器才能啟用它:
-XX:+UseG1GC -XX:+UseStringDeduplication
字符串去重是G1的一個可選的階段。它取決于當(dāng)前的系統(tǒng)負(fù)載。
字符串去重會查詢內(nèi)容相同那些字符串,并將它們底層存儲字符的char[]數(shù)組進(jìn)行統(tǒng)一。使用這一特性你不需要寫任何代碼,不過這意味著最后你得到的是不同的字符串對象,每個對象會占用24個字節(jié)。有的時候顯式地調(diào)用String.intern進(jìn)行駐留還是有必要的。
字符串去重不會對年輕的字符串進(jìn)行處理。字符串處理的最小年齡是通過-XX:StringDeduplicationAgeThreshold=3的JVM參數(shù)來進(jìn)行管理的(3是這個參數(shù)的默認(rèn)值)
感謝各位的閱讀,以上就是“如何理解字符串常量池”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對如何理解字符串常量池這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!
當(dāng)前題目:如何理解字符串常量池
本文鏈接:http://aaarwkj.com/article48/igsihp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、微信公眾號、面包屑導(dǎo)航、靜態(tài)網(wǎng)站、網(wǎng)站內(nèi)鏈、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)