作为测试你应该知道的JAVA OOM及定位分析

上周现网一个内存溢出问题导致应用服务器每隔一小时死一次,遂整理下常见的OMM、发现方法和处理方式,加入Bug预防。

常见的OutOfMemoryError有三种:OutOfMemoryError:PermGen space、OutOfMemoryError:Java heap space、OutOfMemoryError:unable to create new native thread

1. OutOfMemoryError:PermGen space
原因:程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。
处理:
一:增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。

JAVA_OPTS="-XX:PermSize=64M -XX:MaxPermSize=128m"

二: 清理应用程序中WEB-INF/lib下的jar,用不上的jar删除掉,多个应用公共的jar移动到Tomcat的lib目录,减少重复加载。
常用方法:

kill -3 PID
Heap
 PSYoungGen      total 17024K, used 10442K [0x00007f9e67560000, 0x00007f9e69000000, 0x00007f9e69000000)
  eden space 9088K, 27% used [0x00007f9e67560000,0x00007f9e677d6410,0x00007f9e67e40000)
  from space 7936K, 99% used [0x00007f9e68840000,0x00007f9e68ffc7b0,0x00007f9e69000000)
  to   space 9088K, 0% used [0x00007f9e67e40000,0x00007f9e67e40000,0x00007f9e68720000)
 PSOldGen        total 54656K, used 45409K [0x00007f9e64000000, 0x00007f9e67560000, 0x00007f9e67560000)
  object space 54656K, 83% used [0x00007f9e64000000,0x00007f9e66c58620,0x00007f9e67560000)
 PSPermGen       total 59968K, used 58914K [0x00007f9e5ec00000, 0x00007f9e62690000, 0x00007f9e64000000)
  object space 59968K, 98% used [0x00007f9e5ec00000,0x00007f9e62588ac8,0x00007f9e62690000)

PSPermGen-使用率已达到98%,需要加到PermSize

jmap -head PID
[root@chances bin]# ./jmap -heap 12379
Attaching to process ID 12379, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 17.0-b16

using thread-local object allocation.
Parallel GC with 6 thread(s)

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 83886080 (80.0MB)
   NewSize          = 1310720 (1.25MB)
   MaxNewSize       = 17592186044415 MB
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 8
   PermSize         = 20971520 (20.0MB)
   MaxPermSize      = 88080384 (84.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 9306112 (8.875MB)
   used     = 5375360 (5.1263427734375MB)
   free     = 3930752 (3.7486572265625MB)
   57.761608714788736% used
From Space:
   capacity = 9306112 (8.875MB)
   used     = 3425240 (3.2665634155273438MB)
   free     = 5880872 (5.608436584472656MB)
   36.80634834397007% used
To Space:
   capacity = 9306112 (8.875MB)
   used     = 0 (0.0MB)
   free     = 9306112 (8.875MB)
   0.0% used
PS Old Generation
   capacity = 55967744 (53.375MB)
   used     = 48354640 (46.11457824707031MB)
   free     = 7613104 (7.2604217529296875MB)
   86.39733629427693% used
PS Perm Generation
   capacity = 62062592 (59.1875MB)
   used     = 60243112 (57.452308654785156MB)
   free     = 1819480 (1.7351913452148438MB)
   97.06831451706046% used

**2. OutOfMemoryError: Java heap space **
原因:java虚拟机创建的对象太多,虚拟机分配给堆内存空间已经用满了,与Heap space有关。
处理:
一:检查程序,看是否有死循环或不必要地重复创建大量对象-修改程序或算法。

public class OOM {
    public static void main(String[] args) {
        Integer sum1=300000;
        Integer sum2=400000;
        OOM oom = new OOM();
        System.out.println("往ArrayList中加入30w内容");
        oom.javaHeapSpace(sum1);
        oom.memoryTotal();
        System.out.println("往ArrayList中加入40w内容");
        oom.javaHeapSpace(sum2);
        oom.memoryTotal();
    }
    public void javaHeapSpace(Integer sum){
        Random random = new Random();  
        ArrayList openList = new ArrayList();
        for(int i=0;i<sum;i++){
            String charOrNum = String.valueOf(random.nextInt(10));
            openList.add(charOrNum);
        }  
    }
    public void memoryTotal(){
        Runtime run = Runtime.getRuntime();
        long max = run.maxMemory();
        long total = run.totalMemory();
        long free = run.freeMemory();
        long usable = max - total + free;
        System.out.println("最大内存 = " + max);
        System.out.println("已分配内存 = " + total);
        System.out.println("已分配内存中的剩余空间 = " + free);
        System.out.println("最大可用内存 = " + usable);
    }
}
执行结果:
往ArrayList中加入30w内容
最大内存 = 20447232
已分配内存 = 20447232
已分配内存中的剩余空间 = 4032576
最大可用内存 = 4032576
往ArrayList中加入40w内容
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:242)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
    at java.util.ArrayList.add(ArrayList.java:440)
    at pers.qingqian.study.seven.OOM.javaHeapSpace(OOM.java:36)
    at pers.qingqian.study.seven.OOM.main(OOM.java:26)

从程序执行结果可明显看出往ArrayList中加入30w内容时内存够用,而当往ArrayList加入40w内容时内存就不够了-抛出异常。

二: 增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。

JAVA_OPTS="-Xms256m -Xmx1024m"

常用方法:
步骤一:获取内存的堆信息
jmap -dump:format=b,file=mem.dat pid
步骤二:使用内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
步骤三:看分析图,查找对应问题

内存溢出.jpg
点击See stacktrace

http-8087-140
  at org.apache.jsp.template.runtime.tp_005fhd_005fppjc.ppdh_005fdetail_jsp._jspService(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V (ppdh_005fdetail_jsp.java:213)

找到ppdh_005fdetail_jsp.java 的213行(jsp文件编译后)找到如下代码:

  ArrayList tempEpilist = (ArrayList)pageContext.getAttribute("episodes");
  ArrayList epiList = new ArrayList();
  int  j=0;
  for(int i=0;i<tempEpilist.size();i++){
         EpgEpisode epi = (EpgEpisode)tempEpilist.get(i);   
         if(epi.getEpisodeIndex() != (j+1)){ #这里会因为数据的原因产生死循环,从而导致epiList一直在加内容。
            epiList.add(epi);
             i--;
         }else{
             epiList.add(epi);
         }
         j++;
  }

内存泄露
内存泄漏是指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏随着被执行的次数越多-最终会导致内存溢出。
而因程序死循环导致的不断创建对象-只要被执行到就会产生内存溢出。
内存泄漏常见几个情况
一:静态集合类
声明为静态(static)的HashMap、Vector 等集合
通俗来讲A中有B,当前只把B设置为空,A没有设置为空,回收时B无法回收-因被A引用。
二:监听器
监听器被注册后释放对象时没有删除监听器
三:物理连接
DataSource.getConnection()建立链接,必须通过close()关闭链接
四:内部类和外部模块等的引用
发现它的方式同内存溢出,可再加个实时观察
jstat -gcutil 7362 2500 70
重点关注:
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)
FGC次数越多,FGCT所需时间越多-可非常有可能存在内存泄漏

3. OutOfMemoryError:unable to create new native thread
原因:线程过多
那么能创建多少线程呢?这里有一个公式:

(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads  
MaxProcessMemory 指的是一个进程的最大内存  
JVMMemory         JVM内存  
ReservedOsMemory  保留的操作系统内存  
ThreadStackSize      线程栈的大小  

当发起一个线程的创建时,虚拟机会在JVM内存创建一个Thread对象同时创建一个操作系统线程,而这个系统线程的内存用的不是JVMMemory,而是系统中剩下的内存:
(MaxProcessMemory - JVMMemory - ReservedOsMemory)
结论:你给JVM内存越多,那么你能用来创建的系统线程的内存就会越少,越容易发生java.lang.OutOfMemoryError: unable to create new native thread。

操作系统总内存为8G、JVM中分配内存4G/6G、保留内存345M、ThreadStackSize 为1M。
线程数=(8G-4G-345M)/1M=3751
线程数=(8G-6G-345M)/1M=1703

cat /var/log/dmesg |grep reserved #可见保留内存
java -XX:+PrintFlagsInitial  >>1.txt  #打印出所有JVM默认参数和值
cat 1.txt  | grep ThreadStackSize  #可见线程栈的大小

代码参考:
http://blog.csdn.net/cutesource/article/details/8244250
线程数影响:
http://my.oschina.net/sub/blog/159295

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容