OutOfMemoryError 到底能不能被捕获?
感觉中,OutOfMemeryError(内存溢出错误) 是jvm抛出的异常,是不能被捕获的。
直到工作中真的遇到OOM异常,而且tomcat服务还一直对外提供服务。
那么问题来了:
  1. OOM 到底能不能被捕获?
		  2. jvm抛出OOM后,是否就会立即停止运行呢?
		  3. jvm什么时候会抛出OOM异常?
先来个例子:
本例子将会一一体现如上问题:(最好设置jvm最大内存如: -Xmx2m -Xms2m)
public class OOMCatchTest {
    /**
     * 可以看作是一个消息队列, 作为 Producer 与 Consumer 的通信桥梁 <br />
     *      其实此处存在并发写的问题,不过不是本文的重点,暂且忽略
     */
    private static volatile List<UserObj> userWaitingList = new ArrayList<>();
    private AtomicInteger uidCenter = new AtomicInteger(0);
    // 随机数生成源
    private final String rndSource = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
    public static void main(String[] args) throws IOException {
        OOMCatchTest oomCatchTest = new OOMCatchTest();
        Thread producer = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis() + ": start producer.");
                oomCatchTest.productUserObj();
                System.out.println(System.currentTimeMillis() + ": end producer.");
            }
        });
        producer.setName("producer-1");
        producer.start();
        Thread consumer = new Thread(() -> {
                System.out.println(System.currentTimeMillis() + ": start consumer.");
                try {
                    oomCatchTest.consumeUserObj();
                }
                catch (Throwable e) {
                    System.out.println("consumer caught exception: " + e.getMessage());
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis() + ": end consumer.");
            });
        consumer.setName("consumer-1");
        consumer.start();
        System.out.println("over the main");
    }
    // 生产者
    public void productUserObj() {
        Random rnd = new Random();
        OOMCatchTest oomTest = new OOMCatchTest();
        // 可作开关
        boolean startProduce = true;
        try {
            while (startProduce) {
                UserObj userTemp = new UserObj();
                userTemp.setAddress(oomTest.generateRandomStr(20));
                userTemp.setAge(rnd.nextInt(100));
                userTemp.setUid(oomTest.generateUid());
                userTemp.setName(oomTest.generateRandomStr(10));
                // 此处展示 ArrayList 导致的抛出OOM类型
                userWaitingList.add(userTemp);
                System.out.println("produce:" + userTemp);
            }
        }
        // 此处可捕获 OOM
        catch (Throwable e) {
            // 模拟一个服务提供者,做死循环
            System.out.println("An Exception: "
                    + e.getClass().getCanonicalName() + " " + e.getMessage()
                    + " occour..., cur uid:" + oomTest.uidCenter);
            int j = 0;
            // 此处运行代表 OOM 并未终止jvm
            while (j++ < 1000) {
                try {
                    Thread.sleep(1000);
                    System.out.println("producer oom, wait: " + j);
                }
                catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
            // 如果打印栈桢,只会更增加内存消耗,从而导致线程异常退出
//            e.printStackTrace();
        }
    }
    // 消费者
    public void consumeUserObj() {
        // 可做开关
        boolean startConsume = true;
        while(startConsume) {
            // 做空等等
            while (userWaitingList.isEmpty()) {
                Thread.yield();
            }
            while (userWaitingList.iterator().hasNext()) {
                try {
                    // do sth
                    Thread.sleep(50);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
                UserObj obj = userWaitingList.iterator().next();
                System.out.println("consume user:" + obj);
                userWaitingList.remove(obj);
            }
        }
    }
    public String generateRandomStr(int digit) {
        StringBuilder sb = new StringBuilder();
        int len = rndSource.length();
        Random random = new Random();
        for(int i = 0; i < digit; i++) {
            sb.append(rndSource.charAt(random.nextInt(len)));
        }
        return sb.toString();
    }
    public Integer generateUid() {
        return uidCenter.incrementAndGet();
    }
    static class UserObj {
        private Integer uid;
        private String name;
        private Integer age;
        private String address;
        public Integer getUid() {
            return uid;
        }
        public void setUid(Integer uid) {
            this.uid = uid;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        public String getAddress() {
            return address;
        }
        public void setAddress(String address) {
            this.address = address;
        }
        @Override
        public String toString() {
            return "UserObj{" +
                    "uid=" + uid +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
}
如上例子,可能抛出如下异常:
produce:UserObj{uid=2135, name='7QSy8X251t', age=44, address='2wwye8WEfR6dHJEQrIHk'}
An Exception: GC overhead limit exceeded occour..., cur uid:2136
java.lang.OutOfMemoryError: GC overhead limit exceeded
consume user:UserObj{uid=1, name='nBf1Ck3T2G', age=20, address='7ubqHrfiHf5WEdPtbJak'}
    at java.util.Arrays.copyOfRange(Arrays.java:3664)
    at java.lang.String.<init>(String.java:207)
    at java.lang.StringBuilder.toString(StringBuilder.java:407)
    at com.xxx.tester.OOMCatchTest.generateRandomStr(OOMCatchTest.java:98)
    at com.xxx.tester.OOMCatchTest.productUserObj(OOMCatchTest.java:58)
1541324629155: end producer.
    at com.xxx.tester.OOMCatchTest$1.run(OOMCatchTest.java:26)
    at java.lang.Thread.run(Thread.java:745)
Exception in thread "consumer-1" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.util.Arrays.copyOfRange(Arrays.java:3664)
    at java.lang.String.<init>(String.java:207)
    at java.lang.StringBuilder.toString(StringBuilder.java:407)
    at com.xxx.tester.OOMCatchTest$UserObj.toString(OOMCatchTest.java:145)
    at java.lang.String.valueOf(String.java:2994)
    at java.lang.StringBuilder.append(StringBuilder.java:131)
Exception in thread "consumer-1" java.lang.OutOfMemoryError: GC overhead limit exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded
An Exception: GC overhead limit exceeded occour..., cur uid:11743
    at java.util.Arrays.copyOf(Arrays.java:3332)
Exception in thread "producer-1" java.lang.OutOfMemoryError: GC overhead limit exceeded
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "consumer-1"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "producer-1"
produce:UserObj{uid=3751, name='dtXmpNGMs1', age=41, address='1Bujt5TzHv04cptNEyUb'}
An Exception: java.lang.OutOfMemoryError GC overhead limit exceeded occour..., cur uid:3752
producer oom, wait: 1
producer oom, wait: 2
producer oom, wait: 3
抛出OOM异常有几种情况:
1. java中直接throw 抛出 OOM;(后面详细列举)
2. 使用new int[MAX] 等基本类型方式时抛出 OOM,这种异常隐式抛出;
3. 当收到外部特殊信号时抛出,如:常用的威胁信号 kill -3 <pid>;
而通常,前两个OOM都是可能被捕获的! 且抛出的OOM只会影响当前线程(和其他异常一样)。不过 OOM 一般会具有普遍性,即一个线程OOM时,通常其他线程也跑不掉!
下面来看几个JAVA中主动抛出 OOM 的样例吧:
// java.util.ArrayList.add(E e), 进行扩容的时候,就可能抛出oom, 也即代码异常,可以捕获
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
} ensureExplicitCapacity(minCapacity);
} private void ensureExplicitCapacity(int minCapacity) {
modCount++; // overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
} /**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
} private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
} // java.nio.ByteBuffer.allocateDirect(int capacity); //分配直接内存时,可能抛出oom /**
* Allocates a new direct byte buffer.
*
* <p> The new buffer's position will be zero, its limit will be its
* capacity, its mark will be undefined, and each of its elements will be
* initialized to zero. Whether or not it has a
* {@link #hasArray backing array} is unspecified.
*
* @param capacity
* The new buffer's capacity, in bytes
*
* @return The new byte buffer
*
* @throws IllegalArgumentException
* If the <tt>capacity</tt> is a negative integer
*/
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
// java.nio.DirectByteBuffer
DirectByteBuffer(int cap) { // package-private super(-1, 0, cap, cap);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
// 此处先抛出oom
Bits.reserveMemory(size, cap); long base = 0;
try {
base = unsafe.allocateMemory(size);
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
}
unsafe.setMemory(base, size, (byte) 0);
if (pa && (base % ps != 0)) {
// Round up to page boundary
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null; } // which a process may access. All sizes are specified in bytes.
static void reserveMemory(long size, int cap) { if (!memoryLimitSet && VM.isBooted()) {
maxMemory = VM.maxDirectMemory();
memoryLimitSet = true;
} // optimist!
if (tryReserveMemory(size, cap)) {
return;
} final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess(); // retry while helping enqueue pending Reference objects
// which includes executing pending Cleaner(s) which includes
// Cleaner(s) that free direct buffer memory
while (jlra.tryHandlePendingReference()) {
if (tryReserveMemory(size, cap)) {
return;
}
} // trigger VM's Reference processing
System.gc(); // a retry loop with exponential back-off delays
// (this gives VM some time to do it's job)
boolean interrupted = false;
try {
long sleepTime = 1;
int sleeps = 0;
while (true) {
if (tryReserveMemory(size, cap)) {
return;
}
if (sleeps >= MAX_SLEEPS) {
break;
}
if (!jlra.tryHandlePendingReference()) {
try {
Thread.sleep(sleepTime);
sleepTime <<= 1;
sleeps++;
} catch (InterruptedException e) {
interrupted = true;
}
}
} // no luck
throw new OutOfMemoryError("Direct buffer memory"); } finally {
if (interrupted) {
// don't swallow interrupts
Thread.currentThread().interrupt();
}
}
} // java.util.concurrentHashMap.toArray(); // public final Object[] toArray() {
long sz = map.mappingCount();
if (sz > MAX_ARRAY_SIZE)
// Required array size too large
throw new OutOfMemoryError(oomeMsg);
int n = (int)sz;
Object[] r = new Object[n];
int i = 0;
for (E e : this) {
if (i == n) {
if (n >= MAX_ARRAY_SIZE)
throw new OutOfMemoryError(oomeMsg);
if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
n = MAX_ARRAY_SIZE;
else
n += (n >>> 1) + 1;
r = Arrays.copyOf(r, n);
}
r[i++] = e;
}
return (i == n) ? r : Arrays.copyOf(r, i);
}
// java.nio.file.Files.readAllBytes(Path path)
    public static byte[] readAllBytes(Path path) throws IOException {
        try (SeekableByteChannel sbc = Files.newByteChannel(path);
             InputStream in = Channels.newInputStream(sbc)) {
            long size = sbc.size();
            if (size > (long)MAX_BUFFER_SIZE)
                throw new OutOfMemoryError("Required array size too large");
            return read(in, (int)size);
        }
    }
    /**
     * Reads all the bytes from an input stream. Uses {@code initialSize} as a hint
     * about how many bytes the stream will have.
     *
     * @param   source
     *          the input stream to read from
     * @param   initialSize
     *          the initial size of the byte array to allocate
     *
     * @return  a byte array containing the bytes read from the file
     *
     * @throws  IOException
     *          if an I/O error occurs reading from the stream
     * @throws  OutOfMemoryError
     *          if an array of the required size cannot be allocated
     */
    private static byte[] read(InputStream source, int initialSize) throws IOException {
        int capacity = initialSize;
        byte[] buf = new byte[capacity];
        int nread = 0;
        int n;
        for (;;) {
            // read to EOF which may read more or less than initialSize (eg: file
            // is truncated while we are reading)
            while ((n = source.read(buf, nread, capacity - nread)) > 0)
                nread += n;
            // if last call to source.read() returned -1, we are done
            // otherwise, try to read one more byte; if that failed we're done too
            if (n < 0 || (n = source.read()) < 0)
                break;
            // one more byte was read; need to allocate a larger buffer
            if (capacity <= MAX_BUFFER_SIZE - capacity) {
                capacity = Math.max(capacity << 1, BUFFER_SIZE);
            } else {
                if (capacity == MAX_BUFFER_SIZE)
                    throw new OutOfMemoryError("Required array size too large");
                capacity = MAX_BUFFER_SIZE;
            }
            buf = Arrays.copyOf(buf, capacity);
            buf[nread++] = (byte)n;
        }
        return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
    }
// java.io.BufferedInputStream.read()/skip()/
// java.io.BufferedInputStream.fill()
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* grow buffer */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }
// java.lang.AbstractStringBuilder.expandCapacity(int minimumCapacity)
// java.lang.AbstractStringBuilder.ensureCapacityInternal(int minimumCapacity)
// java.lang.AbstractStringBuilder.append(String str)
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
扩展,实际使用中捕获OOM的案例:
dubbo 捕获的 server 端的 OOM 异常示例如下:
-- ::55.814 [DubboServerHandler-172.23.0.28:-thread-] ERROR com.alibaba.dubbo.rpc.filter.ExceptionFilter - [DUBBO] Got unchecked and undeclared exception which called by 172.25.0.12. service: com.mobanker.xsxf.sxk.contract.basedata.RpcProtocolService, method: getUserProtocol, exception: java.lang.OutOfMemoryError: Java heap space, dubbo version: 3.0., current host: 172.23.0.28
java.lang.OutOfMemoryError: Java heap space
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:)
at java.lang.reflect.Constructor.newInstance(Constructor.java:)
at com.alibaba.com.caucho.hessian.io.JavaDeserializer.instantiate(JavaDeserializer.java:)
at com.alibaba.com.caucho.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:)
at com.alibaba.com.caucho.hessian.io.SerializerFactory.readObject(SerializerFactory.java:)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:)
at com.alibaba.dubbo.common.serialize.support.hessian.Hessian2ObjectInput.readObject(Hessian2ObjectInput.java:)
at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:)
at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody(DubboCodec.java:)
at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:)
at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.decode(DubboCountCodec.java:)
at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.messageReceived(NettyCodecAdapter.java:)
at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:)
at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:)
at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:)
at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:)
at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:)
at org.jboss.netty.util.internal.DeadLockProofWorker$.run(DeadLockProofWorker.java:)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:)
at java.lang.Thread.run(Thread.java:)
捕获代码:
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
try {
Result result = invoker.invoke(invocation);
if (result.hasException() && GenericService.class != invoker.getInterface()) {
try {
Throwable exception = result.getException(); // directly throw if it's checked exception
if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
return result;
}
// directly throw if the exception appears in the signature
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?>[] exceptionClassses = method.getExceptionTypes();
for (Class<?> exceptionClass : exceptionClassses) {
if (exception.getClass().equals(exceptionClass)) {
return result;
}
}
} catch (NoSuchMethodException e) {
return result;
} // for the exception not found in method's signature, print ERROR message in server's log.
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception); // directly throw if exception class and interface class are in the same jar file.
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
return result;
}
// directly throw if it's JDK exception
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
return result;
}
// directly throw if it's dubbo exception
if (exception instanceof RpcException) {
return result;
} // otherwise, wrap with RuntimeException and throw back to the client
return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
} catch (Throwable e) {
logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
return result;
}
}
return result;
} catch (RuntimeException e) {
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
throw e;
}
}
tomcat 中捕获OOM的异常示例如下:
-Oct- ::04.258 严重 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor.run Unexpected death of background thread ContainerBackgroundProcessor[StandardEngine[Catalina]]
java.lang.OutOfMemoryError: Java heap space Exception in thread "ContainerBackgroundProcessor[StandardEngine[Catalina]]" java.lang.OutOfMemoryError: Java heap space
捕获代码:
    // -------------------------------------- ContainerExecuteDelay Inner Class
    /**
     * Private thread class to invoke the backgroundProcess method
     * of this container and its children after a fixed delay.
     */
    protected class ContainerBackgroundProcessor implements Runnable {
        @Override
        public void run() {
            Throwable t = null;
            String unexpectedDeathMessage = sm.getString(
                    "containerBase.backgroundProcess.unexpectedThreadDeath",
                    Thread.currentThread().getName());
            try {
                while (!threadDone) {
                    try {
                        Thread.sleep(backgroundProcessorDelay * 1000L);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                    if (!threadDone) {
                        processChildren(ContainerBase.this);
                    }
                }
            } catch (RuntimeException|Error e) {
                t = e;
                throw e;
            } finally {
                if (!threadDone) {
                    log.error(unexpectedDeathMessage, t);
                }
            }
        }
        protected void processChildren(Container container) {
            ClassLoader originalClassLoader = null;
            try {
                if (container instanceof Context) {
                    Loader loader = ((Context) container).getLoader();
                    // Loader will be null for FailedContext instances
                    if (loader == null) {
                        return;
                    }
                    // Ensure background processing for Contexts and Wrappers
                    // is performed under the web app's class loader
                    originalClassLoader = ((Context) container).bind(false, null);
                }
                container.backgroundProcess();
                Container[] children = container.findChildren();
                for (int i = 0; i < children.length; i++) {
                    if (children[i].getBackgroundProcessorDelay() <= 0) {
                        processChildren(children[i]);
                    }
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("Exception invoking periodic operation: ", t);
            } finally {
                if (container instanceof Context) {
                    ((Context) container).unbind(false, originalClassLoader);
               }
            }
        }
    }
总结:
1. oom异常一般是java程序在做内存分配时,发现没有足够的剩余空间可用而抛出的异常;
2. 此时的分配空间可能是出于代码的new操作(用户主动),可能是出于内存的复制操作(语言自动),也可能是出于内存数据的重振操作(语言自动),可能是出jvm检测到外部信号(jvm自动);
3. oom只是被建议为不要捕获的异常,因为通常你对这种情况是无能为力的!但你如果实在要捕获,why not ?
4. oom一般只会影响当前线程,而jvm中只要存在一个非daemon线程在运行,jvm就不会退出;
5. 如果是线程池运行环境,一般需要一个统一管理oom的程序,否则不能及时统一处理oom;
OutOfMemoryError 到底能不能被捕获?的更多相关文章
- JavaScript之事件处理详解
		一.事件传播机制 客户端JavaScript程序(就是浏览器啦)采用了异步事件驱动编程模型.当文档.浏览器.元素或与之相关的对象发生某些有趣的事情时,Web浏览器就会产生事件(event).如果Jav ... 
- 超赞的OOM检测(除了mat以外)
		今天看了下微博,扔物线分享了个内存检测的工具: 内存泄露是 OOM 最常见的原因,但它的侦测需人工排查,往往眼看瞎也未必能找到泄露的内存.Square 新库 LeakCanary 用一种巧妙的思路实现 ... 
- Java finally语句到底是在return之前还是之后执行(JVM字节码分析及内部体系结构)?
		之前看了一篇关于"Java finally语句到底是在return之前还是之后执行?"这样的博客,看到兴致处,突然博客里的一个测试用例让我产生了疑惑. 测试用例如下: public ... 
- 异常 Exception 堆栈跟踪 异常捕获 MD
		Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ... 
- JVM内存溢出分析java.lang.OutOfMemoryError: Java heap space
		JVM内存溢出查询java.lang.OutOfMemoryError: Java heap space查出具体原因分为几个预备步骤 1.在运行java程序是必须设置jvm -XX:+HeapDump ... 
- 理解 OutOfMemoryError 异常
		OutOfMemoryError 异常应该可以算得上是一个非常棘手的问题.JAVA 的程序员不用像苦逼的 C 语言程序员手动地管理内存,JVM 帮助他们分配内存,释放内存.但是当遇到内存相关的问题,就 ... 
- iOS - 捕获应用程序崩溃日志
		作为一名iOS移动应用开发者,为了确保你的应用程序正确无误,在将应用程序提交到应用商店之前,你必定会进行大量的测试工作:而且在你测试的过程中应用程序运行的很好,但是在应用商店上线之后,还是有用户抱怨应 ... 
- C#由变量捕获引起对闭包的思考
		前言 偶尔翻翻书籍看看原理性的东西确实有点枯燥,之前有看到园中有位园友说到3-6年工作经验的人应该了解的.NET知识,其中就有一点是关于C#中的闭包,其实早之前在看书时(之前根本不知道C#中还有闭包这 ... 
- java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得
		我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ... 
随机推荐
- linux内存黑洞
			1.问题 k8s集群中node节点的内存使用率居高不下,使用率达到90%多.通过以下命令可以发现此虚拟机分配的内存为15g,但是用户进程使用的内存总共为7个多g,并且slab和pageTables使用 ... 
- centos 下安装java
			yum安装; 查看yum库中的java安装包 :yum -y list java* 安装需要的jdk版本的所有java程序:yum -y install java-1.8.0-openjdk* 查看j ... 
- P1439 最长公共子序列(nlognLCS问题)
			模板 #include <iostream> #include <cstdio> using namespace std; ],loc[],b[],k,n,l,r,mid; i ... 
- Heartbeat详解
			转自:http://blog.sina.com.cn/s/blog_7b6fc4c901012om0.html 配置主节点的Heartbeat Heartbeat的主要配置文件有ha.cf.hares ... 
- Linux 平台 tcpdump 抓包
			一. 在ecs上准备好mysql客户端命令 二. 开启抓包,抓包方法如下:Linux 平台: 1. 打开一个到ECS的ssh连接,并以root身份登录.在该窗口运行下列命令tcpdump -i any ... 
- UVA 2290 Transmitters
			题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ... 
- Python3创建项目时创建了一个叫做“keyword"的包,运行项目时报ImportError: cannot import name 'iskeyword'错误
			导致该问题的原因为在Python3中keyword是python的关键字包,所以在给包命名时应避免使用关键字进行命名.解决方法,将keword包名称修改为'keywords'就可以了. 
- CentOS 7 下安装oracle 11g碰到的一些问题
			OUI预检查时会报错,安装时会报两个不符合项目 1 compat-libstdc++ 提示未安装 奇怪这个,yum install compat-libstdc++ 老是提示找不到包,其实正确的安装方 ... 
- linux crontab 命令,最小的执行时间是一分钟,如需要在小于一分钟内重复执行
			编写shell脚本实现 crontab.sh #!/bin/bash step=2 #间隔的秒数,不能大于60 for (( i = 0; i < 60; i=(i+step) )); do $ ... 
- laravel 对接 UCenter 基础
			说明:1,运行环境 laravel 5.3 php7+nginx+mysql 2,使用安装包 https://github.com/goodspb/laravel5-ucenter 上面有对接方 ... 
