感觉中,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. Python unittest使用小结

    unittest是Python自带的单元测试框架,其中最核心的四个概念是:test case, test suite, test runner, test fixture. 流程:TestLoader ...

  2. hbase_存储模型

    Hbase 是按列存储,所以每个列族存储在一个HDFS文件上. Hbase表中的行是按照rowkey字典序进行排列的,并且表格在行的方向上被分割为多个region(按照行进行分割的) region 是 ...

  3. 设置PL/SQL 快捷键

    TOOLS-preferences--user interface--editor--Autoreplace--enabled (check)--address(C:\Program Files (x ...

  4. OO第一单元单元总结

    总述 三周的时间一晃而过,也到了和表达式说再见的时候了.想起来,现在已经能够优雅地在互测“攻击”别人,然后笑对被别人“攻击”,就觉得OO这三周还是很有意义,也多多少少改变了我.周六已经快习惯早上背着包 ...

  5. java中的 java.util.concurrent.locks.ReentrantLock类中的lockInterruptibly()方法介绍

    在java的 java.util.concurrent.locks包中,ReentrantLock类实现了lock接口,lock接口用于加锁和解锁限制,加锁后必须释放锁,其他的线程才能进入到里面执行, ...

  6. 在html中使用javascript总结

    对于初学者运行代码的第一步,首先是怎么把你所写的js代码与html代码之间关联起来,只有关联了,js才能控制html中的代码,进而达到控制页面的目的,我总结了html引用js的方法,一方面可以时时复习 ...

  7. C# ListView应用

    C#  ListView应用 1. 添加表头标题的方法 a. 直接在ListView控件上编写 b. 通过代码编写 //动态添加lstv_ReaderList列表头 /* lstv_ReaderLis ...

  8. The First Day Of Cnblogs

    The fear of the LORD is the beginning of wisdom,and knowledge of the Holy One is understanding. ——Pr ...

  9. uiautomatorviewer报错“Error taking device screenshot: EOF” ,

    uiautomatorviewer报错“Error taking device screenshot: EOF”  ,千万不要装手机助手,不要装手机助手,不要装手机助手 uiautomatorview ...

  10. Fiddler抓包工具安装与使用

    1.Fiddler简介2.Fiddler安装步骤3.Fiddler目录结构4.Fiddler证书配置5.Fiddler录制配置6.Fiddler工作原理7.Fiddler界面详解 1.Fiddler简 ...