Skip to content

参数逃逸报错问题复现与解决

Jakegogo edited this page Oct 17, 2024 · 1 revision

复现用例:

package badcase

import (
	"github.com/agiledragon/gomonkey"
	"runtime"
	"sync"
	"testing"
)

//go:noinline
func a(i *Arg) int {
	return 1
}

var arr = make([]*Arg, 0)

// TestEscape 测试参数逃逸
func TestEscape(t *testing.T) {

	gomonkey.ApplyFunc(a, func(i *Arg) int {

		wg := sync.WaitGroup{}
		wg.Add(100)
		for k := 0; k < 100; k++ {
			go func() {
				// 触发栈扩容
				runtime.GC()
				for j := 0; j < 100000; j++ {
					cpy := &Arg{field1: i.field1}
					arr = append(arr, cpy)
				}
				wg.Done()

			}()
		}

		wg.Wait()
		return len(i.field1)
	})

	// 调用被mock函数
	arg := &Arg{field1: "ok"}
	print(a(arg))

	// 修复方式
	//go func() {
	//	if arg == nil {
	//		print(arg)
	//	}
	//}()
}

// Arg 测试用参数
type Arg struct {
	field1 string
}

解决方案

对引用对象进行深拷贝:

cpy := &Arg{field1: cloneString(i.field1)}

func cloneString(s string) string {
 if len(s) == 0 {
  return ""
 }
 b := make([]byte, len(s))
 copy(b, s)
 return *(*string)(unsafe.Pointer(&b))
}