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

Java編程中的性能優(yōu)化如何實(shí)現(xiàn)

 更新時(shí)間:2019年10月19日 08:33:33   作者:盛世半月  
這篇文章主要介紹了Java編程中的性能優(yōu)化如何實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

   String作為我們使用最頻繁的一種對(duì)象類型,其性能問題是最容易被忽略的。作為Java中重要的數(shù)據(jù)類型,是內(nèi)存中占據(jù)空間比較大的一個(gè)對(duì)象。如何高效地使用字符串,可以幫助我們提升系統(tǒng)的整體性能。

  現(xiàn)在,我們就從String對(duì)象的實(shí)現(xiàn)、特性以及實(shí)際使用中的優(yōu)化這幾方面來(lái)入手,深入理解以下String的性能優(yōu)化。

  在這之前,首先看一個(gè)問題。通過三種方式創(chuàng)建三個(gè)對(duì)象,然后依次兩兩匹配,得出的結(jié)果是什么?答案留到最后揭曉。

String str1 = "abc";
String str2 = new String("abc");
String str3 = str2.intern();
System.out.println(str1 == str2);
System.out.println(str2 == str3);
System.out.println(str1 == str3);
  

  String對(duì)象是如何實(shí)現(xiàn)的?

  Java中對(duì)String對(duì)象做了大量的優(yōu)化,以此來(lái)節(jié)約內(nèi)存空間,提升String對(duì)象的性能。下圖是Java6 -> Java9 String對(duì)象屬性的變化:

  可以看到,String的屬性有了以下的變化:

  • 在Java6及以前的版本中,String對(duì)象是對(duì)char數(shù)組進(jìn)行了封裝實(shí)現(xiàn)的對(duì)象,主要有:char數(shù)組、偏移量offset、字符數(shù)量count、哈希值hash。String對(duì)象通過offset和count屬性來(lái)定位char數(shù)組,獲取字符串。這樣做可以高效快速地共享數(shù)組對(duì)象,能節(jié)省內(nèi)存空間,但容易出現(xiàn)內(nèi)存泄漏。
  • 從Java7到Java8版本,Java對(duì)String做了一些改變。String類中不再有offset和count兩個(gè)屬性了。這樣做可以使String對(duì)象占用的內(nèi)存減少,并且String.substring方法也不再共享char[],解決了可能出現(xiàn)的內(nèi)存泄漏的問題。
  • 從Java9版本開始,將char[]改成了byte[],并增加了新屬性coder,coder是一個(gè)編碼格式的標(biāo)識(shí)。

  為什么要這么改呢?

  我們知道,一個(gè)char字符占16位,2個(gè)字節(jié)。這種情況下存儲(chǔ)單字節(jié)的字符就很容易浪費(fèi)了。JDK1.9的String類為了節(jié)省內(nèi)存空間,就使用了占8位,1個(gè)字節(jié)的byte數(shù)組來(lái)存儲(chǔ)字符串。

  coder屬性的作用是:在計(jì)算字符串長(zhǎng)度或者使用indexOf()時(shí),需要根據(jù)這個(gè)字段,判斷如何計(jì)算字符串的長(zhǎng)度。coder屬性值默認(rèn)有0和1兩個(gè)值,0代表Latin-1(單字節(jié)編碼),1代表UTF-16。如果String判斷字符串只包含Latin-1,則coder值取0,反之為1。

  String對(duì)象的不可變性

  如果看過String的源碼,就會(huì)發(fā)現(xiàn),String類是被final關(guān)鍵字修飾的,且變量char數(shù)組也被final修飾。

  一個(gè)類被final修飾代表著該類不可繼承,char[]被private和final修飾著,代表String對(duì)象不可被更改。這就叫做String對(duì)象的不可變性。即如果String對(duì)象一旦創(chuàng)建成功了,就不能再對(duì)它進(jìn)行改變。

  這樣做的好處在哪里?  

  第一、保證了String對(duì)象的安全性。假設(shè)String對(duì)象是可變的,那么String對(duì)象就會(huì)被惡意修改。

  第二.、保證hash屬性值不會(huì)頻繁變更,確保了唯一性。使得類似HashMap容器才能實(shí)現(xiàn)相應(yīng)的key-value緩存功能。

  第三、可以實(shí)現(xiàn)字符串常量池。Java中,通常有2種創(chuàng)建字符串對(duì)象的方式,一種是通過字符串常量的方式創(chuàng)建,如String str = "abc";另一種是字符串常量通過new形式的創(chuàng)建,如String str = new String("abc")。

  當(dāng)代碼中使用第一種方式創(chuàng)建字符串對(duì)象時(shí),JVM首先檢查該對(duì)象是否在字符串常量池中,如果在就返回該對(duì)象的引用,否則新的字符串將在常量池中創(chuàng)建。這種方式可以減少同一個(gè)值的字符串對(duì)象的重復(fù)創(chuàng)建,節(jié)約內(nèi)存。

  第二種方式,首先在編譯類文件時(shí),"abc"常量字符串將會(huì)放入到常量結(jié)構(gòu)中,在類加載時(shí),"abc"會(huì)在常量池中創(chuàng)建;然后調(diào)用new時(shí),JVM命令將會(huì)調(diào)用String的構(gòu)造函數(shù),同時(shí)引用常量池的"abc"字符串,在堆內(nèi)存中創(chuàng)建一個(gè)String對(duì)象,最后str引用String對(duì)象。

  

  String對(duì)象的優(yōu)化

  1.如何構(gòu)建超大字符串

  編程過程中字符串的拼接很常見。如果使用String對(duì)象相加,拼接我們想要的字符串,會(huì)不會(huì)產(chǎn)生多個(gè)對(duì)象呢?比如說(shuō)以下代碼:

String str = "ab" + "cd" + "ef";

  分析代碼可知:首先會(huì)生成ab對(duì)象,再生成abcd對(duì)象,最后生成abcdef對(duì)象。理論上說(shuō),代碼很低效。

  但實(shí)際上,會(huì)發(fā)現(xiàn)只有一個(gè)對(duì)象生成,這是為什么呢?編譯時(shí)編譯器會(huì)自動(dòng)幫我們優(yōu)化代碼,使得最后只得出一個(gè)對(duì)象“abcdef”。

  再來(lái)看看,如果進(jìn)行字符串常量的累計(jì),又會(huì)出現(xiàn)什么結(jié)果?

String str = "abcdef";
for (int i = 0; i < 100; i++) {
   str = str + i;
 }

  上面的代碼編譯后,編譯器同樣對(duì)代碼進(jìn)行了優(yōu)化,在進(jìn)行字符串拼接時(shí),偏向使用StringBuilder,這樣可以提升效率。上面的代碼變成了下面這樣:

String str = "abcdef";
for (int i = 0; i < 100; i++) {
  str = (new StringBuilder(String.valueOf(str))).append(i).toString();
}

  總結(jié):即使使用+號(hào)作為字符串的拼接,一樣可以被編譯器優(yōu)化成StringBuilder的方式。但如果每次循環(huán)都生成一個(gè)新的StringBuilder實(shí)例,同樣會(huì)降低系統(tǒng)的性能。所以平時(shí)做字符串拼接的時(shí)候,建議還是顯示使用StringBuilder來(lái)提升性能。在多線程編程時(shí),String對(duì)象的拼接涉及到了線程安全,可以使用StringBuffer。但由于StringBuffer是線程安全的,涉及到鎖競(jìng)爭(zhēng),所以就性能上來(lái)說(shuō)會(huì)比StringBuilder差些。

  2.如何使用String.intern節(jié)省內(nèi)存?

  對(duì)于一些數(shù)據(jù),數(shù)據(jù)量非常大,但同時(shí)又有大部分重合的,該如何處理呢?

  具體做法是,每次賦值的時(shí)候使用String的intern方法,如果常量池中有相同值,就會(huì)重復(fù)使用該對(duì)象,返回對(duì)象的引用,這樣一開始的對(duì)象就可以被回收掉了,這樣的話數(shù)據(jù)量就會(huì)大幅度降低了。

  我們?cè)賮?lái)看一個(gè)例子:

String a = new String("abc").intern();
String b = new String("abc").intern(); 
if (a == b) {
   System.out.println("a == b");
}

  輸出結(jié)果是: a == b

  在字符串常量池中,默認(rèn)會(huì)將對(duì)象放入常量池;在字符串變量中,對(duì)象總是創(chuàng)建在堆內(nèi)存的,同時(shí)也會(huì)在常量池中創(chuàng)建一個(gè)字符串對(duì)象,復(fù)制到堆內(nèi)存對(duì)象中,并返回堆內(nèi)存對(duì)象引用。

  如果調(diào)用intern方法,會(huì)去查看字符串常量池中是否有等于該對(duì)象的字符串,如果沒有,就會(huì)在常量池中新增該對(duì)象,并返回該對(duì)象引用;如果有則返回常量池中的字符串引用。堆內(nèi)存中原有的對(duì)象由于沒有引用指向它,將會(huì)通過垃圾回收器回收。

  3.如何使用字符串的分割方法?

  spilt()方法使用了正則表達(dá)式實(shí)現(xiàn)了強(qiáng)大的分割功能,而正則表達(dá)式的性能是非常不穩(wěn)定的,使用不當(dāng)會(huì)引起回溯問題,很可能導(dǎo)致CPU居高不下。

  所以要慎重使用spilt方法,我們可以用String.indexOf()方法代替spilt()方法完成字符串的分割,如果實(shí)在無(wú)法滿足需求,就在使用spilt方法時(shí),對(duì)回溯問題加以重視就可以了。

  總結(jié)

  通過上面的敘述,我們認(rèn)識(shí)到了做好String字符串性能的優(yōu)化,可以提升整個(gè)系統(tǒng)的性能。在這個(gè)理論基礎(chǔ)上,Java版本在迭代中不斷更改成員變量,節(jié)約內(nèi)存空間,對(duì)String性能進(jìn)行了優(yōu)化。

  我們還提到了String對(duì)象的不可變性,正是這個(gè)特性實(shí)現(xiàn)了字符串常量池,通過減少同一個(gè)值的字符串對(duì)象的重復(fù)創(chuàng)建,進(jìn)一步節(jié)約內(nèi)存。也是因?yàn)檫@個(gè)特性,我們?cè)谧鲩L(zhǎng)字符串的拼接時(shí),需要顯示使用StringBuilder,以提升字符串的拼接性能。最后在優(yōu)化方面,我們還可以使用intern方法,讓變量字符串對(duì)象重復(fù)使用常量池中相同值的對(duì)象,進(jìn)而節(jié)約內(nèi)存。

  最后,公布上面那道題的結(jié)果:

  false、false、true。

  其中, String str1 = “abc”;通過字面量的方式創(chuàng)建,abc存儲(chǔ)于字符串常量池中;

  String str2 = new String("abc");通過new對(duì)象的方式創(chuàng)建字符串對(duì)象,引用地址存放在堆內(nèi)存中,abc則存放在字符串常量池中,所以為false;

  String str3 = str2.intern();由于調(diào)用了intern()方法,會(huì)返回常量池中的數(shù)據(jù),str3此時(shí)就指向常量池中的abc,和str1的方式一樣,所以為true;

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java多態(tài)的全面系統(tǒng)解析

    Java多態(tài)的全面系統(tǒng)解析

    多態(tài)就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量到底會(huì)指向哪個(gè)類的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定
    2022-03-03
  • springboot 整合 freemarker代碼實(shí)例

    springboot 整合 freemarker代碼實(shí)例

    這篇文章主要介紹了springboot 整合 freemarker代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Java數(shù)據(jù)結(jié)構(gòu)之順序表篇

    Java數(shù)據(jù)結(jié)構(gòu)之順序表篇

    順序表,全名順序存儲(chǔ)結(jié)構(gòu),是線性表的一種。線性表用于存儲(chǔ)邏輯關(guān)系為“一對(duì)一”的數(shù)據(jù),順序表自然也不例外,不僅如此,順序表對(duì)數(shù)據(jù)物理存儲(chǔ)結(jié)構(gòu)也有要求。順序表存儲(chǔ)數(shù)據(jù)時(shí),會(huì)提前申請(qǐng)一整塊足夠大小的物理空間,然后將數(shù)據(jù)依次存儲(chǔ)起來(lái),存儲(chǔ)時(shí)數(shù)據(jù)元素間不留縫隙
    2022-01-01
  • springboot集成redis并使用redis生成全局唯一索引ID

    springboot集成redis并使用redis生成全局唯一索引ID

    本文主要介紹了springboot集成redis并使用redis生成全局唯一索引ID,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • java文件上傳至ftp服務(wù)器的方法

    java文件上傳至ftp服務(wù)器的方法

    這篇文章主要為大家詳細(xì)介紹了java文件上傳至ftp服務(wù)器的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 五個(gè)很實(shí)用的IDEA使用技巧分享

    五個(gè)很實(shí)用的IDEA使用技巧分享

    IntelliJ IDEA 是一款優(yōu)秀的 Java 集成開發(fā)環(huán)境,它提供了許多強(qiáng)大的功能和快捷鍵,可以幫助開發(fā)者提高編碼效率和質(zhì)量,本文就在為你介紹博主常用的五個(gè)IntelliJ IDEA使用技巧,希望能夠給你帶來(lái)一些工作效率上的提升
    2023-10-10
  • 詳解MyBatis批量插入數(shù)據(jù)Mapper配置文件的寫法

    詳解MyBatis批量插入數(shù)據(jù)Mapper配置文件的寫法

    本篇文章主要介紹了詳解MyBatis批量插入數(shù)據(jù)Mapper文件的寫法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-04-04
  • Java中的Object類詳細(xì)解讀

    Java中的Object類詳細(xì)解讀

    這篇文章主要介紹了Java中的Object類詳細(xì)解讀,java.lang.Object是類層次結(jié)構(gòu)的根類,即所有其它類的父類,每個(gè)類都使用 Object 作為超類,需要的朋友可以參考下
    2023-11-11
  • java 數(shù)據(jù)結(jié)構(gòu)中棧和隊(duì)列的實(shí)例詳解

    java 數(shù)據(jù)結(jié)構(gòu)中棧和隊(duì)列的實(shí)例詳解

    這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)中棧和隊(duì)列的實(shí)例詳解的相關(guān)資料,主要使用數(shù)組與線性表的方法來(lái)實(shí)現(xiàn),需要的朋友可以參考下
    2017-09-09
  • JAVA?GUI基礎(chǔ)與MouseListener用法

    JAVA?GUI基礎(chǔ)與MouseListener用法

    這篇文章主要介紹了JAVA?GUI基礎(chǔ)與MouseListener用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論