内存溢出原因与解决方案(4大主流方案详解)

简介: 本文详解内存溢出(OOM)的原因及解决方案。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。

关注△mikechen的互联网架构△,10年+BAT架构经验倾囊相授

大家好,我是 mikechen | 陈睿

内存溢出(OOM)是线上经常面临的问题,也是大厂面试经常考察的内容,下面我就全面来详解内存溢出(OOM)的原因及解决方案。@mikechen

内存溢出

内存溢出,简称OOM,内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。

image.png

比如,申请了一个integer, 但给它存了long才能存下的数,那就是内存溢出。

再比如,你的电脑只有16GB的内存,已经把这16GB的内存用完了,但是还在继续用,就会造成内存溢出。

内存溢出的原因

程序常见的内存溢出原因:

  • 启动参数内存值设定的过小;
  • 内存中加载的数据量过于庞大;
  • 对象的引用使用完后未清空,使得 JVM 不能回收;
  • 代码中存在死循环或循环产生过多重复的对象实体。

Web服务器常见内存溢出提示

在不同的Web服务器或程序中,此错误常见的错误提示如下:

tomcat: java.lang.OutOfMemoryError: PermGen space
tomcat: java.lang.OutOfMemoryError: Java heap space
weblogic: Root cause of ServletException java.lang.OutOfMemoryError
resin: java.lang.OutOfMemoryError
java: java.lang.OutOfMemoryError

这些都是属于典型的内存溢出,接下来我们看看具体的内存溢出的解决办法。

内存溢出的解决办法

这里我就以Java举例,常见的Java内存溢出主要是方式:

image.png

堆溢出

这是很常见的一种内存溢出,报错信息如下:

java.lang.OutOfMemoryError: Java heap space
举一个死循环的例子,源码如下:

package com.mikechen.jvm;

/**

 * JVM 内存溢出案例详解
 *
 * @author  mikechen
 *
 */

import java.util.ArrayList;
import java.util.List;

public class MemoryOOM {

    static class Obj{ }

    /** * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * */

    public static void main(String[] args) {
        List<Obj> list = new ArrayList<>();
        try {
            while (true){
                list.add(new Obj());
            }
        }catch (Throwable t){
            t.printStackTrace();
            System.out.println("集合大小"+list.size());
        }
    }
}

这里会不断死循环list.add(new Obj()),不断的创建对象,当不总容量触及最大堆容量时,就会产生溢出,出现如下提示:

image.png

解决办法:

1、首先排查,检查是否存在大对象的分配,最有可能的是大对象分配。
2、其次,再通过jmap命令,把堆内存dump下来,使用mat工具分析一下,检查是否存在内存泄露的问题。
3、如果没有找到明显的内存泄露,可以适当调整-Xms和-Xmx两个JVM参数。

比如上面的例子,在第二个步骤通过dump下来的堆,用工具可以分析出死循环的位置,就可以及时解决堆溢出。

如果通过上面的两个步骤都排查后,没有发现问题,再调整JVM堆的初始化参数,问题基本就可以解决。

堆内存溢出

首先搞清楚java栈空间存储的是什么,栈内存可以分为虚拟机栈(VM Stack)和本地方法栈(Native Method Stack)。

每个方法执行时都会在java栈空间产生一个栈帧,存放方法的变量表,返回值等信息,方法的执行到结束就是一个栈帧入栈到出栈的过程。

具体来说,当线程执行某个方法时,JVM会创建栈帧并压栈,此时刚压栈的栈帧就成为了当前栈帧。

如果该方法进行递归调用时,JVM每次都会将保存了当前方法数据的栈帧压栈,每次栈帧中的数据都是对当前方法数据的一份拷贝,如果递归的次数足够多,多到栈中栈帧所使用的内存超出了栈内存的最大容量,此时JVM就会抛出StackOverflowError。

还是看一个例子:

public class stack{

public void test(){

    this.test();
}
public static void main(String[] args){
    for(; ; ;)
        new stack().test;
        }
}

最后就会出现:

StackOverflowError的错误提示

总之,不论是因为栈帧太大还是栈内存太小,当新的栈帧内存无法被分配时,JVM就会抛出StackOverFlowError,通常栈内存可以通过设置-Xss参数来改变大小。

永久代/元空间溢出
永久区(Perm) 存放了被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等。

在JDK 1.8中,永久区被一块称为元数据的区域替代, 但是它们的功能是类似的,都是为了保存类的元信息,如果一 一个系统定了太多的类型,那么永久区是有可能溢出的,报错信息如下:

java.lang.OutOfMemoryError: PermGen spacejava.lang.OutOfMemoryError: Metaspace
解决方法有如下几种:

1、检查是否永久代空间或者元空间设置的过小

2、检查代码中是否存在大量的反射操作

3、dump之后通过mat检查是否存在大量由于反射生成的代理类

4、放大招,重启JVM

以上,是内存溢出原因与解决方案的详细解析,欢迎评论区留言交流或拓展。

我是 mikechen | 陈睿 ,关注【mikechen的互联网架构】,10年+BAT架构技术倾囊相授。

新的架构专题内容,第一时间更新至:阿里架构师进阶全部合集

本文已同步我的技术博客 www.mikechen.cc,更新至我原创的《30W+字阿里架构技术合集》中。

相关文章
|
13天前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
9 2
|
6月前
|
算法 Java
并发垃圾回收算法对于大规模服务器应用的优势
并发垃圾回收算法对于大规模服务器应用的优势
|
2月前
|
Kubernetes Java 编译器
解锁极致性能:Quarkus如何让JVM应用调优变得前所未有的简单与高效!
Quarkus是一款专为GraalVM和OpenJDK设计的Kubernetes Native Java框架,采用AOT编译技术将Java应用转化为本地代码,大幅提升启动速度与运行效率。它简化了性能调优流程,如自动优化垃圾回收、类加载、内存管理及线程管理等,使开发者无需深入理解JVM细节即可轻松提升应用性能。与传统JVM应用相比,Quarkus显著降低了性能调优的复杂度。
95 2
|
2月前
|
自然语言处理 前端开发 搜索推荐
一套成熟的CMS需要具备的优势
在互联网发达的时代,网站搭建已经不再是一个高门槛的事情,内容管理系统(CMS)使您能够在不触及任何底层代码的情况下控制网站的内容、设计和功能。它是一个全能的助手,可以让用户轻松管理和发布内容,定制网站外观,扩展功能,以及追踪关键指标。 互联网上有数百种内容管理系统,每种系统都提供独特的功能以满足不同的业务需求。要选择最适合您业务的CMS,您需要确保其功能与您的需求和目标相匹配。
85 7
|
3月前
|
存储 缓存 算法
优化Java后台性能的五大最佳实践
在高并发环境中,Java后台系统的性能至关重要。本文探讨了五种有效的优化方法,包括JVM调优、数据库连接池配置、代码优化技巧、异步处理的使用以及缓存机制的实现。通过这些实践,开发人员可以显著提升系统的响应速度和稳定性。
|
3月前
|
开发框架 Android开发 iOS开发
探索移动应用的无限可能:从开发到操作系统的全链路解析
在数字时代,移动应用成为人们日常生活和工作中不可或缺的一部分。本文深入探讨了移动应用的开发流程、技术选型以及与移动操作系统之间的紧密联系。通过分析当前市场上流行的移动操作系统特点,我们揭示了不同平台为应用开发带来的独特挑战和机遇。文章还讨论了移动应用的未来趋势,包括跨平台开发框架的兴起和人工智能技术的整合,旨在为读者提供一个全面而深刻的视角,理解移动应用背后的复杂世界。
|
4月前
|
存储 JSON JavaScript
小程序优化:第三方SDK过大解决方案
小程序开发中,项目目录中存放过大的js包,会被警告影响手机端性能,同时让开发编译启动变得很慢。慢是其次,单是影响性能这一点,就需要解决一下。
|
缓存 算法 大数据
倚天710规模化应用 - 性能优化 - 软件预取分析与优化实践
软件预取技术是编程者结合数据结构和算法知识,将访问内存的指令提前插入到程序,以此获得内存访取的最佳性能。然而,为了获取性能收益,预取数据与load加载数据,比依据指令时延调用减小cachemiss的收益更大。
|
6月前
|
算法 Java UED
垃圾回收机制对于大型和高性能的应用程序有什么影响
垃圾回收机制对于大型和高性能的应用程序有什么影响
|
6月前
|
设计模式 算法 Java
优化Java应用性能的六大策略
提升Java应用性能是每个开发者都追求的目标,而实现这一目标需要综合考虑多个方面的因素。本文将介绍六大有效的策略,帮助开发者优化Java应用性能,包括内存管理、并发控制、代码优化等方面,旨在提供实用的指导和方法,使Java应用更加高效稳定。