1. OOM概述

If your application's execution time becomes longer and longer, or if the operating system seems to be performing slower and slower, this could be an indication of a memory leak. In other words, virtual memory is being allocated but is not being returned when it is no longer needed. Eventually the application or the system runs out of memory, and the application terminates abnormally.

如果您的应用程序的执行时间变得越来越长,或者操作系统看起来速度越来越慢,则这可能表示内存泄漏。换句话说,虚拟内存正在分配,但不再需要时不会返回。最终应用程序或系统内存不足,应用程序异常终止。

以上摘自《Troubleshooting Guide for HotSpot VM》

------------------------------

2. OOM异常

2.1 常见原因

内存泄漏常见的异常提示是java.lang.OutOfMemoryError。产生这个错误通常是出现以下几种情况:

(1) 程序开启过多的线程

(2) 配置参数值设置过小,不满足程序的资源要求

(3) 集合对象(Collection、Map)始终存有对象的引用,使用完后未及时清除,未及时被GC回收。

(4) 一次性从数据库中获取大量的数据量,造成内存加载的数据量过于庞大

(5) 代码中存在死循环,大量出现重复的新对象体

(6) 引用的第三方类库中有BUG

2.2 常见的异常提示

 (1). Exception in thread "main": java.lang.OutOfMemoryError: Java heap space

这个异常信息表示无法在Java 堆中分配对象。比如以下这段代码

/**
* @author Supernova
* @date 2018/06/18
*/
public class HeapOOM{
private static StringBuffer name;
public static void main(String[] args){
name = new StringBuffer("Supernova");
for(int i = 0; i < Integer.MAX_VALUE; i++){
name = name.append(name);
}
}
}

 运行情况如下:抛出了OOM异常提示Java heap space。

当StringBuffer对象超出了容量限制,就无法在堆中分配内存。同样,如果程序中创建了过多的对象也会因对象数量超出容量限制而出现OOM。

 (2). Exception in thread "main": java.lang.OutOfMemoryError: PermGen space

异常信息提示持久代空间已满,它可能出现在加载很多类的时候,比如引用了很多第三方库。持久代存储的是类、方法等信息。持久代在在JVM中保留至JDK1.7,在JDK1.8之后被删除,详情请看上篇博文【JVM】上帝视角看JVM内存模型,分而治之论各模块详情。JDK1.6时,静态变量存储在持久栈中,JDK1.7之后,静态变量存储在堆中。这里我们通过讨论OOM异常证明静态变量的存储位置。

如以下这段可使静态变量造成内存溢出的代码:

import java.util.ArrayList;  

/**
* @author Supernova
* @date 2018/06/18
*/
public class StaticOOM{
private static String name = "Supernova";
private static ArrayList<String> list ;
public static void main(String[] args){
list = new ArrayList<String>();
for(int i = 0; i < Integer.MAX_VALUE; i++){
name = name + name;
list.add(name.intern());
}
}
}

JDK1.6下运行情况:抛出的异常提示PermGen space

JDK1.7下运行情况:抛出的异常提示Java heap space

JDK1.8下运行情况:抛出的异常提示Java heap space

因此,可以得出结论,静态变量在JDK1.6是存放在方法区的持久代中,在JDK1.7之后存放在Java堆中

 (3). Exception in thread "main": java.lang.OutOfMemoryError: Metaspace

在JDK1.7之后,元空间(meta space)取代了持久代,所以在JDK1.7之后,在程序中也会看到元空间的异常信息。通常是由于加载太多的类,超出了限制值。

如以下代码:

package com.nova.test;
/**
* @author Supernova
* @date 2018/06/18
*/
public class MetaSpaceOOM {
public static void main(String[] args) {
javassist.ClassPool jc = new javassist.ClassPool().getDefault();
for(int i = 1 ;i < Integer.MAX_VALUE; i++) {
try {
Class myClass = jc.makeClass("SupernovaClass"+i).toClass();
System.out.println("加载第"+i+"个类");
}catch(Exception e){
e.printStackTrace();
}
}
}
}

为了加快溢出,在Eclipse中设置元空间的最大值为20M。

运行结果如下:

当加载类的数量达到17261个类的时候,抛出了元空间OOM异常。也就是在20m的限制值之下,只能加载17261个类,一旦超出,20m的限制值就会产生OOM,所以在具体项目中,应结合实际情况设置-XX:MaxMetaspaceSize参数来解决或防止OOM的异常。

 (3). Exception in thread "main": java.lang.OutOfMemoryError: Requested array size exceeds VM limit

异常信息表示是由于数组大小超过VM限制。如以下情况,

/**
* @author Supernova
* @date 2018/06/18
*/
public class ArrayOOM{
public static void main(String[] args){
int[] arr = new int[Integer.MAX_VALUE];
}
}

代码中请求创建的是Int类型的数组,int长度为2^31 - 1,多数平台的限制都大约是这个值。

运行结果如下:

 (4). Exception in thread "main": java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space?

本机方法分配故障。它是在JNI或者是本地方法中检测到的。如果遇到这个异常,可能需要用到操作系统特定工具(Operation-System-Specific Tools)才能排障,

详情可参考:https://docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/tooldescr.html#gbmoy

 (5). Exception in thread "main": java.lang.OutOfMemoryError: <reason> <stack trace> (Native method)

这类异常通常是本地堆分配失败造成应用程序崩溃。这种异常的排障比较难,需要根据具体情况,出现这类通常需要从转储日志或者致命错误日志进行诊断。

致命错误日志可参考:https://docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/felog.html

3. 调优

 3.1 代码调优:

(1) 减少使用临时对象,对象不使用时候,最好显示的设置为null。

(2) 不过多的使用静态对象,因为static对象属于全局变量,会一直占用内存,不被回收

(3) 累加字符串尽量使用StringBuffer,而不用String,因为StringBuffer是可变字符串,累加操作是在一个对象里,而String是固定长度的,累加操作时不是在一个String对象里,而是创建新的对象

3.2 参数调优:

--Xmx: 设置JVM堆的最大值

--Xms: 设置JVM堆最小值

--Xss: 设置每个线程栈的大小

--Xmn: 设置年轻代大小

--XX:MaxPermGenSize: 设置持久代大小

--XX:MaxTenuringThreshold: 设置年龄阈值

更多的参数调优参考下表,此表引用于:JVM系列三:JVM参数设置、分析

参数名称 含义 默认值  
-Xms 初始堆大小 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.
-Xmx 最大堆大小 物理内存的1/4(<1GB) 默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn 年轻代大小(1.4or lator)   注意:此处的大小是(eden+ 2 survivor space).与jmap -heap中显示的New gen是不同的。
整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.
增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize 设置年轻代大小(for 1.3/1.4)    
-XX:MaxNewSize 年轻代最大值(for 1.3/1.4)    
-XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64  
-XX:MaxPermSize 设置持久代最大值 物理内存的1/4  
-Xss 每个线程的堆栈大小   JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右
一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。(校长)
和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"”
-Xss is translated in a VM flag named ThreadStackSize”
一般设置这个值就可以了。
-XX:ThreadStackSize Thread Stack Size   (0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]
-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)   -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-XX:SurvivorRatio Eden区与Survivor区的大小比值   设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes 内存页的大小不可设置过大, 会影响Perm的大小   =128m
-XX:+UseFastAccessorMethods 原始类型的快速优化    
-XX:+DisableExplicitGC 关闭System.gc()   这个参数需要严格的测试
-XX:MaxTenuringThreshold 垃圾最大年龄   如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率
该参数只有在串行GC时才有效.
-XX:+AggressiveOpts 加快编译    
-XX:+UseBiasedLocking 锁机制的性能改善    
-Xnoclassgc 禁用垃圾回收    
-XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间 1s softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap
-XX:PretenureSizeThreshold 对象超过多大是直接在旧生代分配 0 单位字节 新生代采用Parallel Scavenge GC时无效
另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象.
-XX:TLABWasteTargetPercent TLAB占eden区的百分比 1%  
-XX:+CollectGen0First FullGC时是否先YGC false  

参考

https://blog.csdn.net/renfufei/article/details/78170188?locationNum=2&fps=1

https://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html

https://blog.csdn.net/wys839348916/article/details/46312523

【JVM】TroubleShooting之内存溢出异常(OOM)与调优的更多相关文章

  1. OutOfMemory相关问题(内存溢出异常OOM)

    OutOfMemory(内存溢出异常OOM) java.lang.OutOfMemoryError :Thrown when the Java Virtual Machine cannot alloc ...

  2. JVM(2) Java内存溢出异常

    在Java虚拟机运行时数据区中,除了程序计数器之外,虚拟机栈.本地方法栈.方法区和Java堆都有发生OutOfMemoryError(简称OOM)异常的可能. 一.Java堆溢出 Java堆用于存储对 ...

  3. JVM内存区域与内存溢出异常

    Java虚拟机在执行java程序时会把它所管理的内存会分为若干个不同的数据区域,不同的区域在内存不足时会抛出不同的异常. >>运行时数据区域的划分 (1)程序计数器程序计数器(Progra ...

  4. OutOfMemoryError/OOM/内存溢出异常实例分析--虚拟机栈和本地方法栈溢出

    关于虚拟机栈和本地方法栈,在JVM规范中描述了两种异常: 1.如果线程请求的栈深度大于JVM所允许的深度,将抛出StackOverflowError异常: 2.如果虚拟机在扩展栈时无法申请到足够的内存 ...

  5. JVM:Java常见内存溢出异常分析

    转载自:http://www.importnew.com/14604.html Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆 ...

  6. JVM高级特性与实践(一):Java内存区域 与 内存溢出异常

    套用<围城>中的一句话,“墙外面的人想进去,墙里面的人想出来”,用此来形容Java与C++之间这堵内存动态分配和垃圾收集技术所围成的“围墙”就再合适不过了. 对于从事C.C++的开发人员而 ...

  7. 1篇文章搞清楚8种JVM内存溢出(OOM)的原因和解决方法

    前言 撸Java的同学,多多少少会碰到内存溢出(OOM)的场景,但造成OOM的原因却是多种多样. 堆溢出 这种场景最为常见,报错信息: java.lang.OutOfMemoryError: Java ...

  8. JVM学习与问题总结——java内存区域与内存溢出异常

    java虚拟机将内存分为哪些区域? 根据Java SE7版本的Java虚拟机规范,虚拟机管理的内存包括5个运行时数据区域: 程序计数器 虚拟机栈 本地方法栈 方法区 堆 运行时数据区各部分的作用? 程 ...

  9. JVM自动内存管理-Java内存区域与内存溢出异常

    摘要: JVM内存的划分,导致内存溢出异常的可能区域. 1. JVM运行时内存区域 JVM在执行Java程序的过程中会把它所管理的内存划分为以下几个区域: 1.1 程序计数器 程序计数器是一块较小的内 ...

随机推荐

  1. Effective C++(1-2) 编译器替换预处理器

    1 C++最主要的四部分: C Object-Oriented C++: 面向对象 Template C++:泛型编程 STL C++高效编程守则视状况而变化,取决于你使用C++的哪一部分. 2 尽量 ...

  2. Redis 集群缓存测试要点--关于 线上 token 失效 BUG 的总结

    在测试账户系统过程中遇到了线上大面积用户登录态失效的严重问题,事后对于其原因及测试盲点做了一些总结记录以便以后查阅,总结分为以下7点,其中原理性的解释有些摘自网络. 1.账户系统token失效问题复盘 ...

  3. php读取大文件如日志文件

    需求如下: 现有一个1G左右的日志文件,大约有500多万行, 用php返回最后几行的内容. 1. 直接采用file函数来操作 or file_get_content() 肯定报内存溢出注: 由于 fi ...

  4. Tsql 获取服务器信息

    Tsql 获取服务器属性,如服务器版本.服务器名 ref:http://technet.microsoft.com/zh-cn/library/ms174396.aspx select serverp ...

  5. Coursera 算法二 week 5 BurrowsWheeler

    本打算周末完成这次作业,但没想到遇到了hard deadline,刚开始看不懂题意,后来发现算法4书上有个类似的问题,才理解了题意.最后晚上加班,上课加班,还好在11:35也就是课程结束前25分钟完成 ...

  6. (八)Linux之挂载命令

    挂载命令 其实挂载在Linux中可以理解为分配盘符的意思.想一下,比如一张光盘插入了Linux系统的 电脑上,要想读取其中的内容,需要做哪些操作呢?首先你要考虑的是这张外来光盘的的数据也是 外来的,如 ...

  7. Python读取Json字典写入Excel表格的方法

    需求: 因需要将一json文件中大量的信息填入一固定格式的Excel表格,单纯的复制粘贴肯定也能完成,但是想偷懒一下,于是借助Python解决问题. 环境: Windows7 +Python2.7 + ...

  8. 以整数元素构成的list中的数字组成最小整数

    问题 把一个int型数组中的数字拼成一个串,这个串代表的数字最小. 思路说明 不同角度,对原题理解有所不同.我依照以下的理解方式求解. 对这个问题的理解: 有一个元素是int类型的list: 将上述l ...

  9. concatenate函数

    numpy.concatenate((a1, a2, ...), axis=0) Join a sequence of arrays along an existing axis.(按轴axis连接a ...

  10. QTP基本方法3-----截屏

    1.桌面截屏 Desktop.captureBitMap  path[,bolean] path:保存路径,可选择绝对路径或相对路径 相对路径是保存在脚本保存的目录下编号最大的res目录下. bole ...