当前位置: 首页 > news >正文

Ehcache Java 缓存框架

详解

下图是 Ehcache 在应用程序中的位置:

Ecache 是一个广泛使用的 Java 缓存框架,能够有效提升应用性能,并减少与后端数据库的交互次数。它采用了一系列高级缓存策略,包括内存缓存、磁盘缓存、分布式缓存等,并提供了丰富的 API 和工具类,可以方便地完成缓存的读写和管理。快速:Ecache 采用了一系列高效的缓存策略,能够实现快速的数据访问和读写,从而提高应用程序的性能。可扩展:Ecache 支持分布式缓存,可以方便地扩展到多台服务

一、Ehcache 简

Ehcache 是一个广泛使用的 Java 缓存框架,能够有效提升应用性能,并减少与后端数据库的交互次数。它采用了一系列高级缓存策略,包括内存缓存、磁盘缓存、分布式缓存等,并提供了丰富的 API 和工具类,可以方便地完成缓存的读写和管理。

Ehcache 主要有以下特点:

  • 快速:Ehcache 采用了一系列高效的缓存策略,能够实现快速的数据访问和读写,从而提高应用程序的性能。
  • 可扩展:Ehcache 支持分布式缓存,可以方便地扩展到多台服务器上,从而提高系统的容错性和吞吐量。
  • 可靠性高:Ehcache 内置了多种缓存策略,支持数据持久化和恢复,同时还提供了完善的故障检测和纠正机制,从而保证了缓存数据的可靠性和稳定性。
  • 易于使用:Ehcache 提供了丰富的 API 和工具类,可以方便地完成缓存的读写和管理。同时,它还与多种框架和技术集成,如 Spring、Hibernate、MyBatis 等,使得用户可以更加便捷地使用 Ecache。

二、工作原理

1. 缓存写入

当应用程序向 Ehcache 中写入数据时,Ehcache 会首先检查该数据是否已经存在于缓存中。如果数据已经存在于缓存中,则更新该数据;否则,将该数据写入缓存。以下是 Ehcache 缓存写入的详细流程。

  1. 当应用程序请求写入一个数据项到 Ehcache 中时,这个数据项被传递给Ehcache API。

  2. Ehcache首先根据该数据项的键值对定位其对应的Cache对象。

  3. Ehcache根据配置中的缓存策略,比如是否应该在缓存中创建一个新的元素,以及新元素是否会淘汰老的元素。

  4. 如果需要创建一个新缓存元素,则Ehcache创建一个新的元素并将其添加到Cache对象中。

  5. 如果缓存中已经存在该元素,Ehcache会根据缓存策略对该元素进行更新或替换。

  6. Ehcache将更新后的Cache对象返回给应用程序。

2. 缓存查找

当应用程序需要查询缓存中的数据时,Ehcache 首先会检查该数据是否存在于缓存中。如果数据存在于缓存中,则直接返回缓存数据;否则,从数据库或其他资源获取数据,并将其存入缓存中。以下是 Ehcache 缓存查找的详细流程。

  1. 当应用程序请求从 Ehcache 中读取一个数据项时,这个请求被传递给Ehcache API。

  2. Ehcache首先根据该数据项的键值对定位其对应的Cache对象。

  3. Ehcache检查该数据项是否已经存在于缓存中。

  4. 如果数据项存在于缓存中,Ehcache可以直接将其返回给应用程序。

  5. 如果数据项不存在于缓存中,Ehcache就需要从数据库或其他数据源(如文件、网络等)中获取数据。

  6. 获取到数据后,Ehcache会将其添加到缓存中并返回给应用程序。

3. 缓存过期和驱逐

Ehcache 提供了多种缓存失效策略,例如基于时间的缓存失效、基于访问的缓存失效、基于大小的缓存失效等。当缓存数据过期或缓存空间不足时,Ehcache 会选择一部分缓存元素进行驱逐以腾出更多的内存空间。以下是 Ehcache 缓存过期和驱逐的详细流程。

  1. Ehcache会周期性地扫描缓存中的元素来标记那些已经过期的元素。

  2. Ehcache根据缓存策略(如基于时间、基于访问、基于大小等)判断哪些缓存元素应该被驱逐。

  3. 驱逐过程通常是异步执行的,Ehcache会在后台线程上执行这个操作。

4. 缓存持久化

Ehcache 还提供了缓存持久化功能,它可以将缓存中的数据持久化到磁盘或者其他数据源。在应用程序重启或者缓存失效后,Ehcache 可以从持久化存储中恢复数据,从而保证数据的完整性和可靠性。以下是 Ehcache 缓存持久化的详细流程。

  1. Ehcache使用磁盘存储或数据库等持久化技术来存储缓存数据。

  2. 当缓存中的数据更新时,Ehcache会自动将此数据持久化到持久化存储中。

  3. 在应用程序重启或者缓存失效后,Ehcache会从持久化存储中读取缓存数据并重新加载到内存中。

三、Ehcache 的基本使用

Spring Boot 中集成 Ehcache 缓存代码示例。

1. 添加 Ehcache 依赖

首先需要在项目的 pom.xml 文件中添加 Ehcache 缓存依赖,如下所示:

<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.9.3</version>
</dependency>
2. 配置 Ehcache

在 application.yml 或 application.properties 配置文件中添加 Ehcache 的配置信息,例如:

spring:ehcache:config: classpath:ehcache.xml

在上述配置中,我们指定了 Ehcache 的配置文件为 classpath:ehcache.xml,这样 Spring Boot 在启动时会自动加载该配置文件。

接下来,需要在 src/main/resources 目录下添加 ehcache.xml 配置文件,并编写相应的 Ehcache 配置信息。例如,下面是一个简单的 Ehcache 配置文件:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.ehcache.org/v3"xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd"><cache alias="myCache"><key-type>java.lang.String</key-type><value-type>java.lang.String</value-type><expiry><ttu value="10m"/></expiry><heap>100</heap></cache></config>

在上述配置中,我们定义了一个名为 myCache 的缓存,配置了键值类型、过期时间、最大存储数量等参数。

3. 使用 Ehcache

在完成 Ehcache 的配置后,就可以在 Spring Boot 中使用 Ehcache 缓存了。Spring Boot 提供了 @Cacheable@CachePut@CacheEvict 等注解来简化缓存操作,同时也支持基于缓存管理器的编程方式。

3.1 基于注解的缓存

首先介绍一下基于注解的缓存。Spring Boot 中,可以通过 @Cacheable@CachePut 和 @CacheEvict 注解来实现对缓存的读写和删除操作。

3.1.1 @Cacheable 注解

@Cacheable 注解用于指定方法的返回值可被缓存,同时也可以指定缓存的 key 和 cacheNames,例如:

@Service
public class UserService {@Cacheable(cacheNames = "myCache", key = "#userId")public User getUserById(String userId) {System.out.println("get user by id: " + userId);return new User(userId, "张三");}}

在上述代码中,我们使用 @Cacheable 注解将 getUserById() 方法返回值进行缓存,并指定了缓存的 key 为 userId,缓存名称为 myCache

3.1.2 @CachePut 注解

@CachePut 注解用于更新缓存中的数据,例如:

@Service
public class UserService {@CachePut(cacheNames = "myCache", key = "#user.id")public User updateUser(User user) {System.out.println("update user: " + user);return user;}}

在上述代码中,我们使用 @CachePut 注解更新了缓存中键为 user.id 的缓存数据。

3.1.3 @CacheEvict 注解

@CacheEvict 注解用于清除缓存中的数据,例如:

@Service
public class UserService {@CacheEvict(cacheNames = "myCache", key = "#userId")public void deleteUserById(String userId) {System.out.println("delete user by id: " + userId);}}

在上述代码中,我们使用 @CacheEvict 注解清除了缓存中键为 userId 的缓存数据。

3.2 基于缓存管理器的编程方式

除了注解方式外,还可以基于缓存管理器的编程方式来实现对缓存的读写和删除操作。Spring Boot 中,可以使用 CacheManager 和 Cache 接口来实现缓存的管理和操作。

3.2.1 注入缓存管理器

使用基于缓存管理器的编程方式,首先需要注入使用的缓存管理器,例如:

@Service
public class UserService {@Autowiredprivate CacheManager cacheManager;}

在上述代码中,我们注入了 CacheManager 类型的 cacheManager 实例。

3.2.2 获取缓存实例

获取缓存实例的方式也很简单,可以通过缓存管理器的 getCache() 方法获取:

@Service
public class UserService {@Autowiredprivate CacheManager cacheManager;public User getUserById(String userId) {Cache cache = cacheManager.getCache("myCache");Element element = cache.get(userId);if (element != null) {System.out.println("get user by id from cache: " + userId);return (User) element.getObjectValue();}System.out.println("get user by id from db: " + userId);User user = new User(userId, "张三");cache.put(new Element(userId, user));return user;}}

在上述代码中,我们使用缓存管理器的 getCache() 方法获取名为 myCache 的缓存实例。然后,我们使用缓存实例的 get() 方法获取指定 key 对应的缓存元素,如果获取到了元素,则直接返回缓存数据;否则,从数据库中获取数据,并将其存入缓存中。

3.2.3 缓存操作

除了读取缓存数据外,我们还可以使用缓存实例的 put() 方法向缓存中添加数据,使用 remove() 方法删除缓存数据,例如:

@Service
public class UserService {@Autowiredprivate CacheManager cacheManager;public User updateUser(User user) {Cache cache = cacheManager.getCache("myCache");cache.put(new Element(user.getId(), user));return user;}public void deleteUserById(String userId) {Cache cache = cacheManager.getCache("myCache");cache.remove(userId);}}

在上述代码中,我们分别使用了缓存实例的 put() 和 remove() 方法对缓存数据进行了更新和清除操作。

四、 Ehcache 的高级用法

1. 缓存策略

Ehcache 提供了多种缓存策略,可以根据实际需求选择合适的策略。其中,最常用的包括:

  • LRU(Least Recently Used):移除最近最少使用的缓存项。
  • LFU(Least Frequently Used):移除最不经常使用的缓存项。
  • FIFO(First In, First Out):先进先出,即移除最早加入的缓存项。
  • TTL(Time To Live):根据缓存项的过期时间来判断是否要移除该项。
  • 随机替换:随机选择一项进行移除。

在 Ehcache 中,默认使用的是 LRU 策略。如果需要更改策略,可以在 ehcache.xml 配置文件中进行设置。

2. 内存缓存和磁盘缓存

Ehcache 支持将缓存数据同时存储在内存和磁盘上,并根据实际情况调整数据的存储位置。例如,可以将经常访问的缓存数据存储在内存中,而不常使用的数据则存储在磁盘上,从而达到提高缓存效率和降低成本的目的。

在 ehcache.xml 配置文件中,可以通过 <heap><offheap> 和 <disk> 标签来定义缓存存储的位置。例如,下面的配置将缓存数据存储在内存中,并限制最大存储数量为 100 个:

<cache alias="myCache"><key-type>java.lang.String</key-type><value-type>java.lang.String</value-type><heap>100</heap>
</cache>

如果需要将部分缓存数据存储在磁盘上,可以添加如下配置:

<cache alias="myCache"><key-type>java.lang.String</key-type><value-type>java.lang.String</value-type><heap>10</heap> <!-- 最多存储 10 个缓存项到堆中 --><offheap>1MB</offheap> <!-- 最多存储 1MB 缓存项到堆外内存中 --><diskPersistent>true</diskPersistent> <!-- 开启磁盘持久化,即使程序重启后,缓存仍然有效 --><diskSpoolBufferSizeMB>30</diskSpoolBufferSizeMB> <!-- 磁盘缓存区大小,缓存数据先以临时文件的形式写入磁盘缓存区,待达到一定阈值后再写入磁盘 --><diskExpiryThreadIntervalSeconds>120</diskExpiryThreadIntervalSeconds> <!-- 磁盘数据过期检测间隔时间,单位为秒 --><maxEntriesLocalDisk>1000</maxEntriesLocalDisk> <!-- 磁盘缓存最大存储数量 -->
</cache>
3. 分布式缓存

Ehcache 提供了分布式缓存的支持,并可以通过 RMI、JMS、Spring 等方式进行实现。在分布式环境下,每个缓存节点都可以通过网络访问远程节点上的缓存数据,从而实现数据共享和分布式缓存的目的。

在 ehcache.xml 配置文件中,可以添加 <cacheManagerPeerProviderFactory> 和 <cacheManagerPeerListenerFactory> 标签来定义缓存节点的配置信息。例如,下面的配置定义了一个名为 myCache 的缓存,采用了 RMI 方式实现分布式缓存:

<cache-manager-peer-provider-factory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="peerDiscovery=automatic,multicastGroupAddress=230.0.0.1,multicastGroupPort=4446,timeToLive=32"/><cache-manager-peer-listener-factory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"properties="port=40001,socketTimeoutMillis=2000"/><cache alias="myCache"><key-type>java.lang.String</key-type><value-type>java.lang.String</value-type><diskPersistent>true</diskPersistent><diskSpoolBufferSizeMB>30</diskSpoolBufferSizeMB><maxEntriesLocalDisk>1000</maxEntriesLocalDisk><cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true"/>
</cache>

在上述配置中,我们首先定义了一个 RMI 的缓存管理器提供者工厂 RMICacheManagerPeerProviderFactory 和一个 RMI 的缓存管理器监听器工厂 RMICacheManagerPeerListenerFactory,并分别指定了自动发现、组播地址、端口等相关参数。

接着,我们定义了名为 myCache 缓存添加了一个 cacheEventListenerFactory 属性,用于定义缓存事件的处理方式,即使用 RMICacheReplicatorFactory 实现缓存数据的复制和同步。

需要注意的是,分布式缓存的配置和使用与单机缓存有所不同,同时也需要考虑到网络通信等问题,因此更加复杂和耗费资源。在选择是否需要使用分布式缓存时,需要权衡其带来的性能、复杂性和成本等因素。

相关文章:

Ehcache Java 缓存框架

详解 下图是 Ehcache 在应用程序中的位置&#xff1a; Ecache 是一个广泛使用的 Java 缓存框架&#xff0c;能够有效提升应用性能&#xff0c;并减少与后端数据库的交互次数。它采用了一系列高级缓存策略&#xff0c;包括内存缓存、磁盘缓存、分布式缓存等&#xff0c;并提供了…...

详解Spring IoCDI(二)

目录 承接上文&#xff1a;详解Spring IoC&DI &#xff08;一&#xff09; 1.IoC详解 1.1方法注解Bean 1.2方法注解要配合类注解使用 1.3定义多个对象 1.4重命名Bean 1.5扫描路径 2.DI详解 2.1DI与IoC的关系 2.2属性注入 2.3构造方法注入 2.4Setter注入 2.5 三…...

说明白计算机网络之TCP的流量控制与拥塞控制之慢开始算法与拥塞避免算法

TCP的流量控制 利用滑动窗口实现流量控制 设A向B发送数据&#xff0c;连接建立时候&#xff0c;B告诉A自身的接收窗口大小&#xff0c;A的发送窗口大小不能超过接收方B的窗口大小 流量控制&#xff1a;发送方发送速率不要太快&#xff0c;要让接收方来得及接收。窗口大小的单…...

这款信创FTP软件,可实现安全稳定的文件传输

信创&#xff0c;即信息技术应用创新&#xff0c;2018年以来&#xff0c;受“华为、中兴事件”影响&#xff0c;国家将信创产业纳入国家战略&#xff0c;并提出了“28n”发展体系。“8”具体指金融、石油、电力、电信、交通、航空航天、医院、教育等主要行业。目前企业使用比较…...

代码随想录算法训练营第十天|232.用栈实现队列、225. 用队列实现栈

232.用栈实现队列 题目链接&#xff1a;232. 用栈实现队列 文档讲解&#xff1a;代码随想录 状态&#xff1a;写出来 &#xff0c;但差强人意 思路&#xff1a; 定义两个容器&#xff0c;可以是Stack&#xff0c;也可以是Deque&#xff0c;stackIn相当于临时容器,用来存放元素&…...

STM32 IIC协议

本文代码使用 HAL 库。 文章目录 前言一、什么是IIC协议二、IIC信号三、IIC协议的通讯时序1. 写操作2. 读操作 四、上拉电阻作用总结 前言 从这篇文章开始为大家介绍一些通信协议&#xff0c;包括 UART&#xff0c;SPI&#xff0c;IIC等。 UART串口通讯协议 SPI通信协议 一、…...

Java生成随机数的几种方式

随机数&#xff0c;在一些特殊场景下&#xff0c;是非常常用的。比如一些测试和验证场景、安全加密、随机抽样等都有随机数的‘身影’。 一、 使用java.util.Random类 java.util.Random类提供了更全面的随机数生成功能&#xff0c;包括随机整数、随机浮点数、随机布尔值等。 p…...

【面试】什么是Java虚拟机

目录 1. 说明2. 关键点 1. 说明 1.Java虚拟机&#xff08;Java Virtual Machine&#xff0c;简称JVM&#xff09;是运行所有Java程序的抽象计算机&#xff0c;是Java语言的运行环境。2.JVM是Java平台无关性的关键&#xff0c;它允许Java程序在任何支持JVM的硬件和操作系统上运…...

Go 语言的基本构成、要素与编写规范

Go 语言&#xff0c;作为由 Google 开发的现代编程语言&#xff0c;以其简洁、高效和并发编程能力而著称。在构建高性能分布式系统和现代软件开发中&#xff0c;Go 语言正日益受到欢迎。本篇文章将详细探讨 Go 语言程序结构的各个要素&#xff0c;包括函数定义、注释规范、数据…...

从了解到掌握 Spark 计算框架(二)RDD

文章目录 RDD 概述RDD 组成RDD 的作用RDD 算子分类RDD 的创建1.从外部数据源读取2.从已有的集合或数组创建3.从已有的 RDD 进行转换 RDD 常用算子大全转换算子行动算子 RDD 算子综合练习RDD 依赖关系窄依赖宽依赖宽窄依赖算子区分 RDD 血统信息血统信息的作用血统信息的组成代码…...

香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试(三)

整期笔记索引 香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试&#xff08;一&#xff09; 香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试&#xff08;二&#xff09; 香橙派OrangePi AIpro上手笔记——之USB摄像头目标检测方案测试&#xff08;…...

【git】常用命令

删除 删除本地分支&#xff1a; // 删除本地分支 git branch -d localBranchName 删除远程仓库分支 git push origin --delete <branch_name> 验证远程分支是否删除 git fetch -p //会清理已经删除的远端分支的引用 git branch -r //列出所有远端分支&#xff0…...

JavaWeb_MySQL数据库

数据库&#xff1a; MySQL数据模型&#xff1a; MySQL是关系型数据库。 SQL&#xff1a; 简介 分类&#xff1a; 数据库设计-DDL 对数据库操作&#xff1a; 表操作&#xff1a; 小练习&#xff1a; 创建下表 SQL代码&#xff1a; create table tb_user (id int primar…...

中国BI步入增长大周期,腾讯云ChatBI加速AI+BI融合

过去十年&#xff0c;大数据技术的快速发展&#xff0c;让数据消费前进一大步&#xff0c;数据价值得到一定程度的挖掘与释放&#xff0c;真正开启了“用数”的大时代。但数据分析繁杂的技术栈、复杂的处理过程以及程式化的交互方式&#xff0c;让“数据消费”的门槛始终降不下…...

揭秘Python:下划线的特殊用法,你绝对想不到!

在Python编程中&#xff0c;下划线&#xff08;underscore&#xff09;是一个常见而又强大的工具。它不仅仅是一个普通的字符&#xff0c;而是具有特殊含义和用法的符号。今天&#xff0c;我们就来揭开Python下划线的神秘面纱&#xff0c;探索它的各种妙用。 下划线的基本用法…...

深入探索Java世界中的Jackson魔法:玩转JsonNode

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 揭秘Jackson库&#xff1a;JSON处理的瑞士军刀 在Java的世界里&#xff0c;处理JSON数据就像是一场探险。幸运的是&#xff0c;Jackson库就像一把多功能的瑞士军刀&#xff0c;为提供了强大而灵活的工具来解析和操作…...

为什么要使用动态代理IP?

一、什么是动态代理IP&#xff1f; 动态代理IP是指利用代理服务器来转发网络请求&#xff0c;并通过不断更新IP地址来保护访问者的原始IP&#xff0c;从而达到匿名访问、保护隐私和提高访问安全性的目的。动态代理IP在多个领域中都有广泛的应用&#xff0c;能够帮助用户…...

【PB案例学习笔记】-09滚动条使用

写在前面 这是PB案例学习笔记系列文章的第8篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gitee…...

C++中常见的构造函数类别

目录 摘要 默认构造函数&#xff08;Default Constructor&#xff09;&#xff1a; 带参数的构造函数&#xff08;Parameterized Constructor&#xff09;&#xff1a; 拷贝构造函数&#xff08;Copy Constructor&#xff09;&#xff1a; 移动构造函数&#xff08;Move C…...

万界星空科技MES系统功能介绍

制造执行系统或MES 是一个全面的动态软件系统&#xff0c;用于监视、跟踪、记录和控制从原材料到成品的制造过程。MES在企业资源规划(ERP) 和过程控制系统之间提供了一个功能层&#xff0c;为决策者提供了提高车间效率和优化生产所需的数据。 万界星空科技MES 系统基础功能&am…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...