JVM性能优化一:初识内存泄露-内存溢出-垃圾回收

本文主要是让你充分的认识到什么叫做内存泄露,什么叫做内存溢出,别再傻傻分不清了,别再动不动的升级服务器的内存了。

文章目录

    • 1.基本概念
      • 1.1.内存泄露
      • 1.2.内存溢出
      • 1.3.垃圾回收
      • 1.4.内存泄露-垃圾回收-内存溢出三者的关系关系
    • 2.代码示例
      • 2.1.示例1:模拟逐渐占用内存-释放内存
      • 2.2.示例2:模拟逐渐占用内存-不释放内存(内存泄露)
      • 2.3.示例3:模拟逐渐占用内存-释放内存(内存溢出)

1.基本概念

1.1.内存泄露

内存泄漏是指程序申请了内存后,不再使用某些内存空间,但未能正确释放,导致这部分内存无法被再次利用。虽然系统可能还有足够的内存供其他操作使用,但长期累积会逐渐耗尽可用内存,最终可能导致内存溢出。

1.2.内存溢出

当应用程序请求的内存超出了 JVM 分配的最大堆内存时,出现OOM异常

1.3.垃圾回收

当应用程序释放占用内存以后,JVM会按照自己的策略对已经释放的内存进行回收。并不是说程序执行结束,立马就进行内存回收,而是会在适当的时机进行回收。这意味着即使方法执行结束,内存的释放也可能会延迟,直到 JVM 认为有必要进行垃圾回收。

1.4.内存泄露-垃圾回收-内存溢出三者的关系关系

当存在内存溢出后,会导致内存逐渐被占用,而此时垃圾回收机制无法对这些占用的内存进行回收,由于内存不会被释放,导致可使用内存越来越少。如果最后内存无法满足接下来或者正在运行的代码所需要的内存,就会发生内存溢出。总结就是下面三条

  1. 内存泄露:逐步吞噬内存
  2. 垃圾回收:无法回收内存
  3. 内存溢出:内存不够分配

2.代码示例

以下代码,并未设置相关JVM参数,全是默认,每个人的JDK,内存都不一样,有人内存8G、16G、32G都有可能,因此可以视情况调整下面的循环次数,这里我是循环3000 * 10000次

2.1.示例1:模拟逐渐占用内存-释放内存

以下代码,就是一个很常见的,需求大概就是创建很多对象,然后把这个对象添加到集合中。为了演示效果,中间加了暂停效果,Thread.sleep。

/*** @description:内存泄露demo* @author:hutao* @throws InterruptedException * @mail:hutao1@epri.sgcc.com.cn*/
@GetMapping("/memory/leak")
public String leak() throws InterruptedException {log.info("开始调用/memory/leak");List<UserVO> list = new ArrayList<>();for (int i = 0; i < 3000 * 10000; i++) {if(i % (300 * 10000) == 0) {Thread.sleep(500);}UserVO temp = new UserVO();temp.setUserId("ID_"+ i);temp.setUserName("胡涛_" + i);temp.setUserAge(i);list.add(temp);}log.info("结束调用/memory/leak");return "leakTest";
}

启动我们的java程序以后,等待一段时间波动以后,观察到内存的占用率此时呈现一条水平线
在这里插入图片描述
接着通过浏览器调用我们上面的接口,在观察内存使用率,可以发现大概使用了5个G的内存,也就是说,该接口在被调用的时候,居然就使用了5个G的内存。
http://127.0.0.1:8080/demo1/memory/leak
在这里插入图片描述
然后持续观察一段时间以后,我们不难发现,好像内存没有释放哎?等了好久也没等到内存释放。怎么回事?这个代码是不是存在内存泄露的问题?
在这里插入图片描述
这时候别着急,思考一下,如果你没理解咱们上面说的基本概念,你大概会想
第一次调用就占用了5个G,第二次调用不就占用5个G了,第三次调用就内存溢出了。然后当你连续几次调用的时候,你就会发现,咦,咋回事?怎么没有继续占用内存啊,怎么内存呈现波浪形,一会占用,一会释放了。上面代码为啥没有出现我们最终设想内存溢出?到底为啥?
在这里插入图片描述
这时候你在看这句话是不是理解了?

1.内存泄露:逐步吞噬内存
2.垃圾回收:无法回收内存
3.内存溢出:内存不够分配

之所以没有出现内存溢出,为啥?因为垃圾回收机制,回收到了内存。然后内存被释放了。所以内存会介于被占用,被释放之间来回跳转。
这里提一句
假设你和我在代码中一样,想要方法结束调用以后,内存直接释放,写了如下这个代码

System.gc();

在这里插入图片描述
我的IDE提示我,当然提示语有点侮辱人,不要让我以为我比JVM还聪明,建议我删掉该代码,最主要的是,你发现没暖用,并没有出现程序执行完毕,立马释放内存的情况。

Don't try to be smarter than the JVM, remove this call to run the garbage collector.

2.2.示例2:模拟逐渐占用内存-不释放内存(内存泄露)

上面,我们模拟了会让JVM在他认为该回收垃圾的时候,去回收垃圾,然后释放内存,接下来,我们模拟一个JVM无法释放内存的例子,最红内存溢出OOM错误。
这里改动一下代码。仅需要两行代码,就能阻止垃圾回收机制释放内存,然后导致最后内存溢出。
添加一个cache的属性,并且该属性一直引用我们每次调用接口创建的list

private Map<String, List<UserVO>> cache = new HashMap<>();cache.put(UUID.randomUUID().toString(), list);

完整代码如下。思路就是把每次接口调用的数据都往cache里面存储。
你可以这样粗鲁的理解:list往cache里面不停的存放,最后导致cache越来越大,最后内存不够

你也可以这样正规的理解下:虽然方法中局部变量list不在使用,但是list被cache引用,而cache是demo1Controller(Spring创建的Controller对象) 对象的属性,demo1Controller一直被Spring引用,Spring一直在整个web引用程序中,因此cache相当于在整个web程序的生命周期都有效.

Spring 框架中,Controller 对象默认是单例的。
这意味着每个 Controller 类在 Spring 容器中只会有一个实例
所有的请求都会共享这个实例。
@RestController
@RequestMapping("/demo1")
@Log4j2
public class Demo1Controller {private Map<String, List<UserVO>> cache = new HashMap<>();/*** @description:内存泄露demo* @author:hutao* @throws InterruptedException * @mail:hutao1@epri.sgcc.com.cn*/@GetMapping("/memory/leak")public String leak() throws InterruptedException {log.info("开始调用/memory/leak");List<UserVO> list = new ArrayList<>();for (int i = 0; i < 3000 * 10000; i++) {if(i % (300 * 10000) == 0) {Thread.sleep(500);}UserVO temp = new UserVO();temp.setUserId("ID_"+ i);temp.setUserName("胡涛_" + i);temp.setUserAge(i);list.add(temp);}cache.put(UUID.randomUUID().toString(), list);log.info(cache.keySet());log.info("结束调用/memory/leak");return "leakTest";}
}

可以看到,如下所示,虽然没有出现内存一直持续暴涨的情况,但是如我们期待的那样,内存没有被释放,并且出现了OOM:Java heap space错误。如果你和我一样运行了代码,你可能会遇到,此时电脑特别卡,很卡。卡的要死。
在这里插入图片描述

2.3.示例3:模拟逐渐占用内存-释放内存(内存溢出)

在示例2中,我们不难发现,当我们第一次调用的时候,程序正常的,而程序在第二次调用的时候,内存并没有有释放,所以内存必然不够,因此第二次请求的时候,内存就不够分配了,因此导致内存溢出。

提示:虽然实际上还有接近5G物理内存,但是并不会把所有内存都分配给JVM这里的内存不够指,JVM分配到的内存不够

为了方便演示,这里我们开始引入JVM的一下参数说明

-Xms512m -Xmx1g-Xmx:设置 Java 堆的最大值。默认值通常为物理内存的四分之一。建议根据物理内存大小和其他内存开销来调整此值。
-Xms:设置 Java 堆的初始值。对于服务器端的 JVM,最好将此值与 -Xmx 设置为相同,以避免在运行时频繁调整内存。

在IDE启动中中,添加JVM参数,这里我设置,最大为1g
在这里插入图片描述
为了方便观察,这里我们记录一下内存使用情况

/*** @description:内存溢出demo* @author:hutao* @throws InterruptedException * @mail:hutao1@epri.sgcc.com.cn*/
@GetMapping("/memory/oom")
public String oom() throws InterruptedException {log.info("开始调用/memory/oom");log.info("最大可用内存:{}",Runtime.getRuntime().maxMemory());List<UserVO> list = new ArrayList<>();for (int i = 0; i < 3000 * 10000; i++) {if(i % (300 * 10000) == 0) {log.info("当前占用内存:{},当前空闲内存:{}", Runtime.getRuntime().totalMemory(),Runtime.getRuntime().freeMemory());Thread.sleep(500);}UserVO temp = new UserVO();temp.setUserId("ID_"+ i);temp.setUserName("胡涛_" + i);temp.setUserAge(i);list.add(temp);}log.info("结束调用/memory/oom");return "oomTest";
}

通过下面的截图,不难发现几个问题
1通过手动限制最大内存以后,第一次调用就内存溢出
2内存并没有像之前一样,直接占用了5g,而是按照我们分配的占用
3可以看到占用的内存逐渐增加,最终占满内存,无法给予程序所需要的内存
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/493797.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装milvus以及向量库增删改操作

首先电脑已经安装了docker windows电脑可下载yml文件 https://github.com/milvus-io/milvus/releases/download/v2.4.6/milvus-standalone-docker-compose.yml 创建milvus文件夹&#xff0c;并在这个目录下创建五个文件夹&#xff1a;conf、db、logs、pic、volumes、wal 然后…

ARP..

ARP 0 前言 真正接触到现网才发现ARP十分重要&#xff0c;无论是排错还是S-MLAG都需要用到ARP这个协议&#xff0c;以前对于ARP的理解比较混乱&#xff1b;所以这次对其中的主要内容做个梳理&#xff1b;一定要学好ARP&#xff01;&#xff01;&#xff01; 1 ARP的概念 Ar…

单片机上电后程序不运行怎么排查问题?

1.电源检查。使用电压表测量单片机的电源电压是否正常&#xff0c;确保电压在规定的范围内&#xff0c;如常见的5V。 2.复位检查。检查复位引脚的电压是否正常&#xff0c;在单片机接通电源时&#xff0c;复位引脚通常会有一个高电平&#xff0c;按下复位按钮时&#xff0c;复位…

SKETCHPAD——允许语言模型生成中间草图,在几何、函数、图算法和游戏策略等所有数学任务中持续提高基础模型的性能

概述 论文地址&#xff1a;https://arxiv.org/pdf/2406.09403 素描是一种应用广泛的有效工具&#xff0c;包括产生创意和解决问题。由于素描能直接传达无法用语言表达的视觉和空间信息&#xff0c;因此从古代岩画到现代建筑图纸&#xff0c;素描在世界各地被用于各种用途。儿童…

Linux应用开发————mysql数据库表

mysql数据库表操作 查看表的结构 mysql> desc / describe 表名; 或者&#xff1a; mysql> show create table 表名; 常见数据库引擎&#xff1a; innodb, myISAM... 删除表 mysql> drop tabl…

【C#】实现Json转Lua (Json2Lua)

关键词: C#、JsonToLua、Json2Lua、对象序列化Lua 前提需引入NewtonsofJson&#xff0c;引入方法可先在Visual Studio 2019 将Newtonsoft.Json.dll文件导入Unity的Plugins下。 Json格式字符串转Lua格式字符串&#xff0c;效果如下&#xff1a; json字符串 {"1": &q…

2.4 网络概念(分层、TCP)

网络层与传输层概述 网络层&#xff1a; 抽象概念&#xff1a;网络层是基于 IP 的抽象概念&#xff0c;与数据链路层用 MAC 地址标记设备不同。MAC 地址是一种具体化的概念&#xff0c;绑定于所在的物理网络&#xff0c;而 IP 地址可以是固定的&#xff0c;也可以通过路由动态…

LabVIEW伸缩臂参数监控系统

LabVIEW开发伸缩臂越野叉车参数监控系统主要应用于工程机械中的越野叉车&#xff0c;以提高车辆的作业效率和故障诊断能力。系统通过PEAK CAN硬件接口和LabVIEW软件平台实现对叉车作业参数的实时监控和故障分析&#xff0c;具有良好的实用性和推广价值。 系统组成 系统主要由P…

多目标优化常用方法:pareto最优解

生产实际中的许多优化问题大都是多目标问题&#xff0c;举个例子&#xff1a;我们想换一份工资高、压力小、离家近的新工作&#xff0c;这里工资高、压力小、离家近就是我们的目标&#xff0c;显然这是一个多目标问题&#xff0c;那我们肯定想找到这三个目标同时最优的工作&…

跨站脚本攻击的多种方式——以XSS-Labs为例二十关详解解题思路

一、XSS-Labs靶场环境搭建 1.1、XSS介绍 跨站脚本攻击&#xff08;XSS&#xff09;_跨站脚本测试-CSDN博客https://coffeemilk.blog.csdn.net/article/details/142266454 1.2、XSS-Labs XSS-Labs是一个学习XSS攻击手法的靶场&#xff0c;方便我们系统性的学习掌握跨站脚本攻击…

【win10+RAGFlow+Ollama】搭建本地大模型助手(教程+源码)

一、RAGFlow简介 RAGFlow是一个基于对文档深入理解的开源RAG&#xff08;Retrieval-augmented Generation&#xff0c;检索增强生成&#xff09;引擎。 主要作用&#xff1a; 让用户创建自有知识库&#xff0c;根据设定的参数对知识库中的文件进行切块处理&#xff0c;用户向大…

54、库卡机器人轴的软限位设置

步骤1&#xff1a;将用户组改为“专家”。 步骤2&#xff1a;点击“投入运行”----“售后服务”-----“软件限位开关” 步骤3&#xff1a;就可以针对每个轴修改对应的角度值&#xff0c;然后点击“保存”。

0基础学前端-----CSS DAY9

0基础学前端-----CSS DAY9 视频参考&#xff1a;B站Pink老师 今天是CSS学习的第九天&#xff0c;今天开始的笔记对应Pink老师课程中的CSS第四天的内容。 本节重点&#xff1a;常见网页布局以及清除浮动 2. 常见网页布局 2.1 常见网页布局 有以下三种&#xff1a; 参考代码…

【自动化】Python SeleniumUtil 工具 开启开发者模式 自动安装油猴用户脚本等

【自动化】Python SeleniumUtil 工具 【Python】使用Selenium 操作浏览器 自动化测试 记录-CSDN博客文章浏览阅读58次。文章浏览阅读42次。【附件】Selenium chromedriver 驱动及浏览器下载。【附件】Selenium chromedriver 驱动及浏览器下载-CSDN博客。3.安装Chrome浏览器驱动…

UE5 移植Editor或Developer模块到Runtime

要将源码中的非运行时模块移植到Runtime下使用&#xff0c;个人理解就是一个解决编译报错的过程&#xff0c;先将目标模块复制到项目的source目录内&#xff0c;然后修改模块文件夹名称&#xff0c;修改模块.build.cs与文件夹名称保持一致 修改build.cs内的类名 &#xff0c;每…

全志H618 Android12修改doucmentsui选中图片资源详情信息

背景: 由于当前的文件管理器在我们的产品定义当中,某些界面有改动的需求,所以需要在Android12 rom中进行定制以符合当前产品定义。 需求: 进入file文件管理器后,点击选中图片资源,选中功能按钮,获取信息,不显示“调试信息(仅开发者)”;现状是,获取信息,显示“调试信…

【WPS安装】WPS编译错误总结:WPS编译失败+仅编译成功ungrib等

WPS编译错误总结&#xff1a;WPS编译失败仅编译成功ungrib等 WPS编译过程问题1&#xff1a;WPS编译失败错误1&#xff1a;gfortran: error: unrecognized command-line option ‘-convert’; did you mean ‘-fconvert’?解决方案 问题2&#xff1a;WPS编译三个exe文件只出现u…

Spring(二)---基于注解的方式实现Bean管理和注入属性

目录 引入 什么是注解 Spring针对Bean管理中创建对象提供的注解 用注解的方式创建对象 ①&#xff1a;编写接口和实现类 ②&#xff1a;在需要管理的类上添加Component注解&#xff08;上边四个都可以&#xff09; ③&#xff1a;编写配置文件&#xff0c;重点是开启注解…

集成自然语言理解服务,让应用 “听得懂人话”

如今&#xff0c;应用程序智能化已成趋势&#xff0c;开发者想要实现智能化&#xff0c;那么首先需要赋予应用理解自然语言的能力&#xff0c;使其能够准确地听懂人话&#xff0c;进而响应用户需求&#xff0c;并提供一系列智能化服务。比如用户语音控制应用程序帮忙订票&#…

Redis常见阻塞原因总结

O(n) 命令 Redis 中的大部分命令都是 O(1)时间复杂度&#xff0c;但也有少部分 O(n) 时间复杂度的命令&#xff0c;例如&#xff1a; KEYS *&#xff1a;会返回所有符合规则的 key。HGETALL&#xff1a;会返回一个 Hash 中所有的键值对。LRANGE&#xff1a;会返回 List 中指定…