Java内存溢出问题的定位过程

标签: java 内存 溢出 | 发表时间:2013-12-06 00:52 | 作者:zhuyufufu
出处:http://www.iteye.com
相信通过写java程序讨生活的人对内存溢出并不陌生,如下文字的出现更是让人恼火:
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: PermGen space
尤其当应用服务器(Java容器)出现上述情况更是让人有一种天塌下来的感觉。

   好的编码实践可能会大大降低内存溢出的产生。
   本文并不是写如何规避内存溢出,但是我还是要介绍一下如何能够尽量规避内存溢出:
   1. 编码规范认真执行。找几个资深程序猿(或者整个项目组讨论后)写一个Java编码规范,让项目组成员尽量遵守。一目了然的代码更容易定位问题,当然也更能让人写出好的代码。
   2. 单元测试要覆盖所有分支与边界条件。不要拿某种情况不会出现做借口。有句老话说常在河边站哪有不湿鞋(学名墨菲定律)。
   3. 代码审查要走。代码写完了,找资深程序猿扫扫代码没有坏处。
   4. 有条件的项目组要充分利用测试人员的能动性。
   5. 如果项目的期望较高,就把上面的尽量、可能等词汇改成一定要。

   以上五条建议对非性命攸关型项目足够了。





下面说正题:

   对于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。

  

   对于java.lang.OutOfMemoryError: Java heap space 可能定位的过程就需要折腾一翻了:

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

   我对java虚拟机的实现不感兴趣,对各种虚拟机的内存分配机制与gc的交互关系也不了解。但是我大致认为内存分配机制与gc是有联系的,也就是说内存不够分配时gc肯定也释放不了堆内存。从这一点出发,我们就需要找为什么gc释放不了堆内存。通常来说释放不了是因为内存还在使用。对于java对象产生的堆内存占用,只要其不再被继续引用gc是能够顺利回收的(对于通过本地方法调用,即JNI调用产生内存泄露的情况暂不考虑)。

   问题的关键就找到了,当产生heap space内存溢出时,堆内存中对象数量过多的就可能是问题的根源了。例外的情况是,程序确实需要那么多内存,这时就要考虑增大堆内存。

   例外的情况在本文中就不再多说了,下面介绍jdk自带的两个可视化工具来定位问题。

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



   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这个类的对象出了问题。


以上是一个演示问题产生及定位过程,生产环境的问题千奇百怪需要具体问题具体分析。

当堆内存巨大时可能要调整jdk\lib\visualvm\etc\visualvm.conf文件中的-xms -xmx大小来导入转储文件。

生产环境为linux的较多,可以借助jdk自带的jmap来转储堆内存文件来分析。


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 内存 溢出] 推荐:

Java 内存溢出排查

- - ImportNew
Java OOM 毫无疑问是开发人员常见并且及其痛恨的问题,但是任何服务的开发都没法避免 OOM. 因此,OOM 的排查及定位是每个 Java 工程师都必备的技能. 在使用 scala 开发的一个 web 服务,在用户使用中,经常出现:  java.lang.OutOfMemoryError: Java heap space .

Java内存溢出与栈溢出

- - CSDN博客推荐文章
JVM运行时内存 = 共享内存区 + 线程内存区. 共享内存区 = 持久带 + 堆. 持久带 = 方法区 + 其他. 堆 = Old Space + Young Space. JVM用持久带(Permanent Space)实现方法区,主要存放所有已加载的类信息,方法信息,常量池等等. 可通过-XX:PermSize和-XX:MaxPermSize来指定持久带初始化值和最大值.

Java 内存区域与内存溢出

- - CSDN博客综合推荐文章
Java虚拟机在执行Java程序的过程中会把他所管理的内存划分为若干个不同的数据区域. Java虚拟机规范将JVM所管理的内存分为以下几个运行时数据区:程序计数器,Java虚拟机栈,本地方法栈,Java堆,方法区. 下面详细阐述各数据区所存储的数据类型. 程序计数器(Program Counter Register).

Java 堆内存溢出梗概分析

- - ITeye资讯频道
原文:Java Out of Memory Heap Analysis. 链接: https://dzone.com/articles/java-out-of-memory-heap-analysis. 译者:dreamanzhao, 无若. 任何使用过基于 Java 的企业级后端应用的软件开发者都会遇到过这种低劣、奇怪的报错,这些报错来自于用户或是测试工程师: java.lang.OutOfMemoryError:Java heap space.

java 内存溢出 栈溢出的原因与排查方法

- - 互联网 - ITeye博客
 1、 内存溢出的原因是什么.       内存溢出是由于没被引用的对象(垃圾)过多造成JVM没有及时回收,造成的内存溢出. 如果出现这种现象可行代码排查:.     一)是否App中的类中和引用变量过多使用了Static修饰 如public staitc Student s;在类中的属性中使用 static修饰的最好只用基本类型或字符串.

java内存泄露和内存溢出区别

- - 互联网 - ITeye博客
虽然jvm可以通过GC自动回收无用的内存,但是代码不好的话仍然存在内存溢出的风险. 最近在网上搜集了一些资料,现整理如下:. 一、为什么要了解内存泄露和内存溢出. 1、内存泄露一般是代码设计存在缺陷导致的,通过了解内存泄露的场景,可以避免不必要的内存溢出和提高自己的代码编写水平;. 2、通过了解内存溢出的几种常见情况,可以在出现内存溢出的时候快速的定位问题的位置,缩短解决故障的时间.

Java内存溢出问题的定位过程

- - Java - 编程语言 - ITeye博客
相信通过写java程序讨生活的人对内存溢出并不陌生,如下文字的出现更是让人恼火:. 尤其当应用服务器(Java容器)出现上述情况更是让人有一种天塌下来的感觉.    好的编码实践可能会大大降低内存溢出的产生.    本文并不是写如何规避内存溢出,但是我还是要介绍一下如何能够尽量规避内存溢出:. 找几个资深程序猿(或者整个项目组讨论后)写一个Java编码规范,让项目组成员尽量遵守.

译文:解密Java内存溢出之持久代

- - 研发管理 - ITeye博客
       垃圾回收是Java程序员了解最少的一部分. 他们认为Java虚拟机接管了垃圾回收,因此没必要去担心内存的申请,分配等问题. 但是随着应用越来越复杂,垃圾回收也越来越复杂,一旦垃圾回收变的复杂,应用的性能将会大打折扣. 所以,Java程序员了解垃圾回收的机制并且知道怎样解决“内存溢出”问题会有很大的益处.

Java常见问题分析(内存溢出、内存泄露、线程阻塞等)

- - Java - 编程语言 - ITeye博客
Java垃圾回收机制(GC) . 堆内存3代分布(年轻代、老年代、持久代) . ML(内存泄露) OOM(内存溢出)问题现象及分析 . IBM DUMP分析工具使用介绍. Java应用CPU、线程问题分析. Java垃圾回收机制(GC). 1.GC机制作用 . 1.1 JVM自动检测和释放不再使用的对象内存 .

使用JProfiler解决Java应用程序内存溢出问题实例

- - Java - 编程语言 - ITeye博客
    前段时间基于OpenJms部署了一个消息中间件服务器,通过主题订阅模式在各个消息节点之间传递信息,但是某个类型的消息节点长时间运行后出现了内存溢出问题,最后使用JProfiler的基本线程监测功能找到问题所在,并且进行解决. Java 版本 java version "1.7.0_40". JProfiler 版本 v8.0.7.