【转】LBQ + GC = Slow

本文深入分析了Java并发库中的LinkedBlockingQueue实现中存在的引用泄漏问题,并通过示例展示了如何修复该问题以提高垃圾回收效率。通过对比原始版本与修改后的版本在GC性能上的差异,说明了在大规模数据处理场景下,正确处理队列节点引用的重要性。

原文链接: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.

转载于:https://www.cnblogs.com/michelleAnn2011/archive/2012/02/22/2363370.html

export CHM_DIR = $(shell pwd) export ENABLE_FTRACE=n CFLAGS += -Wall -g -ffunction-sections -ldl -rdynamic -Wall -DREENTRANT -pthread -I$(CHM_DIR)/ipcd/include \ -I$(CHM_DIR)/../public/include -I$(CHM_DIR)/include -I$(CHM_DIR)/ubus -I$(CHM_DIR)/onvif/include CFLAGS += -ltrace_alloc -ldl CFLAGS += -fno-omit-frame-pointer TARGET := chm all: $(TARGET) SLP_OBJ := $(CHM_DIR)/slp/slp.o ONVIF_OBJ := $(CHM_DIR)/onvif/onvif.o PROT_ADAPTER_OBJ := $(CHM_DIR)/protocol_adapter/protocol_adapter.o UBUS_OBJ := $(CHM_DIR)/ubus/ubus.o IPCD_OBJ := $(CHM_DIR)/ipcd/ipcd.o RECV_CENTER_OBJ := $(CHM_DIR)/recv_center/chm_recv_center.o DSD_OBJ := $(CHM_DIR)/dsd_client/chm_dsd_process.o OBJ := chm_main.o chm_common.o chm_dbg.o tp_timer.o chm_dev_manager.o chm_display.o chm_stream.o chm_ptz.o chm_event_detect.o chm_ipaddr_manager.o tplog.o ifeq ($(CONFIG_CHM_CASCADE_SUPPORT), y) OBJ += chm_cascade.o endif LDFLAGS += -L$(CHM_DIR)/../public/lib -ljson -lubus -lubox -lblobmsg_json -ldsd_client\ -luci -lm -lssl -lcrypto -lrt -liconv -ldecrypter -laes -lmsglog -lbq -lsecurity -Wl,--gc-sections LDFLAGS += -ltrace_alloc -ldl CFLAGS += -I$(STAGING_DIR)/usr/include LDFLAGS += -L$(STAGING_DIR)/usr/lib/ -lvigi_log -lnvrutil ifeq ($(ENABLE_TELESHM), y) LDFLAGS += -ltele_shm endif $(TARGET): $(OBJ) $(PROT_ADAPTER_OBJ) $(SLP_OBJ) $(ONVIF_OBJ) $(UBUS_OBJ) $(IPCD_OBJ) $(RECV_CENTER_OBJ) $(DSD_OBJ) $(CC) $(CFLAGS) -o $@ $(OBJ) $(PROT_ADAPTER_OBJ) $(SLP_OBJ) $(ONVIF_OBJ) $(UBUS_OBJ) $(IPCD_OBJ) $(RECV_CENTER_OBJ) $(DSD_OBJ) $(LDFLAGS) $(SLP_OBJ): FORCE make -C $(CHM_DIR)/slp $(PROT_ADAPTER_OBJ): FORCE make -C $(CHM_DIR)/protocol_adapter $(ONVIF_OBJ): FORCE make -C $(CHM_DIR)/onvif $(UBUS_OBJ): FORCE make -C $(CHM_DIR)/ubus $(IPCD_OBJ): FORCE make -C $(CHM_DIR)/ipcd $(RECV_CENTER_OBJ): FORCE make -C $(CHM_DIR)/recv_center $(DSD_OBJ): FORCE make -C $(CHM_DIR)/dsd_client install: #remove object files and executable when user executes "make clean" .PHONY:clean FORCE clean: @rm -f $(TARGET) syms.$(TARGET) @rm -f $(OBJ) @cd $(CHM_DIR)/slp && make clean @cd $(CHM_DIR)/protocol_adapter && make clean @cd $(CHM_DIR)/onvif && make clean @cd $(CHM_DIR)/ubus && make clean @cd $(CHM_DIR)/LuCI && make clean @cd $(CHM_DIR)/ipcd && make clean @cd $(CHM_DIR)/recv_center && make clean FORCE:
09-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值