当前位置: 首页 > article >正文

【JVM】OOM

OOM

概述

OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”。

当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。

内存泄露:申请使用完的内存没有释放,导致虚拟机不能再次使用该内存,此时这段内存就泄露了,因为申请者不用了,而又不能被虚拟机分配给别人用。

内存溢出:申请的内存超出了JVM能提供的内存大小,此时称之为溢出。

在之前没有垃圾自动回收的日子里,比如C语言和C++语言,我们必须亲自负责内存的申请与释放操作,如果申请了内存,用完后又忘记了释放,比如C++中的new了但是没有delete,那么就可能造成内存泄露。偶尔的内存泄露可能不会造成问题,而大量的内存泄露可能会导致内存溢出。

而在Java语言中,由于存在了垃圾自动回收机制,所以,我们一般不用去主动释放不用的对象所占的内存,也就是理论上来说,是不会存在“内存泄露”的。但是,如果编码不当,比如,将某个对象的引用放到了全局的Map中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。如果该种情况出现次数多了,就会导致内存溢出,比如系统中经常使用的缓存机制。Java中的内存泄露,不同于C++中的忘了delete,往往是逻辑上的原因泄露。

Java 中会存在内存泄漏吗

        理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。

出现原因

  • 一次性申请的对象太多了。
    • 举例:做数据列表的查询,一次性去数据库把所有数据都查出来,若数据量达到千万级,把所有数据都放入list中,有可能就会造成内存溢出。
    • 解决:定位到具体业务代码。更改一次性申请对象的数量,分页查询。
  • 内存资源耗尽未释放
    • 举例:使用线程、使用数据库查询,假设不断的创建线程,不断的使用jdbcConnection却不释放,久而久之就会造成内存溢出。
    • 解决:定位到具体业务代码。用完后立马关闭,使用池化思想。
  • 本身应用分配的堆内存空间不够
    • 举例:应用本身存在较大的对象。
    • 解决:调整堆内存大小。

在linux服务器上先随便启动一个java程序,获取进程号,使用jmap -head 进程号 命令,就会打印当前java应用所使用堆的最大内存等信息。根据信息指标结合自己的应用做适当调整。

出现位置

按照JVM规范,JAVA虚拟机在运行时会管理以下的内存区域:

程序计数器:当前线程执行的字节码的行号指示器,线程私有
JAVA虚拟机栈:Java方法执行的内存模型,每个Java方法的执行对应着一个栈帧的进栈和出栈的操作。
本地方法栈:类似“ JAVA虚拟机栈 ”,但是为native方法的运行提供内存环境。
JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。
方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。
运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。
直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。


按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。
 

常见情况

  • java.lang.OutOfMemoryError: Java heap space ------>java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。
  • java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。
  • java.lang.StackOverflowError ------> 不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

如何定位

若系统已宕机:

oom定位有据可循:使用 java -Xms 100M -Xmx 100M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./jvm_logs/ -jar xxx.jar 命令,分配好堆栈内存启动java程序,这样当内存溢出的时候会生成一个异常文件。

查看异常文件步骤:1、先将异常文件导入windows系统。2、使用jdk自带的JVisualVM软件工具载入这个文件。Win+r输入jvisualvm启动,点击文件,装入该异常文件。3、分析文件,点击类,找到最占内存的实例,点击某一个实例,展开找到gcRoot,右击选择在线程中显示,即可看到程序的具体错误。

若系统未宕机:

直接使用jmap -dump:format=b,file=xxx.hprof 进程号 命令导出内存溢出文件。但是这条命令会造成一次fullGC,stop the world,所有的线程都会中断。

或者使用Arthas工具调试。


http://www.kler.cn/a/516935.html

相关文章:

  • Flink运行时架构
  • BGP(1)邻居建立,路由宣告
  • SQL进阶实战技巧:用户会话内行为模式挖掘
  • 除了基本的事件绑定,鸿蒙的ArkUI
  • windows平台通过命令行安装前端开发环境
  • 【Go面试】工作经验篇 (持续整合)
  • python——Django 框架
  • QT QListWidget控件 全面详解
  • 使用LabVIEW的History功能实现队列数据的读取而不清空
  • 在 VS Code 中使用 TypeScript 进行开发和打包的几种方法
  • Vue.js 渐进式增强:如何逐步为传统项目注入活力
  • 【深度学习】微积分
  • 移动端ui库uv-ui实现弹窗式picker选择控件
  • Node.js NativeAddon 构建工具:node-gyp 安装与配置完全指南
  • 【小游戏篇】三子棋游戏
  • Ubuntu18.04 搭建DHCP服务器
  • 08.七种排序算法实现(C语言)
  • Kafka中bin目录下面kafka-run-class.sh脚本中的JAVA_HOME
  • 浅谈APP之历史股票通过echarts绘图
  • 回归预测 | MATLAB基于TCN-BiGRU时间卷积神经网络结合双向门控循环单元多输入单输出回归预测
  • Jenkins pipline怎么设置定时跑脚本
  • js封装vue组件
  • C# OpenCV机器视觉:卡尔曼滤波
  • qt QNetworkRequest详解
  • 【springboot集成knife4j】
  • c++常见设计模式之装饰器模式