我一直都有一个疑问,丰巢业务服务的生产环境jvm参数设置是禁止system.gc的,也就是开启设置:-XX:+DisableExplicitGC,但是生产环境却从来没有出现过堆外内存溢出的情况。说明一下,丰巢使用了阿里开源的dubbo,而dubbo底层通信默认情况下使用了3.2.5.Final版本的netty,而我们对于netty的常规认知里,netty一定是使用了堆外内存,并且堆外内存在禁止了system.gc这个函数调用的话,在服务没有主动回收分配的堆外内存的情况下,一定会出现堆外内存的泄露。带着这个问题,刚好前天晚上有些时间,研究了一下3.2.5版本的netty源码,又是在科兴科兴园等馒头妈妈时候,发现了秘密之所在,我只能说,科兴科学园真是我的宝地啊。
 
涉及到的netty类:NioWorker、HeapChannelBufferFactory、BigEndianHeapChannelBuffer、SocketReceiveBufferPool
 
核心的秘密在SocketReceiveBufferPool中
 final class SocketReceiveBufferPool {

     private static final int POOL_SIZE = 8;

     @SuppressWarnings("unchecked")
private final SoftReference<ByteBuffer>[] pool = new SoftReference[POOL_SIZE]; SocketReceiveBufferPool() {
super();
} final ByteBuffer acquire(int size) {
final SoftReference<ByteBuffer>[] pool = this.pool;
for (int i = 0; i < POOL_SIZE; i ++) {
SoftReference<ByteBuffer> ref = pool[i];
if (ref == null) {
continue;
} ByteBuffer buf = ref.get();
if (buf == null) {
pool[i] = null;
continue;
} if (buf.capacity() < size) {
continue;
} pool[i] = null; buf.clear();
return buf;
} ByteBuffer buf = ByteBuffer.allocateDirect(normalizeCapacity(size));
buf.clear();
return buf;
} final void release(ByteBuffer buffer) {
final SoftReference<ByteBuffer>[] pool = this.pool;
for (int i = 0; i < POOL_SIZE; i ++) {
SoftReference<ByteBuffer> ref = pool[i];
if (ref == null || ref.get() == null) {
pool[i] = new SoftReference<ByteBuffer>(buffer);
return;
}
} // pool is full - replace one
final int capacity = buffer.capacity();
for (int i = 0; i< POOL_SIZE; i ++) {
SoftReference<ByteBuffer> ref = pool[i];
ByteBuffer pooled = ref.get();
if (pooled == null) {
pool[i] = null;
continue;
} if (pooled.capacity() < capacity) {
pool[i] = new SoftReference<ByteBuffer>(buffer);
return;
}
}
} private static final int normalizeCapacity(int capacity) {
// Normalize to multiple of 1024
int q = capacity >>> 10;
int r = capacity & 1023;
if (r != 0) {
q ++;
}
return q << 10;
}
}
SocketReceiveBufferPool中维护了一个SoftReference<ByteBuffer>类型的数组,关于java的SoftReference,大家可以自行搜索。其实就是在此类中维护了一个directbuffer的内存池,此部分的内存是可以重复利用的。那么问题来了,如果我们把netty用于接收网络信息的directbuffer直接传给dubbo的业务代码,那么这个内存池的作用是什么呢,内存如何被release回内存池?带着这个疑问,继续分析调用了SocketReceiveBufferPool的NioWorker代码。
     private boolean read(SelectionKey k) {
final SocketChannel ch = (SocketChannel) k.channel();
final NioSocketChannel channel = (NioSocketChannel) k.attachment(); final ReceiveBufferSizePredictor predictor =
channel.getConfig().getReceiveBufferSizePredictor();
final int predictedRecvBufSize = predictor.nextReceiveBufferSize(); int ret = 0;
int readBytes = 0;
boolean failure = true; ByteBuffer bb = recvBufferPool.acquire(predictedRecvBufSize);
try {
while ((ret = ch.read(bb)) > 0) {
readBytes += ret;
if (!bb.hasRemaining()) {
break;
}
}
failure = false;
} catch (ClosedChannelException e) {
// Can happen, and does not need a user attention.
} catch (Throwable t) {
fireExceptionCaught(channel, t);
} if (readBytes > 0) {
bb.flip(); final ChannelBufferFactory bufferFactory =
channel.getConfig().getBufferFactory();
final ChannelBuffer buffer = bufferFactory.getBuffer(readBytes);
buffer.setBytes(0, bb);
buffer.writerIndex(readBytes);
//if(buffer instanceof BigEndianHeapChannelBuffer){
// logger2.info("buffer instanceof BigEndianHeapChannelBuffer.");
//}
recvBufferPool.release(bb); // Update the predi||\\|||||
predictor.previousReceiveBufferSize(readBytes); // Fire the event.
fireMessageReceived(channel, buffer);
} else {
recvBufferPool.release(bb);
} if (ret < 0 || failure) {
k.cancel(); // Some JDK implementations run into an infinite loop without this.
close(channel, succeededFuture(channel));
return false;
} return true;
}
在代码里发现了netty会再创造一个chanelbuffer对象,然后将directbuffer里的内容复制到chanelbuffer里面,而这个chanelbuffer对象实际上是一个堆内内存,然后netty会真对这块内存进行解码及返回给上层调用服务等,也就是说没有直接将directbuffer返回给dubbo服务,这样也就解释了,我们在提供dubbo服务的jvm里,禁止掉了system.gc的情况下,没有发生过堆外内存泄漏的原因。后面我会找时间详细的分析一下netty4和kafka使用directbuffer的情况。

由dubbo服务禁用system.gc而引起的思考的更多相关文章

  1. 实战Java虚拟机之四:提升性能,禁用System.gc() ?

    今天开始实战Java虚拟机之四:"禁用System.gc()". 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟 ...

  2. Dubbo_创建Dubbo服务并在ZooKeeper注册,然后通过Jar包执行

    一.安装ZooKeeper(略) 二.创建Dubbo服务  1.DemoService 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...

  3. RPC -dubbo 服务导出实现

    在阅读此文章之前,我希望阅读者对Spring 扩展机制的有一定的了解,比如:自定义标签与Spring整合, InitializingBean 接口,ApplicationContextAware,Be ...

  4. Dubbo 服务治理-mock实例

    转: Dubbo 服务治理-mock实例 老生住长亭 2017.02.28 10:56* 字数 514 阅读 2552评论 10喜欢 2 Dubbo的mock自己折腾的实例,配置信息有点简陋,有点粗鄙 ...

  5. dubbo系列四、dubbo服务暴露过程源码解析

    一.代码准备 1.示例代码 参考dubbo系列二.dubbo+zookeeper+dubboadmin分布式服务框架搭建(windows平台) 2.简单了解下spring自定义标签 https://w ...

  6. (五)消费Dubbo服务

    前面我们搞了发布Dubbo服务,发布的服务就是用来消费的,所以我们这里来调用服务,消费下: 创建maven项目 dubbo-demo-consumer pom.xml配置下: <dependen ...

  7. (三)发布Dubbo服务

    我们现在来学习下发布Dubbo服务,主要参考dubbo开发包里的demo源码:由浅入深的讲解下这个小demo: github地址:https://github.com/apache/incubator ...

  8. 关于dubbo服务的xml配置文件报错的问题

    在配置dubbo服务的过程中,经常会遇到虽然程序能够跑起来,但是配置文件一堆红叉,虽然不影响功能,但是确实很让人恶心. 报错信息如下: Multiple annotations found at th ...

  9. Dubbo学习笔记2:Dubbo服务提供端与消费端应用的搭建

    Demo结构介绍 Demo使用Maven聚合功能,里面有三个模块,目录如下: 其中Consumer模块为服务消费者,里面TestConsumer和consumer.xml组成了基于Spring配置方式 ...

随机推荐

  1. 分布式服务通讯框架XXL-RPC

    <分布式服务通讯框架XXL-RPC>    一.简介 1.1 概述 XXL-RPC 是一个分布式服务通讯框架,提供稳定高性能的RPC远程服务调用功能.现已开放源代码,开箱即用. 1.2 特 ...

  2. 有终将被编程潮流淹没的程序员,那是因为没学python人工智能吧?

    2017年被称为中国人工智能元年--在两会时期,人工智能曾经成为国度计策,同时被写进当局报告.在从前的一年里,人工智能获得注重,已逐步浸透到别的行业,无人超市.主动驾驶.人脸识别.智能家居等" ...

  3. 关于JAVA中异常处理的简单阐释.

    ---恢复内容开始--- 这是我的一篇要在博客园发布的随笔,主要是简单的概括一下我本次所学的关于异常处理的知识.有讲的不妥当的地方,或者有需要补充的,还请各位高人给指点,共同学习,虚心求学.谢谢啦~ ...

  4. ORACLE 数据库选择性导出表中数据&导入已存在表数据

    在dos界面下选择性导出表中的数据语句为: exp his/linker@orcl tables=(sysreprot) file="D:\20131218.dmp" query= ...

  5. 单片机开发——03工欲善其事必先利其器(AD软件安装破解)

    在单片机开发中,有了Keil的程序编程,Protues的模拟仿真,那么问题来了,怎么去进行电路图设计以及硬件调试呢?此刻就必须引入本文的Altium Designer (下文简称AD)安装与破解.在硬 ...

  6. JavaScript常用的事件模型

    一.事件绑定模型 DOM0事件模型 1.内联模型(行内绑定):将函数名直接作为HTML标签中事件属性的属性值 <button id="btn" onclick="f ...

  7. C语言 > 数组和指针

    C语言 数组和指针 const: 关于指针和const需要注意一些规则.首先,把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的. 然而,只能把非const数据的地 ...

  8. left join 后的条件 位置不同,查询的结果不同

    表t_a id name 1 a1 2 a2 表t_b a1_id name num 2 b2 1 3 b3 100 left join 后加查询条件 select a.* from t_a a le ...

  9. 传统IT公司/创业公司/互联网大公司的offer如何选择?[转载+原创]

    背景介绍: 第一家工作的公司是一家跨国外企安全公司, 骄傲的称自己不是互联网公司而是传统软件公司, 第二家公司是当下最热的知识分享社区, 创业公司. 第三家公司是挤走谷歌, 曾一度称霸中国的搜索引擎公 ...

  10. bootstrap学习地址2017.6.1

    http://www.runoob.com/bootstrap/bootstrap-tutorial.html