#关于mina1.7及其之前版本 Exception in thread "OutDbThread" java.lang.OutOfMemoryError: Java heap space可能的“臭虫”
同步blog http://redsnow-fenglin.iteye.com/blog/454328
引起OutOfMemoryError的因素可能很多,但有一点可以肯定的是和内存申请、释放有关,鉴于此,我来谈一下在做过的一个项目中,使用mina1.7遇到此问题的解决方案,下面,简单阐述一下项目出现此Exception的背景情形,以便有类似情形的朋友做个参考,找出这个相似的可能“臭虫”。
项目中mina的主要功能是做文件传输的,也是项目的核心,下面我将分阶段来阐述“臭虫”:
第一阶段:在项目模块测试时,系统启动内存就占去500多M,随着文件传输时间的推移,内存渐增,在数小时内增至1.2G多,之后就core掉了,问题很easy的定位在取货配送模块,即文件收发模块
第二阶段:core掉的异常提示为OurOfMemoryError,或java heap space不足引起jvm达到hotspot,直观原因是内存申请的问题,但内存申请是mina做的啊,于是,同事经过多番尝试,终于前进了很大一步,问题暂时解决了,那么,同事做了什么事呢?两点:一点是改mina 的 seesion.write为线程处理,即在发送的时候,开辟新的短线程来完成此工作,表现效果大大提高;而是调整jvm参数,增多jvm可申请内存及jvm回收机制;
第三阶段:该阶段是最要命的,因为已经进入了项目联调准备入网检测的阶段了,只有一周很紧的时间,core掉又跳出来了,表现形式和阶段一大致雷同,入网检测在即啊,大家忙得团团转,开会、讨论、请公司测试部门帮忙...,一时间项目被重视的高度白炽化了;下面,我先具体阐述下引起此bug的业务流程环节,以便有同样类似问题的朋友参考,然后在讲解一下我们的解决方法;
引起bug的业务流程环节:内存的增长,只与文件发送次数成正比,而与发送文件大小无关;
bug的解决过程:会议讨论并无结论,有的仅是各自的想法,上面的意思很简单,尽快解决问题,不影响项目入网检测,要求测试组全力协助我们开发组,测试组自然是从常规的测试入手,观察内存变化,定位引起变化的原因...,而我呢?想到内存增长与发送次数成正比,而无关文件大小,问题应该出在每次发送内存申请与释放上,会后10分钟 头 就问我是否有进展,我汗,看来头期待我能有所进展(也难怪,我是组内搞java最久的,另外几位都是为了项目而转为java),我告诉头:“还没,我在研究mina关于session.write的代码,那两个测试人员应该在做测试方面的监控”,...,先告诉大家结论:问题的确出在mina上,而其解决方法也很简单--该mina内存申请由direct方式为nod-direct方式,缩短mina池资源释放时间,具体解决过程为(问题的解决在于思路、过程,结果是附属的重要产物):
1、优化JVM参数,治标不治本,若真的存在bug,但我们期待简单的优化可以过关,然,无果,但还是建议有兴趣的朋友看看若下几个链接,了解下JVM参数优化的问题,相信你看过之后会觉得牺牲刚才时间是值得的:
JVM参数基础知识:http://blog.163.com/dxf_dgq_sureme/blog/static/97987377200932311451423/
http://jero.iteye.com/blog/75879
JVM参数白皮书:http://java.sun.com/performance/reference/whitepapers/tuning.html#section4.2
JVM参数优化实例,很不错的:http://topic.csdn.net/u/20081015/15/788c61e8-2773-41b6-988e-2b180f315678.html
2、既然上面是治标非本的方法,那我们不得不另寻它方;debug模式观察到mina首先启动线程一个池类PooledByteBufferAllocator,这便成了成功解决此问题的突破口,我们看一下这个类:
public class PooledByteBufferAllocator implements ByteBufferAllocator {
private static final int MINIMUM_CAPACITY = 1;
private static int threadId = 0;
private final Expirer expirer;
private final ExpiringStack[] heapBufferStacks = new ExpiringStack[] {
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), };
private final ExpiringStack[] directBufferStacks = new ExpiringStack[] {
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), new ExpiringStack(),
new ExpiringStack(), new ExpiringStack(), };
private int timeout;
private boolean disposed;
/**
* Creates a new instance with the default timeout.
*/
public PooledByteBufferAllocator() {
this(60);
}
/**
* Creates a new instance with the specified <tt>timeout</tt>.
*/
public PooledByteBufferAllocator(int timeout) {
setTimeout(timeout);
expirer = new Expirer();
expirer.start();
}
...
/**
* Sets the timeout value of this allocator in seconds.
*
* @param timeout <tt>0</tt> or negative value to disable timeout.
*/
public void setTimeout(int timeout) {
if (timeout < 0) {
timeout = 0;
}
this.timeout = timeout;
if (timeout > 0) {
}
}
public ByteBuffer allocate(int capacity, boolean direct) {
ensureNotDisposed();
UnexpandableByteBuffer ubb = allocate0(capacity, direct);
PooledByteBuffer buf = allocateContainer();
buf.init(ubb, true);
return buf;
}
private PooledByteBuffer allocateContainer() {
return new PooledByteBuffer();
}
...
}
是可以修改默认的60(秒),调小其之,加快程序资源释放周期,但第一点在哪里呢?在类ByteBuffer.java,因为我们程序中是用ByteBuffer来分配资源的,下面是其原码:
public abstract class ByteBuffer implements Comparable<ByteBuffer> {
private static ByteBufferAllocator allocator = new PooledByteBufferAllocator();
/**
* default is set true by mina
*/
private static boolean useDirectBuffers = true;
/**
* Returns the current allocator which manages the allocated buffers.
*/
public static ByteBufferAllocator getAllocator() {
return allocator;
}
/**
* Changes the current allocator with the specified one to manage
* the allocated buffers from now.
*/
public static void setAllocator(ByteBufferAllocator newAllocator) {
if (newAllocator == null) {
throw new NullPointerException("allocator");
}
ByteBufferAllocator oldAllocator = allocator;
allocator = newAllocator;
if (null != oldAllocator) {
oldAllocator.dispose();
}
}
public static boolean isUseDirectBuffers() {
return useDirectBuffers;
}
public static void setUseDirectBuffers(boolean useDirectBuffers) {
ByteBuffer.useDirectBuffers = useDirectBuffers;
}
...
}
从代码中我们可以看到,有用的信息是private static boolean useDirectBuffers = true;即使用直存方式申请内存,可以通过setUseDirectBuffers(boolean useDirectBuffers)方法修改其默认值
第四阶段:问题成功解决的方法:1、修改ByteBuffer.java内存申请方式默认为对战模式;2、缩短PooledByteBufferAllocator.java对Expire处理的时长;(一种方式直接改变源代码,一种方式修改程序、调整相应参数,我采用前者,因为文件传输模块非我开放维护,不便修改代码),顺便提下:mina其实也已经意识到此问题了,在mina2.0中已经修改了内存申请的默认方式为堆栈而非直存了,而且重写了池的管理,mina2.0已不具备后兼容性了,因为变化太大了;
经过上面的调整之后,系统启动占去内存已不足200M,在1小时内增长过两次,每次增长47M左右,之后稳定在200多M,运行24小时,表现良好,至此,问题告一段落,项目顺利通过入网检测各项指标考核,包括压力、疲劳测试;
第五阶段:大家一定疑惑为何会有如上的解决方案,其实第四阶段是在第五阶段之后产生的,本阶段的核心是:java内存使用方式及申请方式的关系,简单说就是直存方式速度快,但资源回收在GC之外,堆栈方式速度稍逊,但资源回收在GC之内,顺便提下GC并发优于并行及其它,详细的就不自我解说了,有兴趣的朋友可以看看如下信息:
JVM 如何使用 Windows 和 Linux 上的本机内存:http://www.ibm.com/developerworks/cn/java/j-nativememory-linux/
Direct vs. non-direct buffers http://java.sun.com/j2se/1.5.0/docs/api/ ByteBuffer
apche mina关于OutOfMemoryError 的mail list :http://mail-archives.apache.org/mod_mbox/mina-dev/200611.mbox/<8C29AD40787A694A81108B3FBF4C15F1CEAF6D@hue.cybersoft-vn.com>
http://mail-archives.apache.org/mod_mbox/mina-dev/200710.mbox/%3CABFD05FF09D13E4883B757E75603624DA3404A@ex-sea1.vistocorp.com%3E
似乎写的过于累赘,可能不太适合朋友们的口味,只是想阐述下具体的情况与之对应,因为Bug甚多,模式仅可借鉴非硬搬...
相关推荐
mina-core-2.0.0-M6.jar mina-example-2.0.0-M6.jar mina-filter-codec-netty-2.0.0-M6.jar mina-filter-compression-2.0.0-M6.jar mina-integration-beans-2.0.0-M6.jar mina-integration-jmx-2.0.0-M6.jar mina-...
标签"apache-mina-2.0."是关于Apache Mina的一个特定版本号,这表明讨论的是2.0系列的一个版本,其中"."可能是标签输入时的误操作。Apache Mina的每个版本都有可能包含性能优化、新功能的添加或者已知问题的修复。 ...
mina-filter-compression-2.0.7.jar,mina 过滤器jar包,核心包之一
mina-core-2.0.0-M1.jar/mina-example-1.0.5.jar/slf4j-jdk14-1.6.1.jar/slf4j-log4j12-1.6.1.jar mina 所用jar
gcc-arm-11.2-2022.02-mingw-w64-i686-arm-none-eabi.exe
简单的Mina Server用到的三个jar包,官网的资料可以...http://mina.apache.org/mina-project/quick-start-guide.html qndroid老师的视频基础跳过了,加了这三个包服务端是没报错了 https://www.imooc.com/video/12197
Apache MINA是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利...当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发、串口通讯程序(只在最新的预览版中提供)。
mina-integration-ognl-2.0.0-M4.jar mina-integration-ognl-2.0.0-M4.jar
《mina-core-2.0.0-M1.jar与SLF4J:构建高效网络通信与日志记录》 mina-core-2.0.0-M1.jar是Apache Mina项目的核心库,它是一个高度可扩展的网络通信框架,主要用于构建高性能、高效率的服务端和客户端应用程序。...
《mina-http-2.0.7.jar:Java中的MINA框架与HTTP支持详解》 MINA(Multipurpose Infrastructure for Network Applications)是一个强大的网络应用程序框架,它由Apache软件基金会开发,主要用于构建高性能、高效率...
mina-core-2.0.0-M1-sources.jar是构建mina框架的主要文件
Apache Mina是一个高性能、异步事件驱动的网络应用程序..."apache-mina-2.0.2-bin.tar.gz"提供了一切你需要开始使用Mina的资源,只需按照Java开发流程,正确引入依赖并根据需求配置,即可轻松构建高性能的网络应用。
标题中的"apache-mina-2.0.7-bin.tar"指的是Apache Mina 2.0.7版本的二进制发布包,该包包含了编译好的库文件和运行时所需的资源,用户可以直接使用而无需自行编译源代码。 描述中提及的"apache-mina-2.0.7-src.tar...
mina-transport-apr-2.0.2.jar
android 上的ftp服务器例子,通过ApacheFtpServer实现,依赖以下jar包: commons-...太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
mina-core-2.0.0-M6.jar
米娜 apache-mina-2.0.16-bin.zip米娜 apache-mina-2.0.16-bin.zip米娜 apache-mina-2.0.16-bin.zip米娜 apache-mina-2.0.16-bin.zip
在这个场景中,我们关注的是MINA的核心组件以及两个特定的过滤器和传输组件:`mina-core-2.0.0-RC1.jar`、`mina-filter-compression-2.0.0-RC1.jar`和`mina-transport-apr-2.0.0-RC1.jar`。 **MINA Core (mina-core...
mina-integration-xbean-2.0.2.jar
这个"apache-mina-2.0.4.rar"压缩包包含的是Apache Mina 2.0.4版本的源代码,是深入理解和定制Mina的理想资源。 Apache Mina的核心理念是提供一个简洁的API,使得开发者能够专注于业务逻辑,而不是网络通信的底层...