JVM内存泄漏和内存溢出的原因

2023-11-07

1 概念

  • 内存泄漏:
    分配出去的内存没有被回收回来,失去对内存区域的控制,造成资源的浪费,比如:new出来了对象并没有引用,垃圾回收器不会回收他,造成内存泄漏
  • 内存溢出:
    程序所需要的内存超出了系统所能分配的内存。

2 分析内存溢出可能出现的地方

从 Java代码的运行过程来看,有三个区域会发生 OOM,它们分别是:Metaspace、Java 虚拟机栈、堆内存。

Java栈

虚拟机栈,每执行一个方法都会有一个栈帧入栈,栈帧中包含参数、局部变量、返回值地址等信息。如果代码层次太深,不断有方法入栈却没有出栈,Java虚拟机栈就会OOM。

  1. 虚拟机中的栈内存也是有限的,我们调用方法的时候会创建一个栈帧,紧接着方法入栈。如果一个线程一直调用方法入栈,栈内存终归是要满的,此时线程的栈中就会发生 OOM。
  2. 发生这种情况一般就是代码除了问题,比如写了个递归调用,和 Metaspace 的内存溢出一样,也很少发生。
  3. 如果在单线程的情况下,无论是栈帧太大还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机抛出的是StackOverflowError异常。
  4. 如果在多线程下,不断地建立线程可能会产生OutOfMemoryError异常。

Metaspace

保存类的基本信息,如果加载太多类就会 OOM

永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。

回收废弃常量与回收 Java 堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串"abc"已经进入了常量池中,但是当前系统没有任何一个 String 对象是叫做"abc"的,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个"abc"常量就会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。

注意:在大量使用反射、动态代理、CGLib 等 ByteCode 框架、动态生成 JSP 以及 OSGi这类频繁自定义 ClassLoader 的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。

堆中创建的对象过多就会触发GC,GC 的速度赶不上新建对象的速度也会发生 OOM。

  1. 高并发场景下,请求量太大,创建了大量新的对象,且这些都是有用的、存活的。堆中无法放入更多对象就会导致堆内存溢出
  2. 内存泄漏问题,长生命周期的对象引用了大量短生命周期的对象,没有及时取消对它们的引用,导致 GC 无法回收这些理应被回收的对象,就导致了堆内存溢出
  3. Java堆中只会产生OutOfMemoryError异常。

注意:类需要同时满足下面 3 个条件才能算是“无用的类”

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
  • 加载该类的 ClassLoader 已经被回收。
  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足上述3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样,不使用了就必然会回收。

注意:方法区溢出方法区中只会产生OutOfMemoryError异常。

2 分析内存泄漏的原因

原因:

长生命周期对象持有短生命周期对象的引用可能会引起内存泄漏

1、静态集合类:容器使用时引起的内存泄漏

HashMap、Vector等很容易出现内存泄漏,

集合被定义成静态的时候,由于它们的生命周期跟应用程序一样长

他们引用的所有对象Object不能被释放,如果将Object对象置为null,也还是会被Vector引用,可以将Vector对象置为null,切出对静态集合类的引用。

Vector vector = new Vector();
for (int i = 1; i<100; i++) {
    Object object = new Object();
    vector.add(object);
    object = null;
}
//这样会造成内存泄漏
//...对vector的操作
//...与vector无关的其他操作这样会造成短暂的内存泄漏,method方法结束后被回收,
//...对vector的操作
vector = null;
//...与vector无关的其他操作

2、各种连接时:未正确使用close()方法导致的内存泄漏

各种IO或者数据库连接时,最后都需要close()释放对象,这样也是长对象引用短对象,造成的内存泄漏。

SessionFactory factory = new SessionFactory();
try {Session session = factory.connect();
} finally{
    session.close();
}

这里必须用close关闭连接,因为SessionFactory是长对象,session是短对象。

3、外部模块的引用

调用外部模块的时候,也应该注意防止内存泄漏。如模块A调用了外部模块B的一个方法,如:public void register(Object o)。这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了去除引用的方法,如unregister()

4、单例模式

使用单例模式的时候也有可能导致内存泄漏。因为单例对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象(生命周期比较短)的引用,那么这个外部对象就不能被回收,而导致内存泄漏。如果这个外部对象还持有其它对象的引用,那么内存泄漏会更严重

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
    	this.context = context;
    }
    public static AppManager getInstance(Context context) {
   	 if (instance != null) {
   		 instance = new AppManager(context);
   	 }
   	 return instance;
    }
}

这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:

  • 如果此时传入的是 Application 的 Context,因为 Application 的生命周期就是整个应用的生命周期,所以这将没有任何问题。

  • 如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会被回收,这就造成泄漏了。

正确的方式应该改为下面这种方式:

public class AppManager {
    private static AppManager instance;
    private Context context; 
    private AppManager(Context context) { 
   		 // 使用Application 的context 
   		 this.context = context.getApplicationContext();
    } 
    public static AppManager getInstance(Context context) { 
   	 if (instance != null) { 
   	 		instance = new AppManager(context); 
   	 } 
   	 return instance; 
    } 
}

或者这样写,连 Context 都不用传进来了:
在你的 Application 中添加一个静态方法,getContext() 返回 Application 的 context,

... 
context = getApplicationContext(); 
...
 //获取全局的context - - >return返回全局context对象
 public static Context getContext(){
	 return context;
 }
 
 public classAppManager{
     private static AppManager instance;
     private Context context;
     private AppManager() {
   		  // 使用Application 的context
   		  this.context = MyApplication.getContext();
     }
     public static AppManager getInstance() {
   		  if (instance != null) {
   			  instance = new AppManager();
   		  }
   		  return instance;
     }
 }

如何解决以及监控JVM内存、CPU的情况,请关注下篇文章。。。

更多文章和干活请关注公众号

本文作者:Java技术债务
原文链接:https://www.cuizb.top/myblog/article/1642654909
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM内存泄漏和内存溢出的原因 的相关文章

  • 尝试使用 Eclipse 启动 Glassfish 服务器时出现 org.apache.catalina.LifecycleException

    我一直忙于使用 angularjs 前端构建一个 REST 应用程序 使用 MAVEN jersey quickstart webapp 使用 GLASSFISH Web 服务器在 Eclipse 上开发 今天 当我开始对项目进行一些开发时
  • 如何使用 SLF4J 和 Log4j2 记录 FATAL(或任何自定义日志级别)

    我有那些具体的要求 需要能够登录FATAL level 需要使用SLF4J 需要使用Log4j2 现在 这是我的执行 final Logger logger LoggerFactory getLogger HelloWorld class
  • 在 Kotlin 中实现返回 Collection 的 Java 方法

    我将 Kotlin 与 Spring Security 结合使用 实现该方法时 public interface UserDetails extends Serializable Collection
  • Spring webflow 应用程序:HTTP 302 暂时移动

    我的 java 应用程序中的每个请求都会生成另外 2 个带有 HTTP 302 错误的请求 例如 如果请求查看名为板 html 这个请求是从首页 html 我收到按以下顺序生成的 3 个请求 POST home html 302 Moved
  • 使用 Java 编程式 HTML 文档生成

    有谁知道如何在 Java 中以编程方式生成 HTMLDocument 对象 而不需要在外部生成字符串 然后使用 HTMLEditorKit read 来解析它 我问的两个原因 首先 我的 HTML 生成例程需要非常快 并且我认为将字符串解析
  • 方法不必要地被调用?

    我有一个 BaseActivity 它可以通过其他所有活动进行扩展 问题是 每当用户离开 暂停 活动时 我都会将音乐静音 我也不再接听电话 问题是 onPause每当用户在活动之间切换时就会被调用 这意味着应用程序不必要地静音和停止tele
  • 如何解决错误:java.lang.ClassNotFoundException:io.netty.util.concurrent.GenericFutureListener?

    昨天我第一次尝试用 Java 制作 Prometheus 客户端 从 Python 开始 最后是 GoLang 是否找到示例 import io prometheus client Counter import io prometheus
  • 在气球内显示带有照片的多个地标的最佳做法是什么?

    我有一个项目如下 从手机上拍摄几张照片 将照片保存在网络系统中 然后将照片显示在其中的谷歌地球上 我读过很多文章 但它们都使用 fetchKml 我读过的一篇好文章是使用 php 但使用 fetchKml 我不知道是否可以使用 parseK
  • 如何在 OpenAPI 3.0 中定义字节数组

    我正在将 API 从 Swagger 2 0 迁移到 OpenAPI 3 0 在 DTO 中 我有一个指定为字节数组的字段 Swagger 对 DTO 的定义 Job type object properties body type str
  • firestore快照监听器生命周期和定价之间有什么关系?

    在我的活动中 我有一个字符串列表 这些字符串表示我想要附加快照侦听器的 Firestore 文档 我使用 Acivity ModelView 存储库结构 在活动的 onCreate 中 我向 ViewModelProvider 询问适当的
  • JSP 标签+ scriptlet。如何启用脚本?

    我有一个使用标签模板的页面 我的 web xml 非常基本 我只是想在页面中运行一些代码 不 我对标签或其他替代品不感兴趣 我想使用不好的做法 scriptlet 哈哈 到目前为止 我收到了 HTTP ERROR 500 错误 Script
  • Vertx HttpClient getNow 不工作

    我的 vertx HttpClient 有问题 下面的代码显示使用 vertx 和纯 java 测试 GET Vertx vertx Vertx vertx HttpClientOptions options new HttpClientO
  • Java G1 GC 处理引用对象运行缓慢

    我已经在 J ava 上运行了计数器 它24小时工作 每秒点击通过100次左右 白天 GC 处理时间从 20 60 毫秒缓慢上升到 10000 60000 毫秒 然后下降到 20 60 毫秒 这种模式不时地重复 从 GC 日志中我发现 GC
  • Java中的DRY原则[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我一直在读关于DRY https en wikipedia org wiki Don 27t repeat yourself原则 虽然看起来
  • 存储过程将多个表返回到 spring jdbc 模板

    我正在使用 JdbcTemplate 从 Spring DAO 类调用存储过程 我的问题是 存储过程返回多个表 有没有办法使用 Spring JdbcTemplate 访问多个表 如果我使用jdbcTemplate queryForList
  • java中的比较器链

    正在阅读Oracle 关于接口的 Java 教程 https docs oracle com javase tutorial java IandI createinterface html其中给出了一个例子Card 打牌 我试图理解接口中的
  • 将字符串中的字符向左移动

    我是 Stack Overflow 的新手 有一道编程课的实验室问题一直困扰着我 该问题要求我们将字符串 s 的元素向左移动 k 次 例如 如果输入是 Hello World 和3 它将输出 lo WorldHel 对于非常大的 k 值 它
  • 防止 Firebase 中的待处理写入事务不起作用

    我的目标是在单击按钮时将名称插入 Cloud Firestore 中 但如果用户未连接到互联网 我不希望保存处于挂起状态 我不喜欢 Firebase 保存待处理写入的行为 即使互联网连接已恢复 我研究发现Firebase 开发人员建议使用事
  • JDK 7 的快速调试/调试构建

    我正在寻找 JDK 的调试 或者我猜他们称之为快速调试构建 以启用在运行时生成的打印程序集以及查找性能问题时所需的其他诊断 就目前情况而言 我似乎找不到可以直接使用的 现成的 快速调试构建二进制包 有人可以帮我提供下载链接 或者至少提供有关
  • Java泛型类型

    当我有一个界面时 public interface Foo

随机推荐

  • FileReader与FileWriter

    FileReader与FileWriter分别继承Reader和Writer 以字符为单位广泛用于文件操作的节点流 FileReader类用于从文本文件读数据 每次读入一个字符或者一个字符数组 FileWriter类用于从文本文件写数据 每
  • 学python的第十四天---小蓝(5)

    一 最长公共子序列 dp 二 蓝桥骑士 最长递增子序列 三 蓝肽子序列 最长公共子序列 四 合唱队形 最长递增子序列 五 字符串编辑问题 引入一个难一点的题目 最优包含 一 最长公共子序列 dp Maxn 1005 dp 0 for in
  • 英寸和厘米的换算python_运用python实现英制单位英寸与公制单位厘米互换

    原标题 运用python实现英制单位英寸与公制单位厘米互换 python能实现的东西有很多 其中就有运用python来实现英制单位英寸与公制单位厘米互换 那么具体应用怎么换算呢 下面羽忆教程网为你提供运用python实现英制单位英寸与公制单
  • 【cocosStudio】查看官方示例--调用cocoStudio控件

    理解 将cocoStudio的ui控件转换成cocos的GUI控件来操作 通过widgetFromJsonFile 加载根容器Plane控件 通过Helper seekWidgetByTag 寻找子控件 打开官方示例 打开CMD CD到D
  • Java远程调试(Remote Debug)可导致远程命令执行漏洞

    2019独角兽企业重金招聘Python工程师标准 gt gt gt Java远程调试 Remote Debug 可导致远程命令执行漏洞 注释startup sh第2行 bin bash declare x CATALINA OPTS ser
  • css元素定位:通过元素的标签或者元素的id、class属性定位

    前言 大部分人在使用selenium定位元素时 用的是xpath元素定位方式 因为xpath元素定位方式基本能解决定位的需求 xpath元素定位方式更直观 更好理解一些 css元素定位方式往往被忽略掉了 其实css元素定位方式也有它的价值
  • python2.7下input()函数输入参数

    使用input 函数 在运行时输入字符需要用引号括起来 但在交互模式下不需要 question Traceback most recent call last File
  • 从零编写c++之http服务器(3)-http服务

    http全称超文本传输协议 可调试性高 扩展性也强 上两个篇章我们已经拥有了epoll事件驱动框架和线程池处理网络事件 接下来我们要先写一个基础网络套接字 然后在此基础上扩展出http的套接字 献上类图如下 完整源码见
  • 也要适当晋升

    和一个哥们聊天 说到要不要当组长这个话题 我说我被无良组长们坑坏了 自己搞不定老往下属身上推 我如果当组长 肯定也是无良组长 水平菜 成为自己讨厌的人 他说 哥们 你还是不懂职场规则啊 别说让当 就是不让当 也要创造条件去当 公司资源可以提
  • connect to host master port 22: No route to host 问题解决方案

    问题描述 connect to host master port 22 No route to host 问题分析 先去ifconfig能不能出现你自己配的网卡 一般情况是不能的 或者重启网卡会启动失败 当然了使用远程连接工具也会连接不上
  • h5微信公众号分享:错误40048,invalid url domain与错误63002,invalid signature问题解决

    1 错误 63002 config fail Error 系统错误 错误码 63002 invalid signature 20201207 15 10 10 1 发送当前接口请求的页面URL需要跟后台协商 是否需要编码 以什么样的形式发送
  • 【Docker】Swarm集群的配置与管理

    实现Docker Swarm集群部署安装 在线创建Swarm集群和自动编配 安装Docker Swarm 1 通过实训平台进入到操作系统界面 该实验需要两台虚拟机 首先修改主机名 修改第一台主机的主机名叫smoker 第二台主机的主机名叫j
  • 【Maki ‘ s Lab学习讲座】超前学习法

    作者 Maki Maki的完美算术教室 排版 Penguin IT鹅 当人们愉快地承受苦难时 苦难也会变得美丽 这不是麻木 而是由于心灵的伟大 亚里士多德 Maki s Lab简介 Maki s Lab核心成员来自多伦多大学 清华大学等世界
  • 咕咕驴AI短视频换脸小程序源码搭建教程

    咕咕驴AI短视频一键换脸小程序源码 带流量主搭建教程 源码下载 https github com peizhou faceoff 下载源码到本地后 修改appid 修改为你自己的微信小程序 打开 dist pages ele index j
  • python笔记7--常见异常处理

    python笔记7 常见异常处理 功能 代码 说明 异常处理是提高程序稳定 可靠的一个重要因素 笔者最近整理了下一常用的python异常处理方法 以及使用案例 记录在此处以便后续查阅 功能 包括raise try except两中常见异常处
  • 软件设计七大原则

    一 开闭原则定义 一个软件实体如类 模块函数应该对扩展开放 对修改关闭 是其他原则的基础或者说是总宗旨 其他原则可以说是此原则的一个延伸 说人话 不修改现有代码的基础上 去新增功能 二 依赖倒置原则定义 高层模块不应该依赖低层模块 二者都应
  • 头文件中string、string.h和cstring的区别

    string和string h的区别 h后缀都是c的头文件 与其相对应的不加 h的都是c 的头文件 在c 标准化的过程中 为了表示头文件来源于c 有时也在前面加上c 比如cmath就来源于math h 但是string和string h没有
  • Windows10子系统WSL修改默认安装目录到其他盘

    WSL修改默认安装目录到其他盘 1 查看WSL分发版本 2 导出分发版为tar文件到d盘 3 注销当前分发版 4 重新导入并安装WSL在d wsl ubuntu20 04 5 设置默认登陆用户为安装时用户名 6 删除tar文件 可选 7 结
  • tcp头、三次握手、四次挥手

    1 tcp头 1 源端口和目的端口 找到目标进程 实现分用 2 序号 seq tcp传送的字节流 每个都按顺序编号 首部中的seq是本报文段中首个字节的序号 3 确认号 ack 两个作用 确认收到报文段和下一次希望收到对方报文段首字节编号
  • JVM内存泄漏和内存溢出的原因

    文章目录 1 概念 2 分析内存溢出可能出现的地方 Java栈 Metaspace 堆 2 分析内存泄漏的原因 1 静态集合类 容器使用时引起的内存泄漏 2 各种连接时 未正确使用close 方法导致的内存泄漏 3 外部模块的引用 4 单例