回覆列表
  • 1 # 小碼資訊

    在JAVA程式設計中,字串是我們最常用的一個物件型別,作為Java中重要的資料型別,是記憶體中佔據空間比較大的一個物件。如何高效地使用字串,可以幫助我們提升系統的整體效能,下面我們就從String物件的實現和使用中來具體分析下String物件底層原理,深入理解後給出String的效能最佳化。

    在講解其具體實現之前我們先給出一個簡單的例子:

    String str01 = new String("hello");

    String str02 = "hello";

    String str03 = str02.intern();

    System.out.println(str01 == str02); 5

    System.out.println(str02 == str03); 6

    System.out.println(str01 == str03);

    在給出具體的結果之前,我們先看看String的實現原理。

    注意,在JAVA中由於JDK版本不同,實現也會有差異,我們分別針對JAVA6到JAVA9來具體講解下String的實現。

    String的屬性變遷:

    JAVA6:

    char[]

    offset

    count

    hash

    JAVA7/8:

    char[]

    hash

    JAVA9:

    byte[]

    coder

    hash

    在JAVA6裡是透過char[]來儲存實際的字串內容的,而且還提供了offset和count來訪問字元陣列,獲取其子串,這樣做雖然可以高效共享和訪問字串,但是,同時也容易造成記憶體洩漏。

    在JAVA7/8裡,直接去掉了offset和count,這樣一方面String更節略記憶體,另外字串變得更加安全和獨立,真正變成只讀,而且其substring方法返回的字串也是獨立的,不再共享其本身的記憶體。

    在JAVA9裡,char[]直接變成了byte[],並且增加了coder(編碼支援),這樣改動有什麼好處呢?我們知道在JAVA裡,char是兩個位元組的,改成byte後變成一個位元組了,這樣會節省記憶體,然後透過coder來標識是單位元組編碼還是雙位元組編碼。

    String物件的不可變性

    在JAVA裡,String物件使用final修飾的,就是說我們不可以繼承,這樣做有如下好處:

    安全,防止透過繼承等進行惡意篡改和替換;

    保證hash值不會頻繁變更,保證其唯一性;

    可以實現字串常量池;

    好了,我們知道了String的特性後,然後看看怎麼樣進行實際的效能最佳化?

    構建超大型字串

    String myStr = "hello" + "," + "world" + "!";

    上述程式碼會依次構建字串,每次構建都會建立新的記憶體,釋放掉舊的,這樣的話如果字串非常大,效能是很槽糕的,會造成記憶體的抖動。

    最佳化:

    使用StringBuilder

    在現場安全的情況下,我們可以使用StringBuilder來實現上述邏輯,這樣子不會頻繁建立和釋放String物件,因為StringBuilder會自動對記憶體進行管理,如果對StringBuilder不明白的可以看看其相關資料或原始碼。

    使用String.intern

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

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

      我們再來看一個例子:

    String str01 = new String("hello").intern(); String str02 = new String("hello").intern(); if (str01 == str02) { System.out.println("str01 equal str02"); }

      輸出結果是: str01 equal str02

      在字串常量池中,預設會將物件放入常量池;在字串變數中,物件總是建立在堆記憶體的,同時也會在常量池中建立一個字串物件,複製到堆記憶體物件中,並返回堆記憶體物件引用。

    最後,我們看看開篇的輸出結果:false、false、true。

    JAVA中效能最佳化String篇就講到這裡,小夥伴們,看懂了嗎?

  • 2 # ksfzhaohui

    效能最佳化我覺得應該分兩步走,第一步:尋找效能瓶頸,第二步:效能調優;

    下面分別進行分析:

    第一步:尋找效能瓶頸

    通常效能瓶頸的表象是資源消耗過多、外部處理系統的效能不足;或者資源消耗不多,但是程式效應還是很慢;

    資源主要消耗在cpu,檔案io,網路io以及記憶體方面,當某一資源消耗過多會造成系統響應慢;

    外部處理系統的效能不足主要是所呼叫其他系統提供的功能或資料庫的響應速度不夠,外部系統慢可能也是資源消耗過多導致,資料庫響應慢可以對資料庫進行調優;

    資源消耗不多但仍然慢主要原因是程式程式碼執行效率不高,未充分使用資源或程式結構不合理;

    1.1cpu消耗分析

    可以透過相關命令比如top,pidstat,找出各個型別消耗cpu的佔比,最常見的就是us和sy型別分別代表使用者程序消耗和執行緒間切換消耗;如果us過高可以找到相關的執行緒ID然後分析程式碼;如果sy過高是不是啟動了過多的執行緒導致執行緒切換過多;

    1.2檔案io消耗

    要跟蹤執行緒的檔案IO消耗,可以透過pidstat來查詢,可以查到每秒的讀寫kb數;找到讀寫kb數多個執行緒,然後結合jstack找到相關的java程式碼,然後分析;

    1.3網路io消耗

    可以透過sar來分析網路的消耗狀況,但是不能具體到每個執行緒所消耗的網路IO,只能對執行緒dump,查詢產生了大量網路io的執行緒;

    1.4記憶體消耗

    結合top或pidstat,以及jvm的記憶體分析工具來分析記憶體消耗;要區分是jvm外的物理記憶體還是jvm heap區記憶體;如果是jvm外的物理記憶體要分析程式中Direct ByteBuffer,如果是jvm heap可以透過jvisualvm來分析;

    1.5資源消耗不多但仍然慢

    主要原因是:鎖競爭激烈,未充分使用硬體資源,資料量增長

    第二步:效能調優

    2.1jvm調優

    主要包括各個代的大小、GC策略等;代大小的設定:避免新生代大小設定過小,或者過大;避免Survivor區過小或過大;合理設定新生代存活週期;GC策略根據吞吐量優先還是延遲優先進行設定策略;

    2.2程式調優

    1.CPU消耗嚴重解決

    us過高主要是執行執行緒無任何掛起動作,可以進行Thread.sleep操作;sy過高主要是因為建立了過多的執行緒導致執行緒上下文切換;

    2.檔案IO消耗嚴重解決

    造成檔案IO消耗嚴重的原因主要是多個執行緒寫大量的資料到同一個檔案,導致檔案很快變的很大,從而寫入速度越來越慢,並造成各執行緒激烈競爭爭搶檔案鎖,常用的調優方法:非同步寫檔案,批次讀寫,限流,限制檔案大小;

    3.網路IO消耗嚴重解決

    主要原因是同時傳送或者接受的包太多,解決辦法就是限流;

    4.記憶體消耗嚴重解決

    解決:釋放不必要的引用,使用物件快取池,採用合理的快取失效策略,合理使用softReference和WeakReference;

    2.3資源消耗不多但仍然慢

    主要原因是:鎖競爭激烈,未充分使用硬體資源

  • 3 # 千鋒頭號粉絲

    程式碼最佳化是對程式程式碼進行交換,以實現精簡程式碼、清除程式碼垃圾的目的。有很多新手Java程式設計師想知道程式碼最佳化的方法有哪些,還有很多想要轉行學Java的同學想知道2020年學Java就業前景如何,接下來就給大家簡單分析一下。

    Java程式碼最佳化的方法有哪些?

    1)儘量指定類、方法的final修飾符。帶有final修飾符的類是不可派生的,Java編譯器會尋找機會內聯所有的final方法,內聯對於提升Java執行效率作用重大,此舉能夠使效能平均提高50%。

    2)儘量重用物件。由於Java虛擬機器不僅要花時間生成物件,以後可能還需要花時間對這些物件進行垃圾回收和處理,因此生成過多的物件將會給程式的效能帶來很大的影響。

    3)儘可能使用區域性變數。呼叫方法時傳遞的引數以及在呼叫中建立的臨時變數都儲存在棧中速度較快,其他變數,如靜態變數、例項變數等,都在堆中建立速度較慢。

    4)慎用異常。異常對效能不利,只要有異常被丟擲,Java虛擬機器就必須調整呼叫堆疊,因為在處理過程中建立了一個新的物件。異常只能用於錯誤處理,不應該用來控制程式流程。

    5)乘法和除法使用移位操作。用移位操作可以極大地提高效能,因為在計算機底層,對位的操作是最方便、最快的,但是移位操作雖然快,可能會使程式碼不太好理解,因此最好加上相應的註釋。

    6)儘量使用HashMap、ArrayList、StringBuilder,除非執行緒安全需要,否則不推薦使用 Hashtable、Vector、StringBuffer,後三者由於使用同步機制而導致了效能開銷。

    7)儘量在合適的場合使用單例。使用單例可以減輕載入的負擔、縮短載入的時間、提高載入的效率,但並不是所有地方都適用於單例。

  • 中秋節和大豐收的關聯?
  • 柚子的吃法什麼時候吃比較好?