• 【转】LBQ + GC = Slow


    原文链接:http://tech.puredanger.com/2009/02/11/linkedblockingqueue-garbagecollection/

    相关链接:http://www.oracle.com/technetwork/java/javase/6u19-141078.html

    There was an interesting exchange today on the concurrency-interest mailing list about LinkedBlockingQueue, one of the most important and commonly used queue implementations in java.util.concurrent.  LBQ is an optionally-bounded thread-safe queue based on a linked list. 

    The key method in question is the extract() method used to remove the head of the queue:

        private E extract() {         Node<E> first = head.next;         head = first;         E x = first.item;         first.item = null;         return x;     }

    This method is going to release the first item (head) out into the world and head.next will become the new head.  The issue here is that head will be pulled off, used, and probably become garbage but the head Node retains its head.next link to the next Node. 

    This is bad because it means that there will be a chain of references back through all the old Node references even after they’ve been removed from the queue and thrown away.  GC will of course take care of all those references since the Nodes are garbage. 

    But the garbage collectors we use are generational – they periodically sweep through young objects and kill the vast numbers of them that have been short-lived.  The ones that survive live to the next generation and eventually if objects are old enough they move to the old generation where they are collected less frequently by a full GC. 

    The problem in the queue nodes is that if they live in the queue long enough (if the queue is big), then one of them will enter the old gen and at that point the reference from old gen into young gen prevents the whole chain of dead nodes from being reclaimed by young generational collection.  This is highly inefficient for the GC.

    This problem can of course be solved simply by nulling the Node next reference:

        private E extract() {         Node<E> first = head.next;         head.next = null;     // Kill this reference         head = first;         E x = first.item;         first.item = null;         return x;     }

    This breaks the chain and prevents the reference from old gen into young gen.  This makes a huge difference in GC and performance.  You can see some results from a test demonstrating the problems:

    Test 1 took: 1595ms
    Test 2 took: 2639ms
    Exception in thread "main" java.lang.AssertionError: Full GC has
    occured during the test: 47 times
    	at TestLBQ.main(TestLBQ.java:72)
    
    My workaround of null'ing head.next before moving the head works
    perfectly and displays (as expected):
    Test 1 took: 1629ms
    Test 2 took: 1592ms
    

    On the original version (the one in the JDK), the second pass through the test is significantly slower and full gc occurred 47 times as opposed to the fixed version which shows a faster second pass and no full gc. 

    Even worse was a test with the new G1 garbage collector in Java 7.  This new collector takes the generational idea and pushes it even further.  Unfortunately, that means it’s even more affected by this problem (while in general it is better for many apps):

    JDK7 version:
    Test 1 took: 5456ms
    Test 2 took: 5575ms
    
    With null assignment:
    Test 1 took: 1698ms
    Test 2 took: 1602ms
    

    It seems this was not the first discovery of this issue as a couple of people were already using modified LBQs with this change.  Hopefully this issue will be fixed in Java 7 at least.

  • 相关阅读:
    C#Light 和 uLua的对比第二弹
    Unity3D热更新全书FAQ
    Unity3D热更新全书-脚本(五) NGUI
    Unity3d热更新全书-加载(二)如何在不用AssetBundle的前提下动态加载预设
    Unity3D热更新全书-下载 唯一的一篇
    Unity3D热更新全书-PageZero
    Netty4.x中文教程系列(二) – 白话概念
    Netty4.x中文教程系列(一) Hello World !
    Java NIO(New I/O)的三个属性position、limit、capacity
    Buffer
  • 原文地址:https://www.cnblogs.com/michelleAnn2011/p/2363370.html
Copyright © 2020-2023  润新知