什么是OOM?out of memory
- 内存用完了
- JVM没有足够的内存来为对象分配空间并且垃圾回收器也没有空间可以回收时,就会抛出这个error。
为什么会OOM?
即为什么会没有内存?
- 分配的少了:虚拟机本身可以使用的内存就少(一般通过启动时的VM参数指定)
- 应用使用的太多,并且用完没有释放,浪费了,此时会出现内存泄漏或者内存溢出。
内存泄漏:申请使用的内存没有释放,导致虚拟机不能再次使用的内存,因为申请者不用了,而又不能被jvm分配给别人用。
内存溢出:申请的内存超出了JVM能提供的内存大小。
java堆溢出
原因
- 无法在java堆中分配对象
- 应用程序保留了无法被GC回收的对象
- 应用程序过度使用finalizer
java堆溢出排查解决思路
1.查找关键报错信息,如
java.lang.OutOfMemoryError: Java heap space
2.使用内存映像分析工具(如Eclipsc Memory Analyzer或者Jprofiler)对Dump出来的堆储存快照进行分析,分析清楚是内存泄漏还是内存溢出。
3.如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,修复应用程序中的内存泄漏。
4.如果不存在泄漏,先检查代码是否有死循环,递归等,再考虑用 -Xmx 增加堆大小
栈溢出
关于虚拟机栈和本地方法栈,在java虚拟机栈规范中描述了两种异常:
- 如果线程请求的栈深度大于虚拟机所允许的栈深度,将抛出StackOverflowError异常;
- 如果虚拟机可以动态扩展,当扩展时无法申请到最够的内存抛出 OutOfMemoryError 异常。
栈溢出原因
- 在线程下,栈帧太大,或者虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出StackOverflowError异常
- 不断建立线程会产生异常或内存泄漏
知识点补充:
一个栈随着一个方法的调用开始创建,方法调用完以后销毁。栈帧内存放着局部变量、操作数栈等数据。
Java栈又叫虚拟机栈,JVM栈只对栈帧进行存储、压栈、出栈操作。
- 每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象,对象都存放在堆区中)。
栈溢出排查解决思路
- 查找关键报错信息,确定是StackOverflowError还是OutOfMemoryError
- 如果是StackOverflowError,检查代码是否是递归调用
- 如果是OutOfMemoryError,检查是否有死循环创建线程等,通过-Xss降低每个线程栈大小的容量。
方法区溢出
方法区(又叫永久代,JDK8后,元空间替换了永久代),用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。运行时会产生大量的类,会填满方法区
方法区溢出原因
- 使用CGLib生成了大量的代理类,导致方法区被撑爆
- 在Java7之前,频繁的错误使用String.intern方法
- 大量jsp和动态产生jsp
- 应用长时间运行,没有重启
方法区溢出排查解决思路
- 检查是否永久代空间设置得过小
- 检查代码是否频繁错误得使用String.intern方法
- 检查是否跟jsp有关。
- 检查是否使用CGLib生成了大量的代理类
- 重启大法,重启JVM
补充知识点:
方法区中的常量池:
- 常量池分为字面常量池和符号引用常量池:
字面常量(表示存储的是值):
1.文本字符串:1、文本字符串:
定义的字符串常量的值 public String str = “Hello World”;
“Hello World” 会存在常量池当中
public String str1 = new String(“Hello Java");
“Hello Java” 的值不会存在常量池中,而是存在堆内存当中。
2.final类型的常量值:public final int age = 11,11后被存储在常量池中
3.基本数据类型的值byte, short, int, float, double, long, char, boolean ,并不是所有基本数据类型的值都会存储在常量池中 byte, short, char, boolean 这四种类型的值不会存在常量池中。
字符引用:表示引用的存储。
1.类或者接口的完全限定名:即是类或者接口的全路径名称
2.字段的名称和描述符:类中的属性的名称,以及属性的描述。
3.方法的名称和描述符。