感觉中,OutOfMemeryError(内存溢出错误) 是jvm抛出的异常,是不能被捕获的。
直到工作中真的遇到OOM异常,而且tomcat服务还一直对外提供服务。
那么问题来了:
1. OOM 到底能不能被捕获?
2. jvm抛出OOM后,是否就会立即停止运行呢?
3. jvm什么时候会抛出OOM异常?
先来个例子:
本例子将会一一体现如上问题:(最好设置jvm最大内存如: -Xmx2m -Xms2m)
public classOOMCatchTest {/*** 可以看作是一个消息队列, 作为 Producer 与 Consumer 的通信桥梁
* 其实此处存在并发写的问题,不过不是本文的重点,暂且忽略*/
private static volatile List userWaitingList = new ArrayList<>();private AtomicInteger uidCenter = new AtomicInteger(0);//随机数生成源
private final String rndSource = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";public static void main(String[] args) throwsIOException {
OOMCatchTest oomCatchTest= newOOMCatchTest();
Thread producer= new Thread(newRunnable() {
@Overridepublic voidrun() {
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 voidproductUserObj() {
Random rnd= newRandom();
OOMCatchTest oomTest= newOOMCatchTest();//可作开关
boolean startProduce = true;try{while(startProduce) {
UserObj userTemp= newUserObj();
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 voidconsumeUserObj() {//可做开关
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(intdigit) {
StringBuilder sb= newStringBuilder();int len =rndSource.length();
Random random= newRandom();for(int i = 0; i < digit; i++) {
sb.append(rndSource.charAt(random.nextInt(len)));
}returnsb.toString();
}publicInteger generateUid() {returnuidCenter.incrementAndGet();
}static classUserObj {privateInteger uid;privateString name;privateInteger age;privateString address;publicInteger getUid() {returnuid;
}public voidsetUid(Integer uid) {this.uid =uid;
}publicString getName() {returnname;
}public voidsetName(String name) {this.name =name;
}publicInteger getAge() {returnage;
}public voidsetAge(Integer age) {this.age =age;
}publicString getAddress() {returnaddress;
}public voidsetAddress(String address) {this.address =address;
}
@OverridepublicString 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:2136java.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.(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.(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:11743at 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:3752producer oom, wait:1producer oom, wait:2producer oom, wait:3
抛出OOM异常有几种情况:
1. java中直接throw 抛出 OOM;(后面详细列举)
2. 使用new int[MAX] 等基本类型方式时抛出 OOM,这种异常隐式抛出;
3. 当收到外部特殊信号时抛出,如:常用的威胁信号 kill -3 ;
而通常,前两个OOM都是可能被捕获的! 且抛出的OOM只会影响当前线程(和其他异常一样)。不过 OOM 一般会具有普遍性,即一个线程OOM时,通常其他线程也跑不掉!
下面来看几个JAVA中主动抛出 OOM 的样例吧:
// java.util.ArrayList.add(E e), 进行扩容的时候,就可能抛出oom, 也即代码异常,可以捕获
/*** Appends the specified element to the end of this list.
*
*@parame element to be appended to this list
*@returntrue (as specified by {@linkCollection#add})*/
public booleanadd(E e) {
ensureCapacityInternal(size+ 1); //Increments modCount!!
elementData[size++] =e;return true;
}private void ensureCapacityInternal(intminCapacity) {if (elementData ==DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity=Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}private void ensureExplicitCapacity(intminCapacity) {
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.
*
*@paramminCapacity the desired minimum capacity*/
private void grow(intminCapacity) {//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(intminCapacity) {if (minCapacity < 0) //overflow
throw newOutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}//java.nio.ByteBuffer.allocateDirect(int capacity);//分配直接内存时,可能抛出oom
/*** Allocates a new direct byte buffer.
*
*
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.
*
*@paramcapacity
* The new buffer's capacity, in bytes
*
*@returnThe new byte buffer
*
*@throwsIllegalArgumentException
* If the capacity is a negative integer*/
public static ByteBuffer allocateDirect(intcapacity) {return newDirectByteBuffer(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);throwx;
}
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, newDeallocator(base, size, cap));
att= null;
}//which a process may access. All sizes are specified in bytes.
static void reserveMemory(long size, intcap) {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 finalObject[] toArray() {long sz =map.mappingCount();if (sz >MAX_ARRAY_SIZE)//Required array size too large
throw newOutOfMemoryError(oomeMsg);int n = (int)sz;
Object[] r= newObject[n];int i = 0;for (E e : this) {if (i ==n) {if (n >=MAX_ARRAY_SIZE)throw newOutOfMemoryError(oomeMsg);if (n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1)
n=MAX_ARRAY_SIZE;elsen+= (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) throwsIOException {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 {@codeinitialSize} as a hint
* about how many bytes the stream will have.
*
*@paramsource
* the input stream to read from
*@paraminitialSize
* the initial size of the byte array to allocate
*
*@returna byte array containing the bytes read from the file
*
*@throwsIOException
* if an I/O error occurs reading from the stream
*@throwsOutOfMemoryError
* if an array of the required size cannot be allocated*/
private static byte[] read(InputStream source, int initialSize) throwsIOException {int capacity =initialSize;byte[] buf = new byte[capacity];int nread = 0;intn;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() throwsIOException {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(intminimumCapacity) {int newCapacity = value.length * 2 + 2;if (newCapacity - minimumCapacity < 0)
newCapacity=minimumCapacity;if (newCapacity < 0) {if (minimumCapacity < 0) //overflow
throw newOutOfMemoryError();
newCapacity=Integer.MAX_VALUE;
}
value=Arrays.copyOf(value, newCapacity);
}
扩展,实际使用中捕获OOM的案例:
dubbo 捕获的 server 端的 OOM 异常示例如下:
2018-11-01 17:23:55.814 [DubboServerHandler-172.23.0.28:20880-thread-198] 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.0, current host: 172.23.0.28java.lang.OutOfMemoryError: Java heap space
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.alibaba.com.caucho.hessian.io.JavaDeserializer.instantiate(JavaDeserializer.java:271)
at com.alibaba.com.caucho.hessian.io.JavaDeserializer.readObject(JavaDeserializer.java:155)
at com.alibaba.com.caucho.hessian.io.SerializerFactory.readObject(SerializerFactory.java:397)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObjectInstance(Hessian2Input.java:2070)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2005)
at com.alibaba.com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:1990)
at com.alibaba.dubbo.common.serialize.support.hessian.Hessian2ObjectInput.readObject(Hessian2ObjectInput.java:88)
at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:94)
at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:117)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody(DubboCodec.java:98)
at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:134)
at com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:95)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.decode(DubboCountCodec.java:46)
at com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter$InternalDecoder.messageReceived(NettyCodecAdapter.java:134)
at org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:80)
at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564)
at org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:274)
at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:261)
at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:351)
at org.jboss.netty.channel.socket.nio.NioWorker.processSelectedKeys(NioWorker.java:282)
at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:202)
at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:44)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
捕获代码:
@Overridepublic Result invoke(Invoker> invoker, Invocation invocation) throwsRpcException {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 instanceofException)) {returnresult;
}//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)) {returnresult;
}
}
}catch(NoSuchMethodException e) {returnresult;
}//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)) {returnresult;
}//directly throw if it's JDK exception
String className =exception.getClass().getName();if (className.startsWith("java.") || className.startsWith("javax.")) {returnresult;
}//directly throw if it's dubbo exception
if (exception instanceofRpcException) {returnresult;
}//otherwise, wrap with RuntimeException and throw back to the client
return new RpcResult(newRuntimeException(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);returnresult;
}
}returnresult;
}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);throwe;
}
}
View Code
tomcat 中捕获OOM的异常示例如下:
28-Oct-2018 14:21: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
Exceptionin 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 implementsRunnable {
@Overridepublic voidrun() {
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;throwe;
}finally{if (!threadDone) {
log.error(unexpectedDeathMessage, t);
}
}
}protected voidprocessChildren(Container container) {
ClassLoader originalClassLoader= null;try{if (container instanceofContext) {
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 instanceofContext) {
((Context) container).unbind(false, originalClassLoader);
}
}
}
}
View Code
总结:
1. oom异常一般是java程序在做内存分配时,发现没有足够的剩余空间可用而抛出的异常;
2. 此时的分配空间可能是出于代码的new操作(用户主动),可能是出于内存的复制操作(语言自动),也可能是出于内存数据的重振操作(语言自动),可能是出jvm检测到外部信号(jvm自动);
3. oom只是被建议为不要捕获的异常,因为通常你对这种情况是无能为力的!但你如果实在要捕获,why not ?
4. oom一般只会影响当前线程,而jvm中只要存在一个非daemon线程在运行,jvm就不会退出;
5. 如果是线程池运行环境,一般需要一个统一管理oom的程序,否则不能及时统一处理oom;