当然,这是一种依赖于实现的行为. HotSpot有一些堆内存对于普通分配是不可访问的,JVM可以用它来构造一个OutOfMemoryError.但是,由于
Java允许任意数量的线程,因此任意数量的线程可能同时碰壁,所以没有保证内存足以为每个实例构造一个独特的OutOfMemoryError实例.
因此,在整个会话期间持久存在的JVM启动时会创建紧急OutOfMemoryError实例,以确保即使没有剩余内存也可以抛出错误.由于实例将为遇到错误的所有线程共享,而实际上没有剩余内存,因此您将通过此错误将没有堆栈跟踪这一事实识别出这种无关的条件.
以下程序
ConcurrentHashMap instances = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newCachedThreadPool();
executor.invokeAll(Collections.nCopies(1000, () -> {
ArrayList list = new ArrayList<>();
for(;;) try {
list.add(new int[10_000_000]);
} catch(OutOfMemoryError err) {
instances.merge(err, 1, Integer::sum);
return err;
}
}));
executor.shutdown();
System.out.println(instances.size()+" distinct errors created");
instances.forEach((err,count) -> {
StackTraceElement[] trace = err.getStackTrace();
System.out.println(err.getClass().getName()+"@"+Integer.toHexString(err.hashCode())
+(trace!=null&&trace.length!=0? " has": " has no")+" stacktrace, used "+count+'x');
});
用-Xmx100M在jdk1.8.0_65下运行并等了半分钟给了我
5 distinct errors created
java.lang.OutOfMemoryError@c447d22 has no stacktrace, used 996x
java.lang.OutOfMemoryError@fe0b0b7 has stacktrace, used 1x
java.lang.OutOfMemoryError@1e264651 has stacktrace, used 1x
java.lang.OutOfMemoryError@56eccd20 has stacktrace, used 1x
java.lang.OutOfMemoryError@70ab58d7 has stacktrace, used 1x
显示保留的内存可以用于构造四个不同的OutOfMemoryError实例(包括记录其堆栈跟踪所需的内存),而所有其他线程必须回退到保留的共享实例.
当然,数字可能因不同环境而异.