css hack技巧_5种减少Hack的编码技巧
css hack技巧
在本文中,我們將探討五種方法,這些方法可以使用有效的編碼來幫助垃圾回收器花費更少的CPU時間分配和釋放內存,并減少GC開銷。 較長的GC通常會導致我們的代碼在回收內存時停止(也稱為“停止世界”)。
一些背景
建立GC的目的是處理大量短期對象的分配(例如渲染網頁等,其中大部分分配的對象在服務頁面后就已過時)。
GC使用所謂的“年輕”來完成此工作–分配新對象的堆段。 每個對象都有一個“年齡”(放置在對象的標題位中),該年齡定義了在沒有回收的情況下“生存”了多少個集合。 一旦達到一定年齡,該對象將被復制到堆中稱為“幸存者”或“舊”一代的另一部分。
該過程雖然有效,但仍然要付出代價。 能夠減少臨時分配的數量確實可以幫助我們提高吞吐量,尤其是在大規模應用程序中。
下面是五種我們可以編寫日常代碼的方法,這些代碼可以提高內存效率,而不必花費大量時間或減少代碼的可讀性。
1.避免隱式字符串
字符串幾乎是我們管理的每個數據結構不可或缺的一部分。 它們比其他原始值重得多,它們對內存使用量的影響要大得多。
要注意的最重要的事情之一是字符串是不可變的 。 分配后不能修改它們。 用于連接的運算符(例如“ +”)實際上分配了一個新的String,其中包含要連接的字符串的內容。 更糟糕的是,有一個隱式的StringBuilder對象被分配來實際進行組合它們的工作。
例如 -
a = a + b; // a and b are Strings編譯器在后臺生成可比較的代碼:
StringBuilder temp = new StringBuilder(a). temp.append(b); a = temp.toString(); // a new String is allocated here.// The previous “a” is now garbage.但情況變得更糟。
讓我們看看這個例子–
String result = foo() + arg; result += boo(); System.out.println(“result = “ + result);在此示例中,我們在后臺分配了3個StringBuilder-每個加號操作一個,另外兩個Strings-一個用于保存第二個賦值的結果,另一個用于保存傳遞給print方法的字符串。 那是另外5個對象 ,否則它們看起來很簡單。
想一想在實際的代碼場景中會發生什么,例如生成網頁,使用XML或從文件讀取文本。 嵌套在循環結構中,您可能正在查看成百上千個隱式分配的對象。 盡管VM具有處理此問題的機制, 但它是有代價的 –由用戶支付。
解決方案:減少這種情況的一種方法是主動使用StringBuilder分配。 下面的示例獲得與上面的代碼相同的結果,同時僅分配一個StringBuilder和一個String來保存最終結果,而不是原來的五個對象。
StringBuilder value = new StringBuilder(“result = “); value.append(foo()).append(arg).append(boo()); System.out.println(value);通過注意Strings和StringBuilders的隱式分配方式,可以從實質上減少大規模代碼位置中的短期分配量。
2.計劃清單的能力
諸如ArrayLists之類的動態集合是保存動態長度數據的最基本的結構之一。 ArrayList和其他集合(例如HashMaps和TreeMaps)是使用基礎Object []數組實現的。 像字符串(char []數組本身包裝)一樣,數組也是不可變的。 顯而易見的問題變成了-如果底層數組的大小是不變的,我們如何在集合中添加/放置項目? 答案也很明顯–通過分配更多的數組 。
讓我們看看這個例子–
List<Item> items = new ArrayList<Item>();for (int i = 0; i < len; i++) {Item item = readNextItem();items.add(item); }len的值確定循環結束后項目的最終長度。 但是,此值對于ArrayList的構造方法未知,該方法會分配一個具有默認大小的新Object數組。 每當超出內部陣列的容量時,它就會被具有足夠長度的新陣列替換,從而使先前的陣列成為垃圾。
如果要執行數千次循環,則可能會強制分配一個新數組,并多次收集前一個數組。 對于在大規模環境中運行的代碼,這些分配和取消分配都從計算機的CPU周期中扣除。
解決方案:盡可能分配具有初始容量的列表和地圖,如下所示:
List<MyObject> items = new ArrayList<MyObject>(len);這樣可以確保在運行時不會發生內部數組的不必要分配和釋放,因為列表現在具有足夠的容量開始。 如果您不知道確切的大小,最好對平均大小進行估計(例如1024、4096),并添加一些緩沖區以防止意外溢出。
3.使用有效的原始集合
Java編譯器的當前版本通過使用“裝箱”來支持具有原始鍵或值類型的數組或映射-將原始值包裝在可由GC分配和回收的標準對象中。
這可能會帶來一些負面影響 。 Java使用內部數組實現大多數集合。 對于添加到HashMap的每個鍵/值條目,分配一個內部對象來容納兩個值。 這在處理地圖時是必不可少的,這意味著每次將商品放入地圖時都要進行額外的分配和可能的重新分配。 還可能存在容量增加和必須重新分配新的內部陣列的代價。 當處理包含數千個或更多條目的大型地圖時,這些內部分配可能會增加GC的成本。
一種非常常見的情況是在原始值(例如Id)和對象之間保留映射。 由于Java的HashMap是為保存對象類型(相對于基元)而構建的,因此這意味著映射中的每個插入都可以潛在地分配另一個對象來保存基元值(“裝箱”它)。
標準的Integer.valueOf方法緩存0到255之間的值,但是對于每個大于0的數字,除了內部鍵/值輸入對象之外,還將分配一個新對象。 這可能會使映射的GC開銷增加三倍以上。 對于那些來自C ++背景的人來說,這確實是令人不安的消息,因為STL模板可以非常有效地解決此問題。
幸運的是,此問題正在Java的下一版本中進行。 在此之前,一些出色的庫已經對其進行了相當有效的處理,這些庫為每種Java的原始類型提供了原始樹,映射和列表。 我強烈推薦Trove ,我已經使用了很長時間 ,發現它確實可以減少大規模代碼中的GC開銷。
4.使用流而不是內存中的緩沖區
我們在服務器應用程序中處理的大多數數據都是以文件或數據的形式從網絡上從另一個Web服務或DB傳給我們的。 在大多數情況下,傳入的數據是序列化的形式,在我們開始對其進行操作之前,需要將其反序列化為Java對象。 這個階段很容易出現大量的隱式分配 。
通常最簡單的方法是使用ByteArrayInputStream,ByteBuffer將數據讀取到內存中,然后將其傳遞給反序列化代碼。
這可能是一個不好的舉動 ,因為您需要在構造出新的對象時為整個數據分配和釋放空間。 而且由于數據的大小可能是未知的,您猜對了–您必須分配和取消分配內部byte []數組,以在數據超出初始緩沖區的容量時容納它們。
解決方案非常簡單。 大多數持久性庫(例如Java的本機序列化,Google的協議緩沖區等)都可以直接從傳入的文件或網絡流中反序列化數據,而不必將其保留在內存中,也不必分配新的內部字節數組來保存數據。隨著數據的增長。 如果可以的話,請采用這種方法,而不是將數據加載到內存中。 您的GC將感謝您。
5.匯總列表
不變性是一件美麗的事情,但是在某些大規模情況下,它可能會存在一些嚴重的缺點。 一種情況是在方法之間傳遞List對象。
從函數返回集合時,通常建議在方法內創建集合對象(例如ArrayList),將其填充并以不可變的Collection接口的形式返回。
在某些情況下這不能很好地工作 。 最引人注目的是將集合從多個方法調用中聚合到最終集合中。 雖然不變性提供了更多的清晰度,但在大規模情況下,這也可能意味著臨時集合的大量分配。
在這種情況下,解決方案不是返回新的集合,而是將值聚合到單個集合中,該集合作為參數傳遞到這些方法中。
示例1(效率低下)–
List<Item> items = new ArrayList<Item>();for (FileData fileData : fileDatas) {// Each invocation creates a new interim list with possible// internal interim arraysitems.addAll(readFileItem(fileData)); }示例2 –
List<Item> items =new ArrayList<Item>(fileDatas.size() * avgFileDataSize * 1.5);for (FileData fileData : fileDatas) {readFileItem(fileData, items); // fill items inside }示例2在不遵守不變性規則(通常應遵守該規則)的同時,可以保存N個列表分配(以及任何臨時數組分配)。 在大規模情況下,這可以為您的GC帶來福音。
補充閱讀
- 字符串實習– http://plumbr.eu/blog/reducing-memory-usage-with-string-intern
- 高效的包裝器– http://vanillajava.blogspot.co.il/2013/04/low-gc-coding-efficient-listeners.html
- 使用Trove – http://java-performance.info/primitive-types-collections-trove-library/
此帖子也可以在Speaker Deck上找到
翻譯自: https://www.javacodegeeks.com/2013/07/5-coding-hacks-to-reduce-gc-overhead.html
css hack技巧
總結
以上是生活随笔為你收集整理的css hack技巧_5种减少Hack的编码技巧的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾讯视频vip怎么取消自动续费
- 下一篇: rust语法丑陋_抛出异常–缓慢而丑陋