预防OOM,Tomcat是这样做的

写Java程序的,或者是基于JVM进行应用开发的,一定对OOM不陌生。正所谓

「常在河边走,那能不OOM」,夜路走多了,总会碰见OOM

OOM,全称Out of Memory。维基百科上如下解释

Out of memory (OOM) is an often undesired state of computer operation where no additional memory can be allocated for use by programs or the operating system.

对,就是内存不够用了。为了解决和预防OOM的发生,我们的杀手锏当然是

加大内存。除此之外,还有以下几种手段:

  • 调整应用的内存占用

  • 调整分代策略与GC算法

  • 分析可能的内存泄露,优化代码

  • ...

当然,针对Tomcat这个Java应用本身,我们仍然可以使用上述的这些方式来应用OOM。而Tomcat自身,为了保证应用的健壮,也做了一些努力。例如下面的这项配置,官方是这样解释的:

The NIO connector implements an OutOfMemoryError strategy called parachute. It holds a chunk of data as a byte array. In case of an OOM, this chunk of data is released and the error is reported. This will give the VM enough room to clean up. The oomParachute represents the size in bytes of the parachute(the byte array). The default value is1024*1024(1MB). Please note, this only works for OOM errors regarding the Java Heap space, and there is absolutely no guarantee that you will be able to recover at all. If you have an OOM outside of the Java Heap, then this parachute trick will not help.

看重点,OutOfMemoeryError strategy,名字叫parachute。只针对Java堆,默认1M的内存会被预先占用,在OOM的时候,会被释放,以便虚拟机有足够的空间进行回收。

高筑墙,广积粮,大力防止OOM。古人的思想老外也学会了。

我们再来看代码中,Tomcat是如何实现的。

在Connector启动,进行bind操作时,会判断oomParachute的判断

   if (oomParachute>0) reclaimParachute(true);  // 默认1M

        selectorPool.open();

而在应用服务器运行过程中,如果Acceptor捕获到OOME,就会进行内存的回收,此时逻辑是这个样子

        catch (OutOfMemoryError oom) {

                    try {

                        oomParachuteData = null; // 此处会清空占用

                        releaseCaches();   //释放当前占用的缓存

                        log.error("", oom);

                    }catch ( Throwable oomt ) {

                        try {

                            try {

                                System.err.println(oomParachuteMsg);

                                oomt.printStackTrace();

                            }catch (Throwable letsHopeWeDontGetHere){

                                ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);

                            }

同时,在NIO的Poller线程中,每次连接请求执行时,也会判断是否配置了

parachute或者在清空后是否有重新申请

protected void checkParachute() {

        boolean para = reclaimParachute(false);

        if (!para && (System.currentTimeMillis()-lastParachuteCheck)>10000) {

            try {

                log.fatal(oomParachuteMsg);

            }catch (Throwable t) {

                ExceptionUtils.handleThrowable(t);

                System.err.println(oomParachuteMsg);

            }

            lastParachuteCheck = System.currentTimeMillis();

        }

    }

protected boolean reclaimParachute(boolean force) {

        if ( oomParachuteData != null ) return true;

        if ( oomParachute > 0 && ( force || (Runtime.getRuntime().freeMemory() > (oomParachute*2))) )  // 要申请也会先判断是否足够大,2倍的大 :)

            oomParachuteData = new byte[oomParachute];

        return oomParachuteData != null;

    }

总结一下,就是预先占用一小块内存,在OOM产生的时候进行释放,同时清空其它的缓存,以便应用可以恢复服务

这就是源码的主体功能之外,我们能get到的一些其它的东西。

你可以还感兴趣:

Tomcat的Connector组件

怎样阅读源代码?

读源码时,我们到底在读什么?

Tomcat那些事儿

本公众号由曾从事应用服务器核心研发的工程师维护。文章深入Tomcat源码,分析应用服务器的实现细节,工作原理及与之相关的技术,使用技巧,工作实战等。起于Tomcat但不止于此。同时会分享并发、JVM等,内容多为原创,欢迎关注。


扫描或长按下方二维码,即可关注!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值