内存溢出介绍。
内存溢出和内存泄漏的联系:
内存泄漏会最终导致内存溢出。
相同点:都会导致应用程序运行出现问题,性能下降或挂起。
不同点:
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_ID | 30 B |
CUST_ZH_NAME | 10 B |
CUST_ID | 30 B |
CUST_BASE_ID | 30 B |
一条数据 | 70B |
10000条 | 683.59KB=0.667MB |
100人同时使用 | 66.7MB |
结论:对堆内存几乎没有影响。
固定类
固定类的查找,会发生内存泄漏的。
结合具体业务逻辑。
感谢支持~
thanks~
来源:CSDN
作者:诗萧尘
链接:https://blog.csdn.net/shiXiaoChenblog/article/details/103844292