ThreadLoad如何防止内存溢出
优质博文:IT-BLOG-CN
从 ThreadLocalMap看 ThreadLocal使用不当的内存泄漏问题
【1】基础概念 : 首先我们先看看ThreadLocalMap的类图,我们知道 ThreadLocal只是一个工具类,他为用户提供get、set、remove接口操作实际存放本地变量的threadLocals(调用线程的成员变量),也知道 threadLocals是一个ThreadLocalMap类型的变量,下面我们来看看ThreadLocalMap这个类。在此之前,我们回忆一下Java中的四种引用类型链接

【2】分析ThreadLocalMap内部实现: 我们知道ThreadLocalMap内部实际上是一个Entry数组private Entry[] table,我们先看看Entry的这个内部类
/*** 是继承自WeakReference的一个类,该类中实际存放的key是指向ThreadLocal的弱引用和与之对应的value值(该value值* 就是通过ThreadLocal的set方法传递过来的值)由于是弱引用,当get方法返回null的时候意味着回收引用*/
static class Entry extends WeakReference<ThreadLocal<?>> {/** value就是和ThreadLocal绑定的 */Object value;//k:ThreadLocal的引用,被传递给WeakReference的构造方法Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}
//WeakReference构造方法(public class WeakReference<T> extends Reference<T> )
public WeakReference(T referent) {super(referent); //referent:ThreadLocal的引用
}//Reference构造方法
Reference(T referent) {this(referent, null);//referent:ThreadLocal的引用
}Reference(T referent, ReferenceQueue<? super T> queue) {this.referent = referent;this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

在上面的代码中,我们可以看出,当前ThreadLocal的引用k被传递给WeakReference的构造函数,所以ThreadLocalMap中的key为ThreadLocal的弱引用。当一个线程调用ThreadLocal的set方法设置变量的时候,当前线程的 ThreadLocalMap就会存放一个记录,这个记录的key值为ThreadLocal的弱引用,value就是通过set设置的值。如果当前线程一直存在且没有调用该ThreadLocal的remove方法,如果这个时候别的地方还有对ThreadLocal的引用,那么当前线程中的ThreadLocalMap中会存在对ThreadLocal变量的引用和 value对象的引用,是不会释放的,就会造成内存泄漏。
考虑这个ThreadLocal变量没有其他强依赖,如果当前线程还存在,由于线程的ThreadLocalMap里面的key是弱引用,所以当前线程的ThreadLocalMap里面的ThreadLocal变量的弱引用在gc的时候就被回收,但是对应的value还是存在的这就可能造成内存泄漏(因为这个时候 ThreadLocalMap会存在key为 null但是value不为null的entry项 )。
ThreadLocalMap中的Entry的key使用的是ThreadLocal对象的弱引用,在没有其他地方对ThreadLocal依赖,ThreadLocalMap中的ThreadLocal对象就会被回收掉,但是对应的value不会被回收,这个时候Map中就可能存在key为null但是value不为null的项,这需要实际使用的时候使用完毕及时调用remove方法避免内存泄漏。
如果使用线程池,由于线程可能并不是真正的关闭(比如newFixedThreadPool会保持线程一只存活)。因此,如果将一些大对象存放到ThreadLocalMap中,可能会造成内存泄漏。因为线程没有关闭,无法回收,但是这些对象不会再被使用了。如果希望及时回收对象,则可以使用Thread.remove()方法将变量移除。
ThreadLocal<Object> threadLocal = new ThreadLocal<>();
// 存储数据
threadLocal.set(someData);
// 使用完毕后清除
threadLocal.remove();
我们再看下ThreadLocal底层的源码:
public T get() { //获取当前线程Thread t = Thread.currentThread(); //获取当前线程的ThreadLocalMap变量ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue();
}public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);
}public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this);
}
remove()方法逻辑比较简单,首先获取当前线程的ThreadLocalMap对象,然后循环遍历key,将目标key以及对应的value都设置为null。
private void remove(ThreadLocal<?> key) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);// 循环遍历目标key,然后将key和value都设置为nullfor (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {if (e.get() == key) {e.clear();// 清理value值expungeStaleEntry(i);return;}}
}
使用try-with-resources或try-finally块:如果你的ThreadLocal变量在需要清理的资源管理上下文中使用,可以使用try-with-resources(自动清理)或try-finally(手动清理)块来确保及时清理。
try (ThreadLocalResource resource = new ThreadLocalResource()) {// 使用 ThreadLocalResource
}
// 或者使用 try-finally
ThreadLocalResource resource = new ThreadLocalResource();
try {// 使用 ThreadLocalResource
} finally {resource.close(); // 在 close 方法中清理 ThreadLocal 变量
}
使用InheritableThreadLocal:如果需要在子线程中访问父线程的ThreadLocal变量,并且确保在子线程中正确清理,可以考虑使用InheritableThreadLocal。这个类允许子线程继承父线程的ThreadLocal变量,并在子线程完成后自动清理。
ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("Hello, Parent Thread");
Runnable childTask = () -> {String value = threadLocal.get(); // 子线程可以访问父线程的 ThreadLocal 变量// ...
};
Thread childThread = new Thread(childTask);
childThread.start();
相关文章:
ThreadLoad如何防止内存溢出
优质博文:IT-BLOG-CN 从 ThreadLocalMap看 ThreadLocal使用不当的内存泄漏问题 【1】基础概念 : 首先我们先看看ThreadLocalMap的类图,我们知道 ThreadLocal只是一个工具类,他为用户提供get、set、remove接口操作实际存放本地变…...
2024.8.19 学习记录 —— 作业
一、TCP机械臂测试 #include <myhead.h>#define SER_PORT 8888 // 与服务器保持一致 #define SER_IP "192.168.0.114" // 服务器ip地址int main(int argc, const char *argv[]) {// 创建文件描述符打开键盘文件int fd open("/dev/input/event1…...
Java 阿里云视频直播开发流程
首先来看一下直播效果 推流工具有很多种(例如OBS、阿里云直播Demo推流、等等,我用的是芯象导播)阿里播放器地址 一、直播基础服务概述 官方文档说明 二、直播域名配置需要两个域名(推流域名、播流域名) 官方文档说…...
SQLite 轻量级的嵌入式关系型数据库的替代软件
SQLite 是一个轻量级的嵌入式关系型数据库,由于其简单易用和跨平台的特性,被广泛应用于各种应用程序中。以下是一些可作为SQLite替代品的数据库软件或可视化管理工具: 1. **SQLiteStudio**:这是一个免费、开源的跨平台SQLite数据…...
Flutter-自适用高度PageView
需求 在 Flutter 中,PageView 是一个非常常用的组件,能够实现多个页面的滑动切换。然而,默认的 PageView 高度是固定的,这在展示不同高度的页面时,可能会导致不必要的空白或内容裁剪问题。为了使 PageView 能够根据每…...
群晖NAS本地搭建可远程交互的大型语言模型LLM聊天机器人
文章目录 前言1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址 前言 本文主要分享如何在群晖NAS本地部署并运行一个基于大语言模型Llama 2的个人本地聊天机器人并结合内网穿透工具…...
TypeScript 构建工具之 webpack
在实际开发中,直接使用TypeScript 编译器的情况不多。 在项目中,需要使用构建工具对代码进行打包,不可能脱离项目使用TypeScript 编译器单独打包TypeScript 。 那如何将 webpack 和 TypeScript 进行集成? 参考文档: w…...
conda环境下在pycharm中调试scrapy项目
前提条件 已经创建好了conda环境已经安装好了scrapy框架项目初始化完成 编写一个爬虫脚本 import scrapyclass StackOverflowSpider(scrapy.Spider):name stackoverflowstart_urls [http://stackoverflow.com/questions?sortvotes]def parse(self, response):print("…...
contenteditable=“true“的标签限制字数的时候修改光标位置
contenteditable"true"的标签限制字数的时候修改光标位置 有时候input和textarea并不能完全满足ui需求,这个时候我们就用contenteditable"true"来将别的标签修改为可编辑状态,但当我们通过js修改了内容之后光标的位置就是一个问题&…...
51单片机-LED灯蜂鸣器数码管按键DS18B20温度传感器
LDE灯的相关程序 LED灯闪烁 LED流水灯 方法1 方法二: 因为P1口可以直接控制P1^0~P1^7的8个led灯,利用一个8位的二进制数字来进行控制即可。如果要点亮P1^0 只需要给P1口传递 1111 1110即可。 蜂鸣器的使用 什么是蜂鸣器? 蜂鸣器是一种一…...
笔记本一线品牌有哪些
笔记本电脑的一线品牌通常指的是在市场上具有较高市场份额、良好口碑、较强的技术实力和服务能力的品牌。根据目前的信息,笔记本电脑市场的一线品牌主要包括以下几个: 联想 (Lenovo):联想在全球笔记本市场上的占有率较高,其产品线…...
mysql聚合函数和分组
我最近开了几个专栏,诚信互三! > |||《算法专栏》::刷题教程来自网站《代码随想录》。||| > |||《C专栏》::记录我学习C的经历,看完你一定会有收获。||| > |||《Linux专栏》࿱…...
ubuntu20.04+RealSenseD455
ubuntu20.04安装驱动双目相机RealSenseD455 安装环境安装RealSense SDK 2.0ROS包安装启动Realsense摄像头存在的 bugD455标定安装环境 系统:Ubuntu20.04 ROS:Noetic 视觉传感器:Intel RealSense D455 安装RealSense SDK 2.0 该安装有两种方式,一个是用命令安装,另一个是…...
WAF绕过技巧
WAF绕过技巧 WAF(Web Application Firewall)是一种安全系统,旨在监控和控制网络流量,以防止攻击,如SQL 注入、跨站脚本(XSS)和拒绝服务(DoS)。 WAF 可以通过多种方式绕过…...
HarmonyOS应用三之组件生命周期和参数传递
目录: 1、生命周期的执行顺序2、页面数据传递3、图片的读取4、数据的备份和恢复5、轮播图6、页面布局图 1、生命周期的执行顺序 /** Copyright (c) 2023 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* yo…...
[Qt][Qt 网络][上]详细讲解
目录 0.概述1.UDP Socket1.核心API概览2.回显服务器3.回显客户端 0.概述 要使用Qt中有关网络编程的API,需要添加network模块 1.UDP Socket 1.核心API概览 主要的类有两个:QUdpSocket和QNetworkDatagramQUdpSocket表⽰⼀个UDP的socket⽂件 bind(const …...
读零信任网络:在不可信网络中构建安全系统21读后总结与感想兼导读
1. 基本信息 零信任网络:在不可信网络中构建安全系统 道格巴斯(Doug Barth) 著 人民邮电出版社,2019年8月出版 1.1. 读薄率 书籍总字数252千字,笔记总字数73194字。 读薄率73194252000≈29.5% 这个读薄率是最高的吧&#x…...
Java基础——注释
在开发中注释是必不可少的,帮助我们更好的标记阅读代码,下面介绍几种常用的注释方式。 一、注释种类 1. 单行注释 使用//一行代码来进行注释,只能注释一行内容 2. 多行注释 使用斜杠星号的方式 /*注释多行代码*/,注释多行代…...
Redis未授权访问漏洞利用合集
一、基本信息 靶机:IP:192.168.100.40 攻击机:IP:192.168.100.60 二、漏洞 & 过程 Redis 未授权访问漏洞利用无口令远程登录靶机 靶机 cd redis-4.0.8/src./redis-server ../redis.conf 攻击机 ./redis-cli -h 192.168.100.40 Redis 未授权访问…...
基于asp.net的在线考试系统、基于c#的在线考试管理系统
摘 要 伴随着社会以及科学技术的发展,互联网已经渗透在人们的身边,网络慢慢的变成了人们的生活必不可少的一部分,紧接着网络飞速的发展,管理系统这一名词已不陌生,越来越多的学校、公司等机构都会定制一款属于自己个…...
Skill 不是 Prompt 模板,而是 Code Agent 的领域知识接口
很多人第一次把 Code Agent 接进老项目,都会经历一个落差: Demo 里它能十分钟写完一个 CRUD;一进真实业务系统,它开始犯一些“刚入职新人”才会犯的错。 它能看懂 Controller,却不知道这个字段为什么不能改ÿ…...
3个步骤让你的Mac原生支持200+视频格式预览
3个步骤让你的Mac原生支持200视频格式预览 【免费下载链接】QuickLookVideo This package allows macOS Finder to display thumbnails, static QuickLook previews, cover art and metadata for most types of video files. 项目地址: https://gitcode.com/gh_mirrors/ql/Qu…...
微积分入门书籍之日韩篇
微积分的奇幻旅程(2020.02) 超简单的微积分 函数、图、斜率、面积 ,一小时掌握微积分的本质(2024.03) 简单微积分 学校未教过的超简易入门技巧(2018.07) 数学女孩的秘密笔记:微分篇 数学女孩的秘密笔记:积分篇 超图解趣…...
RK3399嵌入式Linux开发:Sysfs内核虚拟文件系统深度探索与实践指南
1. 项目概述:为什么从Sysfs开始内核探索拿到一块RK3399这样的高性能开发板,无论是做产品原型还是学习嵌入式Linux,第一步往往都是“点亮”和“跑起来”。但当系统启动,命令行提示符闪烁时,很多开发者,尤其是…...
AI 测试必修课:深入理解 LLM 的 Token、上下文与温度参数
一位资深测试工程师的血泪忠告:“你花三个月搭建的测试框架崩溃了——不是因为代码逻辑有bug,而是因为昨天 temperature 是 0 的时候模型输出是‘PASS’,今天同样的代码、同样的输入,模型说‘FAIL’。你以为温度设成 0 就稳了?天真。” 这是一篇写给 AI 测试工程师、LLM 应…...
AI职业成长地图:软件测试从业者的精准发展路径
在AI技术重塑软件工程生态的当下,软件测试行业正经历从自动化到智能化的范式跃迁。2026年全球AI测试市场规模突破12亿美元,传统测试岗位需求年复合增长率不足2%,而AI测试工程师岗位增幅达45%。对于软件测试从业者而言,构建清晰的A…...
Python 中的 @property:像访问属性一样调用方法
# Python 中的 property:像访问属性一样调用方法在写类的时候,我们经常会遇到一个问题: 对象的属性如果可以被随便修改,就可能出现一些不合理的数据。比如一个人的年龄:python class Person:def __init__(self, age)…...
别再只盯着ADC了!74HC4067的另类玩法:DIY一个简易多路信号切换器与逻辑分析仪探头
74HC4067的创意实践:打造多功能信号切换与逻辑分析工具 在电子设计与调试过程中,多路信号切换和逻辑分析是两项基础但至关重要的任务。传统解决方案往往需要昂贵的专业设备,而本文将展示如何利用常见的74HC4067芯片,配合开源硬件和…...
5分钟快速上手:使用免费在线EPUB编辑器制作专业电子书
5分钟快速上手:使用免费在线EPUB编辑器制作专业电子书 【免费下载链接】EPubBuilder 一款在线的epub格式书籍编辑器 项目地址: https://gitcode.com/gh_mirrors/ep/EPubBuilder 你是否梦想过出版自己的电子书,却被复杂的EPUB格式和技术门槛吓退&a…...
【亲测免费】 ADS1118驱动程序
ADS1118驱动程序 【下载地址】ADS1118驱动程序 本仓库提供了专用于ADS1118模数转换器(ADC)的驱动程序。ADS1118是一款高性能、高精度的16位模拟到数字转换器,广泛应用于需要精准测量的应用场景中,例如传感器数据采集系统、医疗设备…...
