博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
内存泄露 内存溢出
阅读量:5226 次
发布时间:2019-06-14

本文共 7650 字,大约阅读时间需要 25 分钟。

1.JVM Heap(堆)溢出:java.lang.OutOfMemoryError: Java heap space

     JVM在启动的时候会自动设置JVM Heap的值, 可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap的大小是Young Generation 和Tenured Generaion 之和。在JVM中如果98%的时间是用于GC,且可用的Heap size 不足2%的时候将抛出此异常信息。

解决方法:手动设置JVM Heap(堆)的大小。

Java堆用于储存对象实例。当需要为对象实例分配内存,而堆的内存占用又已经达到-Xmx设置的最大值。将会抛出OutOfMemoryError异常。例子如下:

package com.demo.test;import java.util.ArrayList;import java.util.List;/** * VM Args: -Xms5m -Xmx5m */public class HeapOOM {        public static void main(String[] args) {        int count = 0;        List list = new ArrayList();        while(true){            list.add(new Object());            System.out.println(++count);        }    }}

然后在运行时设置jvm参数,如下:

 -Xmx为5m。其中的一次测试结果为,当count的值累加到360145时,发生如下异常:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space    at java.util.Arrays.copyOf(Arrays.java:2245)    at java.util.Arrays.copyOf(Arrays.java:2219)    at java.util.ArrayList.grow(ArrayList.java:213)    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:187)    at java.util.ArrayList.add(ArrayList.java:411)    at com.demo.test.HeapOOM.main(HeapOOM.java:12)

修改-Xmx为10m。其中的一次测试结果为,当count的值累加到540217时,发生OutOfMemoryError异常。随着-Xmx参数值的增大,java堆中可以存储的对象也越多。

 2.PermGen space溢出: java.lang.OutOfMemoryError: PermGen space

      PermGen space的全称是Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被JVM存放Class和Meta信息的,Class在被Load的时候被放入PermGen space区域,它和存放Instance的Heap区域不同,sun的 GC不会在主程序运行期对PermGen space进行清理,所以如果你的APP会载入很多CLASS的话,就很可能出现PermGen space溢出。一般发生在程序的启动阶段。

解决方法: 通过-XX:PermSize和-XX:MaxPermSize设置永久代大小即可。

      方法区用于存放java类型的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。在类装载器加载class文件到内存的过程中,虚拟机会提取其中的类型信息,并将这些信息存储到方法区。当需要存储类信息而方法区的内存占用又已经达到-XX:MaxPermSize设置的最大值,将会抛出OutOfMemoryError异常。对于这种情况的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。这里需要借助CGLib直接操作字节码运行时,生成了大量的动态类。例子如下:

package com.demo.test;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodProxy;import net.sf.cglib.proxy.MethodInterceptor;/** * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M */public class MethodAreaOOM {    public static void main(String[] args) {        int count = 0;        while (true) {            Enhancer enhancer = new Enhancer();            enhancer.setSuperclass(MethodAreaOOM.class);            enhancer.setUseCache(false);            enhancer.setCallback(new MethodInterceptor() {                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {                    return proxy.invoke(obj, args);                }            });            enhancer.create();            System.out.println(++count);        }    }}

-XX:MaxPermSize为10m。其中的一次测试结果为,当count的值累加到800时,发生如下异常: 

Caused by: java.lang.OutOfMemoryError: PermGen space    at java.lang.ClassLoader.defineClass1(Native Method)    at java.lang.ClassLoader.defineClass(ClassLoader.java:792)    ... 8 more

随着-XX:MaxPermSize参数值的增大,java方法区中可以存储的类型数据也越多。

3.栈溢出: java.lang.StackOverflowError : Thread Stack space

      栈溢出了,JVM依然是采用栈式的虚拟机,这个和C和Pascal都是一样的。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。 通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K的空间(这个大约相当于在一个C函数内声明了256个int类型的变量),那么栈区也不过是需要1MB的空间。通常栈的大小是1-2MB的。通俗一点讲就是单线程的程序需要的内存太大了。 通常递归也不要递归的层次过多,很容易溢出。

解决方法:1:修改程序。2:通过 -Xss: 来设置每个线程的Stack大小即可。

在Java虚拟机规范中,对这个区域规定了两种异常状况:StackOverflowError和OutOfMemoryError异常。

(1)StackOverflowError异常

       每当java程序代码启动一个新线程时,Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。当线程调用java方法时,虚拟机压入一个新的栈帧到该线程的java栈中。只要这个方法还没有返回,它就一直存在。如果线程的方法嵌套调用层次太多(如递归调用),随着java栈中帧的逐渐增多,最终会由于该线程java栈中所有栈帧大小总和大于-Xss设置的值,而产生StackOverflowError内存溢出异常。例子如下:

package com.demo.test;/** * VM Args: -Xss128k */public class JavaVMStackSOF {        private int count = 0;    public static void main(String[] args) {        new JavaVMStackSOF().method();    }        public void method() {        System.out.println(++count);        method();    }}

-Xss为128k。其中的一次测试结果为,当count的值累加到2230时,发生如下异常:

Exception in thread "main" java.lang.StackOverflowError    at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)    at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:564)    at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:619)    at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:561)    at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)    at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)    at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)    at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)    at java.io.PrintStream.write(PrintStream.java:526)    at java.io.PrintStream.print(PrintStream.java:597)    at java.io.PrintStream.println(PrintStream.java:736)    at com.demo.test.JavaVMStackSOF.method(JavaVMStackSOF.java:15)

     随着-Xss参数值的增大,可以嵌套的方法调用层次也相应增加。综上所述,StackOverflowError异常是由于方法调用的层次太深,最终导致为某个线程分配的所有栈帧大小总和大于-Xss设置的值,从而发生StackOverflowError异常。

(2)OutOfMemoryError异常

java程序代码启动一个新线程时,没有足够的内存空间为该线程分配java栈(一个线程java栈的大小由-Xss参数确定),jvm则抛出OutOfMemoryError异常。例子如下:

package com.demo.test;/** * VM Args: -Xss128k */public class JavaVMStackOOM {    public static void main(String[] args) {        int count = 0;        while (true) {            Thread thread = new Thread(new Runnable() {                public void run() {                    while (true) {                        try {                            Thread.sleep(5000);                        } catch (Exception e) {                        }                    }                }            });            thread.start();            System.out.println(++count);        }    }}

-Xss为128k。其中的一次测试结果为,当count的值累加到11958时,发生如下异常:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread    at java.lang.Thread.start0(Native Method)    at java.lang.Thread.start(Thread.java:693)    at com.demo.test.JavaVMStackOOM.main(JavaVMStackOOM.java:21)

随着-Xss参数值的增大,java程序可以创建的总线程数越少。

4.所以Server容器启动的时候我们经常关心和设置JVM的几个参数如下:

-Xms:java Heap初始大小, 默认是物理内存的1/64。

-Xmx:java Heap最大值,不可超过物理内存。
-Xmn:young generation的heap大小,一般设置为Xmx的3、4分之一 。增大年轻代后,将会减小年老代大小,可以根据监控合理设置。
-Xss:每个线程的Stack大小,而最佳值应该是128K,默认值好像是512k。
-XX:PermSize:设定内存的永久保存区初始大小,缺省值为64M。
-XX:MaxPermSize:设定内存的永久保存区最大大小,缺省值为64M。
-XX:SurvivorRatio:Eden区与Survivor区的大小比值,设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
-XX:+UseParallelGC:F年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:+UseParNewGC:设置年轻代为并行收集,JDK5.0以上,JVM会根据系统配置自行设置,所无需再设置此值。
-XX:ParallelGCThreads:并行收集器的线程数,值最好配置与处理器数目相等 同样适用于CMS。
-XX:+UseParallelOldGC:年老代垃圾收集方式为并行收集(Parallel Compacting)。
-XX:MaxGCPauseMillis:每次年轻代垃圾回收的最长时间(最大暂停时间),如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
-XX:+ScavengeBeforeFullGC:Full GC前调用YGC,默认是true。

实例如:JAVA_OPTS=”-Xms4g -Xmx4g -Xmn1024m -XX:PermSize=320M -XX:MaxPermSize=320m -XX:SurvivorRatio=6″


第一种OutOfMemoryError: PermGen space

发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:

1、增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大 小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行: JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m" 如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。

2、清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到 tomcat共同的lib下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。

第二种OutOfMemoryError: Java heap space

发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:

1、检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。 我以前写一个使用K-Means文本聚类算法对几万条文本记录(每条记录的特征向量大约10来个)进行文本聚类时,由于程序细节上有问题,就导致了 Java heap space的内存溢出问题,后来通过修改程序得到了解决。

2、增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m

转载于:https://www.cnblogs.com/yuanjiangw/p/10470024.html

你可能感兴趣的文章
堆栈平衡
查看>>
Struts中的常用常量配置(转)
查看>>
python中的静态成员方法和类成员方法
查看>>
Win10系列:VC++绘制几何图形3
查看>>
如何在两个线程之间共享数据
查看>>
笔记之_java的angularjs整理
查看>>
MSP430看门狗使用笔记&&低功耗模式下如何用看门狗监控程序跑飞
查看>>
C#通过DllImport引入dll中的C++非托管类(转)
查看>>
sparkSQL、dataframe
查看>>
h5+css 垂直导航菜单
查看>>
VMware12 + Ubuntu16.04 虚拟磁盘扩容
查看>>
pwershell switch 语句
查看>>
学习Spring Boot:(五)使用 devtools热部署
查看>>
三人行有我师?取长补短?影响力?
查看>>
设计模式——设计模式概述
查看>>
封装一个获取module.exports内容的方法
查看>>
动态连接库
查看>>
ServletContext 与application的异同
查看>>
水平垂直居中
查看>>
CSS3教程:border-image属性
查看>>