记一次内存溢出查找分析文档

内存溢出介绍。

内存溢出和内存泄漏的联系:
内存泄漏会最终导致内存溢出。
相同点:都会导致应用程序运行出现问题,性能下降或挂起。
不同点:
1 内存泄漏是导致内存溢出的原因之一,内存泄漏积累起来导致内存溢出。
2 内存泄漏可以通过完善代码来避免,内存溢出可以通过调整完善代码来避免,内存溢出可以通过调整配置来减少发生频率,但无法彻底避免。

内存溢出

内存溢出 (out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory ;
比如申请了一个integer,但给它存了long才能存下的数。
产生原因 :
1内存中加载的数据量过于庞大,如一次从数据库取出过多的数据。(主要排查方式 导出,导入。以及排查未分页的查询:表现为对应web页面假死。)
2 JVM 启动参数内存值设定的过小。
3使用的第三方软件中的BUG。

内存泄漏

内存泄漏 (memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏危害可以忽略,但内存泄漏堆积后果很严重,无论多少内存,迟早会被占光。Memory leak 最终会导致 out of memory。
内存泄漏对象特点:
1 这些对象是可达的,即在有向图中,存在通路可以与其相连;
2 这些对象是无用的,程序以后不会在使用这些对象。
对象满足这两个条件,可以判定位Java 中的内存泄漏,这些对象不会被GC 所回收。
产生原因:
1 集合类中有对对象的引用,使用完未清空,使得JVM不能回收。
2 代码中存在死循环或循环产生过多重复的对象实体;
3 使用的第三方软件中的BUG。

JVM内存介绍

主要分为 head (堆)和 Non-head(非堆)
Head(堆):
是JVM 的内存数据区,是被所有线程共享的内存区域,在JVM启动时进行创建。是垃圾回收的主要场所。
存放所有的对象实例,和数组。
分为新生代和老年代:新生代用于存放刚创建的对象以及年轻的对象,如果对象一直没有被回收,生存得足够长,对象就会被移入老年代。
这里主要的情形是使用反射动态的生成对象的实例。
Non-head(非堆)
主要放置 类,方法的定义,方法的参数、局域变量的引用,方法执行顺序按照栈的先入后出方式。

JVM 虚拟机内存处理方式

JVM参数介绍:
-XX:+PrintGCDetails 打印垃圾回收信息(怎么打印?)
-Xms: head 区域的初始值,线上环境需要与Xmx 设置为一致,否则capacity 的值会来回飘动。(capacity 是指的啥)
-Xmx : head 的最大值
-Xss(或-ss) 线程栈大小(指一个线程的native 空间)1.5 以后是 1M 的默认大小
-XX:PermSize 与 –XX:MaxPermSize 方法区(永久代)的初始大小和最大值(但不是本地方法区)
-XX: NewRatioa 老年代与新生代比率。
-XX:SurvivorRatio Eden 与 Survivor 的占用比例。
例子 8 表示, 一个survivor区占用1/8的Eden内存,即1/10的新生代内存,为什么不是1/9?因为我们的新生代有2个survivor,即S1和S22.所以survivor 总共是占用新生代内存的2/10 ,Eden与新生代的占比为8/10.
-XX:MaxHeapFreeRatio GC 后,如果发现空闲堆内存占到整个预估的比例小于这个值,则减少堆空间。
-XX:NewSize 新生代大小。
官方给出的堆内存分配方式
默认空余堆内存小于 40% 时,JVM 就会增大堆直到-Xmx的最大限制。空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制

内存溢出报错简介。

PermGen space

程序中使用了大量的jar 或class,使JVM 装载类的空间不够,与Permanent Generation space 有关。
解决方案:
改变 XX:MaxPermSize
XX:PermSize 的大小。
例子
comcat 在 catalina.sh 或 catalina.bat
大约在 70行左右
增加一行
JAVA_OPTS= “ -XX:PermSize=64M –XX:MaxPermSize=256m ”
Eclipse
-Xms512m -Xmx512m -XX:PermSize=64M -XX:MaxPermSize=256m

Java heap space

在进行垃圾回收之前,JVM 分配的到堆内存空间已经满了,与heap space 有关。可用的 head size 不足 2% 时候抛出此类异常。
检查程序:
看是否有死循环或不必要地重复创建大量对象。
增加Java 虚拟机中Xms 和Xmx 参数的大小。

Unable to create new native thread

因为JVM已经被系统分配了大量的内存,并且它至少要占用可用内存的一半,在线程个数很多的情况下,你分配给JVM 的内存越多,此错误发生的可能性越大。
主要发生在 32 位系统中,每个32 位的进程最多使用2G的可用内存。
简单解释:
假设分给JVM 1.5 G 内存,余下500M可用内存。这500M内存中的一部必须用于系统dll 的加载,也许真正剩下的 400 M ,关键的地方出现了,当使用 java 创建一个线程,在JVM的内存里也会创建一个Thread对象,但是同时也会在操作系统里创建一个真正的物理线程,操作系统会在余下的400M 内存里创建这个物理线程,而不是在JVM 的 1500 M 的内存堆里创建。在 jdk1.4 里,默认栈的大小是256 KB。在jdk1.5 里,默认栈大小 1M /线程。 在余下的400M 内存最多也只能创建400 个可用线程。

内存溢出代码排查方法。

Connect

数据库连接排查,主要代码开连接和关闭连接
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
这里面需要先关 rs 在关 stmt ,在关 conn
不能只关闭conn ,会导致 rs,stmt 在内存中一致占用。rs 也必须主动释放。
代码:

finally {
			              // 关闭链接
			                  JdbcUtil.close(rs, null, conn);
		                          }
public static void close(ResultSet rs, Statement ps, Connection conn) {
	if (rs != null) {
		try {
			rs.close();
		} catch (SQLException e) {
			log.error("ResultSet¹Ø±Õ´íÎó"+e);
		}
	}
	if (ps != null) {
		try {
			ps.close();
		} catch (SQLException e) {
			log.error("Statement¹Ø±Õ´íÎó"+e);
		}
	}
	if (conn != null) {
		try {
			conn.close();
		} catch (SQLException e) {
			log.error("Connection¹Ø±Õ´íÎó"+ e);
		}
	}
}

Stream

主要方法未 Ctrl + H 在 java 搜索 stream。
找到未关闭的流,在finally 中关闭。
代码:

    FileInputStream in =  null;
    ByteArrayOutputStream bout = null;
finally {
       	try {
			if(bout!=null)
			bout.close();
			if(in!=null)
			in.close();
			if(out!=null){
				out.flush();
				out.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
    }

导出/导入

如果导出/导入的数量量巨大,是导致内存溢出主要原因。
参考:
1个汉字=2B
1个字母/数字 = 1B
1KB =1024B
1MB=1024KB
1GB=1024MB
计算时:考虑 导入数量,同时导入人数。
例子: 场景客户群导入,导入字段3个。导入10000条,100人同时使用。

字段长度
CUST_ID30 B
CUST_ZH_NAME10 B
CUST_ID30 B
CUST_BASE_ID30 B
一条数据70B
10000条683.59KB=0.667MB
100人同时使用66.7MB

结论:对堆内存几乎没有影响。

固定类

   固定类的查找,会发生内存泄漏的。
   结合具体业务逻辑。

感谢支持~
感谢
thanks~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值