最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于Java垃圾回收開銷降低的幾條建議

 更新時間:2017年02月04日 14:08:08   作者:搬磚工  
垃圾回收(Garbage Collection)是Java虛擬機(JVM)垃圾回收器提供的一種用于在空閑時間不定時回收無任何對象引用的對象占據(jù)的內(nèi)存空間的一種機制,下面這篇文章主要介紹了關(guān)于Java垃圾回收開銷降低的幾條建議,需要的朋友可以參考借鑒,下面來一起看看吧。

保持GC低開銷的竅門有哪些?

隨著一再拖延而即將發(fā)布的 Java9,G1(“Garbage First”)垃圾回收器將被成為 HotSpot 虛擬機默認的垃圾回收器。從 serial 垃圾回收器到CMS 收集器, JVM 見證了許多 GC 實現(xiàn),而 G1 將成為其下一代垃圾回收器。

隨著垃圾收集器的發(fā)展,每一代 GC 與其上一代相比,都帶來了巨大的進步和改善。parallel GC 與 serial GC 相比,它讓垃圾收集器以多線程的方式工作,充分利用了多核計算機的計算能力。CMS(“Concurrent Mark-Sweep”)收集器與 parallel GC 相比,它將回收過程分成了多個階段,使得應(yīng)用線程正在運行的時候,收集工作可以并發(fā)地完成,大大改善了頻繁執(zhí)行 “stop-the-world” 的情況。G1 對于擁有大量堆內(nèi)存的 JVM 表現(xiàn)出更好的性能,并且具有更好的可預(yù)測和統(tǒng)一的暫停過程。

Tip #1: 預(yù)測集合的容量

所有標準的 Java 集合,包括定制和擴展的實現(xiàn)(比如 Trove 和 Google 的 Guava),底層都使用了數(shù)組(原生數(shù)據(jù)類型或者基于對象的類型)。因為數(shù)組一旦被分配,其大小就不可變,因此添加元素到集合時,大多數(shù)情況下都會導(dǎo)致需要重新申請一個新的大容量數(shù)組替換老的數(shù)組(指集合底層實現(xiàn)使用的數(shù)組)。

即使沒有提供集合初始化的大小,大多數(shù)集合的實現(xiàn)都盡量優(yōu)化重新分配數(shù)組的處理并且將其開銷平攤到最低。不過,在構(gòu)造集合的時候就提供大小可以得到最佳的效果。

讓我們將下面的代碼作為一個簡單的例子分析一下:

public static List reverse(List & lt; ? extends T & gt; list) {
 
 List result = new ArrayList();
 
 for (int i = list.size() - 1; i & gt; = 0; i--) {
  result.add(list.get(i));
 }
 
 return result;
}

This method allocates a new array, then fills it up with items from another list, only in reverse order. 這個方法分配了一個新的數(shù)組,然后用另一個 list 中元素對該數(shù)組進行填充,只是元素的數(shù)序發(fā)生了變化。

這個處理方式可能會付出慘重的性能代價,其優(yōu)化的點在添加元素到新的 list 中這行代碼。 隨著每一次添加元素,list 都需要確保其底層數(shù)組擁有足夠的位置來容納新的元素。如果有空閑的位置,那么只是簡單地將新元素存儲到下一個空閑的槽位。如果沒有的話,將分配一個新的底層數(shù)組,拷貝舊的數(shù)組內(nèi)容到新的數(shù)組中,然后添加新的元素。這將導(dǎo)致多次分配數(shù)組,那些剩余的舊數(shù)組最終被 GC 所回收。

我們可以通過在構(gòu)造集合時讓其底層的數(shù)組知道它將存儲多少元素,從而避免這些多余的分配

public static List reverse(List & lt; ? extends T & gt; list) {
 
 List result = new ArrayList(list.size());
 
 for (int i = list.size() - 1; i & gt; = 0; i--) {
  result.add(list.get(i));
 }
 
 return result;
 
}

上面的代碼通過 ArrayList 的構(gòu)造器指定足夠大的空間來存儲 list.size() 個元素,在初始化時完成分配的執(zhí)行,這意味著 List 在迭代的過程中無需再次分配內(nèi)存。

Guava 的集合類則更進一步,允許初始化集合時明確指定期望元素的個數(shù)或者指定一個預(yù)測值。

List result = Lists.newArrayListWithCapacity(list.size());
List result = Lists.newArrayListWithExpectedSize(list.size());

上面的代碼中,前者用于我們已經(jīng)準確地知道集合將要存儲多少元素,而后者的分配方式考慮了錯誤預(yù)估的情況。

Tip #2:直接處理數(shù)據(jù)流

當處理數(shù)據(jù)流時,比如從一個文件讀取數(shù)據(jù)或者從網(wǎng)絡(luò)中下載數(shù)據(jù),下面的代碼是非常常見的:

byte[] fileData = readFileToByteArray(new File("myfile.txt"));

所產(chǎn)生的字節(jié)數(shù)組可能被解析 XML 文檔、JSON 對象或者協(xié)議緩沖消息,以及一些常見的可選項。

當處理大文件或者文件的大小無法預(yù)測時,上面的做法很是不明智的,因為當 JVM 無法分配一個緩沖區(qū)來處理真正文件時,就會導(dǎo)致OutOfMemeoryErrors。

即使數(shù)據(jù)的大小是可管理的,當?shù)嚼厥諘r,使用上面的模式依然會造成巨大的開銷,因為它在堆中分配了一塊非常大的區(qū)域來存儲文件數(shù)據(jù)。

一種更加好的處理方式是使用合適的 InputStream (比如在這個例子中使用 FileInputStream)直接傳遞給解析器,不再一次性將整個文件讀取到一個字節(jié)數(shù)組中。所有主流的開源庫都提供相應(yīng)的 API 來直接接受一個輸入流進行處理,比如:

FileInputStream fis = new FileInputStream(fileName);
MyProtoBufMessage msg = MyProtoBufMessage.parseFrom(fis);

Tip #3: 使用不可變的對象

不變性有太多的好處。甚至不用我贅述什么。然而,有一個優(yōu)點會對垃圾回收產(chǎn)生影響,應(yīng)該關(guān)注一下。

一個不可變對象的屬性在對象被創(chuàng)建后就不能被修改(在這里的例子使用的是引用數(shù)據(jù)類型的屬性),比如:

public class ObjectPair {
 
 private final Object first;
 private final Object second;
 
 public ObjectPair(Object first, Object second) {
  this.first = first;
  this.second = second;
 }
 
 public Object getFirst() {
  return first;
 }
 
 public Object getSecond() {
  return second;
 }
 
}

將上面的類實例化后會產(chǎn)生一個不可變對象—它的所有屬性用 final 修飾,構(gòu)造完成后就不能改變了。

不可變性意味著所有被一個不可變?nèi)萜魉玫膶ο?,在容器?gòu)造完成前對象就已經(jīng)被創(chuàng)建。就 GC 而言:這個容器年輕程度至少和其所持有的最年輕的引用一樣。這意味著當在年輕代執(zhí)行垃圾回收的過程中,GC 因為不可變對象處于老年代而跳過它們,直到確定這些不可變對象在老年代中不被任何對象所引用時,才完成對它們的回收。

更少的掃描對象意味著對內(nèi)存頁更少的掃描,越少的掃描內(nèi)存頁就意味著更短的 GC 生命周期,也意味著更短的 GC 暫停和更好的總吞吐量。

Tip #4: 小心字符串拼接

字符串可能是在所有基于 JVM 應(yīng)用程序中最常用的非原生數(shù)據(jù)結(jié)構(gòu)。然而,由于其隱式地開銷負擔和簡便的使用,非常容易成為占用大量內(nèi)存的罪歸禍首。

這個問題很明顯不在于字符串字面值,而是在運行時分配內(nèi)存初始化產(chǎn)生的。讓我們快速看一下動態(tài)構(gòu)建字符串的例子:

public static String toString(T[] array) {
 
 String result = "[";
 
 for (int i = 0; i & lt; array.length; i++) {
  result += (array[i] == array ? "this" : array[i]);
  if (i & lt; array.length - 1) {
   result += ", ";
  }
 }
 
 result += "]";
 
 return result;
}

這是個看似不錯的方法,接收一個字符數(shù)組然后返回一個字符串。但是這對于對象內(nèi)存分配卻是災(zāi)難性的。

很難看清這語法糖的背后,但是幕后的實際情況是這樣的:

public static String toString(T[] array) {
 
 String result = "[";
 
 for (int i = 0; i & lt; array.length; i++) {
 
  StringBuilder sb1 = new StringBuilder(result);
  sb1.append(array[i] == array ? "this" : array[i]);
  result = sb1.toString();
 
  if (i & lt; array.length - 1) {
   StringBuilder sb2 = new StringBuilder(result);
   sb2.append(", ");
   result = sb2.toString();
  }
 }
 
 StringBuilder sb3 = new StringBuilder(result);
 sb3.append("]");
 result = sb3.toString();
 
 return result;
}

字符串是不可變的,這意味著每發(fā)生一次拼接時,它們本身不會被修改,而是依次分配新的字符串。此外,編譯器使用了標準的 StringBuilder 類來執(zhí)行這些拼接操作。這就會有問題了,因為每一次迭代,既隱式地分配了一個臨時字符串,又隱式分配了一個臨時的 StringBuilder 對象來幫助構(gòu)建最終的結(jié)果。

最佳的方式是避免上面的情況,使用 StringBuilder 和直接的追加,以取代本地拼接操作符(“+”)。下面是一個例子:

public static String toString(T[] array) {
 
 StringBuilder sb = new StringBuilder("[");
 
 for (int i = 0; i & lt; array.length; i++) {
  sb.append(array[i] == array ? "this" : array[i]);
  if (i & lt; array.length - 1) {
   sb.append(", ");
  }
 }
 
 sb.append("]");
 return sb.toString();
}

這里,我們只在方法開始的時候分配了唯一的一個 StringBuilder。至此,所有的字符串和 list 中的元素都被追加到單獨的一個StringBuilder中。最終使用 toString() 方法一次性將其轉(zhuǎn)成成字符串返回。

Tip #5: 使用特定的原生類型的集合

Java 標準的集合庫簡單且支持泛型,允許在使用集合時對類型進行半靜態(tài)地綁定。比如想要創(chuàng)建一個只存放字符串的 Set 或者存儲 Map<Pair, List>這樣的 map,這種處理方式是非常棒的。

真正的問題源于當我們想要使用一個 list 存儲 int 類型,或者一個 map 存儲 double 類型作為 value。因為泛型不支持原生數(shù)據(jù)類型,因此另外的一種選擇是使用包裝類型來進行替換,這里我們使用 List 。

這種處理方式是非常浪費的,因為一個 Integer 是一個完全的對象,一個對象的頭部占用12個字節(jié)以及其內(nèi)部的所維護的 int 屬性,每個Integer 對象總共占用16個字節(jié)。這比起存儲相同個數(shù)的 int 類型的 list 而言,其消耗的空間是它的四倍!比這個更加嚴重的問題在于,事實上因為 Integer 是真正的對象實例,因此它需要垃圾收集階段被垃圾收集器所考慮是否要回收。

為了處理這個問題,我們在 Takipi 中使用非常棒的 Trove 集合庫。Trove 摒棄了部分泛型的特定來支持特定的使用內(nèi)存更高效的原生類型的集合。比如,我們使用非常消耗性能的 Map<Integer, Double> ,在 Trove 中有另一種特別的選擇方案,其形式為 TIntDoubleMap

TIntDoubleMap map = new TIntDoubleHashMap();
map.put(5, 7.0);
map.put(-1, 9.999);
...

Trove 的底層實現(xiàn)使用了原生類型的數(shù)組,所以當操作集合的時候不會發(fā)生元素的裝箱(int->Integer)或者拆箱(Integer->int), 沒有存儲對象,因為底層使用原生數(shù)據(jù)類型存儲。

最后

隨著垃圾收集器持續(xù)的改進,以及運行時的優(yōu)化和 JIT 編譯器也變得越來越智能。我們作為開發(fā)者將會發(fā)現(xiàn)越來越少地考慮如何編寫 GC 友好的代碼。然而,就目前階段,不論 G1 如何改進,我們?nèi)匀挥泻芏嗫梢宰龅氖聛韼?JVM 提升性能。

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家學(xué)習(xí)或者使用Java能帶來一定的幫助,如果有疑問大家可以留言交流。

相關(guān)文章

  • 詳解使用Jenkins自動編譯部署web應(yīng)用

    詳解使用Jenkins自動編譯部署web應(yīng)用

    本篇主要介紹基于Jenkins實現(xiàn)持續(xù)集成的方式,通過案例介紹線上自動編譯及部署的配置過程,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • Spring Boot與Kotlin 整合全文搜索引擎Elasticsearch的示例代碼

    Spring Boot與Kotlin 整合全文搜索引擎Elasticsearch的示例代碼

    本篇文章主要介紹了Spring Boot與Kotlin 整合全文搜索引擎Elasticsearch的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • java實現(xiàn)學(xué)籍管理系統(tǒng)

    java實現(xiàn)學(xué)籍管理系統(tǒng)

    這篇文章主要為大家詳細介紹了java實現(xiàn)學(xué)籍管理系統(tǒng),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • Java定時清理過期文件的實例代碼

    Java定時清理過期文件的實例代碼

    這篇文章主要介紹了Java定時清理過期文件的實例代碼,非常不錯,具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2018-12-12
  • RocketMQ producer同步發(fā)送單向發(fā)送源碼解析

    RocketMQ producer同步發(fā)送單向發(fā)送源碼解析

    這篇文章主要為大家介紹了RocketMQ producer同步發(fā)送單向發(fā)送源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • Java 畫時鐘遇到的問題及解決方案

    Java 畫時鐘遇到的問題及解決方案

    我是一個剛?cè)腴T的小菜鳥,希望我寫的東西可以幫助和我一樣剛?cè)腴T的兄弟們少走一些彎路,也希望大佬們可以多指點指點我。感謝!解決在畫時鐘遇到的問題讓我花費不少時間...說兩個困擾我比較久的
    2021-11-11
  • Java順序查找算法詳解

    Java順序查找算法詳解

    順序查找又稱線性查找,主要用于在線性表中進行查找。順序查找通常分為對一般的無序線性表的順序查找和對按關(guān)鍵字有序的順序表的順序查找,下面我們來一探究竟
    2022-08-08
  • java基礎(chǔ)-數(shù)組擴容詳解

    java基礎(chǔ)-數(shù)組擴容詳解

    這篇文章主要介紹了Java數(shù)組擴容實現(xiàn)方法解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2021-08-08
  • SpringBoot+MyBatisPlus+Vue 前后端分離項目快速搭建過程(后端)

    SpringBoot+MyBatisPlus+Vue 前后端分離項目快速搭建過程(后端)

    這篇文章主要介紹了SpringBoot+MyBatisPlus+Vue 前后端分離項目快速搭建過程(后端),快速生成后端代碼、封裝結(jié)果集、增刪改查、模糊查找,畢設(shè)基礎(chǔ)框架,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-05-05
  • idea?intellij快速修復(fù)if語句缺少大括號的問題

    idea?intellij快速修復(fù)if語句缺少大括號的問題

    這篇文章主要介紹了idea?intellij快速修復(fù)if語句缺少大括號的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04

最新評論

彭山县| 和林格尔县| 沂源县| 高雄市| 镇宁| 洱源县| 无锡市| 瓦房店市| 宁津县| 双城市| 沛县| 辉南县| 芒康县| 元阳县| 莎车县| 科技| 芜湖县| 沙洋县| 扬中市| 独山县| 东乡县| 钟祥市| 措美县| 元江| 鄂托克旗| 营山县| 容城县| 桃园县| 府谷县| 灵台县| 化隆| 正宁县| 沅陵县| 浦江县| 阿拉善左旗| 定兴县| 搜索| 股票| 托克逊县| 庆阳市| 文成县|