Huge map[string]*Object and GC where *Object is from sync.Pool

214 views
Skip to first unread message

Peter Mogensen

unread,
Sep 27, 2018, 9:05:17 AM9/27/18
to golan...@googlegroups.com
Hi,

I noticed the GC fix which avoids scanning maps if both key and value
are not pointer types. [1] and see it used [2] and discussed [3]

So... Initially, I figured this was not worth thinking too much about,
but then I came to thing about the relatively common use case where your
map value is an object you get from a sync.Pool

In other words... you already have exempted the *Objects from normal
garbage collection probably already have some structure in place for
knowing when to call pool.Put() to return the objects.
I would seem silly to scan an entire map which only contains Pool issued
pointers.

So, I tried to measure GC time the same way as [3] but now with a
map[int]uintptr holding pointers to the objects I got from sync.Pool and
it seems there are still significant GC gains to be had.

Of course... it requires that you handle any collisions in hashing the
string key to an int yourself, but wrt. the value I curious if anyone
can see issued with just storing a uintptr instead of the pointer for
sync.Pool managed objects. - (provided you remember to call pool.Put()
before loosing the map reference or doing delete() on a key.)

m[key] = uintptr(unsafe.Pointer(pool.Get().(*Object)))

/Peter


1: https://go-review.googlesource.com/c/go/+/3288
2: https://github.com/allegro/bigcache/blob/master/shard.go#L14
3: https://www.komu.engineer/blogs/go-gc-maps

Ian Davis

unread,
Sep 27, 2018, 9:13:03 AM9/27/18
to golan...@googlegroups.com


On Thu, 27 Sep 2018, at 2:04 PM, Peter Mogensen wrote:
>
> Of course... it requires that you handle any collisions in hashing the
> string key to an int yourself, but wrt. the value I curious if anyone
> can see issued with just storing a uintptr instead of the pointer for
> sync.Pool managed objects. - (provided you remember to call pool.Put()
> before loosing the map reference or doing delete() on a key.)
>
> m[key] = uintptr(unsafe.Pointer(pool.Get().(*Object)))
>

sync.Pools are cleared on every GC run so I expect that will cause some issues with your approach

Peter Mogensen

unread,
Sep 27, 2018, 9:20:33 AM9/27/18
to golan...@googlegroups.com
I don't think so... the point being that the "huge" part comes from a
lot of objects tied up in the map, not so much in the garbage pile.
Of course I would still like Objects which I don't use anymore to be
collected. I just don't want the map to be checked for Objects I've
already promised myself to tell when are garbage.

/Peter

Robert Engels

unread,
Sep 27, 2018, 9:25:42 AM9/27/18
to Peter Mogensen, golan...@googlegroups.com
Based on my experience and I believe many others, I would caution against the use of pools. Although it can be useful for objects that are very expensive to create/initialize using it in a more general case just to improve GC can be fraught with issues in a highly concurrent and/or complex system. It is much more difficult to know when an object can be “put back” and be sure there are no more references. This is why there is GC in the first place, to avoid the types of bugs this leads to.
> --
> 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...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Peter Mogensen

unread,
Sep 27, 2018, 9:29:42 AM9/27/18
to golan...@googlegroups.com


On 09/27/2018 03:25 PM, Robert Engels wrote:
> Based on my experience and I believe many others, I would caution against the use of pools. Although it can be useful for objects that are very expensive to create/initialize using it in a more general case just to improve GC can be fraught with issues in a highly concurrent and/or complex system. It is much more difficult to know when an object can be “put back” and be sure there are no more references. This is why there is GC in the first place, to avoid the types of bugs this leads to.

regardless of the map GC issues, if one doesn't know when to call
Pool.Put(), one shouldn't use sync.Pool ... that's probably a given.

But for the question at hand, let's assume that proper use of sync.Pool
is mastered.

/Peter

Robert Engels

unread,
Sep 27, 2018, 9:59:11 AM9/27/18
to Peter Mogensen, golan...@googlegroups.com
It wasn’t necessarily a warning to you :)

It comes from the days of GC bashing in Java and so everyone tried to manually write garbage free programs using pools and it had a bad effect on both performance and reliability. GC is there for a reason, use it... :) I would just hate to see Go developers “go” down the same path...

Peter Mogensen

unread,
Sep 27, 2018, 10:06:45 AM9/27/18
to Robert Engels, golan...@googlegroups.com


On 09/27/2018 03:58 PM, Robert Engels wrote:
> It wasn’t necessarily a warning to you :)
>
> It comes from the days of GC bashing in Java and so everyone tried to manually write garbage free programs using pools and it had a bad effect on both performance and reliability. GC is there for a reason, use it... :) I would just hate to see Go developers “go” down the same path...

Well... being blissfully ignorant of JVM garbage collection pain.... I
occasionally use sync.Pool to improve code in Go with measurable
results. I've never really found it difficult to know when to call Put()

I'm not that familiar with the GC issues around Go maps with pointers
though, but I can see that the difference between map[string]*Object and
map[int]int are not trivial for huge maps ... and I wondered whether you
really needed to pay the cost for using pointers if they came for a
sync.Pool anyway.
... which seems like a rather common use case to me.

/Peter

Robert Engels

unread,
Sep 27, 2018, 10:17:16 AM9/27/18
to Peter Mogensen, golan...@googlegroups.com
In a highly concurrent system it can be difficult to know when to call Put if the object reference is shared among threads/routines. Similar to why C++ has shared pointers. It might be easy for the original author to know when a Put should be called but as a system grows and new functionality is added, it can become increasing difficult to perform manual reference counting - which is what you are doing...

Ian Davis

unread,
Sep 27, 2018, 10:21:00 AM9/27/18
to golan...@googlegroups.com
On Thu, 27 Sep 2018, at 3:06 PM, Peter Mogensen wrote:
>
>
> On 09/27/2018 03:58 PM, Robert Engels wrote:
> > It wasn’t necessarily a warning to you :)
> >
> > It comes from the days of GC bashing in Java and so everyone tried to manually write garbage free programs using pools and it had a bad effect on both performance and reliability. GC is there for a reason, use it... :) I would just hate to see Go developers “go” down the same path...
>
> Well... being blissfully ignorant of JVM garbage collection pain.... I
> occasionally use sync.Pool to improve code in Go with measurable
> results. I've never really found it difficult to know when to call Put()
>

https://github.com/golang/go/issues/23199 describes another gotcha when using pools: pinning of memory buffers.

Peter Mogensen

unread,
Sep 27, 2018, 10:31:10 AM9/27/18
to golan...@googlegroups.com


On 09/27/2018 04:20 PM, Ian Davis wrote:
>
> https://github.com/golang/go/issues/23199 describes another gotcha when using pools: pinning of memory buffers.
>

Yes... that's a good point for people considering to use sync.Pool.

For the purpose of this question however, let's assume the decision to
use sync.Pool for the actual objects already has been taken and that
knowing when to call Put() is not an issue.
We can also perfectly assume that the pool managed objects are NOT of
variable size (as in the above link) and do not use growing buffers,
either by bytes.Buffer or by using append() ... which is actually the
case for my current code.

I hope I'm not rude, when I try to steer this thread back on topic and
avoid that it degenerates to a discussion of whether sync.Pool is hard
to use in general - which was not the intention of my original question
regarding the fix in https://go-review.googlesource.com/c/go/+/3288

Well meaning,
Peter

Robert Engels

unread,
Sep 27, 2018, 10:39:48 AM9/27/18
to Peter Mogensen, golan...@googlegroups.com
No offense, sorry for high jacking, I was trying to counter point their usefulness, as a way to limit additional resources being spent to improve GC when using them, since I think their application is error prone in many most? cases.

Francis

unread,
Sep 27, 2018, 1:19:49 PM9/27/18
to golang-nuts
It's unclear to me how this could be useful.

It is true that uintptr is not looked at by the garbage collector. This will definitely reduce your GC costs. But there is no way (that I know of) to convert your uintptr back into a *Object that is safe.

Because uintptr is ignored by the GC it won't keep the memory it points to alive. So the *Object could, and very likely will, be collected as garbage. As noted above the sync.Pool is allowed to discard things that it is pooling. The last time I read the details the strategy was to clear _everything_ in the pool during a GC run.

You can avoid this by going old-fashioned and using a `chan *Object` as your pool. Assuming, you are storing these things in the channel at the same time you are storing their `uintptr`s in the map they won't be recovered by the garbage collector. This will probably work. But, critically _only_ with the non-moving GC that Go uses currently. There is a reasonable chance that Go, or some implementation of Go, will adopt a moving garbage collector. Then your `uintptr`s would potentially point to an old memory location after a GC run.

Peter Mogensen

unread,
Sep 27, 2018, 1:28:07 PM9/27/18
to golan...@googlegroups.com


On 09/27/2018 07:19 PM, Francis wrote:
> It is true that uintptr is not looked at by the garbage collector. This
> will definitely reduce your GC costs. But there is no way (that I know
> of) to convert your uintptr back into a *Object that is safe.

I assume of course that we know that all uintptr in the map is actuall
*Object

> Because uintptr is ignored by the GC it won't keep the memory it points
> to alive. So the *Object could, and very likely will, be collected as
> garbage. As noted above the sync.Pool is allowed to discard things that
> it is pooling. The last time I read the details the strategy was to
> clear _everything_ in the pool during a GC run.

But not objects returned by Get() for which Put() has not yet been
called, I assume. ?

/Peter

Francis

unread,
Sep 27, 2018, 1:44:42 PM9/27/18
to golang-nuts
I believe the pool does not track the objects that it returns from a call to `Get()`. I would be surprised if it did track these objects. There is no real need for it to track these objects.

The role of a sync pool is to give you an instance of *Object when you need it. As an optimisation if it has some pooled instances it will give you those instead of creating a new one. Whatever it gives you as a return to Get() it doesn't need to keep track of. It can't give that instance to anyone else, so it just forgets about it, unless you call Put(), then it will track that - so it can give that instance to someone else.

I think now your proposal makes a lot more sense to me. If I understand correctly you had understood that the sync.Pool would track all the instances of objects it had created, via Get(), and so they wouldn't be GCed, This would be (to me) a very surprising implementation of sync.Pool.

Peter Mogensen

unread,
Sep 27, 2018, 1:52:41 PM9/27/18
to golan...@googlegroups.com


On 09/27/2018 07:44 PM, Francis wrote:
> I believe the pool does not track the objects that it returns from a
> call to `Get()`. I would be surprised if it did track these objects.
> There is no real need for it to track these objects.

Exactly, that was my point. Else there would be no reason to call Put()

> I think now your proposal makes a lot more sense to me. If I understand
> correctly you had understood that the sync.Pool would track all the
> instances of objects it had created, via Get(), and so they wouldn't be
> GCed, This would be (to me) a very surprising implementation of sync.Pool.

No. My understanding of sync.Pool is that only the objects Put() back
into it would be subject to GC. (I could of course, just read the source)
The objects handed out by Get() and still in the hand of the program
would be the responsibility of the application.

My intention was then to handle that responsibility by temporarily
storing these pointers as uintptr and by using them as a value in a map,
be able to exploit the map-of-non-pointers optimization to avoid GC
scans of the map.

... and of course remember to Put() back any map entries delete()'ed
from the map and any entries left before the reference to the map it
self is forgotten and the map is left to be garbage collected.

/Peter



Dan Kortschak

unread,
Sep 27, 2018, 5:13:25 PM9/27/18
to Peter Mogensen, golan...@googlegroups.com
Unless you are holding a real pointer to the value, they will have no
root to hold them and so will by GC'd. Further, the uintptr in the map
will not be updated if the value is moved (does not happen now but may
in the future).

What you're doing here is unsafe.

Francis

unread,
Sep 27, 2018, 5:15:11 PM9/27/18
to golang-nuts
Ah, ok. In that case keeping a reference to a *Object in a uintptr will not prevent it from being garbage collected.

For each garbage collection cycle the runtime visits every memory reference that is still 'alive'. Any references which were not visited will be reclaimed as free memory.

So if you wrote a map `map[uintptr]bool` the GC will not visit any of the locations pointed to by any of the `uintptr`s in the map. So they would all look like unused memory locations, and they would be reclaimed by the garbage collector.

So when you went to cast them back into a *Object, you would be pointing into memory the runtime thought had been freed.

keith....@gmail.com

unread,
Sep 27, 2018, 9:04:42 PM9/27/18
to golang-nuts
Objects returned by Get() are not special in any way. They will be GCd just like an object returned by new(). In fact, they will often be just a new()'d object.
There is no association of such an object with the pool.  A pool is just a fancy free list. Get might return a previously Put'd object instead of a new() one.

Your scheme is unsafe. Storing a *Object as a uintptr means the underlying Object will be collected out from under your map.

Peter Mogensen

unread,
Sep 28, 2018, 2:14:27 AM9/28/18
to Dan Kortschak, golan...@googlegroups.com


On 09/27/2018 11:12 PM, Dan Kortschak wrote:
> Unless you are holding a real pointer to the value, they will have no
> root to hold them and so will by GC'd.

Also if the pointer is returned by Pool.Get() ?
That's probably one of the main questions here. Will an object from
sync.Pool which have not been Put() be garbage collected if the
reference is lost?

> Further, the uintptr in the map
> will not be updated if the value is moved (does not happen now but may
> in the future).

!??!.... what?

Are you saying this statement from the Go spec is subject to change:
"Pointer values are comparable. Two pointer values are equal if they
point to the same variable or if both have value nil. Pointers to
distinct zero-size variables may or may not be equal. "

?

> What you're doing here is unsafe.

I know this involves the unsafe package.

Peter Mogensen

unread,
Sep 28, 2018, 2:17:35 AM9/28/18
to golan...@googlegroups.com


On 09/28/2018 03:04 AM, keith....@gmail.com wrote:
> Objects returned by Get() are not special in any way. They will be GCd
> just like an object returned by new(). In fact, they will often be just
> a new()'d object.
> There is no association of such an object with the pool.  A pool is just
> a fancy free list. Get might return a previously Put'd object instead of
> a new() one.
>
> Your scheme is unsafe. Storing a *Object as a uintptr means the
> underlying Object will be collected out from under your map.

Ok... thanks... I guess I might have made the wrong assumption about
when objects returned from pool.Get() are subject to garbage collection.

/Peter

Peter Mogensen

unread,
Sep 28, 2018, 2:26:45 AM9/28/18
to golan...@googlegroups.com
However... (it's early here, so I forgot).

Of course the uintptr entries in the map is not the only reference to
the objects. In order to properly clean up there will be another
datastructure holding (say) a linked list of the objects.

For instance... if the map is only used as index to a huge database,
where the objects are actually stored in a priority queue to, say,
implement TTL or LRU.

So, there would always be another reference to the objects preventing
them for being garbage collected.

/Peter

Keith Randall

unread,
Sep 28, 2018, 2:36:57 AM9/28/18
to a...@one.com, golang-nuts
It's not guaranteed to work according to the spec. The thing that will break your code is moving objects.
The GC currently doesn't move objects in the heap.
We do copy stacks, but the current escape analysis never puts objects pointed to by a map entry on the stack.
So for now, you're ok. If we ever implement a moving garbage collector or improve escape analysis, you're out of luck.
 
/Peter

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/NPdHLvOoCp8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

Henrik Johansson

unread,
Sep 28, 2018, 3:14:14 AM9/28/18
to kei...@alum.mit.edu, a...@one.com, golang-nuts
This sounds much like the trickery people have used both successfully but also disastrously using weak references in Java.
Is that where you got the idea from?


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...@googlegroups.com.

Peter Mogensen

unread,
Sep 28, 2018, 3:17:33 AM9/28/18
to golan...@googlegroups.com


On 09/28/2018 09:13 AM, Henrik Johansson wrote:
> This sounds much like the trickery people have used both successfully
> but also disastrously using weak references in Java.
> Is that where you got the idea from?

No.
I think I mentioned in my first post where I got the idea from.
I haven't programmed Java the last 20 years.

Anyway... Thinking about it, I might be able to achieve the same by just
maintaining a []Object and having the map hold integer indexes instead.

/Peter

Dan Kortschak

unread,
Sep 28, 2018, 3:40:18 AM9/28/18
to Peter Mogensen, golan...@googlegroups.com
On Fri, 2018-09-28 at 08:14 +0200, Peter Mogensen wrote:
>
> On 09/27/2018 11:12 PM, Dan Kortschak wrote:
> >
> > Unless you are holding a real pointer to the value, they will have
> > no
> > root to hold them and so will by GC'd.
> Also if the pointer is returned by Pool.Get() ?
> That's probably one of the main questions here. Will an object from
> sync.Pool which have not been Put() be garbage collected if the
> reference is lost?

A value obtained from a sync.Pool is not special. The only things that
keep values alive as far as the GC is concerned is if they are attached
to a root. The root that holds things in the pool if they are there is
the global that is the pool. When you Get something from a pool, it no
longer holds it and so it's your job to look after it. If it is not
attached to a pointer value that you hold it is subject to collection

> >
> > Further, the uintptr in the map
> > will not be updated if the value is moved (does not happen now but
> > may
> > in the future).
> !??!.... what?
>
> Are you saying this statement from the Go spec is subject to change:
> "Pointer values are comparable. Two pointer values are equal if they
> point to the same variable or if both have value nil. Pointers to
> distinct zero-size variables may or may not be equal. "
>
> ?

I don't understand what you are asking. First, uintptr is not a pointer
type, it's an integer type that is the same size as a pointer and that
can be interconverted with an unsafe.Pointer. If you hold a uintptr
that was converted from an unsafe.Pointer and you throw away the
unsafe.Pointer, you now have no pointer to the value you got from the
pool. The GC when it become a moving GC (if that happens) will need to
make sure pointers are adjusted when values move, but uintptr is not a
pointer, so it will not need to be adjusted.

What you are asking though is not related to what I wrote.

> >
> > What you're doing here is unsafe.
> I know this involves the unsafe package.

OK, I'll restate, it's not just unsafe, it's catastrophically unsafe.
Unsafe can work, this won't.

Peter Mogensen

unread,
Sep 28, 2018, 3:43:13 AM9/28/18
to Dan Kortschak, golan...@googlegroups.com


On 09/28/2018 09:39 AM, Dan Kortschak wrote:
>
> I don't understand what you are asking. First, uintptr is not a pointer
> type, it's an integer type that is the same size as a pointer and that
> can be interconverted with an unsafe.Pointer. If you hold a uintptr
> that was converted from an unsafe.Pointer and you throw away the
> unsafe.Pointer, you now have no pointer to the value you got from the
> pool. The GC when it become a moving GC (if that happens) will need to
> make sure pointers are adjusted when values move, but uintptr is not a
> pointer, so it will not need to be adjusted.
>
> What you are asking though is not related to what I wrote.

You're right... testing for equality, of course depends on having
something to compare with.

/Peter

Robert Engels

unread,
Sep 28, 2018, 8:04:42 AM9/28/18
to Henrik Johansson, kei...@alum.mit.edu, a...@one.com, golang-nuts
This is in no way similar to weak references in Java. Weak references are safe and appropriate for many caching data structures. The code idea presented here is not related. 

Peter Mogensen

unread,
Sep 28, 2018, 8:18:07 AM9/28/18
to Robert Engels, golang-nuts


On 09/28/2018 02:04 PM, Robert Engels wrote:
> This is in no way similar to weak references in Java. Weak references
> are safe and appropriate for many caching data structures. The code idea
> presented here is not related. 

Correct.
I've used weak references in other languages (not Java) to, say, prevent
leaks due to reference cycles.

This is not that.
My idea could maybe be restated simpler as having a huge double-linked
list of sync.Pool objects and using wanting to supplement it by a map
index, but avoiding storing pointers in the map.

I don't think I've seen any argument that it's technically impossible by
now, but maybe there's better ways to do it ... maybe even write the
index manually instead of using the build in map.

I think the warning about this being "catastrophically unsafe" forgets
that these objects will be kept with proper pointers in a separate data
structure and the map was only intended as an index.
It was never the intention to let an uintptr be the only remaining
reference to the objects. There will of course, always be a need for
keeping proper pointers in a datastructure (like a list) to be able to
do proper Put() on the objects before the map reference is lost.

Anyway... I think I've found a more efficient way using integer slice
indexes instead of uintptr.

Thanks for input

/Peter

Henrik Johansson

unread,
Sep 28, 2018, 8:53:58 AM9/28/18
to Peter Mogensen, Robert Engels, golang-nuts
How is storing unintptrs in a map different from say java.util.WeakHashMap?
The data pointed to by the uintptrs can at any given time have been reclaimed by the GC much the same as weak references in Java.

I am not saying you are using it the same way as one would normally use weak references in other languages.

Peter Mogensen

unread,
Sep 28, 2018, 8:56:30 AM9/28/18
to Henrik Johansson, Robert Engels, golang-nuts


On 09/28/2018 02:53 PM, Henrik Johansson wrote:
> How is storing unintptrs in a map different from say java.util.WeakHashMap?
> The data pointed to by the uintptrs can at any given time have been
> reclaimed by the GC much the same as weak references in Java.
>
> I am not saying you are using it the same way as one would normally use
> weak references in other languages.

As I said ... I have no experience with java.util.WeakHashMap and that's
not where I have the inspiration from. But I have experience with using
weak references with other garbage collectors to avoid leaks due to
cycles - and that's not what I'm doing here.

/Peter

Jan Mercl

unread,
Sep 28, 2018, 8:58:15 AM9/28/18
to Henrik Johansson, Peter Mogensen, Robert Engels, golang-nuts
On Fri, Sep 28, 2018 at 2:53 PM Henrik Johansson <dahan...@gmail.com> wrote:

> The data pointed to by the uintptrs ...

Uintptrs are integers. They do not point to anything.
--

-j

Henrik Johansson

unread,
Sep 28, 2018, 9:00:48 AM9/28/18
to Jan Mercl, Peter Mogensen, Robert Engels, golang-nuts
That's clever but irrelevant for this discussion.

Robert Engels

unread,
Sep 28, 2018, 9:12:22 AM9/28/18
to Henrik Johansson, Jan Mercl, Peter Mogensen, golang-nuts
His statement is correct. First of all, a weak reference in java is not like a weak pointer in C++, at least they are not needed to break cycles, as the GC is immune to that issue. The difference is that a weak hashmap uses weak references to refer to the contained objects so that they will be collected if nothing else refers to them, similar to a Lru cache. In this case it is a uintptr which is not a reference to anything, it is just an integer. 

Peter Mogensen

unread,
Sep 28, 2018, 9:14:58 AM9/28/18
to golan...@googlegroups.com


On 09/28/2018 03:11 PM, Robert Engels wrote:
> His statement is correct. First of all, a weak reference in java is not
> like a weak pointer in C++, at least they are not needed to break
> cycles, as the GC is immune to that issue. The difference is that a weak
> hashmap uses weak references to refer to the contained objects so that
> they will be collected if nothing else refers to them, similar to a Lru
> cache. In this case it is a uintptr which is not a reference to
> anything, it is just an integer. 

Sure... Ok... it sounds like it might be the same thing I was
considering whether could be done.

In other words.. using a map as index into another datastructure without
the map values being considered by the garbage collector as references.

/Peter

Henrik Johansson

unread,
Sep 28, 2018, 9:15:06 AM9/28/18
to Robert Engels, Jan Mercl, Peter Mogensen, golang-nuts
I know what a uintptr is but what would you put in it if not a pointer to another object?
Isn't this very analogous to what you said: "a weak hashmap uses weak references to refer to the contained objects so that they will be collected if nothing else refers to them".

Maybe I am missing something. I never meant to imply that they worked the same way _internally_ but at a conceptual level.

Tamás Gulácsi

unread,
Sep 28, 2018, 9:18:52 AM9/28/18
to golang-nuts

This is not that.
My idea could maybe be restated simpler as having a huge double-linked
list of sync.Pool objects and using wanting to supplement it by a map
index, but avoiding storing pointers in the map.


There's no such thing as a "list of sync.Pool objects" (if you refer the objects, not the pool(s)),
as anything you've Put into the pool should NOT be referenced, and may disappear anytime.

So they sould be either in your map, or in the sync.Pool.

sync.Pool is just to amortize the creation/deletion cost of objects, nothing more.

And just as you've found out, to avoid costly GC pointer chasing in maps, use a slice of your objects, and store their indices in the map.


Peter Mogensen

unread,
Sep 28, 2018, 9:22:36 AM9/28/18
to golan...@googlegroups.com

On 09/28/2018 03:18 PM, Tamás Gulácsi wrote:
>
> This is not that.
> My idea could maybe be restated simpler as having a huge double-linked
> list of sync.Pool objects and using wanting to supplement it by a map
> index, but avoiding storing pointers in the map.
>
>
> There's no such thing as a "list of sync.Pool objects" (if you refer the
> objects, not the pool(s)),
> as anything you've Put into the pool should NOT be referenced, and may
> disappear anytime.

You misunderstood what I meant.

> So they sould be either in your map, or in the sync.Pool.

Yeah... That's what's been described the whole time.

> sync.Pool is just to amortize the creation/deletion cost of objects,
> nothing more.

I know

/Peter


Robert Engels

unread,
Sep 28, 2018, 9:25:06 AM9/28/18
to Peter Mogensen, golan...@googlegroups.com
You can always created the objects off heap, and use uintptr to address them, and they will never be moved on you.

The reason your idea doesn’t work is that a uintptr is not an object reference so it will not keep an object from being collected. If you have a object reference then the object is going to tracked by the GC anyway, no matter where it is referenced from, so you are not saving anything, as that reference still must be somewhere...

Use off heap memory of use the GC as designed. Trying to trick the GC is probably not going to be resilient, or even work, probably making things slower...

Peter Mogensen

unread,
Sep 28, 2018, 9:28:10 AM9/28/18
to Robert Engels, golan...@googlegroups.com


On 09/28/2018 03:24 PM, Robert Engels wrote:
> If you have a object reference then the object is going to tracked by the GC anyway, no matter where it is referenced from, so you are not saving anything, as that reference still must be somewhere...

... yes.
I haven't really done measurements to test whether the reference not
being in the map it self will speed up anything, but now I'm going in a
different direction.

/Peter



Robert Engels

unread,
Sep 28, 2018, 9:30:19 AM9/28/18
to Peter Mogensen, golan...@googlegroups.com
If you do the slice of structs and a map of indexes, so are essentially writing you own memory management. Not trivial when you need to dynamically create or destroy instances...

Use the GC as is unless you have a large static object set, it could work for that... but still be slower given the extra indirection not supported by the compiler...

Peter Mogensen

unread,
Sep 28, 2018, 9:31:39 AM9/28/18
to Robert Engels, golan...@googlegroups.com


On 09/28/2018 03:29 PM, Robert Engels wrote:
> Use the GC as is unless you have a large static object set, it could work for that...

That was a part of the whole premise (see subject).
A huge number of same type/size objects.

/Peter

Ian Lance Taylor

unread,
Sep 28, 2018, 9:43:52 AM9/28/18
to Peter Mogensen, Dan Kortschak, golang-nuts
On Thu, Sep 27, 2018 at 11:14 PM, Peter Mogensen <a...@one.com> wrote:
>
> Are you saying this statement from the Go spec is subject to change:
> "Pointer values are comparable. Two pointer values are equal if they
> point to the same variable or if both have value nil. Pointers to
> distinct zero-size variables may or may not be equal. "

That statement is not subject to change. Note that that statement
does not say that if p1 and p2 are pointer values, and that p1 == p2,
that uintptr(unsafe.Pointer(p1)) == uintptr(unsafe.Pointer(p2)). You
must not take intuitions from computer hardware into a managed memory
language like Go. The rules about the safe use of pointers converted
to uintptr are written down at https://golang.org/pkg/unsafe/#Pointer
. Doing anything not explicitly permitted there is unsafe, where
unsafe means that your program may suffer from arbitrary memory
corruption and may crash at any time.

Ian

Peter Mogensen

unread,
Sep 28, 2018, 10:21:02 AM9/28/18
to golan...@googlegroups.com


On 09/28/2018 03:43 PM, Ian Lance Taylor wrote:
> That statement is not subject to change. Note that that statement
> does not say that if p1 and p2 are pointer values, and that p1 == p2,
> that uintptr(unsafe.Pointer(p1)) == uintptr(unsafe.Pointer(p2)). You
> must not take intuitions from computer hardware into a managed memory
> language like Go. The rules about the safe use of pointers converted
> to uintptr are written down at https://golang.org/pkg/unsafe/#Pointer
> . Doing anything not explicitly permitted there is unsafe, where
> unsafe means that your program may suffer from arbitrary memory
> corruption and may crash at any time.


Ah yes... I'm not allowed to convert the uintptr back into a pointer.

Thanks.

/Peter

Dan Kortschak

unread,
Sep 28, 2018, 11:15:14 PM9/28/18
to Henrik Johansson, Robert Engels, Jan Mercl, Peter Mogensen, golang-nuts
You put in it a number that has the same bit pattern as a pointer to a
value. There is nothing more to it than that. It does not refer to
anything. They exist so you can do uncomfortable pointer arithmetic.

Robert Engels

unread,
Sep 28, 2018, 11:27:24 PM9/28/18
to Dan Kortschak, Henrik Johansson, Jan Mercl, Peter Mogensen, golang-nuts
That is just not true. Delete an item while who? some? hold an index reference. You have no way of knowing. That is the crux of memory management. If everything is static it is far simpler but most more than trivial applications need dynamic data structures.

Sent from my iPhone

Dan Kortschak

unread,
Sep 29, 2018, 4:39:33 AM9/29/18
to Robert Engels, Henrik Johansson, Jan Mercl, Peter Mogensen, golang-nuts
Please re-read what I was answering: "I know what a uintptr is but what
would you put in it if not a pointer to another object?".

If you have done that and still believe what I wrote is not true,
please explain what I have got wrong.

I am afraid I do not understand at all what you think is not true.

On Fri, 2018-09-28 at 22:26 -0500, Robert Engels wrote:
> That is just not true. Delete an item while who? some? hold an index
> reference. You have no way of knowing. That is the crux of memory
> management. If everything is static it is far simpler but most more
> than trivial applications need dynamic data structures. 
>
> Sent from my iPhone
>
> >
> > On Sep 28, 2018, at 10:14 PM, Dan Kortschak <dan.kortschak@adelaide

Robert Engels

unread,
Sep 29, 2018, 4:49:31 AM9/29/18
to Dan Kortschak, Henrik Johansson, Jan Mercl, Peter Mogensen, golang-nuts
I think I misunderstood what you were referring to or the point you were trying to make. I still am not certain though, so I’ll drop it and just assume we are saying the same thing. :)

Peter Mogensen

unread,
Sep 29, 2018, 4:51:43 AM9/29/18
to golan...@googlegroups.com


On 09/28/2018 03:14 PM, Peter Mogensen wrote:
> In other words.. using a map as index into another datastructure without
> the map values being considered by the garbage collector as references.

I just quickly read about the java.util.WeakHashMap and it says that the
keys are the weak references - not the values.
So it's not exactly what I was thinking of. I wanted the values as weak
references.
But as Ian Lance Taylor pointed out, the unsafe packages doesn't allow
this, since:
"Conversion of a uintptr back to Pointer is not valid in general. "

... which would be needed to use the map as an index.

I guess that's basically the summary of this thread. Thanks for your input.

/Peter
Reply all
Reply to author
Forward
0 new messages