I was in the middle of writing a post on using Go's Pointer data type and realized that Go 1.3 affects how it can be used.
Here's demonstration of a slight change in behavior of the Garbage collector from Go 1.2 to Go 1.3
As the release notes mentioned of the garbage collector:
... a non-pointer Go value such as an integer will never be mistaken for a pointer and prevent unused memory from being reclaimed... Starting with Go 1.3, the runtime assumes that values with pointer type contain pointers and other values do not.
In other words, addresses should only be assigned to pointers. Integers or any other data type which is used to store an address is at risk of having its space reclaimed by the GC.
Warning, this example makes use of capabilities that are now considered bad practice and even illegal. See the last bullet on the Notes section for more details.
tl;dr: Allocate two integers with
new(). Take one pointer and assign the value of the second pointer to it. Store the address of the lonely portion of memory in an integer variable, in this case a
uintptr. Invoke the GC and display the content of the address stored in the integer variable. Observe that:
In Go 1.2, the integer is still present.
In Go 1.3, the integer value will no longer be present, indicating that its space has been reclaimed. This is because the address was stored in an integer variable. The GC wouldn't have reclaimed it if a pointer was storing the address.
overview: The example program has three primary variables: two pointers to integers
(intPtr1, intPtr2) and one uintptr
(addressHolder). Space for two integers are allocated (with values 100 and 999) with their addresses stored in
addressHolder is assigned the address of
Then a copy of intPtr2 is assigned to intPtr1, essentially leaving nothing to point to the address held in
The garbage collector is finally called, reclaiming the memory at the address held in
To confirm, dereference the address held in addressHolder using the unsafe package (again, see last bullet in notes for details) which yields different results in 1.2 and 1.3. Pay attention to the last lines of output in the following section.
intPtr1 = 0xc210000018, dereferenced value = 100 intPtr2 = 0xc210000020, dereferenced value = 999 addressHolder(uintptr) has been assigned the value of intPtr1 which is the address 0xc210000018. intPtr1 has been assigned the value of inPtr2 which is the address 0xc210000020, dereferenced value = 999 Calling runtime.GC() addressHolder(uintptr), value = 0xc210000018, dereferenced value = 100
intPtr1 = 0x10334020, dereferenced value = 100 intPtr2 = 0x10334024, dereferenced value = 999 addressHolder(uintptr) has been assigned the value of intPtr1 which is the address 0x10334020. intPtr1 has been assigned the value of inPtr2 which is the address 0x10334024, dereferenced value = 999 Calling runtime.GC() addressHolder(uintptr), value = 0x10334020, dereferenced value = 271794224
That's about it, coming up next will be a post on Go's Pointer data type. I'd appreciate any input!
A uintptr is an unsigned integer of 32 or 64 bits depending on your machine.
The release notes also mention that:
Programs that use package unsafe to store integers in pointer-typed values are illegal and will crash if the runtime detects the behavior. Programs that use package unsafe to store pointers in integer-typed values are also illegal but more difficult to diagnose during execution.
The example code does violate this rule, ironically to enforce it.
To detect this behavior on a package, run
go vet PACKAGE_NAME. the vet command is used to examine code for anything fishy. Running this on my example resulted in only warnings. I was not able to make the program crash.
If you're looking for a version manager, I recommend looking into gvm if you haven't already. Inspired by rvm(Ruby Version Manager), it works pretty well and was very useful in testing the example under different versions of Go.
One way to monitor memory is to take advantage of the runtime package which contains a
MemStatstype, used to record memory allocator statistics. Populate the struct with runtime.ReadMemStats() and you're given valuable information such as the number of allocated bytes, the last time the GC ran in absolute time, and total number of allocated objects in the Heap. Click here for the
MemStatstype in the