Java中字符串去重的特征引見。本站提示廣大學習愛好者:(Java中字符串去重的特征引見)文章只能為提供參考,不一定能成為您想要的結果。以下是Java中字符串去重的特征引見正文
字符串在任何運用中都占用了年夜量的內存。特別數包括自力UTF-16字符的char[]數組對JVM內存的消費進獻最多——由於每一個字符占用2位。
內存的30%被字符串消費實際上是很罕見的,不只是由於字符串是與我們互動的最好的格局,並且是因為風行的HTTP API應用了年夜量的字符串。應用Java 8 Update 20,我們如今可以接觸到一個新特征,叫做字符串去重,該特征須要G1渣滓收受接管器,該渣滓收受接管器默許是被封閉的。
字符串去厚利用了字符串外部現實是char數組,而且是final的特征,所以JVM可以隨意率性的把持他們。
關於字符串去重,開辟者斟酌了年夜量的戰略,但終究的完成采取了上面的方法:
不管什麼時候渣滓收受接管器拜訪了String對象,它會對char數組停止一個標志。它獲得char數組的hash value並把它和一個對數組的弱援用存在一路。只需渣滓收受接管器發明另外一個字符串,而這個字符串和char數組具有雷同的hash code,那末就會對二者停止一個字符一個字符的比對。
假如他們正好婚配,那末一個字符串就會被修正,指向第二個字符串的char數組。第一個char數組就不再被援用,也便可以被收受接管了。
這全部進程固然帶來了一些開支,然則被很緊實的下限掌握了。例如,假如一個字符未發明有反復,那末一段時光以內,它會不再被檢討。
那末該特征現實上是怎樣任務的呢?起首,你須要方才宣布的Java 8 Update 20,然後依照這個設置裝備擺設: -Xmx256m -XX:+UseG1GC 去運轉以下的代碼:
public class LotsOfStrings { private static final LinkedList<String> LOTS_OF_STRINGS = new LinkedList<>(); public static void main(String[] args) throws Exception { int iteration = 0; while (true) { for (int i = 0; i < 100; i++) { for (int j = 0; j < 1000; j++) { LOTS_OF_STRINGS.add(new String("String " + j)); } } iteration++; System.out.println("Survived Iteration: " + iteration); Thread.sleep(100); } } }
這段代碼會履行30個迭代以後報OutOfMemoryError。
如今,開啟字符串去重,應用以下設置裝備擺設去跑上述代碼:
-Xmx256m -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics
此時它曾經可以運轉更長的時光,並且在50個迭代以後才終止。
JVM如今異樣打印出了它做了甚麼,讓我們一路看一下:
[GC concurrent-string-deduplication, 4658.2K->0.0B(4658.2K), avg 99.6%, 0.0165023 secs] [Last Exec: 0.0165023 secs, Idle: 0.0953764 secs, Blocked: 0/0.0000000 secs] [Inspected: 119538] [Skipped: 0( 0.0%)] [Hashed: 119538(100.0%)] [Known: 0( 0.0%)] [New: 119538(100.0%) 4658.2K] [Deduplicated: 119538(100.0%) 4658.2K(100.0%)] [Young: 372( 0.3%) 14.5K( 0.3%)] [Old: 119166( 99.7%) 4643.8K( 99.7%)] [Total Exec: 4/0.0802259 secs, Idle: 4/0.6491928 secs, Blocked: 0/0.0000000 secs] [Inspected: 557503] [Skipped: 0( 0.0%)] [Hashed: 556191( 99.8%)] [Known: 903( 0.2%)] [New: 556600( 99.8%) 21.2M] [Deduplicated: 554727( 99.7%) 21.1M( 99.6%)] [Young: 1101( 0.2%) 43.0K( 0.2%)] [Old: 553626( 99.8%) 21.1M( 99.8%)] [Table] [Memory Usage: 81.1K] [Size: 2048, Min: 1024, Max: 16777216] [Entries: 2776, Load: 135.5%, Cached: 0, Added: 2776, Removed: 0] [Resize Count: 1, Shrink Threshold: 1365(66.7%), Grow Threshold: 4096(200.0%)] [Rehash Count: 0, Rehash Threshold: 120, Hash Seed: 0x0] [Age Threshold: 3] [Queue] [Dropped: 0]
為了便利,我們不須要本身去盤算一切數據的加和,應用便利的總計便可以了。
下面的代碼段劃定履行了字符串去重,花了16ms的時光,檢查了約 120 k 字符串。
下面的特征是剛推出的,意味著能夠並沒有被周全的審閱。詳細的數據在現實的運用中能夠看起來有差異,特別是那些運用中字符串被屢次應用和傳遞,是以一些字符串能夠被跳過或許早就有了hashcode(正如你能夠曉得的那樣,一個String的hash code是被懶加載的)。
在上述的案例中,一切的字符串都被去重了,在內存中移除4.5MB的數據。
[Table]部門給出了有關外部跟蹤表的統計信息,[Queue]則列出了有若干對去重的要求因為負載被拋棄,這也是開支削減機制中的一部門。
那末,字符串去重和字符串駐留比擬又有甚麼差異呢?現實上,字符串去重和駐留看起來差不多,除暫留的機制重用了全部字符串實例,而不只僅是字符數組。
JDK Enhancement Proposal 192的發明者的爭辯點在於開辟者們經常不曉得將駐留字符串放在哪裡適合,或許是適合的處所被框架所隱蔽.就像我寫的那樣,當碰著復制字符串(像國度名字)的時刻,你須要一些知識.字符串去重,關於在統一個JVM中的運用法式的字符串復制也有利益,異樣包含像XML Schemas,urls和jar名字等普通以為不會湧現屢次的字符串.
當字符串駐留產生在運用法式線程中的時刻,渣滓收受接管異步並發處置時,字符串去重也不會增長運轉時的消費.這也說明了,為何我們會在下面的代碼中發明Thread.sleep().假如沒有sleep會給GC增長太多的壓力,如許字符串去重基本就不會產生.然則,這只是示例代碼才會湧現的成績.現實的運用法式,經常會在運轉字符串去重的時刻應用幾毫秒的時光.