感觉中,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 到底能不能被捕获?的更多相关文章

  1. JavaScript之事件处理详解

    一.事件传播机制 客户端JavaScript程序(就是浏览器啦)采用了异步事件驱动编程模型.当文档.浏览器.元素或与之相关的对象发生某些有趣的事情时,Web浏览器就会产生事件(event).如果Jav ...

  2. 超赞的OOM检测(除了mat以外)

    今天看了下微博,扔物线分享了个内存检测的工具: 内存泄露是 OOM 最常见的原因,但它的侦测需人工排查,往往眼看瞎也未必能找到泄露的内存.Square 新库 LeakCanary 用一种巧妙的思路实现 ...

  3. Java finally语句到底是在return之前还是之后执行(JVM字节码分析及内部体系结构)?

    之前看了一篇关于"Java finally语句到底是在return之前还是之后执行?"这样的博客,看到兴致处,突然博客里的一个测试用例让我产生了疑惑. 测试用例如下: public ...

  4. 异常 Exception 堆栈跟踪 异常捕获 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  5. JVM内存溢出分析java.lang.OutOfMemoryError: Java heap space

    JVM内存溢出查询java.lang.OutOfMemoryError: Java heap space查出具体原因分为几个预备步骤 1.在运行java程序是必须设置jvm -XX:+HeapDump ...

  6. 理解 OutOfMemoryError 异常

    OutOfMemoryError 异常应该可以算得上是一个非常棘手的问题.JAVA 的程序员不用像苦逼的 C 语言程序员手动地管理内存,JVM 帮助他们分配内存,释放内存.但是当遇到内存相关的问题,就 ...

  7. iOS - 捕获应用程序崩溃日志

    作为一名iOS移动应用开发者,为了确保你的应用程序正确无误,在将应用程序提交到应用商店之前,你必定会进行大量的测试工作:而且在你测试的过程中应用程序运行的很好,但是在应用商店上线之后,还是有用户抱怨应 ...

  8. C#由变量捕获引起对闭包的思考

    前言 偶尔翻翻书籍看看原理性的东西确实有点枯燥,之前有看到园中有位园友说到3-6年工作经验的人应该了解的.NET知识,其中就有一点是关于C#中的闭包,其实早之前在看书时(之前根本不知道C#中还有闭包这 ...

  9. java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得

    我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性 ...

随机推荐

  1. 周一要做的事情Sd31y5YE

    问实习生活动是否有费用 调查结果更新,提醒各个pm 修改模板,把我们追加的东西体现在模板里

  2. py3.0第四天 函数

    列表生成 # -*- coding: utf-8 -*- # data =[1,2,3] # for index,i in enumerate(data): # print (index,i) # d ...

  3. javascript之原型链

    JavaScript 中,万物皆对象!(对于编程而言,可以说万物皆对象.) js中的原型链的作用时什么呢? 我自己的理解是,给一个人赋予一些技能, function people(name,age,s ...

  4. FileReader实现图片预览,并上传(js代码)

    var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i; //控制格式 var iMaxFilesi ...

  5. dremio jdbc使用

    驱动包地址 链接:https://pan.baidu.com/s/1Nivkvze24hRH8pXOQleCgw 提取码:gp9z 使用dremio主要原因 : 1)springboot提供了es组件 ...

  6. Python开发——10.面向对象编程进阶

    一.isinstance(obj,cls)和issubclass(sub,super) 1.isinstance(obj,cls) 判断obj是不是由cls产生的类 2.issubclass(sub, ...

  7. react-eslintrc

    { "extends": ["eslint:recommended"], "plugins": [ "react" ], ...

  8. .NET CORE迁移踩坑

    https://www.cnblogs.com/leolaw/p/10740678.html

  9. MySQL定时器

    MySQL的定时器是一个很有用的功能,有时候需要数据库自动根据时间进行一些必要的操作,此时定时器就派上了用场了. 一.查看MySQL版本号 select version(); 二.查看event的状态 ...

  10. ESP32随笔汇总

    版权声明:本文为博主原创文章,未经博主本人不得转载.联系邮箱:mynoticeable@gmail.com 1.ubuntu 14.04下搭建esp32开发环境 2.UBUNTU14.0.4安装ecl ...