OutOfMemoryError

OutOfMemoryError

导致内存不够的情况主要发生在方发区、堆、虚拟机栈。

1.java.lang.OutOfMemoryError: Java heap space

原因:堆内存不够,意味着新生代和老年代的内存不够。

解决:调整java启动参数-Xms -Xmx来增加Heap的内存。

//堆内存溢出时,首先判断当前最大内存是多少(参数:-Xmx 或 -XX:MaxHeapSize=),可以通过命令 jinfo -flag MaxHeapSize pid 查看运行中的JVM的配置,如果该值已经较大则应通过 mat 之类的工具查找问题,或 jmap -histo查找哪个或哪些类占用了比较多的内存。参数-verbose:gc(-XX:+PrintGC) -XX:+PrintGCDetails可以打印GC相关的一些数据。如果问题比较难排查也可以通过参数-XX:+HeapDumpOnOutOfMemoryError在OOM之前Dump内存数据再进行分析。此问题也可以通过histodiff打印多次内存histogram之前的差值,有助于查看哪些类过多被实例化,如果过多被实例化的类被定位到后可以通过btrace再跟踪。

//下面代码可再现该异常:
List<String> list = new ArrayList<String>();
while(true) list.add(new String("Consume more memory!"));

2.java.lang.OutOfMemoryError: unable to create new native thread

原因:栈空间不足以创建额外的线程,要么是创建的线程过多,要么是Stack确实小。

解决:由于JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;Heap和Stack空间的总量有限,是此消彼长的关系。所以可以通过两个途径解决:

(1)通过-Xss启动参数减少单个线程站大小,这样便能开更多线程(也不能太小,太小会出现StackOverflowError);

(2)通过-Xms -Xmx两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。

//在JVM中每启动一个线程都会分配一块本地内存,用于存放线程的调用栈,该空间仅在线程结束时释放。当没有足够本地内存创建线程时就会出现该错误。

//通过以下代码可以很容易再现该问题
 while(true){
    new Thread(new Runnable(){
        public void run() {
            try {
                Thread.sleep(60*60*1000);
            } catch(InterruptedException e) { }        
        }    
    }).start();
}

3.java.lang.OutOfMemoryError: PermGen space

原因:方发区空间不足,不能加载额外的类。

解决:调整-XX:PermSize= 、-XX:MaxPermSize= 两个参数来增大方法区的内存。一般情况下,这两个参数不要手动设置,只要设置-Xmx足够大即可,JVM会自行选择合适的PermGem大小。

//PermGen space即永久代,是非堆内存的一个区域。主要存放的数据是类结构及调用了intern()的字符串。
List<Class<?>> classes = new ArrayList<Class<?>>();
while(true){
    MyClassLoader cl = new MyClassLoader();
    try{
        classes.add(cl.loadClass("Dummy"));
    }catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
类加载的日志可以通过btrace跟踪类的加载情况:
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class ClassLoaderDefine {

    @SuppressWarnings("rawtypes")
    @OnMethod(clazz = "+java.lang.ClassLoader", method = "defineClass", location = @Location(Kind.RETURN))
    public static void onClassLoaderDefine(@Return Class cl) {
        println("=== java.lang.ClassLoader#defineClass ===");
        println(Strings.strcat("Loaded class: ", Reflective.name(cl)));
        jstack(10);
    }
}
//除了btrace也可以打开日志加载的参数来查看加载了哪些类,可以把参数-XX:+TraceClassLoading打开,或使用参数-verbose:class(-XX:+TraceClassLoading, -XX:+TraceClassUnloading),在日志输出中即可看到哪些类被加载到Java虚拟机中。该参数也可以通过jflag的命令java -jar jflagall.jar -flag +ClassVerbose动态打开-verbose:class。下面是一个使用了String.intern()的例子: 
List<String> list = new ArrayList<String>();
int i=0;
while(true) 
    list.add(("Consume more memory!"+(i++)).intern());
//你可以通过以下btrace脚本查找该类调用:
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class StringInternTrace {    
    @OnMethod(clazz = "/.*/", method = "/.*/", 
              location = @Location(value = Kind.CALL, clazz = "java.lang.String", method = "intern"))    
    public static void m(@ProbeClassName String pcm, 
                         @ProbeMethodName String probeMethod,                         @TargetInstance Object instance) {        
        println(strcat(pcm, strcat("#", probeMethod)));        
        println(strcat(">>>> ", str(instance)));    
    }
}

4.java.lang.OutOfMemoryError: Requested array size exceeds VM limit

原因:这个错误比较少见(试着new一个长度1亿的数组看看),同样是由于Heap空间不足。如果需要new 一个如此之大的数组,程序逻辑多半是不合理的。

解决:修改程序逻辑。或者也可以通过-Xmx来增大堆内存

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值