为什么docker运行后就自动退出了

  <!DOCTYPE html>
  <html lang="zh-CN">
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,Chrome=1" />
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
  <meta name="renderer" content="webkit" />
  <title>Golang题目总结</title>
  <meta name="keywords" content="Golang题目总结" />
  <meta name="description" content="1. slice底层数据结构和扩容原理
  数据结构 Go 的 slice 底层数据结构是由一个 array 指针指向底层数组&amp;#xff0c;len 表示切片长度&amp;#xff0c;cap 表示切片容量。扩容原理 &amp;#xff08;1&amp;#xff09;扩容思路&amp;#xff1a;对于 append 向 slice 添加元素时&amp;#xff0c;若 sl…" />
  <link rel="stylesheet" media="screen" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" />
  <link rel="stylesheet" media="screen" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
  <link rel="stylesheet" media="screen" href="/templates/wed.xjx100.cn/static/css/common.css" />
  <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3426945312236276"
  crossorigin="anonymous"></script>
  <script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
  <script>LA.init({id:"3GoIvY2mdpaTwZF3",ck:"3GoIvY2mdpaTwZF3"})</script>
  </head>
  <body>
  <div class="cl-header container-fluid">
  <div class="header-content container px-0">
  <nav class="navbar navbar-expand-lg navbar-light">
  <a class="navbar-brand" href="/">
  <img
  src="/templates/wed.xjx100.cn/picture/logo.png"
  height="46"
  />
  </a>
  <button
  class="navbar-toggler"
  type="button"
  data-toggle="collapse"
  data-target="#navbarNav"
  aria-controls="navbarNav"
  aria-expanded="false"
  aria-label="Toggle navigation"
  >
  <span class="navbar-toggler-icon"></span>
  </button>
  <div
  class="collapse navbar-collapse justify-content-end"
  id="navbarNav"
  >
  <ul class="navbar-nav">
  <li class="nav-item">
  <a class="nav-link" href="/">首页</a>
  </li>
  <li class="nav-item">
  <a class="nav-link" href="/news.html">编程日记</a>
  </li>
  <li class="nav-item">
  <a class="nav-link" href="http://lihuaxi.xjx100.cn">梨花溪</a>
  </li>
  <li class="nav-item">
  <a class="nav-link" href="http://mw.xjx100.cn/">美文频道</a>
  </li>
  <li class="nav-item">
  <a class="nav-link" href="https://www.xjx100.cn/">金喜网</a>
  </li>
  <li class="nav-item">
  <a class="nav-link" href="http://bk.xjx100.cn/">金喜百科</a>
  </li>
  </ul>
  </div>
  </nav>
  </div>
  </div>
  <div class="container">
  <div class="row">
  <div class="col-md-9 cl-left">
  <div class="cl-artical-content">
  <h2 class="cl-artical-title">
  Golang题目总结</h2>
  <div class="cl-card-tag">
  <div>
  <a href="/news.html" class="text-muted">news</a>/<span>2024/1/23 20:06:39</span></span>
  </div>
  </div>
  <div class="cl-artical">
  <article class="baidu_pl"><div id="article_content" class="article_content clearfix"><link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/kdoc_html_views-1a98987dfd.css"><link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/ck_htmledit_views-25cebea3f9.css"><div id="content_views" class="markdown_views prism-atom-one-dark"><svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path></svg><h1><a id="1_slice_0"></a>1. slice底层数据结构和扩容原理</h1>
  <ol><li><strong>数据结构</strong><br /> Go 的 slice 底层数据结构是由一个 array 指针指向底层数组&#xff0c;len 表示切片长度&#xff0c;cap 表示切片容量。</li><li><strong>扩容原理</strong><br /> &#xff08;1&#xff09;<strong>扩容思路</strong>&#xff1a;对于 append 向 slice 添加元素时&#xff0c;若 slice 容量够用&#xff0c;则追加新元素进去&#xff0c;slice.len&#43;&#43;&#xff0c;返回原来的 slice。当原容量不够&#xff0c;则 slice 先扩容&#xff0c;扩容之后 slice 得到新的 slice&#xff0c;将元素追加进新的 slice&#xff0c;slice.len&#43;&#43;&#xff0c;返回新的 slice。<br /> &#xff08;2&#xff09;<strong>扩容规则</strong>&#xff1a;<strong>当切片比较小时</strong>&#xff08;容量小于 1024&#xff09;&#xff0c;<strong>采用较大的扩容倍速进行扩容</strong>&#xff08;新的扩容会是<strong>原来的 2 倍</strong>&#xff09;&#xff0c;避免频繁扩容&#xff0c;从而减少内存分配的次数和数据拷贝的代价。<strong>当切片较大的时</strong>&#xff08;原来的 slice 的容量大于或者等于 1024&#xff09;&#xff0c;采用较小的扩容倍速&#xff08;新的扩容将扩大<strong>大于或者等于原来 1.25 倍</strong>&#xff09;&#xff0c;主要避免空间浪费&#xff08;网上其实很多总结的是 1.25 倍&#xff0c;那是在不考虑内存对齐的情况下&#xff0c;实际上还要考虑内存对齐&#xff0c;扩容是大于或者等于 1.25 倍&#xff09;。</li></ol>
  <h1><a id="2__6"></a>2. 数组和切片的区别</h1>
  <p>在此之前&#xff0c;先理解下 <strong>引用</strong>和 <strong>指针</strong> 区别&#xff1a;</p>
  <p><strong>引用</strong>可以是一个变量或者一个结构体&#xff0c;<strong>若是结构体&#xff0c;它的属性可以为指针类型</strong>&#xff0c;那么此后&#xff0c;每个<strong>引用的副本中都有该指针类型的属性</strong>&#xff0c;而且它们<strong>都是指向同一个内存块</strong>。</p>
  <p><strong>指针</strong>是 <strong>一个存储内存块的地址</strong> 的变量。</p>
  <ol><li><strong>相同点</strong><br /> &#xff08;1&#xff09;只能存储一组相同类型的数据结构<br /> &#xff08;2&#xff09;都是通过下标来访问</li><li><strong>不同点</strong><br /> &#xff08;1&#xff09;数组定长&#xff0c;而切片可扩容<br /> &#xff08;2&#xff09;<strong>数组作为参数传递时传递数组副本</strong>&#xff0c;而<strong>切片作为参数传递时传递的是指向底层数组中的指针</strong><br /> &#xff08;3&#xff09;切片只能通过make函数进行初始化</li></ol>
  <h1><a id="3__rune__19"></a>3. 能介绍下 rune 类型吗&#xff1f;</h1>
  <p><strong>前言</strong>&#xff1a;<strong>golang中string底层是通过byte数组实现</strong>的&#xff08;所以获取字符串长度是按照字节来的&#xff09;。中文字符在unicode下占2个字节&#xff0c;在utf-8编码下占3个字节。&#xff08;<strong>golang默认编码是utf-8</strong>&#xff09;。</p>
  <p>介绍&#xff1a;<strong>相当于 int32</strong>。rune 是<strong>用来处理unicode或utf-8字符</strong>的&#xff08;byte用来处理ascii字符&#xff09;。举例&#xff08;使用<br /> [ ] rune来接受字符串时&#xff0c;它能够正确获取字符串长度。&#xff09;</p>
  <h1><a id="4__24"></a>4. 调用函数传入结构体时&#xff0c;应该传值还是指针&#xff1f;</h1>
  <p><strong>传值会拷贝整个对象&#xff0c;而传指针只会拷贝指针地址</strong>&#xff0c;指向的对象是同一个。<strong>传指针可以减少值的拷贝</strong>&#xff0c;但是会<strong>导致内存分配逃逸到堆中</strong>&#xff0c;<strong>增加</strong>垃圾回收(<strong>GC</strong>)的<strong>负担</strong>。</p>
  <p><strong>一般情况下&#xff0c;对于需要修改原对象值&#xff0c;或占用内存比较大的结构体&#xff0c;选择传指针。对于只读的占用内存较小的结构体&#xff0c;直接传值能够获得更好的性能。</strong></p>
  <h1><a id="5_slicemapchan_28"></a>5. 调用函数传参&#xff08;slice、map、chan&#xff09;时候&#xff0c;传的是什么&#xff1f;</h1>
  <p><strong>golang中所有函数参数传递都是传值&#xff0c;slice、map和chan看上去像引用只是因为他们内部有指针或本身就是指针而已</strong>。&#xff08;slice其实是一个含有指针的结构体&#xff0c;而map和slice本身就是一个指针&#xff09;</p>
  <h1><a id="6__Go__select__30"></a>6. 讲讲 Go 的 select 底层数据结构和一些特性&#xff1f;</h1>
  <p><strong>概念</strong>&#xff1a;go 的 select语句会依次检查每个case分支&#xff0c;如果其中有一个通道已经准备好&#xff0c;就会执行相应的操作。如果有多个通道都已经准备好&#xff0c;select语句会随机选择一个通道执行相应的操作。<br /> <strong>底层数据结构</strong>&#xff1a;select语句的底层数据结构是一个select结构体&#xff0c;它包含了多个case分支和一个默认分支。<br /> <strong>特性</strong>&#xff1a;<br /> &#xff08;1&#xff09;case语句必须是一个channel操作。<br /> &#xff08;2&#xff09;select中的default子句总是可运行的<br /> &#xff08;3&#xff09;select语句可以阻塞等待通道操作。<br /> &#xff08;4&#xff09;如果有多个case都可以运行&#xff0c;select会随机公平地选出一个执行。<br /> &#xff08;5&#xff09;所有channel表达式都会被求值</p>
  <h1><a id="7__Go__defer__39"></a>7. 讲讲 Go 的 defer 底层数据结构和一些特性&#xff1f;</h1>
  <p><strong>每个defer语句都会创建一个defer结构体</strong>&#xff0c;并<strong>将其添加到当前函数的defer链表中</strong>。当函数返回时&#xff0c;<strong>Go运行时会依次执行defer链表中的函数</strong>&#xff0c;<strong>直到链表为空为止</strong>。这个过程是在函数返回之前执行的&#xff0c;因此可以保证被延迟执行的函数在函数返回之前被执行。<br /> <strong>defer 的规则总结</strong>&#xff1a;<br /> &#xff08;1&#xff09;延迟函数执行按照<strong>后进先出</strong>的顺序执行&#xff0c;即先出现的 defer 最后执行。<br /> &#xff08;2&#xff09;延迟函数<strong>可能操作主函数的返回值</strong>。<br /> &#xff08;3&#xff09;<strong>申请资源后立即使用 defer 关闭资源</strong>是个好习惯</p>
  <h1><a id="8_map__45"></a>8. map 的数据结构是什么&#xff1f;是怎么实现扩容&#xff1f;</h1>
  <p><strong>数据结构</strong>&#xff1a;map的底层数据结构是hmap&#xff0c;hmap有多个bmap桶&#xff0c;每个bmap桶包含一个哈希链表&#xff0c;哈希链表中的每个元素都包含一个键值对。<br /> <strong>解决哈希冲突</strong>&#xff1a;当向map中插入一个元素时&#xff0c;Go运行时会先计算元素的哈希值&#xff0c;然后根据哈希值找到对应的桶。如果桶中已经存在一个元素&#xff0c;那么新元素会被插入到链表的头部&#xff1b;<br /> <strong>怎么扩容</strong>&#xff1a;Go 会<strong>创建一个新的 buckets 数组</strong>&#xff0c;<strong>新的 buckets 数组的容量是旧buckets数组的两倍&#xff08;或者和旧桶容量相同&#xff09;</strong>&#xff0c;将<strong>原始桶数组中的所有元素重新散列到新的桶数组中</strong>。这样做的目的是为了使每个桶中的元素数量尽可能平均分布&#xff0c;以提高查询效率。<strong>旧的buckets数组不会被直接删除</strong>&#xff0c;而是会把原来对旧数组的引用去掉&#xff0c;让GC来清除内存。在<strong>map进行扩容迁移的期间</strong>&#xff0c;<strong>不会触发第二次扩容</strong>。只有在前一个扩容迁移工作完成后&#xff0c;map才能进行下一次扩容操作。&#xff08;注意&#xff1a;<strong>以上的搬迁过程为渐进式搬迁的策略</strong>&#xff09;<br /> <strong>扩容时机</strong>&#xff1a;<br /> &#xff08;1&#xff09;当装载因子超过6.5时&#xff0c;扩容一倍&#xff0c;属于增量扩容&#xff1b;<br /> &#xff08;2&#xff09;当使用的溢出桶&#xff08;bmap中有溢出桶这个属性&#xff09;过多时&#xff0c;重新分配一样大的内存空间&#xff0c;属于等量扩容&#xff1b;</p>
  <h1><a id="9_slicesmapkey_52"></a>9. slices能作为map类型的key吗&#xff1f;</h1>
  <p>在golang规范中&#xff0c;<strong>可比较的类型都可以作为map key</strong>。<br /> 故<strong>不能作为map key 的类型包括</strong>&#xff1a;<br /> &#xff08;1&#xff09;<strong>slices</strong><br /> &#xff08;2&#xff09;<strong>maps</strong><br /> &#xff08;3&#xff09;<strong>functions</strong></p>
  <h1><a id="10_map__58"></a>10. map 使用注意的点&#xff0c;是否并发安全&#xff1f;</h1>
  <ol><li><strong>注意的点</strong>&#xff1a;<br /> &#xff08;1&#xff09;map的键必须是可比较的类型&#xff0c;否则会在编译时报错。<br /> &#xff08;2&#xff09;map是无序的&#xff0c;不能保证遍历的顺序和插入的顺序一致。<br /> &#xff08;3&#xff09;map的值可以为任意类型&#xff0c;但键必须是可比较的类型。<br /> &#xff08;4&#xff09;在并发环境下&#xff0c;<strong>map不是并发安全的</strong>&#xff0c;需要使用互斥锁等机制进行保护。</li><li><strong>解决并发安全</strong><br /> &#xff08;1&#xff09;sync.Map —— 可以安全地在多个goroutine之间并发访问&#xff08;在使用sync.Map时&#xff0c;需要注意它的一些限制&#xff0c;例如不能使用range遍历、不能在Load和Store方法中传递指向map的指针等&#xff09;。</li></ol>
  <h1><a id="11__map__key_66"></a>11. map 中删除一个 key&#xff0c;它的内存会释放么&#xff1f;</h1>
  <p><strong>golang的map在key被删除之后&#xff0c;并不会立即释放内存。将map设置为nil后&#xff0c;内存被释放。</strong></p>
  <h1><a id="12__map__68"></a>12. 解决对 map 进行并发访问&#xff1f;</h1>
  <ol><li><strong>sync.Map</strong> —— 可以安全地在多个goroutine之间并发访问</li><li><strong>对map进行并发访问时&#xff0c;需要使用锁来保证并发安全</strong>。常用的锁包括<strong>互斥锁</strong>&#xff08;sync.Mutex&#xff09;和读<strong>写锁</strong>&#xff08;sync.RWMutex&#xff09;</li></ol>
  <h1><a id="13__nil_map__map__71"></a>13. nil map 和空 map 有何不同&#xff1f;</h1>
  <p><strong>nil map 未初始化&#xff0c;空map是长度为空</strong></p>
  <h1><a id="14_context_context__73"></a>14. context 结构是什么样的&#xff1f;context 使用场景和用途&#xff1f;</h1>
  <p><strong>浅入</strong>&#xff1a;context通知一个或多个goroutine停止。&#xff08; context.WithCancel 函数&#xff09;</p>
  <pre><code class="prism language-go"><span class="token keyword">func</span> <span class="token function">TestContext</span><span class="token punctuation">(</span>t <span class="token operator">*</span>testing<span class="token punctuation">.</span>T<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>ctx<span class="token punctuation">,</span> cancel <span class="token operator">:&#61;</span> context<span class="token punctuation">.</span><span class="token function">WithCancel</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> <span class="token string">&#34;zhang&#34;</span><span class="token punctuation">)</span><span class="token keyword">go</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx<span class="token punctuation">,</span> <span class="token string">&#34;li&#34;</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">5</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token function">cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&#34;all the workers take a rest&#34;</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">worker</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> name <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">for</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">select</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">case</span> <span class="token operator">&lt;-</span>ctx<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&#34;worker stopped&#34;</span><span class="token punctuation">)</span><span class="token keyword">return</span><span class="token keyword">default</span><span class="token punctuation">:</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&#34;working...&#34;</span><span class="token punctuation">)</span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  </code></pre>
  <p><font color="blue"><strong>其余细节待更</strong></font></p>
  <h1><a id="15_channel___105"></a>15. channel 的底层实现 &#xff08;数据结构&#xff09;</h1>
  <pre><code class="prism language-go"><span class="token keyword">type</span> hchan <span class="token keyword">struct</span> <span class="token punctuation">{<!-- --></span>qcount <span class="token builtin">uint</span> <span class="token comment">// 循环队列元素的数量</span>dataqsiz <span class="token builtin">uint</span> <span class="token comment">// 循环队列的大小</span>buf unsafe<span class="token punctuation">.</span>Pointer <span class="token comment">// 循环队列缓冲区的数据指针</span>elemsize <span class="token builtin">uint16</span> <span class="token comment">// chan中元素的大小</span>closed <span class="token builtin">uint32</span> <span class="token comment">// 是否已close</span>elemtype <span class="token operator">*</span>_type <span class="token comment">// chan 中元素类型</span>sendx <span class="token builtin">uint</span> <span class="token comment">// send 发送操作在 buf 中的位置</span>recvx <span class="token builtin">uint</span> <span class="token comment">// recv 接收操作在 buf 中的位置</span>recvq waitq <span class="token comment">// receiver的等待队列</span>sendq waitq <span class="token comment">// senderl的等待队列</span>lock mutex <span class="token comment">// 互斥锁&#xff0c;保护所有字段</span>
  <span class="token punctuation">}</span>
  </code></pre>
  <ol><li><strong>回复</strong>&#xff1a;底层是&#xff0c;qcount 当前队列中剩余元素个数&#xff0c;dataqsiz 环形队列长度&#xff0c;即可以存放的元素个数&#xff0c;buf 环形队列指针&#xff0c;elemsize 每个元素的大小&#xff0c;closed 标识关闭状态&#xff0c;elemtype 元素类型&#xff0c;sendx 队列下标&#xff0c;指示元素写入时存放到队列中的位置&#xff0c;recvx 队列下标&#xff0c;指示元素从队列的该位置读出。recvq 等待读消息的 goroutine 队列&#xff0c;sendq 等待写消息的 goroutine 队列&#xff0c;lock 互斥锁&#xff0c;chan 不允许并发读写。</li><li><strong>注意</strong>&#xff1a;循环队列好处为一个数据被发送之后&#xff0c;其余数据不用移动。</li></ol>
  <h1><a id="16__channel__channel__124"></a>16. 向 channel 写入数据和从 channel 读出数据的流程是什么样的&#xff1f;</h1>
  <ol><li><strong>向channel写入数据</strong>&#xff1a;<br /> 若 读出等待队列不为空&#xff0c;则把数据发送给 读出等待队列的第一个goroutine&#xff0c;并唤醒。<br /> 若 读出等待队列为空&#xff0c;若有缓冲区&#xff0c;则将数据写入缓冲区。若无缓冲区&#xff0c;将走阻塞写入的流程&#xff0c;将当前goroutine加入写入等待队列。并挂起等待唤醒。</li><li><strong>从channel读出数据</strong>&#xff1a;<br /> 若 写入等待队列不为空&#xff0c;则从写入等待队列的第一个goroutine中读出数据&#xff0c;并唤醒。<br /> 若 写入等待队列为空&#xff0c;若有缓冲区&#xff0c;则从缓冲区读出数据。若无缓冲区&#xff0c;将走阻塞读出的流程&#xff0c;将当前goroutine加入读出等待队列。并挂起等待唤醒。</li></ol>
  <h1><a id="17_channel_131"></a>17. channel特点</h1>
  <ol><li>关闭的管道读数据仍然可以读数据&#xff0c;但不可以写入数据&#xff08;会panic&#xff09;</li><li>向nil 管道读写会永久阻塞</li><li>关闭为 nil 的管道 panic</li><li>关闭已经关闭的管道 panic</li></ol>
  <h1><a id="18_channel_136"></a>18. channel有无缓冲区的区别</h1>
  <ol><li>管道没有缓冲区&#xff0c;从管道读数据会阻塞&#xff0c;直到有协程向管道中写入数据。同样&#xff0c;向管道写入数据也会阻塞&#xff0c;直到有协程从管道读取数据。</li><li>管道有缓冲区但缓冲区没有数据&#xff0c;从管道读取数据也会阻塞&#xff0c;直到协程写入数据&#xff0c;如果管道满了&#xff0c;写数据也会阻塞&#xff0c;直到协程从缓冲区读取数据。</li></ol>
  <h1><a id="19_channel__139"></a>19. channel 是否线程安全&#xff1f;锁用在什么地方&#xff1f;</h1>
  <ol><li><strong>为什么设计成线程安全&#xff1f;</strong> 不同协程通过channel通信&#xff0c;所以channel的使用场景是多线程下&#xff0c;故需<strong>保证数据的一致性</strong>。</li><li><strong>如何实现线程安全&#xff1f;</strong> channel的锁是<strong>通过hchan结构体中的mutex字段实现</strong>的&#xff0c;它是一个<strong>互斥锁</strong>&#xff0c;<strong>用于保护channel的读写操作</strong>。&#xff08;当一个goroutine进行读写操作时&#xff0c;它会先获取该锁&#xff0c;然后进行相应的操作&#xff0c;最后释放锁。在进行读写操作时&#xff09;</li><li><strong>锁用在什么地方&#xff1f;</strong> 锁的作用是<strong>保护channel的状态&#xff08;容量、是否已关闭等信息&#xff09;和 缓冲区</strong></li></ol>
  <h1><a id="20__143"></a>20. 有那些方式可以安全的读写共享变量&#xff1f;</h1>
  <ol><li>将共享变量的读写放到一个 goroutine 中&#xff0c;其它 goroutine 通过 channel 进行读写操作。</li><li>可以用个数为 1 的信号量&#xff08;semaphore&#xff09;实现互斥</li><li>将共享变量的读写放到一个 channel 中</li><li>通过 Mutex 锁 或者 RWMutex锁 实现</li><li>WaitGroup</li></ol>
  <h1><a id="21_Go__149"></a>21. Go 如何实现原子操作&#xff1f;</h1>
  <p>Go 语言的标准库代码包 sync/atomic 提供了原子的数据读取 和 数据写入。</p>
  <h1><a id="22_Mutex__151"></a>22. Mutex 是悲观锁还是乐观锁&#xff1f;悲观锁、乐观锁是什么&#xff1f;</h1>
  <p><strong>Mutex是悲观锁</strong>。</p>
  <ol><li><strong>乐观锁</strong>&#xff1a;乐观锁认为对同一数据的并发操作不会总发生&#xff0c;属于小概率事件&#xff0c;不用每次都对数据上锁&#xff0c;但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。不采用数据库自身的锁机制&#xff0c;而是通过程序来实现。</li><li><strong>悲观锁</strong>&#xff1a;对数据被其他事务的修改持保守态度&#xff0c;会通过数据库自身的锁机制来实现&#xff0c;从而保证数据操作的排它性。每次去拿数据的时候都认为别人会修改&#xff0c;所以每次在拿数据的时候都会上锁&#xff0c;这样别人想拿这个数据就会 阻塞 直到它拿到锁</li></ol>
  <h1><a id="23_Mutex__155"></a>23. Mutex 有几种模式&#xff1f;</h1>
  <ol><li><strong>正常模式</strong><br /> 新的goruntine进来&#xff0c;如果当前Mutex已经被获取了&#xff0c;则该goruntine进入一个先入先出的waiter队列&#xff0c;在Mutex被释放后&#xff0c;waiter按照先进先出的方式获取锁。该goruntine会处于自旋状态(不挂起&#xff0c;继续占有cpu)。</li><li><strong>饥饿模式</strong><br /> 在饥饿模式下&#xff0c;Mutex 的拥有者将直接把锁交给队列最前面的 waiter。新来的 goroutine 不会尝试获取锁&#xff0c;即使看起来锁没有被持有&#xff0c;它也不会去抢&#xff0c;也不会 spin&#xff08;自旋&#xff09;&#xff0c;它会乖乖地加入到等待队列的尾部。</li></ol>
  <h1><a id="25__160"></a>25. 怎么控制并发数</h1>
  <ol><li><strong>使用goroutine和channel</strong>。可以使用goroutine和channel来控制并发数&#xff0c;例如创建一个缓冲大小为N的通道&#xff0c;表示最多只能有N个goroutine同时执行。在每个goroutine开始执行时&#xff0c;将一个空结构体&#xff08;或其他无意义的数据&#xff09;发送到通道中&#xff0c;如果通道已满&#xff0c;则当前goroutine会被阻塞&#xff0c;直到有一个goroutine执行完成并从通道中接收了一个数据。在每个goroutine执行完成后&#xff0c;从通道中接收一个数据&#xff0c;以便其他goroutine可以执行。</li></ol>
  <pre><code class="prism language-go"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>count <span class="token operator">:&#61;</span> <span class="token number">10</span> <span class="token comment">// 最大支持并发</span>sum <span class="token operator">:&#61;</span> <span class="token number">100</span> <span class="token comment">// 任务总数</span>wg <span class="token operator">:&#61;</span> sync<span class="token punctuation">.</span>WaitGroup<span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span> <span class="token comment">// 控制主协程等待所有子协程执行完之后再退出 (在该程序中是为了让所有的任务(100个)都执行完)</span>c <span class="token operator">:&#61;</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">struct</span><span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">,</span> count<span class="token punctuation">)</span> <span class="token comment">// 控制任务并发的chan</span><span class="token keyword">defer</span> <span class="token function">close</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token keyword">for</span> i<span class="token operator">:&#61;</span><span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">&lt;</span>sum<span class="token punctuation">;</span>i<span class="token operator">&#43;&#43;</span><span class="token punctuation">{<!-- --></span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>c <span class="token operator">&lt;-</span> <span class="token keyword">struct</span><span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span><span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span> <span class="token comment">// 在通道中满时候&#xff0c;会阻塞在此</span><span class="token keyword">go</span> <span class="token keyword">func</span><span class="token punctuation">(</span>j <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">defer</span> wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token operator">&lt;-</span> c <span class="token comment">// 执行完毕&#xff0c;该goroutine对应的空结构体也被流出</span><span class="token punctuation">}</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">}</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  </code></pre>
  <ol start="2"><li>使用ants</li></ol>
  <pre><code class="prism language-go"><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>pool<span class="token punctuation">,</span> err <span class="token operator">:&#61;</span> ants<span class="token punctuation">.</span><span class="token function">NewPool</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment">// 创建一个大小为 3 的协程池</span><span class="token keyword">if</span> err <span class="token operator">!&#61;</span> <span class="token boolean">nil</span> <span class="token punctuation">{<!-- --></span><span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">defer</span> pool<span class="token punctuation">.</span><span class="token function">Release</span><span class="token punctuation">(</span><span class="token punctuation">)</span> wg <span class="token operator">:&#61;</span> sync<span class="token punctuation">.</span>WaitGroup<span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span> <span class="token comment">// 定义一个等待组&#xff0c;用于等待所有任务完成</span><span class="token keyword">for</span> i <span class="token operator">:&#61;</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> i<span class="token operator">&#43;&#43;</span> <span class="token punctuation">{<!-- --></span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>err <span class="token operator">:&#61;</span> pool<span class="token punctuation">.</span><span class="token function">Submit</span><span class="token punctuation">(</span><span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 使用 submit 方法来提交任务到协程池中</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">&#34;Task %d is done.\n&#34;</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span>wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!&#61;</span> <span class="token boolean">nil</span> <span class="token punctuation">{<!-- --></span><span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  </code></pre>
  <h1><a id="26__defer_205"></a>26. 用defer捕获异常</h1>
  <pre><code class="prism language-go">
  <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">defer</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">if</span> err <span class="token operator">:&#61;</span> <span class="token function">recover</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!&#61;</span> <span class="token boolean">nil</span> <span class="token punctuation">{<!-- --></span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">&#34;捕获到异常&#xff1a;&#34;</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 产生异常</span><span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">&#34;出现了一个异常&#34;</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  </code></pre>
  <p>recover() 函数只能在 defer 语句中使用&#xff0c;否则会导致编译错误。recover() 函数的返回值是 panic() 函数传递的参数。</p>
  <h1><a id="27__220"></a>27. 设计一个优雅的线程池</h1>
  <pre><code class="prism language-go"><span class="token keyword">type</span> GoroutinePool <span class="token keyword">struct</span> <span class="token punctuation">{<!-- --></span>minGoroutines <span class="token builtin">int</span> <span class="token comment">// 最小协程数量</span>maxGoroutines <span class="token builtin">int</span> <span class="token comment">// 最大协程数量</span>queue <span class="token keyword">chan</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 任务队列</span>wg sync<span class="token punctuation">.</span>WaitGroup <span class="token comment">// 等待组(等待任务队列中被各协程完成)</span>
  <span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">NewGoroutinePool</span><span class="token punctuation">(</span>minGoroutines<span class="token punctuation">,</span> maxGoroutines <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token operator">*</span>GoroutinePool <span class="token punctuation">{<!-- --></span><span class="token keyword">return</span> <span class="token operator">&amp;</span>GoroutinePool<span class="token punctuation">{<!-- --></span>minGoroutines<span class="token punctuation">:</span> minGoroutines<span class="token punctuation">,</span>maxGoroutines<span class="token punctuation">:</span> maxGoroutines<span class="token punctuation">,</span>queue<span class="token punctuation">:</span> <span class="token function">make</span><span class="token punctuation">(</span><span class="token keyword">chan</span> <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>GoroutinePool<span class="token punctuation">)</span> <span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">for</span> i <span class="token operator">:&#61;</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> p<span class="token punctuation">.</span>minGoroutines<span class="token punctuation">;</span> i<span class="token operator">&#43;&#43;</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">go</span> p<span class="token punctuation">.</span><span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">go</span> p<span class="token punctuation">.</span><span class="token function">adjust</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>GoroutinePool<span class="token punctuation">)</span> <span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">for</span> task <span class="token operator">:&#61;</span> <span class="token keyword">range</span> p<span class="token punctuation">.</span>queue <span class="token punctuation">{<!-- --></span><span class="token function">task</span><span class="token punctuation">(</span><span class="token punctuation">)</span>p<span class="token punctuation">.</span>wg<span class="token punctuation">.</span><span class="token function">Done</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>GoroutinePool<span class="token punctuation">)</span> <span class="token function">Submit</span><span class="token punctuation">(</span>task <span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>p<span class="token punctuation">.</span>wg<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>p<span class="token punctuation">.</span>queue <span class="token operator">&lt;-</span> task
  <span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>GoroutinePool<span class="token punctuation">)</span> <span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>p<span class="token punctuation">.</span>wg<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token punctuation">(</span>p <span class="token operator">*</span>GoroutinePool<span class="token punctuation">)</span> <span class="token function">adjust</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">for</span> <span class="token punctuation">{<!-- --></span>time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token punctuation">{<!-- --></span><span class="token keyword">case</span> <span class="token operator">&lt;-</span>p<span class="token punctuation">.</span>queue<span class="token punctuation">:</span><span class="token keyword">if</span> p<span class="token punctuation">.</span>minGoroutines <span class="token operator">&lt;</span> p<span class="token punctuation">.</span>maxGoroutines <span class="token operator">&amp;&amp;</span> <span class="token function">len</span><span class="token punctuation">(</span>p<span class="token punctuation">.</span>queue<span class="token punctuation">)</span> <span class="token operator">&gt;</span> p<span class="token punctuation">.</span>minGoroutines <span class="token punctuation">{<!-- --></span>p<span class="token punctuation">.</span>minGoroutines<span class="token operator">&#43;&#43;</span><span class="token punctuation">}</span><span class="token keyword">default</span><span class="token punctuation">:</span><span class="token keyword">if</span> <span class="token function">len</span><span class="token punctuation">(</span>p<span class="token punctuation">.</span>queue<span class="token punctuation">)</span> <span class="token operator">&lt;</span> p<span class="token punctuation">.</span>minGoroutines <span class="token operator">&amp;&amp;</span> p<span class="token punctuation">.</span>minGoroutines <span class="token operator">&gt;</span> <span class="token number">1</span> <span class="token punctuation">{<!-- --></span>p<span class="token punctuation">.</span>minGoroutines<span class="token operator">--</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">go</span> p<span class="token punctuation">.</span><span class="token function">worker</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>pool <span class="token operator">:&#61;</span> <span class="token function">NewGoroutinePool</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span>pool<span class="token punctuation">.</span><span class="token function">Start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token keyword">for</span> i <span class="token operator">:&#61;</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">6</span><span class="token punctuation">;</span> i<span class="token operator">&#43;&#43;</span> <span class="token punctuation">{<!-- --></span>pool<span class="token punctuation">.</span><span class="token function">Submit</span><span class="token punctuation">(</span><span class="token keyword">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span>pool<span class="token punctuation">.</span><span class="token function">Wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  </code></pre>
  <h1><a id="28_Channel__290"></a>28. Channel 分配在栈上还是堆上&#xff1f;哪些对象分配在堆上&#xff0c;哪些对象分配在栈上&#xff1f;</h1>
  <p>在 Golang 中&#xff0c;Channel 是分配在堆上的&#xff0c;因为 Channel 的作用域和生命周期不仅仅限于某个函数内部&#xff0c;而且需要在协程间传递数据&#xff0c;因此需要在堆上分配空间。</p>
  <p>至于哪些对象分配在堆上&#xff0c;哪些对象分配在栈上&#xff0c;这<strong>取决于编译器和逃逸分析的结果</strong>。一般来说&#xff0c;函数的局部变量会被分配在栈上&#xff0c;而全局变量和动态分配的对象&#xff08;如使用 new 或 make 函数创建的对象&#xff09;会被分配在堆上。但是&#xff0c;如果编译器可以确定一个局部变量在函数返回后不再被引用&#xff0c;那么它就可以将该变量分配在栈上&#xff0c;而不是堆上&#xff0c;以提高程序的效率。逃逸分析是一种静态分析技术&#xff0c;可以帮助编译器确定变量的作用域和生命周期&#xff0c;从而优化变量的分配方式。</p>
  <h1><a id="29_a__1a_295"></a>29. a :&#61; 1&#xff0c;a在堆上&#xff0c;还是栈上&#xff1f;</h1>
  <p>在 Golang 中&#xff0c;基本类型的变量&#xff08;如 int、float、bool 等&#xff09;和小的结构体变量通常会被分配在栈上。因此&#xff0c;当你声明一个变量 a :&#61; 1 时&#xff0c;a 变量会被分配在栈上。<br /> <strong>注意</strong>&#xff1a;如果变量 a 被传递给一个函数&#xff0c;或者被存储到一个堆数据结构中&#xff0c;那么它可能会被分配到堆上。这是因为编译器会进行逃逸分析&#xff0c;如果发现变量 a 的生命周期超出了当前函数的作用域&#xff0c;那么它就会被分配到堆上&#xff0c;以确保它在函数返回后仍然可以被访问。</p>
  <h1><a id="30__Go__298"></a>30. 请简述 Go 是如何分配内存的&#xff1f;</h1>
  <ol><li><strong>Go的内存分配原则</strong>&#xff1a;</li></ol>
  <p>Go在程序启动的时候&#xff0c;会先向操作系统申请一块内存&#xff08;注意这时还只是一段虚拟的地址空间&#xff0c;并不会真正地分配内存&#xff09;&#xff0c;切成小块后自己进行管理。申请到的内存块被分配了三个区域&#xff1a;<br /> <img src="https://img-blog.csdnimg.cn/c7e034cde1644fcdba31f93020e5fc56.png" alt="在这里插入图片描述" /><br /> <strong>spans</strong>&#xff1a; spans区域存放mspan&#xff08;是一些arena分割的页组合起来的内存管理基本单元&#xff09;的指针<br /> <strong>bitmap</strong>&#xff1a;bitmap区域标识arena区域哪些地址保存了对象<br /> <strong>arena</strong>&#xff1a; arena区域就是我们所谓的堆区&#xff0c;Go动态分配的内存都是在这个区域&#xff0c;它把内存分割成8KB大小的页&#xff0c;一些页组合起来称为mspan。</p>
  <p>&#xff08;1&#xff09;<strong>小于32Kb的内存分配</strong>&#xff1a;GPM调度模型中&#xff0c;每个M绑定一个P&#xff0c;每个P绑定一个mcache &#xff0c;当该P管理的本地队列中的某个G想要申请内存时候&#xff0c;会从mcache中申请mspan。如果没有空闲的mspan或者没有特定大小的mspan了&#xff0c;则mcache就会向mcentral中获取。mcentral被所有线程共享。当 mcentral 没有空闲的 mspan 时&#xff0c;会向 mheap 申请。而 mheap 没有资源时&#xff0c;会向操作系统申请新内存。<br /> &#xff08;2&#xff09;<strong>大于32kb内存分配</strong>&#xff1a;对于那些超过32KB的内存申请&#xff0c;会直接<strong>从堆上(mheap)上分配对应的数量的内存页</strong>(每页大小是8KB&#xff09;。<br /> &#xff08;3&#xff09;<strong>内存对齐</strong>&#xff1a;Go 语言的内存分配器会将分配的内存按照一定的规则进行对齐。<br /> &#xff08;4&#xff09;<strong>内存复用</strong>&#xff1a;Go 语言的垃圾回收器会尽可能地复用已经分配的内存&#xff0c;以减少内存分配和回收的开销。当某个 mspan 中的对象被回收后&#xff0c;该 mspan 中的空闲对象数量会增加&#xff0c;当空闲对象数量达到一定阈值时&#xff0c;该 mspan 就会被移出空闲列表&#xff0c;以便下次分配内存时可以重复利用。</p>
  <p><strong>以上主旨</strong>&#xff1a;一般小对象通过 mspan 分配内存&#xff1b;大对象则直接由 mheap 分配内存。</p>
  <h1><a id="31__golang__313"></a>31. 知道 golang 的内存逃逸吗&#xff1f;什么情况下会发生内存逃逸&#xff1f;</h1>
  <ol><li><strong>内存逃逸是什么</strong>&#xff1a;本该分配到栈上的变量&#xff0c;跑到了堆上&#xff0c;这就导致了内存逃逸</li><li><strong>哪几种情况下有内存逃逸</strong>&#xff1a;<br /> &#xff08;1&#xff09;<strong>方法内返回局部变量指针</strong><br /> &#xff08;2&#xff09;<strong>向 channel 发送指针数据</strong><br /> &#xff08;3&#xff09;<strong>在闭包中引用包外的值</strong><br /> &#xff08;4&#xff09;<strong>slice 由于 append 操作超出其容量</strong><br /> &#xff08;5&#xff09;<strong>slices 中的值是指针的指针或包含指针字段</strong><br /> &#xff08;6&#xff09;<strong>调用接口类型的方法</strong>&#xff08;接口类型的方法调用是动态调度 - 实际使用的具体实现只能在运行时确定&#xff09;</li><li><strong>怎么避免内存逃逸</strong>&#xff1f;<br /> &#xff08;1&#xff09;尽量避免调用接口类型的方法<br /> &#xff08;2&#xff09;避免使用函数中指针类型的局部变量</li><li><strong>逃逸分析</strong><br /> &#xff08;1&#xff09;<strong>查看</strong>&#xff1a;通过go build -gcflags &#39;-m’命令来观察变量逃逸情况<br /> &#xff08;2&#xff09;<strong>注意</strong>&#xff1a;Go编译器会在编译期对考察变量的作用域&#xff0c;就可能会出现内存逃逸。</li><li><strong>内存逃逸危害</strong><br /> &#xff08;1&#xff09;<strong>变量在堆上的分配和回收都比在栈上开销大的多</strong>。对于 go 这种带 GC 的语言来说&#xff0c;会增加 gc 压力&#xff0c;同时也容易造成内存碎片。</li></ol>
  <h1><a id="32__330"></a>32. 谈谈内存泄露&#xff0c;什么情况下内存会泄露&#xff1f;怎么定位排查内存泄漏问题&#xff1f;</h1>
  <ol><li><strong>内存泄漏是什么</strong>&#xff1a;go 中的内存泄漏一般都是 goroutine 泄漏&#xff0c;就是 goroutine 没有被关闭&#xff0c;或者没有添加超时控制&#xff0c;让 goroutine 一只处于阻塞状态&#xff0c;不能被 GC。</li><li><strong>哪几种情况下有内存泄漏</strong><br /> &#xff08;1&#xff09; goroutine 在执行时被阻塞而无法退出<br /> &#xff08;2&#xff09;互斥锁未释放或者造成死锁会造成内存泄漏<br /> &#xff08;3&#xff09;time.Ticker 是每隔指定的时间就会向通道内写数据。不调用stop&#xff08;&#xff09;方法<br /> &#xff08;4&#xff09;字符串的截取引发的内存泄漏<br /> &#xff08;5&#xff09;切片截取引起子切片内存泄漏<br /> &#xff08;6&#xff09;函数数组传参引发内存泄漏&#xff08;参数内存很大&#xff09;</li><li><strong>排查方式</strong>&#xff1a;<br /> &#xff08;1&#xff09;一般通过 pprof 是 Go 的性能分析工具</li></ol>
  <h1><a id="33_GPM_341"></a>33. GPM调度模型</h1>
  <ol><li> <p><strong>什么是GPM</strong>&#xff1f;<br /> G 代表着 goroutine&#xff0c;P 代表着 协程调度器&#xff0c;M 代表 thread 线程&#xff0c;在 GPM 模型&#xff0c;有一个全局队列&#xff08;Global Queue&#xff09;&#xff1a;存放等待运行的 G&#xff0c;还有一个 P 的本地队列&#xff1a;也是存放等待运行的 G&#xff0c;但数量有限&#xff0c;不超过 256 个。GPM 的调度流程从 go func()开始创建一个 goroutine&#xff0c;新建的 goroutine 优先保存在 P 的本地队列中&#xff0c;如果 P 的本地队列已经满了&#xff0c;则会保存到全局队列中。M 会从 P 的队列中取一个可执行状态的 G 来执行&#xff0c;如果 P 的本地队列为空&#xff0c;会优先从全局队列中取 G&#xff0c;然后再尝试从其它 P 的本地队列中偷取 G。若某个时刻&#xff0c;M 执行某一个 G 时候发生系统调用或者阻塞&#xff0c;会从休眠线程队列中找M&#xff0c;并将与原来的M绑定的P与刚找出的M进行绑定&#xff0c;继续执行原来P的本地队列中的G。若休眠线程中没有M&#xff0c;则该P会被加入空闲P队列。然后原来的M执行的G为非阻塞状态&#xff0c;因为它的P已经走了&#xff0c;所以它没有办法继续执行G&#xff0c;优先会获取原配&#xff0c;然后从空闲P队列中取&#xff0c;否则 该G放入全局队列&#xff0c;该M休眠。</p> </li><li> <p><strong>注意</strong>&#xff1a;<br /> &#xff08;1&#xff09;当某个 P 的本地队列中没有 G 时&#xff0c;<strong>会优先从全局队列中取 G&#xff0c;然后再尝试从其它 P 的本地队列中偷取 G</strong>。&#xff08;从源码可看到&#xff0c;先从本地队列查询&#xff0c;全局队列查询&#xff0c;然后再从其他P里偷取&#xff0c;具体源码在runtime的proc.go里&#xff09;<br /> &#xff08;2&#xff09;GOMAXPROCS是所有P个数&#xff0c;故有 自旋线程 &#43; 执行线程 &lt;&#61; GOMAPPROCS<br /> &#xff08;3&#xff09;<strong>自旋线程</strong>的概念&#xff1a;全局中没有G了&#xff0c;目前执行的是G0&#xff0c;需要从其它P的本地队列中偷取。<br /> &#xff08;4&#xff09;M0、G0的概念&#xff1a;m0是一个进程的第一个线程&#xff0c;负责执行和初始化第一个g&#xff08;通常是main&#xff09;&#xff1b;g0是在协程切换完成上下文切换。</p> </li></ol>
  <h1><a id="34_go_351"></a>34. go调度机制中的抢占</h1>
  <p>因为整个Go程序都是运行在用户态的&#xff0c;所以<strong>不能像操作系统那样利用时钟中断来打断运行中的goroutine</strong>。也得益于完全在用户态实现&#xff0c;goroutine的调度切换更加轻量。goroutine的调度器也用到了时间片算法&#xff0c;但是和操作系统的线程调度还是有些区别的&#xff0c;主要分为<strong>基于协作的抢占式调度</strong>和<strong>基于信号量的抢占式调度</strong>。</p>
  <ul><li>协作式调度的核心在于<strong>G在M上运行的时候主动让出M</strong></li><li>基于信号量的抢占式调度的本质是<strong>G没有主动让出M的时候</strong>&#xff0c;<strong>强行中断M对G的执行</strong></li></ul>
  <p>以下是理解&#xff1a;</p>
  <ol><li><strong>基于协作的抢占式调度</strong><br /> &#xff08;1&#xff09;通过设置环境变量 GODEBUG&#61;asyncpreempt&#61;off启用<br /> &#xff08;2&#xff09;对于Go语言中运行时间过长的goroutine&#xff0c;会有一个后台线程持续监控&#xff0c;一旦运行超过10ms&#xff0c;会设置goroutine的协作标识位&#xff0c;资源会被抢占走。</li><li><strong>基于信号量的抢占式调度</strong><br /> &#xff08;1&#xff09;preemptone和preemptM都是用于实现M抢占机制的。preemptone是一个定时器&#xff0c;用于定期检查所有的M是否需要被抢占。如果某个M的运行时间超过了一定的阈值&#xff0c;preemptone会向该M发送抢占信号&#xff0c;让该M主动放弃CPU&#xff0c;以便其他M有机会运行。preemptM是一个函数&#xff0c;它会在需要进行抢占的时候被调用。它会向需要进行抢占的M发送SIGURG信号&#xff0c;以触发该M的抢占操作。</li></ol>
  <h1><a id="35__362"></a>35. 进程、线程、协程有什么区别?</h1>
  <ol><li><strong>进程</strong>&#xff1a;是操作系统中资源分配的基本单位&#xff0c;每个进程都有独立的内存空间、代码和数据。进程之间的切换开销较大&#xff0c;因此进程间的并发性较低。</li><li><strong>线程</strong>&#xff1a;是进程中的执行单元&#xff0c;一个进程可以包含多个线程&#xff0c;它们共享进程的资源。线程之间的切换开销较小&#xff0c;因此线程间的并发性较高。</li><li><strong>协程</strong>&#xff1a;是一种用户态的轻量级线程&#xff0c;它不需要操作系统的支持&#xff0c;可以在用户程序中实现。协程通常运行在单个线程中&#xff0c;因此协程的并发性比线程更高。</li></ol>
  <h1><a id="36_GC_367"></a>36. GC</h1>
  <ol><li><strong>GC机制随着golang版本变化如何变化的</strong>&#xff1f;<br /> Go V1.3 之前普通标记清除&#xff08;mark and sweep&#xff09;方法&#xff0c;整体过程需要启动 STW&#xff0c;效率极低。<br /> GoV1.5 三色标记法&#xff0c;<strong>堆空间启动写屏障</strong>&#xff0c;<strong>栈空间不启动</strong>。全部扫描之后&#xff0c;需要重新扫描一次栈(需要 STW)&#xff0c;效率普通。<br /> GoV1.8 三色标记法&#xff0c;混合写屏障机制&#xff1a;<strong>栈空间不启动</strong>&#xff08;全部标记成黑色&#xff09;&#xff0c;<strong>堆空间启用混合写屏障</strong>&#xff0c;整个过程不要 STW&#xff0c;效率高。</li><li><strong>三色标记法</strong>&#xff1a;<br /> &#xff08;1&#xff09;<strong>基本思路</strong>&#xff1a;首先将所有对象都放入白色标记表中&#xff0c;然后遍历程序的根节点&#xff08;只遍历一层&#xff09;&#xff0c;得到灰色节点&#xff0c;然后遍历该灰色节点&#xff0c;将可达的对象&#xff0c;从白色标记为灰色&#xff0c;自身变为黑色。重复上面步骤&#xff0c;知道灰色标记表中无任何对象。<br /> &#xff08;2&#xff09;<strong>最不希望发生的事</strong>&#xff08;会造成对象无辜的被清理&#xff09;&#xff1a;一个白色对象被黑色对象引用 <strong>且</strong> 灰色对象与它之间的可达关系的白色对象遭到破坏。<br />    <strong>解决方法</strong>&#xff1a;<strong>强弱三色不变式</strong>&#xff08;<strong>强</strong> —— 破坏条件1&#xff0c;即强制性的不允许黑色对象引用白色对象&#xff1b;<strong>弱</strong> —— 破坏条件2&#xff0c;黑色对象引用白色对象时&#xff0c;需要满足白色对象存在其它灰色对象对它的引用&#xff0c;或者可以达它的链路上游存在灰色对象&#xff09;&#xff0c;这种强弱不变式就是 <strong>屏障机制</strong></li><li><strong>屏障机制的实现&#xff08;插入屏障 和 删除屏障&#xff09;</strong><br /> &#xff08;1&#xff09;<strong>插入屏障&#xff08;强三色不变式&#xff09;</strong>&#xff1a;&#xff08;对象被引用时触发的机制&#xff09;在A对象引用B对象时候&#xff0c;B对象被标记为灰色。&#xff08;不在栈上使用&#xff0c;因为性能影响大&#xff09; <strong>缺点</strong>&#xff1a;结束时需要STW来重新扫描栈&#xff08;防止对象丢失&#xff0c;因为黑色对象会创建对象&#xff0c;所以最终会重新扫描一次栈&#xff0c;需要进行短暂的STW&#xff09;。<br /> &#xff08;2&#xff09;<strong>删除屏障&#xff08;弱三色不变式&#xff09;</strong>&#xff1a;&#xff08;对象被删除时触发的机制&#xff09;被删除的对象&#xff0c;如果自身为灰色或者白色&#xff0c;那么被标记为灰色。<strong>缺点</strong>&#xff1a;回收精度低。一个对象被删除&#xff0c;此轮会存活。下一轮才会被GC清理掉。<br /> &#xff08;3&#xff09;<strong>混合写屏障机制</strong>&#xff08;<strong>GoV1.8的三色标记法</strong>&#xff09;&#xff0c;步骤如下&#xff1a;<br />     1. GC开始将栈上的对象全部扫描并标记为黑色<br />     2. GC期间&#xff0c;任何在栈上创建的新对象&#xff0c;均为黑色<br />     3. 被删除的对象标记为灰色<br />     4. 被添加的对象标记为灰色</li></ol>
  <p><img src="https://img-blog.csdnimg.cn/19cc4077066c4ec9b34beb4b298fe2d9.png" alt="请添加图片描述" /></p></div><link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-98b95bb57c.css" rel="stylesheet"><link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-c216769e99.css" rel="stylesheet"></div><div id="treeSkill"></div>
  </div>
  <div id="vip"></div>
  <div class="entry-copyright">
  <span class="source_url"></span><br>
  <span class="Disclaimers"><a href="http://wed.xjx100.cn/news/130064.html">http://wed.xjx100/news/130064.html</a></span>
  <span class="email"></span>
  </div>
  <div class="floatbar">
  <a href="/"><i class="fa fa-home"></i></a><a href="javascript:" class="gotop"><i
  class="fa fa-arrow-up"></i></a>
  </div>
  </div>
  <h4 class="right-card-title px-3 mb-3 mt-3 bg-white">
  相关文章</h4>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130063.html">
  <img src="https://img-blog.csdnimg.cn/img_convert/bac26ace6b892e27cd51a2389e1266fc.jpeg" class="img-fluid" alt="在 Apple 设备(包括 iPad、iOS 和 MacBook)上为用户提供完整的 SAP GUI" referrerpolicy="no-referrer" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130063.html">在 Apple 设备(包括 iPad、iOS 和 MacBook)上为用户提供完整的 SAP GUI</a>
  </h3>
  <div class="cl-card-main-info">
  苹果应用功能 高效且直观的用户界面。 访问 VA01、MI31、MI04、IW21 等。– 无编程 自动化和简化您的 SAP 流程&amp;#xff0c;如库存盘点 在 Apple 设备&amp;#xff08;包括 iPad、iOS 和 MacBook&amp;#xff09;上为用户提供完整的 SAP GUI&amp;#xff0c;利用他们已经了解的 UI 丰富性并…
  </div>
  <div class="cl-card-more">
  <a href="/news/130063.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130062.html">
  <img src="/images/no-images.jpg" class="img-fluid" alt="Java中throw 、throws的区别以及final、finally、finalize的区别" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130062.html">Java中throw 、throws的区别以及final、finally、finalize的区别</a>
  </h3>
  <div class="cl-card-main-info">
  文章目录 一、throw 和 throws 的区别&amp;#xff1f;二、final、finally、finalize 有什么区别&amp;#xff1f; 一、throw 和 throws 的区别&amp;#xff1f;
  &amp;#xff08;1&amp;#xff09;throw
  作用在 方法内&amp;#xff0c;表示抛出具体异常&amp;#xff0c;由方法体内的语句处理&amp;#xff1b;…
  </div>
  <div class="cl-card-more">
  <a href="/news/130062.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130061.html">
  <img src="https://img-blog.csdnimg.cn/43cca8359439492bb36e48743c210102.png" class="img-fluid" alt="MATLAB实现车牌识别" referrerpolicy="no-referrer" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130061.html">MATLAB实现车牌识别</a>
  </h3>
  <div class="cl-card-main-info">
  车牌识别主要包括三个主要步骤&amp;#xff1a;车牌区域定位、车牌字符分割、车牌字符识别。
  本项目通过对拍摄的车牌图像进行灰度变换、边缘检测、腐蚀及平滑等过程来进行车牌图像预处理&amp;#xff0c;并由此得到一种基于车牌颜色纹理特征的车牌定位方法&amp;#xff0c;最终实现了车牌…
  </div>
  <div class="cl-card-more">
  <a href="/news/130061.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130060.html">
  <img src="https://img-blog.csdnimg.cn/e5d6a7856fd445549ed035d16263c90f.png" class="img-fluid" alt="NSSCTF-[NSSRound#X Basic]ez_z3 [MoeCTF 2022]Art [HDCTF2023]basketball" referrerpolicy="no-referrer" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130060.html">NSSCTF-[NSSRound#X Basic]ez_z3 [MoeCTF 2022]Art [HDCTF2023]basketball</a>
  </h3>
  <div class="cl-card-main-info">
  目录 NSSCTF-[NSSRound#X Basic]ez_z3 [MoeCTF 2022]Art
  [HDCTF2023]basketball NSSCTF-[NSSRound#X Basic]ez_z3
  题目下载&amp;#xff1a;下载
  查壳&amp;#xff1a;
  发现有upx壳&amp;#xff0c;但是使用upx -d命令不能脱壳&amp;#xff0c;载入十六进制编辑器查看 把XYU改为UPX&amp;#x…
  </div>
  <div class="cl-card-more">
  <a href="/news/130060.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130059.html">
  <img src="/images/no-images.jpg" class="img-fluid" alt="二叉树的中序遍历" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130059.html">二叉树的中序遍历</a>
  </h3>
  <div class="cl-card-main-info">
  一、递归
  /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tr…
  </div>
  <div class="cl-card-more">
  <a href="/news/130059.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130058.html">
  <img src="https://img-blog.csdnimg.cn/f00f13ba88b249d2bc68f9780737d249.png" class="img-fluid" alt="二次创业接地气、强内功,三只松鼠从一棵树出发重造“人设”" referrerpolicy="no-referrer" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130058.html">二次创业接地气、强内功,三只松鼠从一棵树出发重造“人设”</a>
  </h3>
  <div class="cl-card-main-info">
  民以食为天&amp;#xff0c;自古以来&amp;#xff0c;“吃”都是一门浅显与深奥并存的生意。产业链看似简单&amp;#xff1a;种、收、制、卖&amp;#xff0c;却足以令众多企业为之前赴后继十年、百年。
  三只松鼠&amp;#xff0c;正在这条变革的道路上砥砺前行。自去年4月开启全面转型以来&amp;#xff…
  </div>
  <div class="cl-card-more">
  <a href="/news/130058.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130057.html">
  <img src="https://img-blog.csdnimg.cn/9eef9a2fe16d4003a6525bc29a9d5a3b.png" class="img-fluid" alt="Java——把数组排成最小的数" referrerpolicy="no-referrer" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130057.html">Java——把数组排成最小的数</a>
  </h3>
  <div class="cl-card-main-info">
  题目链接
  牛客网在线oj题——把数组排成最小的数
  题目描述
  输入一个非负整数数组numbers&amp;#xff0c;把数组里所有数字拼接起来排成一个数&amp;#xff0c;打印能拼接出的所有数字中最小的一个。 例如输入数组[3&amp;#xff0c;32&amp;#xff0c;321]&amp;#xff0c;则打印出这三个数字能…
  </div>
  <div class="cl-card-more">
  <a href="/news/130057.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  <div class="cl-card">
  <div class="row">
  <div class="col-lg-3 col-md-4 col-sm-12 cl-card-image img-zoom">
  <div class="object-responsive object-responsive-4by3">
  <a href="/news/130055.html">
  <img src="https://img-blog.csdnimg.cn/53a1572a921444718a01641465989c3d.png" class="img-fluid" alt="ArcGIS Pro坐标系统" referrerpolicy="no-referrer" />
  </a>
  </div>
  </div>
  <div class="col-lg-9 col-md-8 col-sm-12 cl-card-main">
  <h3 class="cl-card-main-title">
  <a href="/news/130055.html">ArcGIS Pro坐标系统</a>
  </h3>
  <div class="cl-card-main-info">
  目录
  1 测量学基础
  1.1 地球的形状和大小
  1.1.1 大地水准面
  1.1.2 大地体
  1.1.3 地球椭球
  2 坐标系统
  2.1 地理坐标系
  2.2 投影坐标系
  2.3 局部坐标系&amp;#xff08;假定坐标系&amp;#xff09;
  2.4 高程坐标系
  3 地图投影
  3.1 投影概念
  3.2 投影分类
  3.3 投影类型
  3.…
  </div>
  <div class="cl-card-more">
  <a href="/news/130055.html">阅读更多...</a>
  </div>
  </div>
  </div>
  </div>
  </div>
  <div class="col-md-3 cl-right">
  <div class="right-card">
  <div class="right-card-main">
  <!-- 自适应 -->
  <ins class="adsbygoogle" style="display: block" data-ad-client="ca-pub-3426945312236276"
  data-ad-slot="1241064141" data-ad-format="auto" data-full-width-responsive="true">
  </ins>
  <script>
  (adsbygoogle = window.adsbygoogle || []).push({});
  </script>
  </div>
  </div>
  <div class="right-card">
  <div class="right-card-main">
  <div class="right-card-title">
  最新文章</div>
  <ul class="right-recommended-list">
  <li><a href="/news/1117790.html"><i class="fa fa-angle-right"
  aria-hidden="true"></i>配网故障定位装置:实现高效故障排查的利器</a> </li>
  <li><a href="/news/1117789.html"><i class="fa fa-angle-right"
  aria-hidden="true"></i>android 防抖工具类,经纬度检查工具类</a> </li>
  <li><a href="/news/1117788.html"><i class="fa fa-angle-right"
  aria-hidden="true"></i>探索无限:山海鲸可视化搭建的地理展览馆的奇幻之旅</a> </li>
  <li><a href="/news/1117787.html"><i class="fa fa-angle-right"
  aria-hidden="true"></i>【刷题】 leetcode 面试题 01.06 字符串压缩</a> </li>
  <li><a href="/news/1117786.html"><i class="fa fa-angle-right"
  aria-hidden="true"></i>dolphinscheduler节点二次开发需要改动的部分</a> </li>
  <li><a href="/news/1117785.html"><i class="fa fa-angle-right"
  aria-hidden="true"></i>【教程】如何在苹果手机上查看系统文件?</a> </li>
  </ul>
  </div>
  </div>
  </div>
  </div>
  </div>
  <div class="container-fluid cl-footer">
  <div class="container">
  <p class="cl-copyright">
  Copyright @ 2022~2023
  </p>
  </div>
  </div>
  <script type="text/javascript" src="https://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>
  <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.min.js"></script>
  <script type="text/javascript" src="/templates/wed.xjx100.cn/static/js/common.js?source_url=https://blog.csdn.net/qq_61465103/article/details/130363952"></script>
  <script type="text/javascript">
  /*$(function(){
  $('.source_url').text('文章来源:https://blog.csdn.net/qq_61465103/article/details/130363952');
  });
  $('.source_url').on("click",function() {
  window.open('https://blog.csdn.net/qq_61465103/article/details/130363952', '_blank');
  });*/
  </script>
  </body>
  </html>
posted @ 2022-07-01 20:23  南昌拌粉的成长  阅读(7152)  评论(0编辑  收藏  举报