On Fri, Aug 18, 2023 at 8:12 AM Tibor Halter <thal...@zupa.hu> wrote:
>
> Hi Elias, thanks for chiming in!
>
> > The Go garbage collector is precise in that it only considers pointers when 
> > determining memory that can be freed.
>
> Agreed, but it is not as precise on _what_ is a pointer. Specifically, which 
> matters?
> - the type used upon allocating the memory, or
> - the type used upon writing to it.
>
> Because *T or unsafe.Pointer is required upon writing to a memory location 
> for it to be marked as a pointer, I came to the conclusion that the type upon 
> writing is what matters.
>
> So then both types need to be pointers?
>
> Does that mean there is no way to define a struct-like memory layout 
> dynamically, that contains a mix of pointer and non-pointer types, in a way 
> that is supported by the GC?
> (This is what I'm trying to do.)

The rule is pretty simple: a pointer must always have a pointer type.
That includes when it is in a variable and when it is stored in
memory.  In your terms, what matters is the type used upon allocating
the memory.

Your code is not following any of the patterns permitted for
unsafe.Pointer (any patterns that are not explicitly permitted are
forbidden).  The code is effectively converting from *byte to *Num.
As it says at https://pkg.go.dev/unsafe#Pointer that kind of
conversion is only permitted if the types in question "share an
equivalent memory layout".  byte and Num have different memory
layouts.

You can define a struct-like memory layout dynamically by using
reflect.StructOf.

Ian


> On Friday, August 18, 2023 at 3:49:03 PM UTC+2 ma...@eliasnaur.com wrote:
>>
>> On Friday, 18 August 2023 at 05:57:38 UTC-6 Tibor Halter wrote:
>>
>> Thanks Ian!
>>
>> I have managed to strip it down to its essence, see code below.
>>
>> Summary of what I'm doing
>> - reserve memory with make([]byte)
>> - get pointer to underlying array
>> - cast it to type Num
>> - write pointer #2 into it
>> - force GC()
>> - GC does not follow pointer #2, memory is freed
>>
>>
>> The Go garbage collector is precise in that it only considers pointers when 
>> determining memory that can be freed. In your scenario and demonstration 
>> program you explicitly store a pointer in a byte slice, effectively making 
>> it invisible to the collector.
>>
>>
>> --
>>
>>
>> package debug
>>
>> import (
>> "testing"
>> "runtime"
>> "unsafe"
>> )
>>
>> type Num struct {
>> numData *uint64
>> }
>>
>> func Test(t *testing.T) {
>> n := uint64(123456789)
>> numMemSize := 8
>> b := make([]byte, numMemSize, numMemSize)
>> sliceDataPtr := unsafe.Pointer(unsafe.SliceData(b))
>> (*Num)(sliceDataPtr).numData = &n
>>
>>
>> The pointer to &n stored in b here is effectively hidden from the collector.
>>
>>
>>
>> runtime.GC()
>>
>>
>> With no more visible references to (the supposedly heap-allocated) n, it can 
>> be freed.
>>
>>
>>
>> act := (*Num)(sliceDataPtr).numData
>> if *act != 123456789 {
>> t.Fatalf("v[%X]", *act)
>> }
>> }
>>
>>
>> Elias
>
> --
> You received this message because you are subscribed to the Google Groups 
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to golang-nuts+unsubscr...@googlegroups.com.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/golang-nuts/5051da1c-36de-4699-906c-ff8f50754404n%40googlegroups.com.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAOyqgcXKkZdzVebd3U%3DBQ25scsyZC5g%2BKWNS2DFgFhmgHQkb-g%40mail.gmail.com.

Reply via email to