JAVA虚拟机实战篇之内存调优[4](内存溢出问题案例)
文章目录
- 版权声明
- 修复问题
- 内存溢出问题分类
- 分页查询文章接口的内存溢出
- 问题背景
- 解决思路
- 问题根源
- 解决思路
- Mybatis导致的内存溢出
- 问题背景
- 问题根源
- 解决思路
- 导出大文件内存溢出
- 问题背景
- 问题根源
- 解决思路
- ThreadLocal占用大量内存
- 问题背景
- 问题根源
- 解决思路
- 文章内容审核接口的内存问题
- 问题背景
- 设计1:Async异步审核
- 存在问题
- 设计2:生产者消费者模式
- 存在问题
- 设计3:Mq消息队列模式
- 问题根源和解决思路
版权声明
- 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利人所有。本博客的目的仅为个人学习和交流之用,并非商业用途。
- 我在整理学习笔记的过程中尽力确保准确性,但无法保证内容的完整性和时效性。本博客的内容可能会随着时间的推移而过时或需要更新。
- 若您是黑马程序员或相关权利人,如有任何侵犯版权的地方,请您及时联系我,我将立即予以删除或进行必要的修改。
- 对于其他读者,请在阅读本博客内容时保持遵守相关法律法规和道德准则,谨慎参考,并自行承担因此产生的风险和责任。
- 本博客中的部分观点和意见仅代表我个人,不代表黑马程序员的立场。
修复问题
内存溢出问题分类
- 修复内存溢出问题的要具体问题具体分析,问题总共可以分成三类
- 代码中的内存泄漏
- 解决方案:完善代码
- 并发引起内存溢出
- 参数不当 由于参数设置不当,比如堆内存设置过小,导致并发量增加之后超过堆内存的上限。
- 解决方案:调整参数,下一章中详细介绍
- 并发引起内存溢出 – 设计不当
- 系统的方案设计不当,比如:从数据库获取超大数据量的数据、线程池设计不当、生产者-消费者模型,消费者消费性能问题
- 解决方案:优化设计方案
分页查询文章接口的内存溢出
问题背景
- 背景:小李负责的新闻资讯类项目采用了微服务架构,其中有一个文章微服务,这个微服务在业务高峰期出现内存溢出的现象
解决思路
- 服务出现OOM内存溢出时,生成内存快照
- 使用MAT分析内存快照,找到内存溢出的对象
- 尝试在开发环境中重现问题,分析代码中问题产生的原因
- 修改代码
- 测试并验证结果
- MAT使用技巧:从线程对象入手,找到当前的处理器方法,再右键选择处理器方法的outgoing references,即可快速找到当前线程执行的方法。
问题根源
- 文章微服务中的分页接口没有限制最大单次访问条数,并且单个文章对象占用的内存量较大,在业务高峰期并发量较大时这部分从数据库获取到内存之后会占用大量的内存空间。
解决思路
- 与产品设计人员沟通,限制最大的单次访问条数
- 分页接口如果只是为展示文章列表,不需要获取文章内容,可以大大减少对象的大小
- 在高峰期对微服务进行限流保护
Mybatis导致的内存溢出
问题背景
- 小李负责的文章微服务进行了升级,新增加了一个判断id是否存在的接口,第二天业务高峰期再次出现了内存溢出,小李觉得应该和新增加的接口有关系
- 堆内存快照情况如下
问题根源
- Mybatis在使用foreach进行sql拼接时,会在内存中创建对象,如果foreach处理的数组或者集合元素个数过多,会占用大量的内存空间
解决思路
- 限制参数中最大的id个数
- 将id缓存到redis或者内存缓存中,通过缓存进行校验
导出大文件内存溢出
问题背景
- 小李负责的一个管理系统,使用的是k8s将管理系统部署到容器中,这个管理系统支持几十万条数据的excel文件导出。他发现系统在运行时如果有几十个人同时进行大数据量的导出,会出现内存溢出。
问题根源
- Excel文件导出如果使用POI的XSSFWorkbook,在大数据量(几十万)的情况下会占用大量的内存。
解决思路
-
使用poi的SXSSFWorkbook(不推荐)
@GetMapping("/export")public void export(int size, String path) throws IOException {// 1 、创建工作薄Workbook workbook = new XSSFWorkbook();// 2、在工作薄中创建sheetSheet sheet = workbook.createSheet("测试");for (int i = 0; i < size; i++) {// 3、在sheet中创建行Row row0 = sheet.createRow(i);// 4、创建单元格并存入数据row0.createCell(0).setCellValue(RandomStringUtils.randomAlphabetic(1000));}// 将文件输出到指定文件FileOutputStream fileOutputStream = null;try {fileOutputStream = new FileOutputStream(path + RandomStringUtils.randomAlphabetic(10) + ".xlsx");workbook.write(fileOutputStream);} catch (Exception e) {e.printStackTrace();} finally {if (fileOutputStream != null) {fileOutputStream.close();}if (workbook != null) {workbook.close();}}}
-
hutool提供的BigExcelWriter减少内存开销(推荐)
//http://www.hutool.cn/docs/#/poi/Excel%E5%A4%A7%E6%95%B0%E6%8D%AE%E7%94%9F%E6%88%90-BigExcelWriter@GetMapping("/export_hutool")public void export_hutool(int size, String path) throws IOException {List<List<?>> rows = new ArrayList<>();for (int i = 0; i < size; i++) {rows.add( CollUtil.newArrayList(RandomStringUtils.randomAlphabetic(1000)));}BigExcelWriter writer= ExcelUtil.getBigWriter(path + RandomStringUtils.randomAlphabetic(10) + ".xlsx");// 一次性写出内容,使用默认样式writer.write(rows);// 关闭writer,释放内存writer.close();}
-
使用阿里巴巴easy excel,对内存进行大量的优化(推荐)
//https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write#%E9%87%8D%E5%A4%8D%E5%A4%9A%E6%AC%A1%E5%86%99%E5%85%A5%E5%86%99%E5%88%B0%E5%8D%95%E4%B8%AA%E6%88%96%E8%80%85%E5%A4%9A%E4%B8%AAsheet@GetMapping("/export_easyexcel")public void export_easyexcel(int size, String path,int batch) throws IOException {// 方法1: 如果写到同一个sheetString fileName = path + RandomStringUtils.randomAlphabetic(10) + ".xlsx";// 这里注意 如果同一个sheet只要创建一次WriteSheet writeSheet = EasyExcel.writerSheet("测试").build();// 这里 需要指定写用哪个class去写try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {// 分100次写入for (int i = 0; i < batch; i++) {// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> datas = new ArrayList<>();for (int j = 0; j < size / batch; j++) {DemoData demoData = new DemoData();demoData.setString(RandomStringUtils.randomAlphabetic(1000));datas.add(demoData);}excelWriter.write(datas, writeSheet);//写入之后datas数据就可以释放了}}}
ThreadLocal占用大量内存
问题背景
- 小李负责了一个微服务,但是他发现系统在没有任何用户使用时,也占用了大量的内存。导致可以使用的内存大大减少
问题根源
- 很多微服务会选择在拦截器preHandle方法中去解析请求头中的数据,并放入一些数据到ThreadLocal中方便后续使用。
解决思路
- 在拦截器的afterCompletion方法中,必须要将ThreadLocal中的数据清理掉。
import com.itheima.jvmoptimize.practice.demo.common.UserDataContextHolder; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;/*** 拦截器的实现,模拟放入数据到threadlocal中*/ public class UserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {UserDataContextHolder.userData.set(new UserDataContextHolder.UserData());return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserDataContextHolder.userData.remove();} }
文章内容审核接口的内存问题
问题背景
- 文章微服务中提供了文章审核接口,会调用阿里云的内容安全接口进行文章中文字和图片的审核,在自测过程中出现内存占用较大的问题
设计1:Async异步审核
- 使用SpringBoot中的@Async注解进行异步的审核
存在问题
- 线程池参数设置不当,会导致大量线程的创建或者队列中保存大量的数据。
- 任务没有持久化,一旦走线程池的拒绝策略或者服务宕机、服务器掉电等情况很有可能会丢失任务
设计2:生产者消费者模式
- 使用生产者和消费者模式进行处理,队列数据可以实现持久化到数据库。
- 保存文章服务层实现代码
@Override public void saveArticle(ArticleDto article) {BUFFER_QUEUE.add(article);int size = BUFFER_QUEUE.size();if( size > 0 && size % 10000 == 0){System.out.println(size);} }
- 线程池配置代码
@Configuration @EnableAsync public class ThreadPoolTaskConfig {public static final BlockingQueue<ArticleDto> BUFFER_QUEUE = new LinkedBlockingQueue<>(2000);private static final int corePoolSize = 50; // 核心线程数(默认线程数)private static final int maxPoolSize = 100; // 最大线程数private static final int keepAliveTime = 10; // 允许线程空闲时间(单位:默认为秒)private static final int queueCapacity = 200; // 缓冲队列数private static final String threadNamePrefix = "Async-Service-"; // 线程池名前缀@Bean("taskExecutor")public ThreadPoolTaskExecutor getAsyncExecutor(){ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSize);executor.setMaxPoolSize(Integer.MAX_VALUE);//executor.setMaxPoolSize(maxPoolSize);executor.setQueueCapacity(queueCapacity);executor.setKeepAliveSeconds(keepAliveTime);executor.setThreadNamePrefix(threadNamePrefix);// 线程池对拒绝任务的处理策略executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 初始化executor.initialize();return executor;} }
- 审核文章代码
@Component public class ArticleSaveTask {@Autowired@Qualifier("taskExecutor")private ThreadPoolTaskExecutor threadPoolTaskExecutor;@PostConstructpublic void pullArticleTask(){for (int i = 0; i < 50; i++) {threadPoolTaskExecutor.submit((Runnable) () -> {while (true){try {ArticleDto data = BUFFER_QUEUE.take();/*** 获取到队列中的数据之后,调用第三方接口审核数据,但是此时网络出现问题,* 第三方接口长时间没有响应,此处使用休眠来模式30秒*/Thread.sleep(30 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}});}} }
存在问题
- 队列参数设置不正确,会保存大量的数据。
- 实现复杂,需要自行实现持久化的机制,否则数据会丢失
设计3:Mq消息队列模式
- 使用mq消息队列进行处理,由mq来保存文章的数据。发送消息的服务和拉取消息的服务可以是同一个,也可以不是同一个
- 生产者代码
@Autowired private RabbitTemplate rabbitTemplate; @Autowired private ObjectMapper objectMapper;@PostMapping("/demo3/{id}") public void article3(@PathVariable("id") long id, @RequestBody ArticleDto article) throws JsonProcessingException {article.setId(id);rabbitTemplate.convertAndSend("jvm-test",null, objectMapper.writeValueAsString(article)); }
- 消费者代码
@Component public class SpringRabbitListener {@RabbitListener(queues = "queue1",concurrency = "10")public void listenSimpleQueue(String msg) throws InterruptedException {System.out.println(msg);Thread.sleep(30 * 1000);} }
问题根源和解决思路
- 在项目中如果要使用异步进行业务处理,或者实现生产者 – 消费者的模型,如果在Java代码中实现,会占用大量的内存去保存中间数据。
- 尽量使用Mq消息队列,可以很好地将中间数据单独进行保存,不会占用Java的内存。同时也可以将生产者和消费者拆分成不同的微服务
相关文章:

JAVA虚拟机实战篇之内存调优[4](内存溢出问题案例)
文章目录 版权声明修复问题内存溢出问题分类 分页查询文章接口的内存溢出问题背景解决思路问题根源解决思路 Mybatis导致的内存溢出问题背景问题根源解决思路 导出大文件内存溢出问题背景问题根源解决思路 ThreadLocal占用大量内存问题背景问题根源解决思路 文章内容审核接口的…...

qt自定义时间选择控件窗口
效果如图: 布局如图: 参考代码: //DateTimeSelectWidget #ifndef DATETIMESELECTWIDGET_H #define DATETIMESELECTWIDGET_H#include <QWidget> #include <QDateTime>namespace Ui { class DateTimeSelectWidget; }class DateTim…...
如何不解压直接读取gzip文件里面的文件
要在服务器上不解压缩的情况下读取gzip文件中的文件内容,您可以使用类似于zlib模块的库,这些库允许您在内存中对gzip数据进行操作而无需解压缩到磁盘上的文件。 在Python中,您可以使用gzip模块来实现这一目的。以下是一个示例代码࿰…...

python 截取字符串string.split
目录 作用语法只要第一个值获得第3个值遍历 作用 根据某个符号对数据进行截取 从而获得自己想要的内容 语法 使用’string.split’ 方法 对字符串’123/abc/BPYC’ 以 ‘/’ 进行截取 string "123/abc/BPYC" substring string.split("/") print(subs…...

SpringBoot+Vue实现el-table表头筛选排序(附源码)
👨💻作者简介:在笑大学牲 🎟️个人主页:无所谓^_^ ps:点赞是免费的,却可以让写博客的作者开心好几天😎 前言 后台系统对table组件的需求是最常见的,不过element-ui的el…...

Linux学习之线程
目录 线程概念 1.什么是线程? 2.线程的优缺点 3.线程异常 4.线程用途 线程操作 1.如何给线程传参 2.线程终止 3.获取返回值 4.分离状态 5.退出线程 线程的用户级地址空间: 线程的局部存储 线程的同步与互斥 互斥量mutex 数据不一致的主要过…...

【JavaEE初阶】 JVM类加载简介
文章目录 🍃前言🌲类加载过程🚩加载🚩验证🚩准备🚩解析🚩初始化 🎄双亲委派模型🚩什么是双亲委派模型?🚩双亲委派模型的优点 ⭕总结 🍃…...
.NET Core依赖注入(IoC)不使用构造函数实现注入
在.NET Core中,依赖注入(IoC)通常是通过构造函数注入来实现的,这是推荐的方式,因为它使得依赖关系更加明确和可测试。但是,如果你不想或不能使用构造函数注入,你可以考虑使用方法注入࿰…...

WinSCP下载安装并结合内网穿透实现固定公网TCP地址访问本地服务器
文章目录 1. 简介2. 软件下载安装:3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件,它的主要功能是在本地与远程计…...

内联函数|auto关键字|范围for的语法|指针空值
文章目录 一、内联函数1.1概念1.2特性 二、auto关键字2.2类型别名思考2.3auto简介2.4auto使用细则2.4 auto不能推导的场景 三、基于范围的for循环(C11)3.1 范围for的语法 四、指针空值nullptr(C11)4.1 C98中的指针空值 所属专栏:C初阶 一、内联函数 1.1概念 以inline修饰的函…...

家用洗地机哪个型号好用?介绍几个值得考虑的品牌
作为家里的主要清洁工,我一直以来都是负责家里的清洁工作。我经常使用吸尘器和扫地机器人来轮流清洁,虽然效果还不错,但是这种方式太费时间和精力了。特别是在脸上厨房里做完饭和孩子吃完饭后留下的残渣时,我总是需要用传统的拖多…...

力扣-数组题
1. 两数之和 找出map中是否有target-nums[i], class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> hash;for(int i 0 ;i < nums.size(); i){if(hash.find(target - nums[i]) ! hash…...
将List转换为数组或者将数组转换为List,如果改变了原始值,转换后的数据会发生改变吗?
将List转换为数组或将数组转换为List涉及到数据结构的变化。在Java中,这两种转换是否会影响原始数据取决于转换的方式和使用的数据结构。下面分别解释这两种情况: 将List转换为数组 当你将一个List转换为数组时,通常通过List的toArray()方法…...

七彩虹@电脑cpu频率上不去问题@控制中心性能模式cpu频率上不去@代理服务器超时@账户同步设置失败
文章目录 windows电脑cpu频率上不去新电脑的系统时间问题系统时间不准造成的具体问题举例代理超时vscode同步请求失败自动校准时间 windows电脑cpu频率上不去 问题描述,标压处理器的笔记本,cpu频率上不去 如果cpu没问题的话,就应该是系统限制了功耗导致的有的笔记本有控制中心…...
抖音怎么开店?抖音小店开店流程讲解,可收藏!
大家好,我是电商糖果 想在抖音上开一家小店,卖点东西,赚点儿辛苦钱。 如何正确的开通抖音小店呢? 这篇文章就给大家详细的讲解一下,帮大家规避掉一些百分之九十九的商家都会踩的坑。 近期开店的朋友,这…...

leetcode 热题 100_轮转数组
题解一: 新数组存储:另外用一个数组存储移动后的结果,再复制回原数组。 class Solution {public void rotate(int[] nums, int k) {int[] result new int[nums.length];for (int i 0; i < nums.length; i) {result[(i k) % nums.lengt…...

华为设备小型园区网方案(有线+无线+防火墙)
(一)配置有线部分 1.配置LSW2 (1)创建相关vlan [LSW2]vlan batch 10 3000 (2)配置连接LSW1的Eth-Trunk1,透传VLAN 10 3000 [LSW2]int Eth-Trunk 1 [LSW2-Eth-Trunk1]port link-type trunk [LSW2…...

硬件工程师入门基础知识(四)多层陶瓷电容应用(一)
多层陶瓷电容应用(一) 1.多层陶瓷电容器在电子电路中的主要作用以及对应的典型电路图有哪些?1.1 滤波电容1.2 退耦电容1.3 旁路电容1.4 耦合电容1.5 积分电容1.6 微分电容2.多层瓷介电容器能否超类别温度使用?3.瓷介电容器的工作电压如何选择?1.多层陶瓷电容器在电子电路中…...

python的虚拟环境
python的虚拟环境可以为项目创建一个独立的环境,能够解决使用不同版本依赖给项目带来冲突的麻烦。创建虚拟环境的方式有很多种,pipenv会自动帮你管理虚拟环境和依赖文件,并且提供了一系列命令和选项来帮忙你实现各种依赖和环境管理相关的操作…...

设计模式——2_4 中介者(Mediator)
我寄愁心与明月,随风直到夜郎西 ——李白《闻王昌龄左迁龙标遥有此寄》 文章目录 定义图纸一个例子:怎么调度一组地铁站台和地铁开车指挥中心 碎碎念中介者和表单平台思想但是这种平台便利性是要付出代价的变化隔离原则 姑妄言之 定义 用一个中介者对象…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...