1
头图

Code specification check

static scan check on the code according to the Go language specification. This check has nothing to do with business.
For example, a constant is defined in the program and has never been used. Although the code runs without any impact, we can delete it in order to save memory. This situation can be detected through code specification inspection.

golangci-lint

golangci-lint is an integrated tool that integrates many static code analysis tools (static code analysis does not run code). By configuring this tool, we can flexibly enable the required code specification checks.

Install

golangci-lint is written in Go language, you can install it from the source code, enter the command in the terminal:
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.32.2

The version installed here is v1.32.2. After the installation is complete, check whether the installation is successful, and enter the command:

golangci-lint version
//golangci-lint has version v1.32.2

After the installation is successful, we use it to check the code. For example, we have the following code:

const name = "微客鸟窝"
func main() {
}

Terminal input command:
golangci-lint run test/
Indicates the code under the test directory test, the running result:

test\test.go:3:7: `name` is unused (deadcode)
const name = "微客鸟窝"
      ^
test\test.go:4:6: `main` is unused (deadcode)
func main() {
     ^

As you can see, the problem of unused program constants has been detected, and we can improve the code later.

golangci-lint configuration

The configuration of golangci-lint can customize which linters to be enabled. The linters enabled by default in golangci-lint are:

deadcode - 死代码检查
errcheck - 返回错误是否使用检查
gosimple - 检查代码是否可以简化
govet - 代码可疑检查,比如格式化字符串和类型不一致
ineffassign - 检查是否有未使用的代码
staticcheck - 静态分析检查
structcheck - 查找未使用的结构体字段
typecheck - 类型检查
unused - 未使用代码检查
varcheck - 未使用的全局变量和常量检查

For more linter, we can enter the command in the terminal: golangci-lint linters to view.
To modify the linter that is enabled by default, you need to create a new file named .golangci.yml in the project root directory. This is the configuration file of golangci-lint. When running a specification check, golangci-lint will automatically use it.
Why not use a fixed version of golangci-lint in team development, so that everyone can check the code based on the same standard. The following code needs to be added to the configuration file:

service:
  golangci-lint-version: 1.32.2 # use the fixed version to not introduce new linters unexpectedly

There are many configurations of golangci-lint, you can configure according to your needs, you can refer to the official document: https://golangci-lint.run/usage/configuration/ . Here is a common configuration for your reference:

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

Integrate golangci-lint to CI

Code checking must be integrated into the CI process, so that when the code is submitted, CI will automatically check the code, find the problem in time and make corrections.

We can run golangci-lint through Makefile, and create a Makefile in the root directory of the project. The code is:

getdeps:
   @mkdir -p ${GOPATH}/bin
   @which golangci-lint 1>/dev/null || (echo "Installing golangci-lint" && go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.32.2)
lint:
   @echo "Running $@ check"
   @GO111MODULE=on ${GOPATH}/bin/golangci-lint cache clean
   @GO111MODULE=on ${GOPATH}/bin/golangci-lint run --timeout=5m --config ./.golangci.yml
verifiers: getdeps lint

Then you can add the following command to your CI, it can help you automatically install golangci-lint and check your code.
make verifiers

Heap allocation or stack

Go language has two parts of memory space: stack memory and heap memory.

  • The stack memory is automatically allocated and released by the compiler, and the developer cannot control it. The stack memory generally stores local variables, parameters, etc. in the function. When the function is created, these memories are automatically created; when the function returns, these memories are automatically released.
  • The life cycle of heap memory is longer than stack memory. If the value returned by the function will be used elsewhere, then this value will be automatically allocated to the heap by the compiler. Compared with stack memory, heap memory cannot be automatically released by the compiler, and can only be released by the garbage collector, so the stack memory efficiency will be very high.

Escape analysis

Whether a variable is allocated on the heap or on the stack needs to be by 1610758b17f508 escape analysis .
Example:

func newString() *string{
    s := new(string) //通过 new 函数申请了一块内存,赋值给了指针变量 s
    *s = "微客鸟窝"
    return s //通过 return 关键字返回
}

Escape analysis command:

$ go build -gcflags="-m -l" ./test/test.go
# command-line-arguments
test\test.go:4:8: new(string) escapes to heap
  • -m means to print out the escape analysis information
  • -l means that inlining is forbidden, you can better observe the escape

The above results found that an escape occurred. indicates that when the pointer is used as a function return value, an escape must occur . Variables that escape to the heap memory cannot be recycled immediately. They can only be cleared by garbage collection marks, which increases the pressure of garbage collection. Therefore, avoid escaping as much as possible and allocate variables on the stack memory so that resources can be recycled when the function returns. ,Improve efficiency.

Code optimization:

func newString() string {
    s := new(string)
    *s = "微客鸟窝"
    return *s
}

Escape analysis command:

$ go build -gcflags="-m -l" ./test/test.go
# command-line-arguments
test\test.go:4:10: new(string) does not escape

Although the pointer variable s is still declared, the function returns not a pointer, so no escape occurs.

There are three special types in the Go language. They are slice, map and chan. The pointers referenced by these three types will also escape:

func main() {
    m := map[int]*string{}
    s := "微客鸟窝"
    m[0] = &s
}
$ go build -gcflags="-m -l" ./test/test.go
# command-line-arguments
test\test.go:5:2: moved to heap: s
test\test.go:4:22: map[int]*string{} does not escape

The result of escape analysis found that the variable m did not escape, but the variable s referenced by the variable m escaped onto the heap.

  • The pointers referenced by the three types of map, slice and chan must escape.
  • Although pointers can reduce memory copying, they can also cause escapes, so you should choose whether to use pointers according to the actual situation.

微客鸟窝
37 声望3 粉丝