Выделяет ли 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

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

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

Другие вопросы по тегам