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

news2024/12/23 8:14:37

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

文章目录

    • 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.coloradmin.cn/o/2264137.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

为什么使用环形队列

1.看以下两种情况。第一种不会出现问题&#xff0c;当主流程读取次数比较慢时&#xff0c;数据会被覆盖。 2.扩大空间。不可取。 3.什么是队列

【WRF教程第3.6期】预处理系统 WPS 详解:以4.5版本为例

预处理系统 WPS 详解&#xff1a;以4.5版本为例 Geogrid/Metgrid 插值选项详解1. 插值方法的工作机制2. 插值方法的详细说明2.1 四点双线性插值&#xff08;four_pt&#xff09;2.2 十六点重叠抛物线插值&#xff08;sixteen_pt&#xff09;2.3 简单四点平均插值&#xff08;av…

批量提取zotero的论文构建知识库做问答的大模型(可选)——含转存PDF-分割统计PDF等

文章目录 提取zotero的PDF上传到AI平台保留文件名代码分成20个PDF视频讲解 提取zotero的PDF 右键查看目录 发现目录为 C:\Users\89735\Zotero\storage 写代码: 扫描路径‘C:\Users\89735\Zotero\storage’下面的所有PDF文件,全部复制一份汇总到"C:\Users\89735\Downl…

Java模拟Mqtt客户端连接Mqtt Broker

Java模拟Mqtt客户端基本流程 引入Paho MQTT客户端库 <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.mqttv5.client</artifactId><version>1.2.5</version> </dependency>设置mqtt配置数据 …

boost asio 异步服务器

boost网络框架使用方法 boost绑定 首先介绍io_context&#xff0c;可以理解为这是操作系统和应用层数据交互的桥梁。有了它不必关注内核态的缓冲区&#xff0c;只需要关注自己定义在用户态的缓冲区&#xff0c;因为它会通过桥梁运输到用户态的缓冲区。 boost::asio::io_contex…

图解HTTP-HTTP协议

HTTP HTTP是一种不保存状态&#xff0c;即无状态的协议。HTTP协议自身不对请求和响应之间的通信进行保存。为了保存状态因此后面也有一些技术产生比如Cookies技术。 HTTP是通过URI定位网上的资源&#xff0c;理论上将URI可以访问互联网上的任意资源。 如果不是访问特定的资源…

【Go】-限流器的四种实现方法

目录 关于限流和限流器 固定窗口限流器 滑动窗口限流器 漏桶限流器 令牌桶限流器 总结 关于限流和限流器 限流&#xff08;Rate Limiting&#xff09;是一种控制资源使用率的机制&#xff0c;通常用于防止系统过载和滥用。 限流器&#xff08;Rate Limiter&#xff09;是…

CTF_1

CTF_Show 萌新赛 1.签到题 <?php if(isset($_GET[url])){system("curl https://".$_GET[url].".ctf.show"); }else{show_source(__FILE__); }?> 和 AI 一起分析 1.if(isset($_GET[url]))检查GET请求中是否存在名为url的参数。 curl 2.curl…

[文献阅读] Unsupervised Deep Embedding for Clustering Analysis (无监督的深度嵌入式聚类)

文章目录 Abstract:摘要聚类深度聚类 KL散度深度嵌入式聚类(DEC)KL散度聚类软分配&#xff08;soft assignment&#xff09;KL散度损失训练编码器的初始化聚类中心的初始化 实验评估总结 Abstract: This week I read Unsupervised Deep Embedding for Clustering Analysis .It…

记录:virt-manager配置Ubuntu arm虚拟机

virt-manager&#xff08;Virtual Machine Manager&#xff09;是一个图形用户界面应用程序&#xff0c;通过libvirt管理虚拟机&#xff08;即作为libvirt的图形前端&#xff09; 因为要在Linux arm环境做测试&#xff0c;记录下virt-manager配置arm虚拟机的过程 先在VMWare中…

使用C语言编写UDP循环接收并打印消息的程序

使用C语言编写UDP循环接收并打印消息的程序 前提条件程序概述伪代码C语言实现编译和运行C改进之自由设定端口注意事项在本文中,我们将展示如何使用C语言编写一个简单的UDP服务器程序,该程序将循环接收来自指定端口的UDP消息,并将接收到的消息打印到控制台。我们将使用POSIX套…

Spring Boot 教程之三十六:实现身份验证

如何在 Spring Boot 中实现简单的身份验证&#xff1f; 在本文中&#xff0c;我们将学习如何使用 Spring设置和配置基本身份验证。身份验证是任何类型的安全性中的主要步骤之一。Spring 提供依赖项&#xff0c;即Spring Security&#xff0c;可帮助在 API 上建立身份验证。有很…

什么样的LabVIEW控制算自动控制?

自动控制是指系统通过预先设计的算法和逻辑&#xff0c;在无人工干预的情况下对被控对象的状态进行实时监测、决策和调整&#xff0c;达到预期目标的过程。LabVIEW作为一种图形化编程工具&#xff0c;非常适合开发自动控制系统。那么&#xff0c;什么样的LabVIEW控制算作“自动…

GFPS扩展技术原理(七)-音频切换消息流

音频切换消息流 Seeker和Provider通过消息流来同步音频切换能力&#xff0c;触发连接做切换&#xff0c;获取或设置音频切换偏好&#xff0c;通知连接状态等等。为此专门定义了音频切换消息流Message Group 为0x07&#xff0c;Message codes如下&#xff1a; MAC of Audio s…

视频直播点播平台EasyDSS与无人机技术的森林防火融合应用

随着科技的飞速发展&#xff0c;无人机技术以其独特的优势在各个领域得到了广泛应用&#xff0c;特别是在森林防火这一关键领域&#xff0c;EasyDSS视频平台与无人机技术的融合应用更是为传统森林防火手段带来很大的变化。 一、无人机技术在森林防火中的优势 ‌1、快速响应与高…

机器人路径规划和避障算法matlab仿真,分别对比贪婪搜索,最安全距离,RPM以及RRT四种算法

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1贪婪搜索算法原理 4.2最安全距离算法原理 4.3RPM 算法原理 4.4 RRT 算法原理 5.完整程序 1.程序功能描述 机器人路径规划和避障算法matlab仿真,分别对比贪婪搜索,最安全距离,RPM以及R…

【论文笔记】Visual Alignment Pre-training for Sign Language Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Visual Alignment Pre-tra…

【附源码】Electron Windows桌面壁纸开发中的 CommonJS 和 ES Module 引入问题以及 Webpack 如何处理这种兼容

背景 在尝试让 ChatGPT 自动开发一个桌面壁纸更改的功能时&#xff0c;发现引入了一个 wallpaper 库&#xff0c;这个库的入口文件是 index.js&#xff0c;但是 package.json 文件下的 type:"module"&#xff0c;这样造成了无论你使用 import from 还是 require&…

Apache解析漏洞(apache_parsingCVE-2017-15715)

apache_parsing 到浏览器中访问网站 http://8.155.8.239:81/ 我们写一个木马 1.php.jpg 我们将写好的木马上传 会得到我们上传文件的路径 我们访问一下 发现上传成功 发现木马运行成功&#xff0c;接下来使用蚁剑连接我们的图片马 获取 shell 成功 CVE-2013-454 我们还是到…

C++-----函数与库

数学中的函数与编程中的函数对比 数学中的函数 - 数学函数是一种映射关系&#xff0c;例如&#xff0c;函数\(y f(x)x^{2}\)&#xff0c;对于每一个输入值\(x\)&#xff0c;都有唯一确定的输出值\(y\)。它侧重于描述变量之间的数量关系&#xff0c;通常通过公式来表示这种关系…