中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么優化Java編程中的性能

發布時間:2021-01-06 16:45:52 來源:億速云 閱讀:161 作者:Leah 欄目:編程語言

怎么優化Java編程中的性能?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

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

  現在,我們就從String對象的實現、特性以及實際使用中的優化這幾方面來入手,深入理解以下String的性能優化。

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

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對象是如何實現的?

  Java中對String對象做了大量的優化,以此來節約內存空間,提升String對象的性能。下圖是Java6 -> Java9 String對象屬性的變化:

怎么優化Java編程中的性能

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

  • 在Java6及以前的版本中,String對象是對char數組進行了封裝實現的對象,主要有:char數組、偏移量offset、字符數量count、哈希值hash。String對象通過offset和count屬性來定位char數組,獲取字符串。這樣做可以高效快速地共享數組對象,能節省內存空間,但容易出現內存泄漏。

  • 從Java7到Java8版本,Java對String做了一些改變。String類中不再有offset和count兩個屬性了。這樣做可以使String對象占用的內存減少,并且String.substring方法也不再共享char[],解決了可能出現的內存泄漏的問題。

  • 從Java9版本開始,將char[]改成了byte[],并增加了新屬性coder,coder是一個編碼格式的標識。

  為什么要這么改呢?

  我們知道,一個char字符占16位,2個字節。這種情況下存儲單字節的字符就很容易浪費了。JDK1.9的String類為了節省內存空間,就使用了占8位,1個字節的byte數組來存儲字符串。

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

  String對象的不可變性

  如果看過String的源碼,就會發現,String類是被final關鍵字修飾的,且變量char數組也被final修飾。

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

  這樣做的好處在哪里?  

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

  第二.、保證hash屬性值不會頻繁變更,確保了唯一性。使得類似HashMap容器才能實現相應的key-value緩存功能。

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

  當代碼中使用第一種方式創建字符串對象時,JVM首先檢查該對象是否在字符串常量池中,如果在就返回該對象的引用,否則新的字符串將在常量池中創建。這種方式可以減少同一個值的字符串對象的重復創建,節約內存。

  第二種方式,首先在編譯類文件時,"abc"常量字符串將會放入到常量結構中,在類加載時,"abc"會在常量池中創建;然后調用new時,JVM命令將會調用String的構造函數,同時引用常量池的"abc"字符串,在堆內存中創建一個String對象,最后str引用String對象。

  String對象的優化

  1.如何構建超大字符串

  編程過程中字符串的拼接很常見。如果使用String對象相加,拼接我們想要的字符串,會不會產生多個對象呢?比如說以下代碼:

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

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

  但實際上,會發現只有一個對象生成,這是為什么呢?編譯時編譯器會自動幫我們優化代碼,使得最后只得出一個對象“abcdef”。

  再來看看,如果進行字符串常量的累計,又會出現什么結果?

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

  上面的代碼編譯后,編譯器同樣對代碼進行了優化,在進行字符串拼接時,偏向使用StringBuilder,這樣可以提升效率。上面的代碼變成了下面這樣:

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

  總結:即使使用+號作為字符串的拼接,一樣可以被編譯器優化成StringBuilder的方式。但如果每次循環都生成一個新的StringBuilder實例,同樣會降低系統的性能。所以平時做字符串拼接的時候,建議還是顯示使用StringBuilder來提升性能。在多線程編程時,String對象的拼接涉及到了線程安全,可以使用StringBuffer。但由于StringBuffer是線程安全的,涉及到鎖競爭,所以就性能上來說會比StringBuilder差些。

  2.如何使用String.intern節省內存?

  對于一些數據,數據量非常大,但同時又有大部分重合的,該如何處理呢?

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

  我們再來看一個例子:

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

  輸出結果是: a == b

  在字符串常量池中,默認會將對象放入常量池;在字符串變量中,對象總是創建在堆內存的,同時也會在常量池中創建一個字符串對象,復制到堆內存對象中,并返回堆內存對象引用。

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

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

  spilt()方法使用了正則表達式實現了強大的分割功能,而正則表達式的性能是非常不穩定的,使用不當會引起回溯問題,很可能導致CPU居高不下。

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

看完上述內容,你們掌握怎么優化Java編程中的性能的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

张家川| 四子王旗| 克什克腾旗| 民和| 长武县| 宁河县| 长海县| 全椒县| 宁阳县| 克什克腾旗| 壶关县| 张掖市| 罗定市| 林西县| 汶川县| 梨树县| 雷山县| 唐海县| 云梦县| 安达市| 金川县| 屏东市| 交城县| 莒南县| 四川省| 丹巴县| 乐东| 康马县| 双辽市| 克什克腾旗| 沾化县| 博罗县| 连平县| 宜阳县| 婺源县| 十堰市| 寿宁县| 西丰县| 孟村| 长寿区| 义马市|