mirror of
https://github.com/zhwei820/learn.lianglianglee.com.git
synced 2025-11-19 23:53:48 +08:00
fix img & index.html & .md.html
This commit is contained in:
@@ -12,9 +12,7 @@
|
||||
<!-- theme css & js -->
|
||||
<meta name="generator" content="Hexo 4.2.0">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="book-container">
|
||||
<div class="book-sidebar">
|
||||
<div class="book-brand">
|
||||
@@ -27,176 +25,98 @@
|
||||
<ul class="uncollapsible">
|
||||
<li><a href="/" class="current-tab">首页</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="uncollapsible">
|
||||
<li><a href="../">上一级</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="uncollapsible">
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/00 开篇词 Go 为开发者的需求设计,带你实现高效工作.md.html">00 开篇词 Go 为开发者的需求设计,带你实现高效工作.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/00 开篇词 Go 为开发者的需求设计,带你实现高效工作.md.html">00 开篇词 Go 为开发者的需求设计,带你实现高效工作</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/01 基础入门:编写你的第一个 Go 语言程序.md.html">01 基础入门:编写你的第一个 Go 语言程序.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/01 基础入门:编写你的第一个 Go 语言程序.md.html">01 基础入门:编写你的第一个 Go 语言程序</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/02 数据类型:你必须掌握的数据类型有哪些?.md.html">02 数据类型:你必须掌握的数据类型有哪些?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/02 数据类型:你必须掌握的数据类型有哪些?.md.html">02 数据类型:你必须掌握的数据类型有哪些?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/03 控制结构:if、for、switch 逻辑语句的那些事儿.md.html">03 控制结构:if、for、switch 逻辑语句的那些事儿.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/03 控制结构:if、for、switch 逻辑语句的那些事儿.md.html">03 控制结构:if、for、switch 逻辑语句的那些事儿</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/04 集合类型:如何正确使用 array、slice 和 map?.md.html">04 集合类型:如何正确使用 array、slice 和 map?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/04 集合类型:如何正确使用 array、slice 和 map?.md.html">04 集合类型:如何正确使用 array、slice 和 map?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/05 函数和方法:Go 语言中的函数和方法到底有什么不同?.md.html">05 函数和方法:Go 语言中的函数和方法到底有什么不同?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/05 函数和方法:Go 语言中的函数和方法到底有什么不同?.md.html">05 函数和方法:Go 语言中的函数和方法到底有什么不同?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/06 struct 和 interface:结构体与接口都实现了哪些功能?.md.html">06 struct 和 interface:结构体与接口都实现了哪些功能?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/06 struct 和 interface:结构体与接口都实现了哪些功能?.md.html">06 struct 和 interface:结构体与接口都实现了哪些功能?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/07 错误处理:如何通过 error、deferred、panic 等处理错误?.md.html">07 错误处理:如何通过 error、deferred、panic 等处理错误?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/07 错误处理:如何通过 error、deferred、panic 等处理错误?.md.html">07 错误处理:如何通过 error、deferred、panic 等处理错误?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/08 并发基础:Goroutines 和 Channels 的声明与使用.md.html">08 并发基础:Goroutines 和 Channels 的声明与使用.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/08 并发基础:Goroutines 和 Channels 的声明与使用.md.html">08 并发基础:Goroutines 和 Channels 的声明与使用</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/09 同步原语:sync 包让你对并发控制得心应手.md.html">09 同步原语:sync 包让你对并发控制得心应手.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/09 同步原语:sync 包让你对并发控制得心应手.md.html">09 同步原语:sync 包让你对并发控制得心应手</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/10 Context:你必须掌握的多线程并发控制神器.md.html">10 Context:你必须掌握的多线程并发控制神器.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/10 Context:你必须掌握的多线程并发控制神器.md.html">10 Context:你必须掌握的多线程并发控制神器</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/11 并发模式:Go 语言中即学即用的高效并发模式.md.html">11 并发模式:Go 语言中即学即用的高效并发模式.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/11 并发模式:Go 语言中即学即用的高效并发模式.md.html">11 并发模式:Go 语言中即学即用的高效并发模式</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/12 指针详解:在什么情况下应该使用指针?.md.html">12 指针详解:在什么情况下应该使用指针?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/12 指针详解:在什么情况下应该使用指针?.md.html">12 指针详解:在什么情况下应该使用指针?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/13 参数传递:值、引用及指针之间的区别?.md.html">13 参数传递:值、引用及指针之间的区别?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/13 参数传递:值、引用及指针之间的区别?.md.html">13 参数传递:值、引用及指针之间的区别?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/14 内存分配:new 还是 make?什么情况下该用谁?.md.html">14 内存分配:new 还是 make?什么情况下该用谁?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/14 内存分配:new 还是 make?什么情况下该用谁?.md.html">14 内存分配:new 还是 make?什么情况下该用谁?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/15 运行时反射:字符串和结构体之间如何转换?.md.html">15 运行时反射:字符串和结构体之间如何转换?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/15 运行时反射:字符串和结构体之间如何转换?.md.html">15 运行时反射:字符串和结构体之间如何转换?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/16 非类型安全:让你既爱又恨的 unsafe.md.html">16 非类型安全:让你既爱又恨的 unsafe.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/16 非类型安全:让你既爱又恨的 unsafe.md.html">16 非类型安全:让你既爱又恨的 unsafe</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/17 SliceHeader:slice 如何高效处理数据?.md.html">17 SliceHeader:slice 如何高效处理数据?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/17 SliceHeader:slice 如何高效处理数据?.md.html">17 SliceHeader:slice 如何高效处理数据?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/18 质量保证:Go 语言如何通过测试保证质量?.md.html">18 质量保证:Go 语言如何通过测试保证质量?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/18 质量保证:Go 语言如何通过测试保证质量?.md.html">18 质量保证:Go 语言如何通过测试保证质量?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
<a class="current-tab" href="/专栏/22 讲通关 Go 语言-完/19 性能优化:Go 语言如何进行代码检查和优化?.md.html">19 性能优化:Go 语言如何进行代码检查和优化?.md.html</a>
|
||||
<a class="current-tab" href="/专栏/22 讲通关 Go 语言-完/19 性能优化:Go 语言如何进行代码检查和优化?.md.html">19 性能优化:Go 语言如何进行代码检查和优化?</a>
|
||||
|
||||
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/20 协作开发:模块化管理为什么能够提升研发效能?.md.html">20 协作开发:模块化管理为什么能够提升研发效能?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/20 协作开发:模块化管理为什么能够提升研发效能?.md.html">20 协作开发:模块化管理为什么能够提升研发效能?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/21 网络编程:Go 语言如何玩转 RESTful API 服务?.md.html">21 网络编程:Go 语言如何玩转 RESTful API 服务?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/21 网络编程:Go 语言如何玩转 RESTful API 服务?.md.html">21 网络编程:Go 语言如何玩转 RESTful API 服务?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/22 网络编程:Go 语言如何通过 RPC 实现跨平台服务?.md.html">22 网络编程:Go 语言如何通过 RPC 实现跨平台服务?.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/22 网络编程:Go 语言如何通过 RPC 实现跨平台服务?.md.html">22 网络编程:Go 语言如何通过 RPC 实现跨平台服务?</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/23 结束语 你的 Go 语言成长之路.md.html">23 结束语 你的 Go 语言成长之路.md.html</a>
|
||||
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/23 结束语 你的 Go 语言成长之路.md.html">23 结束语 你的 Go 语言成长之路</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
|
||||
<div class="sidebar-toggle-inner"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function add_inner() {
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
inner.classList.add('show')
|
||||
}
|
||||
|
||||
function remove_inner() {
|
||||
let inner = document.querySelector('.sidebar-toggle-inner')
|
||||
inner.classList.remove('show')
|
||||
}
|
||||
|
||||
function sidebar_toggle() {
|
||||
let sidebar_toggle = document.querySelector('.sidebar-toggle')
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
@@ -211,8 +131,6 @@
|
||||
content.classList.add('extend')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function open_sidebar() {
|
||||
let sidebar = document.querySelector('.book-sidebar')
|
||||
let overlay = document.querySelector('.off-canvas-overlay')
|
||||
@@ -225,9 +143,7 @@ function hide_canvas() {
|
||||
sidebar.classList.remove('show')
|
||||
overlay.classList.remove('show')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="off-canvas-content">
|
||||
<div class="columns">
|
||||
<div class="column col-12 col-lg-12">
|
||||
@@ -263,18 +179,14 @@ function hide_canvas() {
|
||||
<p>比如你定义了个常量,从未使用过,虽然对代码运行并没有造成什么影响,但是这个常量是可以删除的,代码如下所示:</p>
|
||||
<p><em>ch19/main.go</em></p>
|
||||
<pre><code>const name = "飞雪无情"
|
||||
|
||||
func main() {
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
<p>示例中的常量 name 其实并没有使用,所以为了节省内存你可以删除它,这种<strong>未使用常量</strong>的情况就可以通过代码规范检查检测出来。</p>
|
||||
<p>再比如,你调用了一个函数,该函数返回了一个 error,但是你并没有对该 error 做判断,这种情况下,程序也可以正常编译运行。但是代码写得不严谨,因为返回的 error 被我们忽略了。代码如下所示:</p>
|
||||
<p><em>ch19/main.go</em></p>
|
||||
<pre><code>func main() {
|
||||
|
||||
os.Mkdir("tmp",0666)
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
<p>示例代码中,Mkdir 函数是有返回 error 的,但是你并没有对返回的 error 做判断,这种情况下,哪怕创建目录失败,你也不知道,因为错误被你忽略了。如果你使用代码规范检查,这类潜在的问题也会被检测出来。</p>
|
||||
@@ -291,7 +203,6 @@ func main() {
|
||||
</code></pre>
|
||||
<p>使用这一命令安装的是 v1.32.2 版本的 golangci-lint,安装完成后,在终端输入如下命令,检测是否安装成功。</p>
|
||||
<pre><code>➜ golangci-lint version
|
||||
|
||||
golangci-lint has version v1.32.2
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
@@ -302,36 +213,23 @@ golangci-lint has version v1.32.2
|
||||
</code></pre>
|
||||
<p>这一示例表示要检测目录中 ch19 下的代码,运行后可以看到如下输出结果。</p>
|
||||
<pre><code>ch19/main.go:5:7: `name` is unused (deadcode)
|
||||
|
||||
const name = "飞雪无情"
|
||||
|
||||
^
|
||||
|
||||
ch19/main.go:8:10: Error return value of `os.Mkdir` is not checked (errcheck)
|
||||
|
||||
os.Mkdir("tmp",0666)
|
||||
</code></pre>
|
||||
<p>通过代码检测结果可以看到,我上一小节提到的两个代码规范问题都被检测出来了。检测出问题后,你就可以修复它们,让代码更加符合规范。</p>
|
||||
<h4>golangci-lint 配置</h4>
|
||||
<p>golangci-lint 的配置比较灵活,比如你可以自定义要启用哪些 linter。golangci-lint 默认启用的 linter,包括这些:</p>
|
||||
<pre><code>deadcode - 死代码检查
|
||||
|
||||
errcheck - 返回错误是否使用检查
|
||||
|
||||
gosimple - 检查代码是否可以简化
|
||||
|
||||
govet - 代码可疑检查,比如格式化字符串和类型不一致
|
||||
|
||||
ineffassign - 检查是否有未使用的代码
|
||||
|
||||
staticcheck - 静态分析检查
|
||||
|
||||
structcheck - 查找未使用的结构体字段
|
||||
|
||||
typecheck - 类型检查
|
||||
|
||||
unused - 未使用代码检查
|
||||
|
||||
varcheck - 未使用的全局变量和常量检查
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
@@ -340,67 +238,41 @@ varcheck - 未使用的全局变量和常量检查
|
||||
<p>如果要修改默认启用的 linter,就需要对 golangci-lint 进行配置。即在项目根目录下新建一个名字为 .golangci.yml 的文件,这就是 golangci-lint 的配置文件。在运行代码规范检查的时候,golangci-lint 会自动使用它。假设我只启用 unused 检查,可以这样配置:</p>
|
||||
<p><em>.golangci.yml</em></p>
|
||||
<pre><code>linters:
|
||||
|
||||
disable-all: true
|
||||
|
||||
enable:
|
||||
|
||||
- unused
|
||||
</code></pre>
|
||||
<p>在团队多人协作开发中,有一个固定的 golangci-lint 版本是非常重要的,这样大家就可以<strong>基于同样的标准检查代码</strong>。要配置 golangci-lint 使用的版本也比较简单,在配置文件中添加如下代码即可:</p>
|
||||
<pre><code>service:
|
||||
|
||||
golangci-lint-version: 1.32.2 # use the fixed version to not introduce new linters unexpectedly
|
||||
</code></pre>
|
||||
<p>此外,你还可以针对每个启用的 linter 进行配置,比如要设置拼写检测的语言为 US,可以使用如下代码设置:</p>
|
||||
<pre><code>linters-settings:
|
||||
|
||||
misspell:
|
||||
|
||||
locale: US
|
||||
</code></pre>
|
||||
<p>golangci-lint 的配置比较多,你自己可以灵活配置。关于 golangci-lint 的更多配置可以参考<a href="https://golangci-lint.run/usage/configuration/">官方文档</a>,这里我给出一个常用的配置,代码如下:</p>
|
||||
<p><em>.golangci.yml</em></p>
|
||||
<pre><code>linters-settings:
|
||||
|
||||
golint:
|
||||
|
||||
min-confidence: 0
|
||||
|
||||
misspell:
|
||||
|
||||
locale: US
|
||||
|
||||
linters:
|
||||
|
||||
disable-all: true
|
||||
|
||||
enable:
|
||||
|
||||
- typecheck
|
||||
|
||||
- goimports
|
||||
|
||||
- misspell
|
||||
|
||||
- govet
|
||||
|
||||
- golint
|
||||
|
||||
- ineffassign
|
||||
|
||||
- gosimple
|
||||
|
||||
- deadcode
|
||||
|
||||
- structcheck
|
||||
|
||||
- unused
|
||||
|
||||
- errcheck
|
||||
|
||||
service:
|
||||
|
||||
golangci-lint-version: 1.32.2 # use the fixed version to not introduce new linters unexpectedly
|
||||
</code></pre>
|
||||
<h4>集成 golangci-lint 到 CI</h4>
|
||||
@@ -408,19 +280,12 @@ service:
|
||||
<p>不管你是使用 Jenkins,还是 Gitlab CI,或者 Github Action,都可以通过<strong>Makefile</strong>的方式运行 golangci-lint。现在我在项目根目录下创建一个 Makefile 文件,并添加如下代码:</p>
|
||||
<p><em>Makefile</em></p>
|
||||
<pre><code>getdeps:
|
||||
|
||||
@mkdir -p ${GOPATH}/bin
|
||||
|
||||
@which golangci-lint 1>/dev/null || (echo "Installing golangci-lint" && go get github.com/golangci/golangci-lint/cmd/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c1a6aeada0afa6a2a8ecada8afb581b7f0eff2f3eff3">[email protected]</a>)
|
||||
|
||||
lint:
|
||||
|
||||
@echo "Running <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="664226">[email protected]</a> check"
|
||||
|
||||
@GO111MODULE=on ${GOPATH}/bin/golangci-lint cache clean
|
||||
|
||||
@GO111MODULE=on ${GOPATH}/bin/golangci-lint run --timeout=5m --config ./.golangci.yml
|
||||
|
||||
verifiers: getdeps lint
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
@@ -448,13 +313,9 @@ verifiers: getdeps lint
|
||||
<p>既然栈内存的效率更高,肯定是优先使用栈内存。那么 Go 语言是如何判断一个变量应该分配到堆上还是栈上的呢?这就需要<strong>逃逸分析</strong>了。下面我通过一个示例来讲解逃逸分析,代码如下:</p>
|
||||
<p><em>ch19/main.go</em></p>
|
||||
<pre><code>func newString() *string{
|
||||
|
||||
s:=new(string)
|
||||
|
||||
*s = "飞雪无情"
|
||||
|
||||
return s
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
<p>在这个示例中:</p>
|
||||
@@ -468,9 +329,7 @@ verifiers: getdeps lint
|
||||
</blockquote>
|
||||
<p>现在我通过逃逸分析来看下是否发生了逃逸,命令如下:</p>
|
||||
<pre><code>➜ go build -gcflags="-m -l" ./ch19/main.go
|
||||
|
||||
# command-line-arguments
|
||||
|
||||
ch19/main.go:16:8: new(string) escapes to heap
|
||||
</code></pre>
|
||||
<p>在这一命令中,-m 表示打印出逃逸分析信息,-l 表示禁止内联,可以更好地观察逃逸。从以上输出结果可以看到,发生了逃逸,<strong>也就是说指针作为函数返回值的时候</strong>,<strong>一定会发生逃逸</strong>。</p>
|
||||
@@ -478,20 +337,14 @@ ch19/main.go:16:8: new(string) escapes to heap
|
||||
<p>下面我对 newString 函数进行了避免逃逸的优化,优化后的函数代码如下:</p>
|
||||
<p><em>ch19/main.go</em></p>
|
||||
<pre><code>func newString() string{
|
||||
|
||||
s:=new(string)
|
||||
|
||||
*s = "飞雪无情"
|
||||
|
||||
return *s
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
<p>再次通过命令查看以上代码的逃逸分析,命令如下:</p>
|
||||
<pre><code>➜ go build -gcflags="-m -l" ./ch19/main.go
|
||||
|
||||
# command-line-arguments
|
||||
|
||||
ch19/main.go:14:8: new(string) does not escape
|
||||
</code></pre>
|
||||
<p>通过分析结果可以看到,虽然还是声明了指针变量 s,但是函数返回的并不是指针,所以没有发生逃逸。</p>
|
||||
@@ -500,44 +353,30 @@ ch19/main.go:14:8: new(string) does not escape
|
||||
</code></pre>
|
||||
<p>同样运行逃逸分析,你会看到如下结果:</p>
|
||||
<pre><code>➜ go build -gcflags="-m -l" ./ch19/main.go
|
||||
|
||||
# command-line-arguments
|
||||
|
||||
ch19/main.go:13:13: ... argument does not escape
|
||||
|
||||
ch19/main.go:13:14: "飞雪无情" escapes to heap
|
||||
|
||||
ch19/main.go:17:8: new(string) does not escape
|
||||
</code></pre>
|
||||
<p>观察这一结果,你会发现「飞雪无情」这个字符串逃逸到了堆上,这是因为「飞雪无情」这个字符串被已经逃逸的指针变量引用,所以它也跟着逃逸了,引用代码如下:</p>
|
||||
<pre><code>func (p *pp) printArg(arg interface{}, verb rune) {
|
||||
|
||||
p.arg = arg
|
||||
|
||||
//省略其他无关代码
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
<p>所以<strong>被已经逃逸的指针引用的变量也会发生逃逸</strong>。</p>
|
||||
<p>Go 语言中有 3 个比较特殊的类型,它们是 slice、map 和 chan,被这三种类型引用的指针也会发生逃逸,看个这样的例子:</p>
|
||||
<p><em>ch19/main.go</em></p>
|
||||
<pre><code>func main() {
|
||||
|
||||
m:=map[int]*string{}
|
||||
|
||||
s:="飞雪无情"
|
||||
|
||||
m[0] = &s
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
<p>同样运行逃逸分析,你看到的结果是:</p>
|
||||
<pre><code>➜ gotour go build -gcflags="-m -l" ./ch19/main.go
|
||||
|
||||
# command-line-arguments
|
||||
|
||||
ch19/main.go:16:2: moved to heap: s
|
||||
|
||||
ch19/main.go:15:20: map[int]*string literal does not escape
|
||||
</code></pre>
|
||||
<p>从这一结果可以看到,变量 m 没有逃逸,反而被变量 m 引用的变量 s 逃逸到了堆上。<strong>所以被map</strong>、<strong>slice 和 chan 这三种类型引用的指针一定会发生逃逸的</strong>。</p>
|
||||
@@ -570,12 +409,10 @@ ch19/main.go:15:20: map[int]*string literal does not escape
|
||||
<a href="/专栏/22 讲通关 Go 语言-完/20 协作开发:模块化管理为什么能够提升研发效能?.md.html">下一页</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
|
||||
</div>
|
||||
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70996dcfd8f33d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
|
||||
@@ -584,11 +421,9 @@ ch19/main.go:15:20: map[int]*string literal does not escape
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'G-NPSEEVD756');
|
||||
var path = window.location.pathname
|
||||
@@ -602,14 +437,12 @@ ch19/main.go:15:20: map[int]*string literal does not escape
|
||||
} else {
|
||||
setCookie("lastPath", path)
|
||||
}
|
||||
|
||||
function setCookie(cname, cvalue) {
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
|
||||
var expires = "expires=" + d.toGMTString();
|
||||
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
|
||||
}
|
||||
|
||||
function getCookie(cname) {
|
||||
var name = cname + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
@@ -619,7 +452,5 @@ ch19/main.go:15:20: map[int]*string literal does not escape
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user