Выделяет ли golang новую память при переназначении новой структуры переменной?
Когда я переназначаю новый объект структуры существующей переменной, адрес не меняется. Код показан ниже:
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 ответ
Спецификация: составные литералы только заявляют, что когда вы берете адрес составного литерала, он будет указывать на безымянную переменную, поэтому требуется выделение:
Принятие адреса составного литерала генерирует указатель на уникальную переменную, инициализированную значением литерала.
Когда вы не берете адрес литерала, просто назначаете его, выделения не требуется, значение структуры может быть присвоено переменной, для которой уже выделена память.
Для проверки мы можем использовать платформу тестирования 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
struct с использованием составного литерала не требует выделения. Взяв его адрес и назначив, для которого требуется выделение 16 байтов (в моей 64-битной архитектуре), это размер вашегоRequest
структура, содержащая одно поле string
тип, а заголовок строки - указатель (8 байтов) и длина (8 байтов).
Присвоение значений в Go всегда создает копию. Поэтому, когда вы присваиваете какое-либо значение (включая значение структуры), значение будет скопировано, и на исходное значение не будет ссылаться переменная, которой вы его присвоили.