Search code examples
gomemorystructpoolcomposite-literals

Does golang allocate new memory when reassign a new struct to a variable?


When I reassign a new struct object to an existing variable, the address doesn't change. Code is shown below:

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)
}

Output:

0xc0004040d0
0xc0004040d0

It's just like the Feild was modified without allocating new memory. So what does Go do when reassignment happens?

If I want to use sync.pool, can I put the obj to pool with the resetting just like r := Request{}? (I mean with this operation, the struct obj can be reused and won't be collected by gc.)


Solution

  • Spec: Composite literals only states that when you take the address of a composite literal, that will point to an unnamed variable, so that requires allocation:

    Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal's value.

    When you don't take the address of the literal just assign it, no allocation is required, the struct value can be assigned to the variable that already has memory allocated.

    To verify, we may use Go's testing framework. Create a test file:

    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"}
        }
    }
    

    Run it with:

    go test -bench . -benchmem
    

    Output:

    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
    

    As you can see, assigning a value of your Request struct using a composite literal requires no allocation. Taking its address and assigning that requires 16 bytes allocation (on my 64-bit architecture), this is the size of your Request struct which contains a single field of string type, and the string header is a pointer (8 bytes) and a length (8 bytes).

    Assigning values in Go always makes a copy. So when you assign any value (including a struct value), the value will be copied and the original value is not referenced by the variable you assigned it to.