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

golang内存泄漏

golang也用了好几年了,趁着有空 整理归纳下,以后忘了好看下
一般认为 Go 10次内存泄漏,8次goroutine泄漏,1次是真正内存泄漏,还有1次是cgo导致的内存泄漏
1:环境
go1.20
win10
2:goroutine泄漏
单个Goroutine占用内存,可参考Golang计算单个Goroutine占用内存, 在不发生栈扩张情况下, 新版本Go大概单个goroutine 占用2.6k左右的内存
Goroutine 泄露的常见原因
1>. 从 channel 里读,但是同时没有写入操作
2> 向 无缓冲 channel 里写,但是同时没有读操作
3> 向已满的 有缓冲 channel 里写,但是同时没有读操作
4> select操作在所有的case上都阻塞
5> goroutine进入死循环或死锁,一直结束不了

处理
<1><2><3> 少撒加撒,没什么解释的
<4> 查看 case 阻塞 原因 有没有缓冲什么的,为什么都阻塞,有没有超时机制
<5> 为什么死循环或死锁
来个demo

package mainimport ("fmt""net/http"_ "net/http/pprof""sync/atomic""time"
)func pprofServer() {ip := "0.0.0.0:6060"if err := http.ListenAndServe(ip, nil); err != nil {fmt.Printf("start pprof failed on %s\n", ip)}
}
// 所有chan 阻塞
func goroutineblock() {ch1 := make(chan string) // 无缓冲channelch2 := make(chan string) // 无缓冲channelgo func() {select {case <-ch1:fmt.Println("output1")case <-ch2:fmt.Println("output2")}}()
}

在这里插入图片描述

补充下 pprof
通过 http://localhost:6060/debug/pprof/CMD 获取对应的采样数据。支持的 CMD 有:
goroutine: 获取程序当前所有 goroutine 的堆栈信息。
heap: 包含每个 goroutine 分配大小,分配堆栈等。每分配 runtime.MemProfileRate(默认为512K) 个字节进行一次数据采样。
threadcreate: 获取导致创建 OS 线程的 goroutine 堆栈
block: 获取导致阻塞的 goroutine 堆栈(如 channel, mutex 等),使用前需要先调用 runtime.SetBlockProfileRate
mutex: 获取导致 mutex 争用的 goroutine 堆栈,使用前需要先调用 runtime.SetMutexProfileFraction

GC的触发场景
在这里插入图片描述

0:gcTriggerHeap 程序检测到距上次 GC 内存分配增长超过一定比例时(默认 100%)触发,就是内存翻倍就GC
heapLive 表示当前堆中存活(正在使用)的对象的总大小。
它反映了程序当前实际使用的堆内存量。
随着程序分配新对象和释放旧对象,这个值会动态变化
gcPercent 是一个控制GC触发频率的参数。
默认值是100,表示当堆内存增长到上次GC后的2倍时触发新的GC。
可以通过环境变量 GOGC 或运行时函数 debug.SetGCPercent() 来调整。

1:gcTriggerTime 从上次GC后间隔时间达到了runtime.forcegcperiod 时间
// This is a variable for testing purposes. It normally doesn’t change.
var forcegcperiod int64 = 2 * 60 * 1e9
2:gcTriggerCycle 用户主动调用runtime.GC().

GoV1.8 三色标记法+混合写屏障法
参考 https://zhuanlan.zhihu.com/p/14541819173
垃圾回收(Garbage Collection,简称GC)是编程语言中提供的自动的内存管理机制,自动释放不需要的对象,让出存储器资源,无需程序员手动执行。
​ Golang中的垃圾回收主要应用三色标记法,GC过程和其他用户goroutine可并发运行,但需要一定时间的STW(stop the world),STW的过程中,CPU不执行用户代码,全部用于垃圾回收,这个过程的影响很大,Golang进行了多次的迭代优化来解决这个问题。

三色并发标记法
三色标记法 实际上就是通过三个阶段的标记来确定清楚的对象都有哪些.
1> 就是只要是新创建的对象,默认的颜色都是标记为“白色”.
2> 每次GC回收开始, 然后从根节点开始遍历所有对象,把遍历到的对象从白色集合放入“灰色”集合。
3> 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
4> 重复第三步, 直到灰色中无任何对象.
5> 回收所有的白色标记表的对象. 也就是回收垃圾.
可以看出,在三色标记法中,导致对象丢失的有两个条件:
1> 一个白色对象被黑色对象引用**(白色被挂在黑色下)**
2> 灰色对象与它之间的可达关系的白色对象遭到破坏**(灰色同时丢了该白色)**

关于 stw
Go的STW持续时间
Go的垃圾回收器通过使用并发标记和后台并发清除来尽量减少STW的时间。这意味着在大多数情况下,Go程序不会因为垃圾回收而完全停止。然而,在某些情况下,比如在高负载或大量内存分配时,Go的垃圾回收器可能会触发一个较长的STW暂停。
较短的STW:在正常情况下,特别是在使用了Go 1.3及以后版本的程序中,STW暂停通常很短,可能只有几毫秒。
较长的STW:在一些极端情况下,如果内存分配非常快或者堆的大小增长非常快,可能会触发一个较长的STW暂停。这通常发生在堆的增长超过了预设的阈值,并且系统需要一次性清理大量对象时。
如何管理和减少STW时间
优化内存使用:通过减少内存分配和优化数据结构的使用,可以降低垃圾回收的频率和STW的必要性。
调整GC参数:Go提供了多个GC调优参数(例如GOGC),可以用来调整垃圾回收的行为。例如,增加GOGC的值可以减少垃圾回收的频率,但可能会增加STW的持续时间。
使用runtime.ReadMemStats监控内存使用:通过监控内存使用情况,可以更好地理解何时会发生垃圾回收,并据此优化代码。

在补充下 Golang中协程调度器
参考 https://blog.csdn.net/tiancityycf/article/details/103857524
三个必知的核心元素。(G、M、P)
G:Goroutine的缩写,一个G代表了对一段需要被执行的Go语言代码的封装
M:Machine的缩写,一个M代表了一个内核线程,等同于系统线程
P:Processor的缩写,一个P代表了M所需的上下文环境

G需要绑定在M上才能运行;
M需要绑定P才能运行;
上所述,一个G的执行需要M和P的支持。一个M在于一个P关联之后就形成一个有效的G运行环境 【内核线程 + 上下文环境】。每个P都含有一个 可运行G的队列【runq】。队列中的G会被一次传递给本地P关联的M并且获得运行时机。
M 与 P 总是一对一,P 与 G 总是 一对多, 而 一个 G 最终由 一个 M 来负责运行。

简单的来说,一个G的执行需要M和P的支持。一个M在与一个P关联之后形成了一个有效的G运行环境【内核线程 + 上下文环境】。每个P都会包含一个可运行的G的队列 (runq )。队列中的G会被一次传递给本地P关联的M并且获得运行时机。
M 与 P 总是一对一,P 与 G 总是 一对多, 而 一个 G 最终由 一个 M 来负责运行。
调度器的有两大思想:

复用线程:协程本身就是运行在一组线程之上,不需要频繁的创建、销毁线程,而是对线程的复用。在调度器中复用线程还有2个体现:1)work stealing,当本线程无可运行的G时,尝试从其他线程绑定的P偷取G,而不是销毁线程。2)hand off,当本线程因为G进行系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的线程执行。

利用并行:GOMAXPROCS设置P的数量,当GOMAXPROCS大于1时,就最多有GOMAXPROCS个线程处于运行状态,这些线程可能分布在多个CPU核上同时运行,使得并发利用并行。另外,GOMAXPROCS也限制了并发的程度,比如GOMAXPROCS = 核数/2,则最多利用了一半的CPU核进行并行。

调度器的两小策略:
抢占:在coroutine中要等待一个协程主动让出CPU才执行下一个协程,在Go中,一个goroutine最多占用CPU 10ms,防止其他goroutine被饿死,这就是goroutine不同于coroutine的一个地方。
全局G队列:在新的调度器中依然有全局G队列,但功能已经被弱化了,当M执行work stealing从其他P偷不到G时,它可以从全局G队列获取G。

3:其他情况
1>slice、string 切片 误用造成内存泄漏 个人认为不应该叫泄漏 应该叫 浪费,就是你只需要吃一口饭就饱了,但你盛了一大碗饭

func main() {go pprofServer()time.Sleep(5 * time.Second)//for i := 0; i < 30; i++ {//	goroutineblock()////}//test2s0 := sliceleak(getStringWithLengthOnHeap(1 << 20)) // 1M bytesprintln("finish")  //第一次 调用  go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap time.Sleep(10 * time.Second)s0 = ""runtime.GC()  //gcTriggerTime  等2分钟太久了,手动GC一次//第2次 调用  go tool pprof -http=:8081 http://localhost:6060/debug/pprof/heap select {}println("finish2", s0)
}
// 2切片   len(s1) >3
func sliceleak(s1 string) string {s0 := s1[:3]return s0
}func getStringWithLengthOnHeap(length int) string {if length < 0 {length = 0 // 处理负长度的情况,避免创建负长度的切片}bytes := make([]byte, length) // 创建一个指定长度的字节切片for i := range bytes {        // 使用空格填充(或根据需要修改填充内容)bytes[i] = ' '}return string(bytes) // 将字节切片转换为字符串
}

一次在 println(“finish”) 后 time.Sleep(10 * time.Second) 前
第2次在等20秒后再调用的 控制再手动gc 后调用
切片浪费的内存也会释放,无非是 没释放前,浪费了,所以切片的 如果浪费很多,用重新分配后小的再copy过去,浪费不多,可以无视
在这里插入图片描述
2>time.After()的使用

func timeleak() {chs := make(chan int, 60)go func() {var  num = 0for {num ++chs <- num }}()for true {select {case <-time.After(time.Second * 60): //定时任务未到期之前,是不会被gc清理的fmt.Printf("time.After:%v", time.Now().Unix())case num := <-chs:fmt.Printf("print:num %v\n", num )}}
//可以这么修改//delay := time.NewTimer(time.Second * 60)//defer delay.Stop()//for true {//	delay.Reset(time.Second * 60)//	select {//	case <-delay.C://		fmt.Printf("time.After:%v", time.Now().Unix())//	case v := <-chs://		fmt.Printf("print:%v\n", v)//	}//}}

print:693435
print:693436
print:693437
print:693438

间隔 执行了2次

在这里插入图片描述

如改成

func timeleak() {chs := make(chan int, 100)go func() {var i = 0for {i++chs <- iif i%10 == 0 {time.Sleep(time.Millisecond)}}}()for true {select {case <-time.After(time.Second * 1000): //定时任务未到期之前,是不会被gc清理的fmt.Printf("time.After:%v", time.Now().Unix())case v := <-chs:if v%1000 == 0 {fmt.Printf("print:%v\n", v)}}}
}

在这里插入图片描述
从 10000内执行一次 第2次 大概是 40000-50000间
内存泄漏速度下降 了好多,泄漏的速度跟 case 执行速度又关

如果用default 如下,内存泄漏 更快,不用default time.After 会阻塞,用了,不阻塞了,死的更快
for loop 下的 select 中 default 需要慎用

func timeleak2() {var i int32 = 0for {select {case <-time.After(time.Second * 1000): //定时任务未到期之前,是不会被gc清理的fmt.Printf("time.After:%v", time.Now().Unix())default:i++if i < 50000 {fmt.Println("i=", i)}}}
}

在这里插入图片描述

3> 可以参考 https://blog.csdn.net/qq_38609643/article/details/144963265
这里就不一一 试了
(1)未及时释放的对象引用
(2)循环引用
(3)未关闭的资源(文件、网络连接等)
(4) 闭包引用外部变量
(5)使用了 sync.Pool 但没有清理
(6)不合理的 defer 使用

4>GC 频繁 排查 参考 https://zhuanlan.zhihu.com/p/18966775221

4: 生成svg
1:http://localhost:6060/debug/pprof/heap 生成heap文件
2:把heap 文件放到 执行文件同一目录
3:https://graphviz.org/download/ 下载 graphviz-12.2.1 (64-bit) ZIP archive [sha256] 配置 path ##路径不要有中文或其他标点符号 有时识别不了
3: go tool pprof heap
4:执行 svg 命令 生成 profile001.svg
5:浏览器打开

other 差异对比
差异对比 eg:
go tool pprof -base C:\Users\Administrator\pprof\pprof.testmemory.exe.alloc_objects.alloc_space.inuse_objects.inuse_space.008.pb.gz C:\Users\Administrator\pprof\pprof.testmemory.exe.alloc_objects.alloc_space.inuse_objects.inuse_space.009.pb.gz

5:如果觉得有用,麻烦点个赞,加个收藏

相关文章:

golang内存泄漏

golang也用了好几年了&#xff0c;趁着有空 整理归纳下&#xff0c;以后忘了好看下 一般认为 Go 10次内存泄漏&#xff0c;8次goroutine泄漏&#xff0c;1次是真正内存泄漏&#xff0c;还有1次是cgo导致的内存泄漏 1:环境 go1.20 win10 2:goroutine泄漏 单个Goroutine占用内存&…...

安科瑞能源物联网平台助力企业实现绿色低碳转型

安科瑞顾强 随着全球能源结构的转型和“双碳”目标的推进&#xff0c;能源管理正朝着智能化、数字化的方向快速发展。安科瑞电气股份有限公司推出的微电网智慧能源管理平台&#xff08;EMS 3.0&#xff09;&#xff0c;正是这一趋势下的创新解决方案。该平台集成了物联网&…...

Android Http-server 本地 web 服务

时间&#xff1a;2025年2月16日 地点&#xff1a;深圳.前海湾 需求 我们都知道 webview 可加载 URI&#xff0c;他有自己的协议 scheme&#xff1a; content:// 标识数据由 Content Provider 管理file:// 本地文件 http:// 网络资源 特别的&#xff0c;如果你想直接…...

腾讯的webUI怎样实现deepseek外部调用 ; 腾讯云通过API怎样调用deepseek

腾讯的webUI怎样实现deepseek外部调用 目录 腾讯的webUI怎样实现deepseek外部调用腾讯云通过API怎样调用deepseekhtml方式curl方式python方式腾讯云通过API怎样调用deepseek 重点说明:不需要SK,仅仅使用ip和端口号 html方式 <!DOCTYPE html> <html lang="e…...

DeepSeek VS ChatGPT-速度、准确性和成本

撰写本文时马斯克刚刚发布了聊天机器人Grok2&#xff0c;10万张算卡体现了马斯克的财大气粗。近年来&#xff0c;人工智能模型取得了长足的发展&#xff0c;每个模型都力求在速度、准确性和成本效率方面超越其他模型。在本文中&#xff0c;我将深入研究比较中美在AI的焦点模型上…...

内外网隔离文件传输解决方案|系统与钉钉集成+等保合规,安全提升70%

一、背景与痛点 在内外网隔离的企业网络环境中&#xff0c;员工与外部协作伙伴&#xff08;如钉钉用户&#xff09;的文件传输面临以下挑战&#xff1a; 1. **安全性风险**&#xff1a;内外网直连可能导致病毒传播、数据泄露。 2. **操作繁琐**&#xff1a;传统方式需频繁切…...

Linux基础开发工具的使用(apt、vim、gcc、g++、gdb、make、makefile)

Linux软件包管理器–apt Linux安装软件的方式 在Linux下安装软件的方法有以下三种&#xff1a; 下载到程序的源代码&#xff0c;自己编译出可执行程序获取deb安装包、然后使用dpkg命令安装。&#xff08;不解决依赖关系&#xff09;通过apt进行安装软件。 小知识点&#xf…...

最新版IDEA下载安装教程

一、下载IDEA 点击前往官网下载 或者去网盘下载 点击前往百度网盘下载 点击前往夸克网盘下载 进去后点击IDEA 然后点击Download 选择自己电脑对应的系统 点击下载 等待下载即可 二、安装IDEA 下载好后双击应用程序 点击下一步 选择好安装目录后点击下一步 勾选这两项后点击…...

MacOS 15.3 卸载系统内置软件

1、关闭系统完整性&#xff08;SIP&#xff09; 进入恢复模式(recovery) 如果您使用的是黑苹果或者白苹果&#xff0c;可以选择 重启按住CommandR 进入&#xff0c;如果是M系列芯片&#xff0c;长按开机键&#xff0c;进入硬盘选择界面进入。 我是MacMini M4芯片&#xff0c;关…...

发现问题 python3.6.13+django3.2.5 只能以asgi启动server 如何解决当前问题

在 Python 3.6.13 和 Django 3.2.5 的组合下&#xff0c;如果你发现只能使用 ASGI 启动 Django 服务&#xff0c;而不能使用 WSGI&#xff0c;可能的原因有几个。我们来分析一下常见的问题和解决方案。 1. 默认 ASGI 支持 从 Django 3.0 开始&#xff0c;Django 引入了对 ASG…...

python3+TensorFlow 2.x(六)自编码器

自动编码器 自动编码器&#xff08;Autoencoder&#xff09;是一种无监督学习算法&#xff0c;主要用于数据降维、特征学习和数据生成等任务。它由编码器和解码器组成&#xff0c;目标是将输入数据压缩为低维表示&#xff08;编码&#xff09;&#xff0c;然后再从这个低维表示…...

Redis-AOF

AOF 前言什么是AOF执行后写入的好处避免额外的检查开销不会阻塞当前写操作命令的执行 潜在风险数据丢失阻塞下一个命令 三种写回策略AOF重写机制AOF后台重写数据副本的生成写时复制写时复制的阻塞问题 AOF重写缓冲区子进程重写期间工作内容 总结 前言 RDB方式不能提供强一致性…...

【DeepSeek】本地部署,保姆级教程

deepseek网站链接传送门&#xff1a;DeepSeek 在这里主要介绍DeepSeek的两种部署方法&#xff0c;一种是调用API&#xff0c;一种是本地部署。 一、API调用 1.进入网址Cherry Studio - 全能的AI助手选择立即下载 2.安装时位置建议放在其他盘&#xff0c;不要放c盘 3.进入软件后…...

并查集算法篇上期:并查集原理及实现

引入 那么我们在介绍我们并查集的原理之前&#xff0c;我们先来看一下并查集所应用的一个场景&#xff1a;那么现在我们有一个长度为n的数组&#xff0c;他们分别属于不同的集合&#xff0c;那么现在我们要查询数组当中某个元素和其他元素是否处于同一集合当中&#xff0c;或者…...

如何在WPS打开的word、excel文件中,使用AI?

1、百度搜索&#xff1a;Office AI官方下载 或者直接打开网址&#xff1a;https://www.office-ai.cn/static/introductions/officeai/smartdownload.html 打开后会直接提示开始下载中&#xff0c;下载完成后会让其选择下载存放位置&#xff1a; 选择位置&#xff0c;然后命名文…...

【Deepseek+Dify】wsl2+docker+Deepseek+Dify部署本地大模型知识库问题总结

wsl2dockerDeepseekDify部署本地大模型知识库问题总结 基于ollama部署本地文本模型和嵌入模型 部署教程 DeepSeekdify 本地知识库&#xff1a;真的太香了 问题贴&#xff1a;启动wsl中docker中的dify相关的容器 发现postgre服务和daemon服务一直在重启&#xff0c;导致前端加…...

C++初阶——简单实现vector

目录 1、前言 2、Vector.h 3、Test.cpp 1、前言 简单实现std::vector类模板。 相较于前面的string&#xff0c;vector要注意&#xff1a; 深拷贝&#xff0c;因为vector的元素可能是类类型&#xff0c;类类型元素可以通过赋值重载&#xff0c;自己实现深拷贝。 迭代器失效…...

1.21作业

1 unserialize3 当序列化字符串中属性个数大于实际属性个数时&#xff0c;不会执行反序列化 外部如果是unserialize&#xff08;&#xff09;会调用wakeup&#xff08;&#xff09;方法&#xff0c;输出“bad request”——构造url绕过wakeup 类型&#xff1a;public class&…...

深度集成DeepSeek大模型:WebSocket流式聊天实现

目录 5分钟快速接入DeepSeek大模型&#xff1a;WebSocket实时聊天指南创建应用开发后端代码 (Python/Node.js)结语 5分钟快速接入DeepSeek大模型&#xff1a;WebSocket实时聊天指南 创建应用 访问DeepSeek官网 前往 DeepSeek官网。如果还没有账号&#xff0c;需要先注册一个。…...

Jmeter连接数据库、逻辑控制器、定时器

Jmeter直连数据库 直接数据库的使用场景 直连数据库的关键配置 添加MYSQL驱动Jar包 方式一&#xff1a;在测试计划面板点击“浏览”按钮&#xff0c;将你的JDBC驱动添加进来 方式二&#xff1a;将MySQL驱动jar包放入到lib/ext目录下&#xff0c;重启JMeter 配置数据库连接信…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

【Java多线程从青铜到王者】单例设计模式(八)

wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本&#xff0c;sleep也是可以指定时间的&#xff0c;也就是说时间一到就会解除阻塞&#xff0c;继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒)&#xff0c;wait能被notify提前唤醒&#xf…...