当JVM内存不足时,会抛出java.lang.OutOfMemoryError.
 
主要的OOM类型右:
  • Java heap space:堆空间不足
  • GC overhead limit exceeded : GC开销超出限制
  • Permgen space:永久代内存不足
  • Metaspace:元空间内存不足
  • Unable to create new native thread:无法创建新的本地线程
  • Out of swap space? : 交换空间内存不足
  • Kill process or sacrifice child

Java heap space:堆空间不足

  • 通用解决方案:通过-Xmx设置更大的堆内存【该方式可能只是延迟报错的发生,如果不能从根本上找到原因,报错还是可能会发生】
  • 进一步原因分析及解决方案:
    • 流量/数据量峰值 : 可以考虑添加机器资源,或者做限流
    • 内存泄漏 : 需要找到持有的对象,修改代码
    • 创建了一个超大对象(通常是一个大数组) : 可以进行业务切分
  • 代码示例
    • 内存泄漏【-Xmx10m】

package oom;

import java.util.HashMap;
import java.util.Map; /**
* 内存泄露
*/
public class JavaHeapSpace2 { public static void main(String[] args) {
Map<Key,String> map = new HashMap<>();
while (true) {
Key key = new Key();
if(!map.containsKey(key)) {
map.put(key, "Java Overhead");
System.out.println(key);
}
} }
} class Key { }

oom.Key@2ef70cb4
......
oom.Key@457298d0
oom.Key@484b94f2
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.HashMap.resize(HashMap.java:704)
at java.util.HashMap.putVal(HashMap.java:663)
at java.util.HashMap.put(HashMap.java:612)
at oom.JavaHeapSpace2.main(JavaHeapSpace2.java:16)

View Result

    • 创建了一个超大对象

package oom;

import java.lang.management.ManagementFactory;
import java.util.List; public class JavaHeapSpace { private static final int SIZE = 12 * 1024 * 2014; public static void main(String[] args) {
List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
System.out.println("JVM Arguments : " + inputArguments);
int[] arr = new int[SIZE];
}
}

JVM Arguments : [-Xmx12m, -Dfile.encoding=GBK]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at oom.JavaHeapSpace.main(JavaHeapSpace.java:13) Process finished with exit code 1

View Result[-Xmx12m]

JVM Arguments : [-Xmx35m,-Dfile.encoding=GBK]

Process finished with exit code 0

View Result[-Xmx35m]

GC overhead limit exceeded : GC开销超出限制

默认情况下,当应用程序花费超过98%的时间用来做GC并且回收了不到2%的堆内存时,会抛出java.lang.OutOfMemoryError:GC overhead limit exceeded错误。
此类问题的原因与解决方案跟 Java heap space 非常类似,可以参考上文
  • 代码演示【使用默认的VM配置】

package oom;

import java.util.HashMap;
import java.util.Map; public class JavaHeapSpace2 { public static void main(String[] args) {
Map<Key,String> map = new HashMap<>();
while (true) {
Key key = new Key();
if(!map.containsKey(key)) {
map.put(key, "Java Overhead");
System.out.println(key);
}
}
}
} class Key { }

oom.Key@61f7f66c
oom.Key@1da844d
......
oom.Key@792b37e7
oom.Key@3d8151c0Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.nio.CharBuffer.wrap(CharBuffer.java:373)
at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265)
at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)

View Result

Permgen space:永久代内存不足
  • 背景:永久代主要存储类的信息,比如:类加载引用、运行时常量池、字段、方法等。因此,Permgen的大小取决于被加载类的数量及类的大小。
  • 原因:
    • 加载了太多的类
    • 加载了超大类
  • 注意:JDK8已经完全移除永久代空间,取而代之的是元空间(Metaspace)
Metaspace:元空间内存不足
  • 背景:Metaspace存储类的元数据信息
  • 原因:
    • 加载了太多的类
    • 加载了超大类
  • 解决方案
    • 调整-XX:MaxMetaspaceSize参数
    • 删除-XX:MaxMetaspaceSize参数,解除限制【默认是没有限制的。默认情况下,对于64位服务器端JVM,MetaspaceSize默认大小是21M(初始限制值),一旦达到这个限制值,FullGC将被触发进行类卸载,并且这个限制值将会被重置,新的限制值依赖于Metaspace的剩余容量。如果没有足够空间被释放,这个限制值将会上升。】
  • 代码演示【JDK1.8】

package oom;

import javassist.CannotCompileException;
import javassist.ClassPool; import java.lang.management.ManagementFactory;
import java.util.List; public class Metaspace{ public static void main(String[] args) throws CannotCompileException {
List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
System.out.println("JVM Arguments : " + inputArguments); for (int i = 0; i < 100000000; i++) {
ClassPool.getDefault().makeClass("User" + i).toClass();
}
}
}

JVM Arguments : [-XX:MaxMetaspaceSize=35m, -Dfile.encoding=GBK]
Exception in thread "main" javassist.CannotCompileException: by java.lang.OutOfMemoryError: Metaspace
at javassist.ClassPool.toClass(ClassPool.java:1099)
at javassist.ClassPool.toClass(ClassPool.java:1042)
at javassist.ClassPool.toClass(ClassPool.java:1000)
at javassist.CtClass.toClass(CtClass.java:1224)
at oom.Permgen.main(Permgen.java:16)
Caused by: java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at javassist.ClassPool.toClass2(ClassPool.java:1112)
at javassist.ClassPool.toClass(ClassPool.java:1093)
... 4 more

View Result

Unable to create new native thread:无法创建新的本地线程
  •  背景:每个线程都需要一定的内存空间,当JVM向底层操作系统请求创建一个新的native线程时,如果没有足够的资源分配就会报这个错误
  • 原因分析及解决方案:
    • 线程数超过了OS最大线程数ulimit限制 : 调高 OS 层面的线程最大数 - 执行 ulimia-a 查看最大线程数限制,使用 ulimit-u xxx 调整最大线程数限制
    • 线程数超过了本地线程最大数:限制线程池大小  ; 
    • native内存不足 : 
      • 使用 -Xss 参数减少线程栈的大小
      • 升级配置,为机器提供更多的内存
  • 代码示例

package oom;

public class UnableCreateThread {

    public static void main(String[] args) {
while (true) {
new Thread(() -> {
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}

#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006a674447, pid=18648, tid=0x0000000000067b68
#
# JRE version: Java(TM) SE Runtime Environment (8.0_171-b11) (build 1.8.0_171-b11)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# V [jvm.dll+0x214447][thread 424944 also had an error]
An unrecoverable stack overflow has occurred.
[thread 424964 also had an error]
An unrecoverable stack overflow has occurred.
[thread 424984 also had an error]
[thread 424992 also had an error]
An unrecoverable stack overflow has occurred.
[thread 424988 also had an error]
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at oom.UnableCreateThread.main(UnableCreateThread.java:14)

View Result

Out of swap space? : 交换空间内存不足
  • 背景:虚拟内存(Virtual Memory)由物理内存(Physical Memory)和交换空间(Swap Space)两部分组成。在JVM请求的总内存大于可用物理内存的情况下,操作系统会将内存中的数据交换到磁盘上去。当交换空间也将耗尽时就会报 Outof swap space? 错误。
  • 原因分析及解决方案:【往往是由操作系统级别的问题引起的】
    • 操作系统配置的交换空间不足 :
      •  加交换空间【对交换空间运行垃圾回收算法会使GC暂停的时间增加几个数量级,因此使用增加交换空间的方法】
      • 升级机器以包含更多内存
    • 系统上的另一个进程消耗所有内存资源:如果应用部署在JVM需要同其他进程激烈竞争获取资源的物理机上,建议将服务隔离到单独的虚拟机中
    • 本地内存泄漏导致应用程序失败: 优化应用程序以减少其内存占用
 
Kill process or sacrifice child
  • 背景:操作系统是建立在进程的概念之上,这些进程在内核中作业,其中有一个非常特殊的进程,名叫“内存杀手(Out of memory killer)”。当内核检测到系统内存不足时,OOM killer被激活,然后选择一个进程杀掉。
  • 原因分析:程序占用大量系统内存导致其他进程没有可用内存
  • 解决方案:
    • 调整OOM Killer配置
    • 升级机器以包含更多内存
 
你可以通过修改各种启动参数来“快速修复”这些内存溢出错误,但你需要正确区分你是否只是推迟或者隐藏了java.lang.OutOfMemoryError的症状。如果你的应用程序确实存在内存泄漏或者本来就加载了一些不合理的类,那么所有这些配置都只是推迟问题出现的时间而已,实际也不会改善任何东西。
 
 
参考文献:

内存溢出(OOM)分析的更多相关文章

  1. JVM:内存溢出OOM

    JVM:内存溢出OOM 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 经典错误 JVM 中常见的两个 OOM 错误 StackoverflowError:栈溢出 ...

  2. 内存溢出(Oom)和内存泄露(Memory leak)

    1.概念 内存溢出(Oom):1.内存不够用:2.数据长度短的数据类型存储了一个数据长度较大的数据类型:3.一个结果 内存泄露(Memory leak):1.忘记释放已用内存,内存管理较为常见的现象: ...

  3. 内存溢出OOM与内存泄漏ML

    附, 微信团队原创分享:Android内存泄漏监控和优化技巧总结 一.如何避免OOM 异常 想要避免OOM 异常首先我们要知道什么情况下会导致OOM 异常. 1.图片过大导致OOM Android 中 ...

  4. java常见内存溢出(OOM)

    jvm内存区域 程序计数器一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器. java栈与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同.通常存放基本数据类型, ...

  5. Java常见内存溢出异常分析(OutOfMemoryError)

    原文转载自:http://my.oschina.net/sunchp/blog/369412 1.背景知识 1).JVM体系结构 2).JVM运行时数据区 JVM内存结构的相关可以参考: http:/ ...

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

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

  7. 本地模拟内存溢出并分析Dump文件

    java Dump文件分析 前言 dump文件是java虚拟机内存在某一时间点的快照文件,一般是.hprof文件,下面自己模拟一下本地内存溢出,生成dump文件,然后通过mat工具分析的过程. 配置虚 ...

  8. android 内存溢出问题分析

      最近的项目中,内存一直再增长,但是不知道是什么问题,导致内存溢出,在网上看到了这么一篇关于内存分析与管理的文章,解决了部分问题,感觉这篇文 章还不错,就转帖到我的blog上了,希望对大家有所帮助. ...

  9. bitmap 内存溢出OOM的解决办法分享

    昨天遇到这个问题就是从一个输入流里调用BitmapFactory.decodeStream(this.getContentResolver().openInputStream(uri))得到一个bit ...

随机推荐

  1. C++20 以 Bazel & Clang 开始

    C++20 如何以 Bazel & Clang 进行构建呢? 本文将介绍: Bazel 构建系统的安装 LLVM 编译系统的安装 Clang is an "LLVM native&q ...

  2. 腾讯云EKS 上部署 eshopondapr

    腾讯云容器服务(Tencent Kubernetes Engine,TKE)基于原生 kubernetes 提供以容器为核心的.高度可扩展的高性能容器管理服务.腾讯云容器服务完全兼容原生 kubern ...

  3. SQLZOO练习(一)SELECT BASICS,SELECT form world

    name continent area population gdp Afghanistan Asia 652230 25500100 20343000000 Albania Europe 28748 ...

  4. super详解(继承)

    //在Java中,所有的类,都默认直接或者间接继承objec类// Person 人 :父类public class Person /*extends object*/ { public Person ...

  5. C++学习记录1

    代码1:转义字符 点击查看代码 #include<iostream> using namespace std; void test01()//换行 { cout << &quo ...

  6. docker容器数据管理

    Docker容器数据卷 Docker中的数据可以存储在类似于虚拟机磁盘的介质中,在Docker中称为数据卷(Data Volume). 数据卷可以用来存储Docker应用的数据,也可以用来在Docke ...

  7. jdbc 11: 封装自己的jdbc工具类

    jdbc连接mysql,封装自己的jdbc工具类 package com.examples.jdbc.utils; import java.sql.*; import java.util.Resour ...

  8. srec_cat 常用参数的使用

    前言 下面介绍映像文件工具 srec_cat 的使用,如何通过相关参数实现自己需要的功能. 下载链接:SRecord 使用方式 文件类型 在输入文件和输出文件文件时要指明文件类型,常用的如: test ...

  9. firewall 命令简单操作

    Firewalld 是维护防火墙策略的守护程序的名称.使用 firewall-cmd 命令与防火墙配置进行交互, 使用区域概念对与系统交互的流量进行分段.网络接口分配给一个或多个区域,每个区域都包含允 ...

  10. 浅谈Meet in the middle——MITM

    目测观看人数 \(0+0+0=0\) \(\mathrm{Meet\;in\;the\;middle}\)(简称 \(\rm MITM\)),顾名思义就是在中间相遇. 可以理解为就是起点跑搜索树基本一 ...