Java内存溢出的详细解决方案

 JVM管理两种类型的内存,堆和非堆。堆是给开发人员用的上面说的就是,是在JVM启动时创建;非堆是留给JVM自己用的,用来存放类的信息的。它和堆不同,运行期内GC不会释放空间。     一、内存溢出类型
     1、java.lang.OutOfMemoryError: PermGen space
     JVM管理两种类型的内存,堆和非堆。堆是给开发人员用的上面说的就是,是在JVM启动时创建;非堆是留给JVM自己用的,用来存放类的信息的。它和堆不同,运行期内GC不会释放空间。如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context更改为新部署的,非堆存的内容就会越来越多。
     PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。
     一个最佳的配置例子:(经过本人验证,自从用此配置之后,再未出现过tomcat死掉的情况)
     set JAVA_OPTS=-Xms800m -Xmx800m -XX:PermSize=128M -XX:MaxNewSize=256m -XX:MaxPermSize=256m
     2、java.lang.OutOfMemoryError: Javaheap space
     第一种情况是个补充,主要存在问题就是出现在这个情况中。其默认空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。如果内存剩余不到40%,JVM就会增大堆到Xmx设置的值,内存剩余超过70%,JVM就会减小堆到Xms设置的值。所以服务器的Xmx和Xms设置一般应该设置相同避免每次GC后都要调整虚拟机堆的大小。假设物理内存无限大,那么JVM内存的最大值跟操作系统有关,一般32位机是1.5g到3g之间,而64位的就不会有限制了。
     注意:如果Xms超过了Xmx值,或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。
     垃圾回收GC的角色
     JVM调用GC的频度还是很高的,主要两种情况下进行垃圾回收:
     当应用程序线程空闲;另一个是java内存堆不足时,会不断调用GC,若连续回收都解决不了内存堆不足的问题时,就会报out of memory错误。因为这个异常根据系统运行环境决定,所以无法预期它何时出现。
     根据GC的机制,程序的运行会引起系统运行环境的变化,增加GC的触发机会。
     为了避免这些问题,程序的设计和编写就应避免垃圾对象的内存占用和GC的开销。显示调用System.GC()只能建议JVM需要在内存中对垃圾对象进行回收,但不是必须马上回收,
     一个是并不能解决内存资源耗空的局面,另外也会增加GC的消耗。
     二、JVM内存区域组成
     简单的说java中的堆和栈
     java把内存分两种:一种是栈内存,另一种是堆内存
     1、在函数中定义的基本类型变量和对象的引用变量都在函数的栈内存中分配;
     2、堆内存用来存放由new创建的对象和数组
   三 . 
内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。 
所以我们应该明确:存在内存溢出的因不一定导致内存溢出的果。。。 
 
 
1。JAVA操作文本文件为什么超过3万行就内存益处啊? 
 
PrintWriter  out  =  new  PrintWriter(new  BufferedWriter(new  FileWriter(fileName)));   
          //PrintWriter  out  =  new  PrintWriter(fileName);   
          //FileOutputStream  out  =  new  FileOutputStream(fileName);   
     
            while  (rs.next())  {   
              for  (int  j  =  1;  j  <=  totalColumn;  j++)  {   
                  out.write(rs.getObject(j).toString());   
                  out.write("\t");   
              }   
                out.write("\n");   
                out.flush();   
     
                      }   
  }   
  我在代码中  写了  out.flush()用来刷新该流的缓冲;  可是当我的记录数超过3W时就报了内存益处的问题了,难道JAVA不能边读边写吗?还是out这个对象随着指向的fileName文件的边大占用内存也大了吗??到底怎么来实现用JAVA写更多的数据而不内存益处呢 
 
 
答案是:就在while(rs.next())  当rs.next()时内存不断增大,而不是写流的问题,JAVA的ResultSet真是麻烦,而且ResultSet还不能clone();  所以记得在做项目的时候,经常要设置:jdbc.setMaxRows(100*10000);  //设置能容纳100万行记录-----这个就是防止内存泄露的哈------------------- 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;  
jdbc.setQueryTimeout(60*30); //设置超时时间是30分钟 
 
 
2。java中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中(例如放在一个全局map对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。 
 public class Stack { //长生命周期 
    private Object[] elements=new Object[10]; //当数组容器中没有东西时是无用的,但是无法回收~~elements是短生命周期 
    private int size = 0; 
    public void push(Object e){ 
    ensureCapacity(); 
    elements[size++] = e; 
    } 
    public Object pop(){ 
    if( size == 0) 
throw new EmptyStackException(); 
//这里还是引用了,只是指针变位置变化而已,他确实返回了那个对象,但是他却没有断开那个对象的引用,也就是说有两个地方会持有这个对象的引用,调用pop的地方,和elements中 
    return elements[--size];  
    } 
    private void ensureCapacity(){ 
    if(elements.length == size){ 
    Object[] oldElements = elements; 
    elements = new Object[2 * elements.length+1]; 
    System.arraycopy(oldElements,0, elements, 0, size); 
    } 
    } 
    } 
    上面的原理应该很简单,假如堆栈加了10个元素,然后全部弹出来,虽然堆栈是空的,没有我们要的东西,但是这是个对象是无法回收的,这个才符合了内存泄露的两个条件(必要条件):无用,无法回收。 
   例子1 
    public class Bad{ 
    public static Stack s=Stack(); 
    static{ 
    s.push(new Object()); 
    s.pop(); //这里有一个对象发生内存泄露 
    s.push(new Object()); //上面的对象可以被回收了,等于是自愈了,因为引用被覆盖了 
    } 
    } 
    因为是static,就一直存在到程序退出,但是我们也可以看到它有自愈功能,就是说如果你的Stack最多有100个对象,那么最多也就只有100个对象无法被回收其实这个应该很容易理解,Stack内部持有100个引用,最坏的情况就是他们都是无用的,因为我们一旦放新的进取,以前的引用自然消失! 
 
内存泄露的另外一种情况:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄露。 
 
 
这是属于: 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收; 
 
3。代码中存在死循环或循环产生过多重复的对象实体; 
 
4。启动参数内存值设定的过小;

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭