老李案例分享:定位JAVA内存溢出

老李案例分享:定位JAVA内存溢出

 

   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标。在poptest的loadrunner的培训中,为了提高学员性能优化的经验,加入了语言以及服务器方面的优化知识,为性能调优的能力打下基础。(大家对课程感兴趣,请加qq:564202718)

    

项目中最佳实践:
1. 编码规范认真执行。制定公司内部Java编码规范,让项目组成员遵守。
2. 单元测试要覆盖所有分支与边界条件。 
3. 代码审查。代码写完了,找资深程序猿走读代码。 
4. 利用测试人员的能动性。 

问题提示:
java.lang.OutOfMemoryError: Java heap space 
java.lang.OutOfMemoryError: PermGen space

解决方法:
a.java.lang.OutOfMemoryError: PermGen space :

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域, 这块内存主要是被JVM存放Class和Meta信息的,Class在被Load时就会被放到PermGen space中, 它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对 PermGen space进行清理,所以如果你的应用中有很多CLASS的话,就很可能出现PermGen space错误。

通过上面的描述就可以得出:如果要加载的class与jar文件大小超过-XX:MaxPermSize就有可能会产生java.lang.OutOfMemoryError: PermGen space 。

-XX:MaxPermSize的大小要超过class与jar的大小。通常-XX:MaxPermSize为-Xmx的1/8。

b.java.lang.OutOfMemoryError: Java heap space:

虽然各种java虚拟机的实现机制不一,但是heap space内存溢出产生的原因相同:那就是堆内存不够虚拟机分配了。

内存分配机制与gc是有联系,内存不够分配时gc释放不了堆内存。释放不了内存是因为内存还在用。java对象产生的堆内存占用,只要其不再被继续引用gc是能够顺利回收的
问题的关键就找到了,当产生heap space内存溢出时,堆内存中对象数量过多的就可能是问题的根源了。例外的情况是,程序确实需要那么多内存,这时就要考虑增大堆内存。

辅助工具:
jdk自带两个可视化工具来定位问题: 
jdk/jconsole.exe jdk/jvisualvm.exe

a.jconsole.exe可以查看本地以及远程主机上的java虚拟机的当前状况,这对服务器健康检查情况非常有用。如下图:

 

b. jvisualvm.exe可以用来查看分析内存转储文件;也可以用其做java虚拟机当前状况查看,但是jvisualvm.exe的侵入性非常强,一旦使用会严重影响应用性能。如下图:

下面写些代码来演示一下内存溢出的产生,堆转储文件的生成,堆内存的分析。

首先创建数据持有对象类:

package com.zas.jvm.om;

/**

 * 数据对象

 * @author zas

 */

public class DataObject {

         //数据对象ID

         private String id;

         //数据对象内容

         private String des;

        public DataObject(String id, String des) {

                   super();

                   this.id = id;

                   this.des = des;

         }

         public String getId() {

                   return id;

         }

        public void setId(String id) {

                   this.id = id;

         }

         public String getDes() {

                   return des;

         }

         public void setDes(String des) {

                   this.des = des;

         }

         @Override

         public String toString() {

                   return "DataObject [id=" + id + ", des=" + des + "]";

         }

         /**

          * @param args

          */

         public static void main(String[] args) {

         }

}

溢出演示代码

package com.zas.jvm.om;

import java.util.ArrayList;

import java.util.List;

public class OutMemeryTest {

         List<DataObject> list = new ArrayList<DataObject>();

         public void testOm(){

                   for (int i = 0; i < 100000; i++) {

                            DataObject data = new DataObject("id&"+i, "des:"+i);

                            list.add(data);

                   }

         }

         /**

          * @param args

          */

         public static void main(String[] args) {

                   OutMemeryTest omt = new OutMemeryTest();

                   for (int i = 0; i < 2; i++) {

                            omt.testOm();

                   }

                   System.out.println("DOne!");

                   try {

                            Thread.sleep(100000000);

                   } catch (InterruptedException e) {

                            e.printStackTrace();

                   }

         }

}

 运行参数设置如下:-Xms64m -Xmx64m -XX:PermSize=8m -XX:MaxPermSize=8m 
-XX:-HeapDumpOnOutOfMemoryError           
见下图:  

   jvisualvm分析效果图:

从上图结合代码明显得出:com.zas.jvm.om.DataObject这个类的对象出了问题。 
以上是一个演示问题产生及定位过程,生产环境的问题千奇百怪需要具体问题具体分析。 
当堆内存巨大时可能要调整jdklibvisualvmetcvisualvm.conf文件中的-xms -xmx大小来导入转储文件。 
生产环境为linux的较多,可以借助jdk自带的jmap来转储堆内存文件来分析。  

原文地址:https://www.cnblogs.com/poptest/p/4897019.html