Выделяет ли golang новую память при переназначении новой структуры переменной? - PullRequest
0 голосов
/ 27 мая 2020

Когда я переназначаю новый объект структуры существующей переменной, адрес не меняется. Код показан ниже:

type Request struct {
    Field string
}
func main(){
    r := Request{Field: "a"}
    fmt.Printf("%p\n", &r)
    r = Request{Field: "b"}
    fmt.Printf("%p\n", &r)
}

Вывод:

0xc0004040d0
0xc0004040d0

Это похоже на изменение Feild без выделения новой памяти. Итак, что делает Go, когда происходит переназначение?

Если я хочу использовать sync.pool, могу ли я поместить объект в пул со сбросом, как r := Request{}? (Я имею в виду, что с помощью этой операции struct obj может быть повторно использована и не будет собрана gc.)

1 Ответ

2 голосов
/ 27 мая 2020

Spe c: Составные литералы только заявляют, что когда вы берете адрес составного литерала, он будет указывать на безымянную переменную, поэтому требуется выделение:

Принятие адреса составного литерала генерирует указатель на уникальную переменную , инициализированную значением литерала.

Когда вы не принимаете адрес литерала просто назначьте его, выделение не требуется, значение структуры можно присвоить переменной, для которой уже выделена память.

Для проверки мы можем использовать среду тестирования Go. Создайте тестовый файл:

package main

import (
    "testing"
)

type Request struct {
    Field string
}

var r = Request{Field: "a"}

func BenchmarkStruct(b *testing.B) {
    for i := 0; i < b.N; i++ {
        r = Request{Field: "b"}
    }
}

var p = &Request{Field: "a"}

func BenchmarkStructPtr(b *testing.B) {
    for i := 0; i < b.N; i++ {
        p = &Request{Field: "b"}
    }
}

Запустите его с:

go test -bench . -benchmem

Вывод:

BenchmarkStruct-4       1000000000       0.948 ns/op       0 B/op    0 allocs/op
BenchmarkStructPtr-4    32160099        37.3 ns/op        16 B/op    1 allocs/op

Как видите, присвоив значение Request структура, использующая составной литерал, не требует выделения. Взяв его адрес и назначив, для которого требуется выделение 16 байтов (в моей 64-битной архитектуре), это размер вашей структуры Request, которая содержит одно поле типа string, и заголовок строки - указатель (8 байтов) и длина (8 байтов).

Присвоение значений в Go всегда делает копию. Поэтому, когда вы присваиваете какое-либо значение (включая значение структуры), значение будет скопировано, и на исходное значение не будет ссылаться переменная, которой вы его присвоили.

...