【JVM】TroubleShooting之内存溢出异常(OOM)与调优
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)与调优的更多相关文章
- OutOfMemory相关问题(内存溢出异常OOM)
OutOfMemory(内存溢出异常OOM) java.lang.OutOfMemoryError :Thrown when the Java Virtual Machine cannot alloc ...
- JVM(2) Java内存溢出异常
在Java虚拟机运行时数据区中,除了程序计数器之外,虚拟机栈.本地方法栈.方法区和Java堆都有发生OutOfMemoryError(简称OOM)异常的可能. 一.Java堆溢出 Java堆用于存储对 ...
- JVM内存区域与内存溢出异常
Java虚拟机在执行java程序时会把它所管理的内存会分为若干个不同的数据区域,不同的区域在内存不足时会抛出不同的异常. >>运行时数据区域的划分 (1)程序计数器程序计数器(Progra ...
- OutOfMemoryError/OOM/内存溢出异常实例分析--虚拟机栈和本地方法栈溢出
关于虚拟机栈和本地方法栈,在JVM规范中描述了两种异常: 1.如果线程请求的栈深度大于JVM所允许的深度,将抛出StackOverflowError异常: 2.如果虚拟机在扩展栈时无法申请到足够的内存 ...
- JVM:Java常见内存溢出异常分析
转载自:http://www.importnew.com/14604.html Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆 ...
- JVM高级特性与实践(一):Java内存区域 与 内存溢出异常
套用<围城>中的一句话,“墙外面的人想进去,墙里面的人想出来”,用此来形容Java与C++之间这堵内存动态分配和垃圾收集技术所围成的“围墙”就再合适不过了. 对于从事C.C++的开发人员而 ...
- 1篇文章搞清楚8种JVM内存溢出(OOM)的原因和解决方法
前言 撸Java的同学,多多少少会碰到内存溢出(OOM)的场景,但造成OOM的原因却是多种多样. 堆溢出 这种场景最为常见,报错信息: java.lang.OutOfMemoryError: Java ...
- JVM学习与问题总结——java内存区域与内存溢出异常
java虚拟机将内存分为哪些区域? 根据Java SE7版本的Java虚拟机规范,虚拟机管理的内存包括5个运行时数据区域: 程序计数器 虚拟机栈 本地方法栈 方法区 堆 运行时数据区各部分的作用? 程 ...
- JVM自动内存管理-Java内存区域与内存溢出异常
摘要: JVM内存的划分,导致内存溢出异常的可能区域. 1. JVM运行时内存区域 JVM在执行Java程序的过程中会把它所管理的内存划分为以下几个区域: 1.1 程序计数器 程序计数器是一块较小的内 ...
随机推荐
- 在 Windows Server Container 中运行 Azure Storage Emulator(一):能否监听自定义地址?
我要做什么? 改 ASE 的监听地址.对于有强迫症的我来说,ASE 默认监听的是 127.0.0.1:10000-10002,这让我无法接受,所以我要将它改成域名 + 80 端口的方式: 放到容器中. ...
- 用setTimeout实现动态时钟的效果
1.获取到系统时间 2.获取到当地时间字符串 3.开启延时器,每一秒刷新一次时间 <!DOCTYPE html> <html> <head> <meta ch ...
- WORD列表缩进的文本起始点
Figure 1 Figure 2 Figure 3 编号位置以刻度尺为起点0.74厘米(2个字符间距),文本缩进以刻度尺为起点2.96厘米(8个字符间距) 以上两者相减得到的值正好=特殊格式悬挂缩进 ...
- [DP]洛谷P1115最大子段和
题目来源 https://www.luogu.org/problemnew/show/P1115 题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入输出格式 输入格式: 第一行是一 ...
- Eureka 集群高可用配置.
SERVER:1 server: port: 1111 eureka: instance: hostname: ${spring.cloud.client.ip-address} instance-i ...
- centos虚拟机安装,配置静态ip可以访问网络
centos安装过程中需要注意几个问题 1.选择安装的软件 默认选择的是mininal,应该选择GNEME Desktop 安装的过程中可以设置network 配置linux网络命令 具体配置 退出键 ...
- 批量修改文件格式到UTF-8
系统环境: Unbuntu14.10 目标: 多个文件夹加下的大量.java文件,需要由ASCII,转为UTF-8编码格式,文件嵌套较深. 解决方案: 执行Console中,目标目录下执行一下命令: ...
- 基于HP DL388 Gen 9服务器基本配置(ESXI 6.5)
最近一段时间由于做毕业设计的原因,一直处于忙碌状态,刚做完毕业设计,导师处于项目的原因,买了一台惠普服务器(人民币1.7万),服务器自带的内存仅有16 G,硬盘也就只有600G,而且磁盘还做了raid ...
- 23、springboot与缓存(1)
一.JSR107 Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry. 1.CachingPro ...
- 机器学习基石笔记:Homework #2 decision stump相关习题
原文地址:http://www.jianshu.com/p/4bc01760ac20 问题描述 程序实现 17-18 # coding: utf-8 import numpy as np import ...