ThreadLocal数据结构、内存泄漏分析

文章目录

  • ⚽ThreadLocal
    • 🎉入门案例
    • 🎈ThreadLocal在线程中怎么存储的
    • 🎗为什么会造成内存泄漏?
    • 🎃ThreadLocalMap的key使用强引用和弱引用有什么区别呢?
    • 🔔补充说明
      • Java中引用类型分类
      • 内存泄漏和内存溢出区别

⚽ThreadLocal

用来为每个线程提供独立的变量副本的

🎉入门案例

使用案例

public class ThreadLocalMultipleExample {private static final ThreadLocal<Integer> threadLocal1 = ThreadLocal.withInitial(() -> 1);private static final ThreadLocal<String> threadLocal2 = ThreadLocal.withInitial(() -> "Hello");public static void main(String[] args) {// 线程A设置值Thread threadA = new Thread(() -> {threadLocal1.set(10);threadLocal2.set("Thread A");System.out.println("Thread A - local1: " + threadLocal1.get()); // 10System.out.println("Thread A - local2: " + threadLocal2.get()); // Thread A});// 线程B设置值Thread threadB = new Thread(() -> {System.out.println("Thread B - local1: " + threadLocal1.get()); // 1System.out.println("Thread B - local2: " + threadLocal2.get()); // Hello});threadA.start();threadB.start();}
}

运行结果:可以看到虽然线程A修改了变量的值,但是在线程B中变量的值还是初始给的值,因为这两个变量在每个线程中都有自己的副本

Thread A - local1: 10
Thread A - local2: Thread A
Thread B - local1: 1
Thread B - local2: Hello

🎈ThreadLocal在线程中怎么存储的

先来了解ThreadLocal在Thread时怎么存储的。

我们先看Thread的源码,里面有个ThreadLocalMap类型的变量。

public class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals = null;
}

从下面ThreadLocal的部分源码中可以看出,这个ThreadLocalMap类是ThreadLocal类中静态内部类。我们可以把ThreadLocalMap当做一个Map,其中ThreadLocalkey,存储的值就是value

可以看到ThreadLocalset、get方法,都是用Thread.currentThread()获取当前线程后,拿到每个线程自己独有的ThreadLocalMap之后进行读写操作,所以这里保证了每个线程都有自己的ThreadLocal副本。

public class ThreadLocal<T> {// ......省略部分代码static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}}// ......省略部分代码public T get() {Thread t = Thread.currentThread();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);}}
}

ThreadLocalMapHashMap在存储结构上有些不同,HashMap是数组+链表(+红黑树)的形式,但是ThreadLocalMap是纯数组的形式,内部只有一个Entry[] table数组,其中一个Entry就是一个键值对。

使用ThreadLocal时需要注意避免出现内存泄漏问题。

🎗为什么会造成内存泄漏?

我们从源码中可以看出ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被GC回收,这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有当thead线程退出以后,value的强引用链条才会断掉。意味着这个线程一直不结束的话,这个value就一直无法回收,造成内存泄漏,这种情况一般发生在使用线程池的场景中,因为里面的线程正常情况下会一直存活。

在平时使用ThreadLocal类时,要避免内存泄漏问题,可以在线程处理完任务后,使用threadLocal.remove()方法,移除当前threadLocal

🎃ThreadLocalMap的key使用强引用和弱引用有什么区别呢?

  • key 使用强引用:当ThreadLocalMap的key为强引用,发生GC时,因为ThreadLocalMap还持有ThreadLocal的强引用,同时ThreadLocalMap和Thread生命周期相同,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
  • key 使用弱引用:当ThreadLocalMap的key为弱引用,发生GC时,由于ThreadLocalMap持有的是ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。当key为null,在下一次其他ThreadLocal调用他们的set(),get(),remove()方法的时候都会清除key为null对应的value值。

🔔补充说明

Java中引用类型分类

  • 强引用:我们常常 new 出来的对象就是强引用类型,只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足的时候
  • 软引用:使用 SoftReference 修饰的对象被称为软引用,软引用指向的对象在内存要溢出的时候被回收
  • 弱引用:使用 WeakReference 修饰的对象被称为弱引用,只要发生垃圾回收,若这个对象只被弱引用指向,那么就会被回收
  • 虚引用:虚引用是最弱的引用,在 Java 中使用 PhantomReference 进行定义。虚引用中唯一的作用就是用队列接收对象即将死亡的通知

内存泄漏和内存溢出区别

  • 内存泄漏 是因为程序没有释放不再使用的内存,导致内存逐渐积累,最终可能引起内存溢出。
  • 内存溢出 是当程序请求的内存超出了系统可分配的最大值时,操作系统无法满足内存请求,从而导致程序崩溃。

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

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

相关文章

vs code 2024编译环境问题记录

之前vs code环境配置了好一会&#xff0c;现在将遇到的问题记录一下&#xff0c;并贴上解决方法。 在这之前&#xff0c;关键的gcc编译器竟然在Python生成exe的过程中不小心下载了Mingw64&#xff0c;然后导致gcc编译器已经安装好在某个目录下了 命令行查看发现&#xff0c;原…

linux网络编程 | c | epoll实现IO多路转接服务器

epoll实现IO多路转接服务器 可通过以下视频学习 06-opell函数实现的多路IO转接_哔哩哔哩_bilibili 通过响应式–多路IO转接实现 文章目录 epoll实现IO多路转接服务器1.思路&功能核心思路 2.代码实现multi_epoll_sever.c运行图 1.思路&功能 **功能&#xff1a;**客…

植物大战僵尸辅助【控制台版本】

前面介绍了使用CE和OD的简单使用&#xff1a;CE和OD介绍和使用CE查找阳光的教学&#xff1a;阳光基地址和偏移地址&#xff0c;下面先使用最简单的控制台程序来实现修改阳光的功能。 项目地址 1.分析程序 我们的控制台程序想要修改植物大战僵尸游戏内的数据&#xff0c;它们…

elasticsearch 使用Painless脚本

文章目录 1. 创建索引2. 插入模拟数据Painless 脚本的基本特点&#xff1a;Painless 脚本的常见用途1. 脚本查询和过滤示例&#xff1a;基于脚本的查询 2. 脚本字段示例&#xff1a;脚本字段 3. 聚合中的脚本示例&#xff1a;脚本聚合 4. 文档更新中的脚本示例&#xff1a;文档…

【Elasticsearch】高亮搜索:从原理到Web呈现

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

短视频矩阵源代码部署过程中可能遇到的常见问题及其概述

技术选型挑战 前端技术栈&#xff1a;不恰当的选择可能导致界面响应迟缓、用户交互体验不佳。例如&#xff0c;HTML、CSS、JavaScript等前端技术的运用不当&#xff0c;会影响页面的渲染速度和交互流畅性。 后端技术&#xff1a;后端开发语言的选择若与项目需求不匹配&#xff…

15.初始接口1.0 C#

这是一个用于实验接口的代码 适合初认识接口的人 【CSDN开头介绍】&#xff08;文心一言AI生成&#xff09; 在C#编程世界中&#xff0c;接口&#xff08;Interface&#xff09;扮演着至关重要的角色&#xff0c;它定义了一组方法&#xff0c;但不提供这些方法的实现。接口作为…

什么是正则化?Regularization: The Stabilizer of Machine Learning Models(中英双语)

正则化&#xff1a;机器学习模型的稳定器 1. 什么是正则化&#xff1f; 正则化&#xff08;Regularization&#xff09;是一种在机器学习模型训练中&#xff0c;通过约束模型复杂性以防止过拟合的技术。 它的核心目标是让模型不仅在训练集上表现良好&#xff0c;还能在测试集上…

Day9 神经网络的偏导数基础

多变量函数与神经网络 在神经网络中&#xff0c;我们经常遇到多变量函数。这些函数通常描述了网络的输入、权重、偏置与输出之间的关系。例如&#xff0c;一个简单的神经元输出可以表示为&#xff1a; z f ( w 1 x 1 w 2 x 2 … w n x n b ) z f(w_1x_1 w_2x_2 \ldots…

map和set题目练习

一、习题一&#xff1a;随机链表的复制 1.1题目详情 1.2思路 在没有学习map和set之前&#xff0c;解决这道题最大的问题就在于无法建立原链表与拷贝链表的映射关系&#xff0c;只能通过在原链表每个节点后面新建一个新的链表来进行节点间的对应&#xff0c;而学习了map之后&a…

Hw亮度省电

1. 亮度控制策略 /decompile-hw/decompile/app/HwPowerGenieEngine3/src/main/res/xml/backlight_policy.xml <?xml version"1.0" encoding"utf-8"?> 2 <backlight_policy xmlns:android"http://schemas.android.com/apk/res/android&qu…

C语言入门(一):A + B _ 基础输入输出

前言 本专栏记录C语言入门100例&#xff0c;这是第&#xff08;一&#xff09;例。 目录 一、【例题1】 1、题目描述 2、代码详解 二、【例题2】 1、题目描述 2、代码详解 三、【例题3】 1、题目描述 2、代码详解 四、【例题4】 1、题目描述 2、代码详解 一、【例…

【21天学习AI底层概念】day8 什么是类意识?

类意识&#xff08;Quasi-Consciousness&#xff09; 是一个用来描述人工智能或复杂系统表现出的类似意识的行为或特性的概念。虽然这种系统不具备真正的意识&#xff08;即主观体验、情感和自我觉知&#xff09;&#xff0c;但在外部表现上&#xff0c;它们可能表现出与有意识…

Docker 镜像源 阿里镜像源限制后其他镜像源

要在Docker中修改镜像源&#xff0c;你需要编辑或创建Docker的配置文件来指定新的镜像源地址。以下是如何为Docker配置中国镜像源的步骤&#xff1a; 找到或创建Docker的配置文件daemon.json。 在Linux系统中&#xff0c;该文件通常位于/etc/docker/目录下。 编辑daemon.jso…

渗透测试学习笔记(五)网络

一.IP地址 1. IP地址详解 ip地址是唯一标识&#xff0c;一段网络编码局域网&#xff08;内网&#xff09;&#xff1a;交换机-网线-pcx.x.x.x 32位置2进制&#xff08;0-255&#xff09; IP地址五大类 IP类型IP范围A类0.0.0.0 到 127.255.255.255B类128.0.0.0 到191.255.25…

《自制编译器》--青木峰郎 -读书笔记 编译hello

在该书刚开始编译hello.cb时就遇到了问题。 本人用的是wsl&#xff0c;环境如下&#xff0c; 由于是64位&#xff0c;因此根据书中的提示&#xff0c;从git上下载了64位的cb编译器 cbc-64bit 问题一: 通过如下命令编译时,总是报错。 cbc -Wa,"--32" -Wl,"-…

LruCache(本地cache)生产环境中遇到的问题及改进

问题&#xff1a;单机qps增加时请求摘要后端&#xff0c;耗时也会增加&#xff0c;因为超过了后端处理能力&#xff08;最大qps&#xff0c;存在任务堆积&#xff09;。 版本一 引入LruCache。为了避免数据失效&#xff0c;cache数据的时效性要小于摘要后端物料的更新时间&…

jedis使用及注意事项

Jedis Jedis 是一个 Java 客户端&#xff0c;用于与 Redis 数据库进行交互。它提供了一系列简单易用的 API&#xff0c;使得在 Java 应用程序中使用 Redis 变得非常方便。以下是 Jedis 的使用方法及一些注意事项。 Jedis的优势 Lettuce客户端及Jedis客户端比较如下&#xff1a;…

CSDN博客:如何使用Python的`datasets`库转换音频采样率

CSDN博客&#xff1a;如何使用Python的datasets库转换音频采样率 什么是采样率&#xff1f;代码用途&#xff1a;调整音频数据的采样率完整代码示例代码详解运行结果&#xff08;示例&#xff09;总结 在这篇文章中&#xff0c;我们将学习如何使用Python的datasets库对音频数据…

浏览器执行机制

主线程 任务1&#xff0c;任务2 微队列微队列任务1&#xff0c; 微队列任务2延时队列延时队列任务1&#xff0c; 延时队列任务2交互队列.... 事件循环的工作原理 主线程执行同步任务&#xff1a; 主线程首先执行所有同步任务&#xff08;即栈中的任务&#xff09;。这些任务会…