欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java的字符串常量池StringTable詳解

 更新時(shí)間:2023年11月29日 09:05:10   作者:安然望川海  
這篇文章主要介紹了Java的字符串常量池StringTable詳解,JVM為了提高性能和減少內(nèi)存開銷,在實(shí)例化字符串常量的時(shí)候進(jìn)行了一些優(yōu)化,為 了減少在JVM中創(chuàng)建的字符串的數(shù)量,字符串類維護(hù)了一個(gè)字符串池,需要的朋友可以參考下

什么是字符串常量池

字符串的分配,和其他的對(duì)象分配一樣,耗費(fèi)高昂的時(shí)間與空間代價(jià)。JVM為了提高性能和減少內(nèi)存開銷,在實(shí)例化字符串常量的時(shí)候進(jìn)行了一些優(yōu)化。為 了減少在JVM中創(chuàng)建的字符串的數(shù)量,字符串類維護(hù)了一個(gè)字符串池,每當(dāng)代碼創(chuàng)建字符串常量時(shí),JVM會(huì)首先檢查字符串常量池。如果字符串已經(jīng)存在池中, 就返回池中的實(shí)例引用。如果字符串不在池中,就會(huì)實(shí)例化一個(gè)字符串并放到池中。

Java能夠進(jìn)行這樣的優(yōu)化是因?yàn)樽址遣豢勺兊模梢圆挥脫?dān)心數(shù)據(jù)沖突 進(jìn)行共享

string的string Pool是一個(gè)圉定大小的Hashtable,默認(rèn)值大小長(zhǎng)度是1009。如果放進(jìn)string Pool的string非常多,就會(huì)造成Hash沖突嚴(yán)重,從而導(dǎo)致鏈表會(huì)很長(zhǎng),而鏈表長(zhǎng)了后直接會(huì)造成的影響就是當(dāng)調(diào)用string.intern時(shí)性能會(huì)大幅下降。

參數(shù)設(shè)置

使用-xX :StringTablesize可設(shè)置stringTable的長(zhǎng)度

在jdk6中stringTable是固定的,就是1009的長(zhǎng)度,所以如果常量池中的字符串過多就會(huì)導(dǎo)致效率下降很快。StringTablesize設(shè)置沒有要求在jdk7中,stringTable的長(zhǎng)度默認(rèn)值是60013 jdk8開始,1009是可設(shè)置的最小值

字符串常量池中是不會(huì)存儲(chǔ)相同內(nèi)容的字符串的(equals方法返回為true,即字面量相等)

常量池位置的調(diào)整

Java 6及以前,字符串常量池存放在永久代。 Java 7 中 Oracle 的工程師對(duì)字符串池的邏輯做了很大的改變,即將字符串常量池的位置調(diào)整到Java堆內(nèi)。

所有的字符串都保存在堆(Heap)中,和其他普通對(duì)象一樣,這樣可以讓你在進(jìn)行調(diào)優(yōu)應(yīng)用時(shí)僅需要調(diào)整堆大小就可以了

字符串常量池概念原本使用得比較多,但是這個(gè)改動(dòng)使得我們有足夠的理由讓我們重新考慮在Java 7 中使用string.intern()。

Java8元空間,字符串常量在堆內(nèi)存中

為什么要從永久代調(diào)整位置到堆中?

因?yàn)槲覀冊(cè)陂_發(fā)中會(huì)創(chuàng)建大量的字符串常量,回收效率低,導(dǎo)致永久代空間不足。放在堆里能及時(shí)回收內(nèi)存

方法原理

常量與常量的拼接結(jié)果在常量池,原理是編譯期優(yōu)化

public class StringTest {
    public static final String A = "a";
    public static void main(String[] args) {
        String s = "a"+"b";//兩個(gè)字符串常量拼接
        String s1 = "ab";
        String s2 = A+"b";//常量引用和字符串常量拼接
        System.out.println(s == s1);
        System.out.println(s == s2);
        System.out.println(s2 == s1);
    }
}

以上三個(gè)字符串都存儲(chǔ)在常量池中,所以打印結(jié)果都是true 因?yàn)楸籪inal修飾的字符串會(huì)在編譯期放入常量池

只要其中有一個(gè)是變量,結(jié)果就在堆中。變量拼接的原理是stringBuilder

		String s1 = "ab";
        String s2 = "a";
        String s3 = s2+"b";

字節(jié)碼

在這里插入圖片描述

可以看到String s3 = s2+"b";的過程等價(jià)于

第6行表示new一個(gè)StringBuilder對(duì)象 (jdk5之前是StringBuffer)

第10行執(zhí)行構(gòu)造方法, 14行表示調(diào)用append方法拼接“a”

19行同理拼接“b”, 然后第22行表示調(diào)用toString方法

StringBuilder sb = new StringBuilder();//6
        sb.append("a");//14
        sb.append("b");//19
        sb.toString();//22

查看StringBuilder源碼:oString方法new 了一個(gè)區(qū)別于常量池地址的新的String 對(duì)象

public String toString() {
	        // Create a copy, don't share the array
	        return new String(value, 0, count);
	    }

如果拼接的結(jié)果調(diào)用intern ()方法,則主動(dòng)將常量池中還沒有的字符串對(duì)象放入池中,并返回此對(duì)象地址。

 private static void test2() {
        String s1 = "ab";
        String s2 = "a";
        String s3 = s2+"b";
        String s4 = s3.intern();
        System.out.println(s3 == s1);//false
        System.out.println(s4 == s1);//true
    }

intern 方法會(huì)先看常量池中是否有“ab”,若有,直接返回地址,若沒有,創(chuàng)建“ab”到常量池并返回結(jié)果。 所以s4的地址指向常量池中的“ab”;

append和+的效率

我們已經(jīng)通過字節(jié)碼看到“a”+“b”會(huì)new一個(gè)StringBuilder,調(diào)用append方法并toString,此時(shí)new 了一個(gè)StringBuilder,new 了一個(gè)String,注意此時(shí)常量池不會(huì)創(chuàng)建“ab”常量

所以在一個(gè)循環(huán)里String a += "xxx"的效率是遠(yuǎn)低于在循環(huán)中sb.append(“xxx”)最后toSring的效率的,后者少創(chuàng)建了相當(dāng)于n-1個(gè)StringBuiler對(duì)象 再看StringBuiler的構(gòu)造方法:

/**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }
/**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

默認(rèn)數(shù)組長(zhǎng)度是16,如果拼接的操作遠(yuǎn)大于16,也是會(huì)引起頻繁擴(kuò)容的,所以如果知道字符串的最終長(zhǎng)度不超過某個(gè)值,可以直接將這個(gè)值通過構(gòu)造器傳入以提高性能

面試題

new string(“a”)創(chuàng)建了幾個(gè)對(duì)象?

private static void test4(){
        String s1 = new String("a");
    
    }

字節(jié)碼

0 new #14 <java/lang/String>
 3 dup
 4 ldc #7 <a>
 6 invokespecial #15 <java/lang/String.<init>>
 9 astore_0
10 return

可以看到答案是兩個(gè),new一個(gè)Stirng對(duì)象字節(jié)碼第0行,方法區(qū)放入“a”對(duì)象對(duì)應(yīng)字節(jié)碼第4行(如果之前常量池沒有的話)

new string(“a”) + new string(“b”)創(chuàng)建了幾個(gè)對(duì)象?

String s1 = new String("a") + new String("b");

字節(jié)碼

 0 new #8 <java/lang/StringBuilder>
 3 dup
 4 invokespecial #9 <java/lang/StringBuilder.<init>>
 7 new #14 <java/lang/String>
10 dup
11 ldc #7 <a>
13 invokespecial #15 <java/lang/String.<init>>
16 invokevirtual #10 <java/lang/StringBuilder.append>
19 new #14 <java/lang/String>
22 dup
23 ldc #11 <b>
25 invokespecial #15 <java/lang/String.<init>>
28 invokevirtual #10 <java/lang/StringBuilder.append>
31 invokevirtual #12 <java/lang/StringBuilder.toString>
34 astore_0
35 return

可以看到常量池兩個(gè)對(duì)象常量“a”和“b”(11和23行),兩個(gè)new的String對(duì)象(7和19),還有一個(gè)StringBudder對(duì)象(0),特別注意31行的toString方法又new了一個(gè)“ab”的對(duì)象,而不放入字符串常量池,所以答案是6個(gè)

intern方法的更改

private static void test5(){
        String s1 = new String("a") ;
        s1.intern();
        String s2 = "a";
        System.out.println(s1 == s2);//false

    }

這里結(jié)果為false,很正常,注意的是s1.intern();并沒有返回重新賦值,如果修改為s1 = s1.intern();則結(jié)果true;

再修改一下:

private static void test5(){
        String s1 = new String("a") + new String ("b");
        s1.intern();
        String s2 = "ab";
        System.out.println(s1 == s2);//false?

    }

我們知道第一段代碼并沒有在常量池生成“ab”,第二行代碼生成了但是沒返回,結(jié)果是不是false?

很遺憾,jdk6及其之前是false,但是jdk7以后是true,為什么呢,這就是提一下intern在jdk7之后做的改動(dòng):如果常量池沒有對(duì)應(yīng)的字符串常量------- jdk6以及之前,該方法會(huì)在常量池中創(chuàng)建新的對(duì)象,這很好理解返回結(jié)果是false,但是在jdk7之后,如果堆空間已經(jīng)有了我們第一行new的“ab”后,只會(huì)在常量池創(chuàng)建一個(gè)指向前者的指針,所以返回是true;更進(jìn)一步,去掉代碼s1.intern();則結(jié)果就是false了,因?yàn)檫@時(shí)候常量池中是新的對(duì)象,而非第一個(gè)對(duì)象的指針

如果你現(xiàn)在回頭看上一個(gè)案例,為應(yīng)該發(fā)現(xiàn)的是重點(diǎn)是String s1 = new String(“a”) ;和String s1 = new String(“a”) + new String (“b”);的區(qū)別:后者沒有在常量池生成“ab”對(duì)象.

到此這篇關(guān)于Java的字符串常量池StringTable詳解的文章就介紹到這了,更多相關(guān)StringTable詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java過濾XSS腳本攻擊詳細(xì)代碼示例

    Java過濾XSS腳本攻擊詳細(xì)代碼示例

    這篇文章主要介紹了Java過濾XSS腳本攻擊的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • java多線程實(shí)現(xiàn)取款小程序

    java多線程實(shí)現(xiàn)取款小程序

    這篇文章主要為大家詳細(xì)介紹了java多線程實(shí)現(xiàn)取款小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • 詳解SpringBoot中Session超時(shí)原理說明

    詳解SpringBoot中Session超時(shí)原理說明

    本篇文章主要介紹了詳解SpringBoot中Session超時(shí)原理說明,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • Calcite使用SQL實(shí)現(xiàn)查詢excel內(nèi)容

    Calcite使用SQL實(shí)現(xiàn)查詢excel內(nèi)容

    因?yàn)閏alcite本身沒有excel的適配器,?所以本文將模仿calcite-file,?搞一個(gè)calcite-file-excel實(shí)現(xiàn)查詢excel內(nèi)容,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-01-01
  • Java統(tǒng)計(jì)50個(gè)10到50之間整數(shù)的隨機(jī)出現(xiàn)次數(shù)

    Java統(tǒng)計(jì)50個(gè)10到50之間整數(shù)的隨機(jī)出現(xiàn)次數(shù)

    這篇文章主要為大家詳細(xì)介紹了Java統(tǒng)計(jì)50個(gè)10到50之間整數(shù)的隨機(jī)出現(xiàn)次數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • springboot2.x整合tkmapper的示例代碼

    springboot2.x整合tkmapper的示例代碼

    這篇文章主要介紹了springboot2.x整合tkmapper,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • 詳解Spring MVC4 純注解配置教程

    詳解Spring MVC4 純注解配置教程

    本篇文章主要介紹了Spring MVC4 純注解配置教程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • servlet的url-pattern匹配規(guī)則詳細(xì)描述(小結(jié))

    servlet的url-pattern匹配規(guī)則詳細(xì)描述(小結(jié))

    在利用servlet或Filter進(jìn)行url請(qǐng)求的匹配時(shí),很關(guān)鍵的一點(diǎn)就是匹配規(guī)則。這篇文章主要介紹了servlet的url-pattern匹配規(guī)則詳細(xì)描述(小結(jié)),非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-07-07
  • 基于Maven?pom文件中屬性變量總結(jié)

    基于Maven?pom文件中屬性變量總結(jié)

    這篇文章主要介紹了Maven?pom文件中屬性變量總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringMVC 參數(shù)綁定意義及實(shí)現(xiàn)過程解析

    SpringMVC 參數(shù)綁定意義及實(shí)現(xiàn)過程解析

    這篇文章主要介紹了SpringMVC 參數(shù)綁定意義及實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論