Причины утечек памяти в Go
1. Утечки памяти при работе со срезами
func findSequence(data []byte) []byte {
for i := 0; i < len(data)-1; i++ {
if data[i] == 0xFF && data[i+1] == 0xEE {
return data[i+2 : i+12]
}
}
return nil
}
func processBigData() {
var data []byte
// let's imagine that data (1GB) was read from a file
sequence := findSequence(data)
_ = sequence // using of sequence later
}
func findSequence(data []byte) []byte {
for i := 0; i < len(data)-1; i++ {
if data[i] == 0xFF && data[i+1] == 0xEE {
copied := make([]byte, 10)
copy(copied, data)
return copied
}
}
return nil
}
func processBigData() {
var data []byte
// let's imagine that data (1GB) was read from a file
sequence := findSequence(data)
_ = sequence // using of sequence later
}
Утечки памяти при ссылке на одно значение среза
func findElement(data []int, target int) *int {
for idx := range data {
if data[idx] == target {
return &data[idx]
}
}
return nil
}
func processBigData() {
var data []int
// let's imagine that data (1GB) was read from a file
pointer := findElement(data, 100_000)
_ = pointer // using of sequence later
}
Утечки памяти при работе с вложенными срезами
type Data struct {
values []byte
}
func getPartialData() []Data {
data := make([]Data, 1000) // ~24KB
for i := 0; i < len(data); i++ {
data[i] = Data{
values: make([]byte, 1<<20),
}
}
return data[:2]
}
type Data struct {
values []byte
}
func getPartialData() []Data {
data := make([]Data, 1000) // ~24KB
for i := 0; i < len(data); i++ {
data[i] = Data{
values: make([]byte, 1<<20),
}
}
clear(data[2:])
return data[:2]
}
Условная утечка памяти при изменении размера среза
func main() {
size := 1 << 30
data := make([]int, size)
for i := 0; i < size; i++ {
data[i] = i
}
// using big data
data = data[:0]
// further I will work only
// with a small number of values
}
2. Утечки памяти при работе со словарями
func main() {
data := make(map[int][128]byte, 1_000_000)
for i := 0; i < 1_000_000; i++ {
data[i] = [128]byte{}
}
// using big data
for i := 0; i < 1_000_000; i++ {
delete(data, i)
}
// further I will work only
// with a small number of keys
}
Чтобы решить проблему, нужно:
3. Утечки памяти при работе со строками
func findSequence(data string) string {
for i := 0; i < len(data)-1; i++ {
if data[i] == '\n' && data[i+1] == '\t' {
return data[i+2 : i+12]
}
}
return ""
}
func processBigData() {
var data string
// let's imagine that data (1GB) was read from a file
sequence := findSequence(data)
_ = sequence // using of sequence later
}
4. Утечки памяти при работе с финализаторами
type CString struct {
cpointer *C.char
}
func Allocate() *CString {
str := CString{cpointer: C.allocate()}
runtime.SetFinalizer(str, func(ptr *CString) {
C.free(ptr.cpointer)
})
return str
}
func newFile(fd int, name string, kind newFileKind, nonBlocking bool) *File {
// other implementation of function
runtime.SetFinalizer(f.file, (*file).close)
return f
}
Проблемы финализаторов: циклические ссылки
type Foo struct {
bar *Bar
}
type Bar struct {
foo *Foo
}
func main() {
foo := &Foo{}
bar := &Bar{}
foo.bar = bar
bar.foo = foo
runtime.SetFinalizer(foo, func(ptr *Foo) {
fmt.Println("finalizer called on addr", ptr, "value is", *ptr)
})
runtime.GC()
time.Sleep(time.Second)
}
5. Утечки памяти при работе с каналами и примитивами синхронизации
func printMessages(messagesCh chan string) {
for message := range messagesCh {
fmt.Println(message)
}
}
func processMessages(messages []string) {
messagesCh := make(chan string)
go printMessages(messagesCh)
for _, message := range messages {
messagesCh <- message
}
}
Утечки при записи в канал
func doQuery(string) string {
return "" // imitation of long remote query
}
func doDistributedQuery(queries []string) string {
resultCh := make(chan string)
for _, query := range queries {
go func() {
result := doQuery(query)
resultCh <- result
}()
}
return <-resultCh
}
func doQuery(string) string {
return "" // imitation of long remote query
}
func doDistributedQuery(queries []string) string {
resultCh := make(chan string, 1)
for _, query := range queries {
go func() {
result := doQuery(query)
select {
case resultCh <- result:
default:
}
}()
}
return <-resultCh
}
func process() {
var mutex sync.Mutex
go func() {
mutex.Lock()
mutex.Lock() // endless waiting
}()
}
func validate(lhs, rhs *sync.Mutex) {
lhs.Lock()
rhs.Lock()
}
func process() {
var mutex1 sync.Mutex
var mutex2 sync.Mutex
// potential deadlock - the order in
// which mutexes are captured is violated
go validate(&mutex1, &mutex2)
go validate(&mutex2, &mutex1)
}
Дополнительные материалы
Заключение
Другие статьи