weak references

3,371 views
Skip to first unread message

Dustin Sallings

unread,
Mar 4, 2013, 6:49:02 PM3/4/13
to golan...@googlegroups.com

I've been looking around for some information on weak references and,
while I've found a few open threads, I haven't seen much of a closure on
this, nor an issue opened on the project.

I'd like to have a weak reference. It seems like this requires GC
cooperation, though I'm open to a supportable strategy that doesn't
require any core changes.

A friendly irc user suggested something like this could theoretically
work: http://play.golang.org/p/IHRU2TwgF9

But in general, being able to build a map[thing]WeakReference would be
very useful for many caching scenarios. WeakReference.Get() ->
interface{} would be fine. Something typesafe would be even better.

Is there a writeup about this? Should I file an issue?

--
dustin

Kyle Lemons

unread,
Mar 4, 2013, 7:16:29 PM3/4/13
to Dustin Sallings, golang-nuts
I argue that this is not particularly useful for caches, because the behavior of your cache will change without corresponding code changes; what works today might break with another compiler or a new compiler version.  If the GC is sufficiently optimal, it seems like with a weak reference you would never find anything cached!



--
dustin

--
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/groups/opt_out.



Jens Alfke

unread,
Mar 4, 2013, 8:03:05 PM3/4/13
to golan...@googlegroups.com, Dustin Sallings


On Monday, March 4, 2013 4:16:29 PM UTC-8, Kyle Lemons wrote:
I argue that this is not particularly useful for caches, because the behavior of your cache will change without corresponding code changes; what works today might break with another compiler or a new compiler version.  If the GC is sufficiently optimal, it seems like with a weak reference you would never find anything cached!

In my experience a weak-map data structure is very useful for caching, because it lets you reuse objects without incurring memory "leaks" due to an ever-growing map. For example, if you keep a weak filename-to-image map in a UI app, you can ensure that multiple uses of the same icon in an app always reuse the same image, while still allowing unused images to be garbage collected. Without weak references, your only options are (a) create a new image on every call, resulting in potentially many redundant images, or (b) put images in a strong map to unique them, but never be able to free an unused image.

What you're pointing out is a limitation of the secondary use, of keeping an object around in the cache for "a while" even when nothing else is using it. This can be valuable, but you're right that the lifespan of such an object is indeterminate and may even be zero. However, in practice this is fairly practical; I believe that GCs that support weak references will usually assign a fairly low priority to zeroing them out _unless_ the system is under memory pressure. In part I'm sure this is done precisely because weak references are so often used for these types of caches.

Note that Java's had weak references and weak caches since 1.2 (actually they existed in 1.0 as private classes and were used in the HotJava browser). Cocoa has NSCache, and iOS developers are strongly encouraged to use it (and related weak-based data structures) to conserve memory.

--Jens

Maxim Khitrov

unread,
Mar 4, 2013, 8:06:09 PM3/4/13
to Dustin Sallings, golan...@googlegroups.com
Seems like doing ^uintptr would be just as effective and faster.
Another option is using cgo and storing pointers in memory allocated
by C.malloc. The only thing I'm not sure about is whether there is a
race condition between converting a weak reference into a strong one
and the GC performing its sweep.

- Max

Maxim Khitrov

unread,
Mar 4, 2013, 9:55:41 PM3/4/13
to Dustin Sallings, golan...@googlegroups.com
Here's a version that seems to work, but is completely untested and
may break in really interesting (read: obscure) ways:

http://play.golang.org/p/f9HY6-z8Pp

You should get the following output when you run it locally:

&{hello world}
<nil>

A map would require some additional logic to delete each WeakRef after
Get() returns nil.

- Max

Dmitry Vyukov

unread,
Mar 5, 2013, 1:56:05 AM3/5/13
to Jens Alfke, golang-nuts, Dustin Sallings
On Tue, Mar 5, 2013 at 3:03 AM, Jens Alfke <je...@mooseyard.com> wrote:
>> I argue that this is not particularly useful for caches, because the
>> behavior of your cache will change without corresponding code changes; what
>> works today might break with another compiler or a new compiler version. If
>> the GC is sufficiently optimal, it seems like with a weak reference you
>> would never find anything cached!
>
>
> In my experience a weak-map data structure is very useful for caching,
> because it lets you reuse objects without incurring memory "leaks" due to an
> ever-growing map. For example, if you keep a weak filename-to-image map in a
> UI app, you can ensure that multiple uses of the same icon in an app always
> reuse the same image, while still allowing unused images to be garbage
> collected. Without weak references, your only options are (a) create a new
> image on every call, resulting in potentially many redundant images, or (b)
> put images in a strong map to unique them, but never be able to free an
> unused image.


Why it is impossible to explicitly manage the cache in this case? I
mean explicitly decide what to cache, when to cache and for how long.

Jan Mercl

unread,
Mar 5, 2013, 2:13:41 AM3/5/13
to Dmitry Vyukov, Jens Alfke, golang-nuts, Dustin Sallings
On Tue, Mar 5, 2013 at 7:56 AM, Dmitry Vyukov <dvy...@google.com> wrote:
> Why it is impossible to explicitly manage the cache in this case? I
> mean explicitly decide what to cache, when to cache and for how long.

You're right, it is not impossible. Elsewhere I know about a DNS cache
implementing the required TTL management. It's just the fancy "weak
refs" tag is not attached to it.

-j
Message has been deleted

Dustin

unread,
Mar 5, 2013, 2:46:26 AM3/5/13
to golan...@googlegroups.com, Jens Alfke, Dustin Sallings

On Monday, March 4, 2013 10:56:05 PM UTC-8, Dmitry Vyukov wrote:
Why it is impossible to explicitly manage the cache in this case? I
mean explicitly decide what to cache, when to cache and for how long.

  It's also possible to explicitly manage memory, but we have a GC.

  I have objects that I don't *need* to be in memory for long periods of time, but it can be awfully convenient to hint to the GC that an object is less important than another object during GC.  I convert a strongref to a weakref when I'm not using the object at all, and copy out a strongref for the times when I need it.  When I need it and it's not there, I have a more expensive path to get it.  When that becomes a really common thing, I have a fairly consistent degradation path.

  In general, it seems like the kind of thing that one would expect from a gc (e.g. those coming from java, python, ruby, smalltalk, etc...), so at the very least, it seems like a canned response for why it isn't/shouldn't be in go might be a good thing to point to.  It's fine if the answer is something along the lines of, "we think this is a bad idea!"

  e.g. Kyle's argument for a straight cache is a good one, though I'm thinking about something closer to Jens' example where I am actually using a lot of the objects and may continue to use them, but my "central" reference is weak, only held by all of the live activity around the object until it's no longer available.

Dmitry Vyukov

unread,
Mar 5, 2013, 2:58:53 AM3/5/13
to Dustin, golang-nuts, Jens Alfke
On Tue, Mar 5, 2013 at 9:46 AM, Dustin <dsal...@gmail.com> wrote:
>
> On Monday, March 4, 2013 10:56:05 PM UTC-8, Dmitry Vyukov wrote:
>>
>> Why it is impossible to explicitly manage the cache in this case? I
>> mean explicitly decide what to cache, when to cache and for how long.
>
>
> It's also possible to explicitly manage memory, but we have a GC.
>
> I have objects that I don't *need* to be in memory for long periods of
> time, but it can be awfully convenient to hint to the GC that an object is
> less important than another object during GC. I convert a strongref to a
> weakref when I'm not using the object at all, and copy out a strongref for
> the times when I need it. When I need it and it's not there, I have a more
> expensive path to get it. When that becomes a really common thing, I have a
> fairly consistent degradation path.


I am not sure how one can want something along the lines of "discard
on the following GC" w/o any knowledge about what it actually means
(can be right now, never or at random time). What you are asking about
seems to be timed and/or memory limited cache component with some
tunable policy on when to discard cached objects. Such a component can
be implemented using some fancy references provided that it has some
intimate channels to and understanding of current GC, but that's an
implementation detail as well.

John Nagle

unread,
Mar 5, 2013, 3:38:32 AM3/5/13
to golan...@googlegroups.com
On 3/4/2013 11:58 PM, Dmitry Vyukov wrote:
> On Tue, Mar 5, 2013 at 9:46 AM, Dustin <dsal...@gmail.com> wrote:
>>
>> On Monday, March 4, 2013 10:56:05 PM UTC-8, Dmitry Vyukov wrote:

> I am not sure how one can want something along the lines of "discard
> on the following GC".

That's not usually what's meant by a "weak reference".

Weak references are useful in Python, which is mostly
reference-counted. If you have a tree-like data structure
with backlinks, backlinks should be weak references. Then
if you discard part of the tree, it gets released by the
reference count mechanism. But Go is purely GC, so
that's not a win.

The combination of weak references and parallelism introduce
some strange problems. You have to convert a weak reference
to a strong one before using it, and that conversion must be
an atomic operation. Now there's a locking problem associated
with weak references. Messy.

If you want to release excess memory held by some cache,
it's appropriate to keep a last-use number or timestamp
on each item, and periodically run a goroutine to drop old
items.

Hooking program semantics to the GC is generally a mistake.
Calling destructors/finalizers from a GC is extremely messy.
It's done in Microsoft Managed C++. Read this discussions:
http://www.codeproject.com/Articles/7965/Deterministic-Destruction-in-C-CLI
You do not want to take Go there.

John Nagle

Jan Mercl

unread,
Mar 5, 2013, 3:41:01 AM3/5/13
to John Nagle, golang-nuts
On Tue, Mar 5, 2013 at 9:38 AM, John Nagle <na...@animats.com> wrote:

Agreed.

-j

bryanturley

unread,
Mar 5, 2013, 1:21:31 PM3/5/13
to golan...@googlegroups.com, na...@animats.com
                                John Nagle


But I agree, I use some time based cache's with a dedicated cleanup goroutine.
To cleanup they just forget an object and eventually the gc grabs it.
Weak pointers have always scared me.

Dustin Sallings

unread,
Mar 5, 2013, 4:19:27 PM3/5/13
to golan...@googlegroups.com
Dmitry Vyukov <dvy...@google.com> writes:

> I am not sure how one can want something along the lines of "discard
> on the following GC" w/o any knowledge about what it actually means
> (can be right now, never or at random time). What you are asking about
> seems to be timed and/or memory limited cache component with some
> tunable policy on when to discard cached objects. Such a component can
> be implemented using some fancy references provided that it has some
> intimate channels to and understanding of current GC, but that's an
> implementation detail as well.

That's not what weak reference means.

It means something closer to "discard on some subsequent GC unless
there's a strong reference to this elsewhere."

That means that an API that provides centralized access to a resource
can have a quick lookup to get to common data quickly *without* forcing
that data to stay resident.

There are many ways to write this, of course, such as requiring the
API that accesses the structure to also return it to the API when
complete so I can maintain an (hopefully) accurate reference count and
remove the item from my map when the last caller accesses it.

But the GC already knows when nothing is referencing this data, and
is hopefully very accurate. Why would I want a complicated API to make
my callers tell me what the GC already knows?

The question isn't so much whether we can think of ways to use a weak
reference poorly, but whether it's something that should definitely not
be in the language. It seems slightly unusual to have a garbage
collector without having the ability to have weak references, so I was
hoping to find out whether the omission was intentional (with perhaps a
brief description as to why) or it's just not there yet.

Alternatively, perhaps there's a way to emulate weak references (using
some of the pointer obfuscation tricks and finalizers) that would be
considered safe enough to use.

--
dustin

Devon H. O'Dell

unread,
Mar 5, 2013, 4:32:46 PM3/5/13
to Dustin Sallings, golan...@googlegroups.com
2013/3/5 Dustin Sallings <dsal...@gmail.com>:
[snip]
> Alternatively, perhaps there's a way to emulate weak references (using
> some of the pointer obfuscation tricks and finalizers) that would be
> considered safe enough to use.

I'm not sure why (in Go) implementing an LRU or MRU or
random-replacement cache wouldn't suit your needs. You could even grow
it if necessary. The only thing weak references do is allow you to
ignore the size of your cache. (Maybe they also make it convenient to
use something with near-linear time searches, like a map, as the
backing store). I think that for a majority of workloads, not
understanding how large of a cache is useful means not understanding
the workload enough to even know whether the cache is going to
increase the performance of that workload.

I'm sure it's possible I'm missing something. Perhaps some examples of
where this would make sense and why would help. I've seen it (and done
it) in Java before (for caching results from Lucene searches), but in
retrospect, I think it was a really bad idea.

--dho

Ian Lance Taylor

unread,
Mar 5, 2013, 4:43:36 PM3/5/13
to Dustin Sallings, golan...@googlegroups.com
On Tue, Mar 5, 2013 at 1:19 PM, Dustin Sallings <dsal...@gmail.com> wrote:
>
> The question isn't so much whether we can think of ways to use a weak
> reference poorly, but whether it's something that should definitely not
> be in the language. It seems slightly unusual to have a garbage
> collector without having the ability to have weak references, so I was
> hoping to find out whether the omission was intentional (with perhaps a
> brief description as to why) or it's just not there yet.

I don't understand how to use a weak reference safely in a parallel
program without using special APIs to get and set the value.

That suggests that there is no need for it in the language proper, but
it could perhaps be added to the runtime package at some point in the
future.

To me it seems like a minor convenience. A weak reference is
effectively a cache. Having the GC manage the cache for you is handy
but on the other hand you give up the ability to control the
replacement strategy or the size of the cache.

Ian

Dustin Sallings

unread,
Mar 5, 2013, 4:54:29 PM3/5/13
to golan...@googlegroups.com
Ian Lance Taylor <ia...@golang.org> writes:

> I don't understand how to use a weak reference safely in a parallel
> program without using special APIs to get and set the value.
>
> That suggests that there is no need for it in the language proper, but
> it could perhaps be added to the runtime package at some point in the
> future.

I agree with all of the above.

> To me it seems like a minor convenience. A weak reference is
> effectively a cache. Having the GC manage the cache for you is handy
> but on the other hand you give up the ability to control the
> replacement strategy or the size of the cache.

I suppose I've not been thinking about what I'm trying to build
currently as being a cache, but about how I'd do it with a weak
reference.

Effectively, I've got a plain map. Things come into it and I can get
them from that map. One of the things that takes things from that map
will persist them to longer-term storage. At that point, I can replace
the strong reference with a weaker one. I may still have other strong
references around, so it's nice to keep a consistent API for accessing
data, but it's not as nice to try to know whether or not all of the
things that are ephemerally using the data are finished with their
tasks.

The alternative is copying the data around more and treating the map
as a cache with the ability to pin things until they're durable.

I haven't heard anything that sounds like "weak references will never
be in go," though it does sounds like many have run into issues in a lot
of application of them.

It also sounds like I can get by with the ^ptr tricks if I wanted to
try to make a package that does the work for me.

Thanks for the discussion.

--
dustin

Jens Alfke

unread,
Mar 5, 2013, 5:06:39 PM3/5/13
to Ian Lance Taylor, Dustin Sallings, golan...@googlegroups.com

On Mar 5, 2013, at 1:43 PM, Ian Lance Taylor <ia...@golang.org> wrote:

I don't understand how to use a weak reference safely in a parallel
program without using special APIs to get and set the value.

All it takes is for reading a weak reference (into a local variable or machine register or whatever) to be atomic with respect to the collector — once the value’s been read out, it exists as a non-weak reference in the thread’s CPU state, which will keep the referred-to object from being collected. The only danger is if the read is _not_ atomic, and one goroutine copies the weak ref to a variable while the collector simultaneously collects the object, leaving a dangling pointer.

(My knowledge of GC algorithms is old and superficial, but this seems to fall into the category of a read barrier.)

To me it seems like a minor convenience.  A weak reference is
effectively a cache.   Having the GC manage the cache for you is handy
but on the other hand you give up the ability to control the
replacement strategy or the size of the cache.

What everyone here is missing is that there’s a big win to allowing the GC to manage cache eviction. In a memory-constrained environment (i.e. anything but a server or a development machine) you would generally like the amount of stuff kept in cache to be determined by the amount of available RAM or address space, and that’s something that changes greatly between machines or even during a single run. It would suck for my cache implementation to have to keep track of OS-level stuff like this. Also, my cache has no idea how many other caches are pursuing similar strategies in the same app; it would be sub-optimal if all of the caches detected the same low-memory signal and flushed themselves, when flushing just one would do. Additionally, flushing objects that have other references to them will have no effect on memory usage, and will actually hurt because the next time the cache is asked for that object it will have to create a new duplicate copy.

In all of these cases the GC has a better perspective. It is presumably intimately aware of available address space, and possibly of memory pressure (there are a number of VM-savvy GC algorithms.) It knows about all of the weak references in the entire process, and it can free some of them at a time and stop when enough garbage is freed.

iOS and Mac OS X have a number of weak-reference/cache mechanisms for this reason. They can be pretty important to keeping an application running smoothly in the RAM-limited environment of an iPhone. I suspect that most Go developers are focusing on server-side code, so these types of concerns may be less obvious to you.

—Jens

Patrick Mylund Nielsen

unread,
Mar 5, 2013, 5:20:54 PM3/5/13
to Jens Alfke, Ian Lance Taylor, Dustin Sallings, golang-nuts
This is the best argument for weak references. I would say that it's obvious to a lot of devs--I've gotten a ton of feature requests for some kind of memory/OOM-based eviction strategy for just my caching packages--and some others, like Vitess' LRU cache, let you implement a Sizer interface to aid in eviction. No package exists, AFAIK, which is flexible or effective when you _don't_ know exactly how much memory the package is allowed to use, and don't want to use too little, i.e. when you want to say "I would like to use the memory I'm not using to cache stuff." OTOH, careless use would likely greatly increase GC overhead when memory is more constrained than the developer expected.

Dustin Sallings

unread,
Mar 5, 2013, 5:24:56 PM3/5/13
to golan...@googlegroups.com
Patrick Mylund Nielsen
<pat...@patrickmylund.com> writes:

> This is the best argument for weak references. I would say that it's
> obvious to a lot of devs--I've gotten a ton of feature requests for
> some kind of memory/OOM-based eviction strategy for just my caching
> packages--and some others, like Vitess' LRU cache, let you implement a
> Sizer interface to aid in eviction. No package exists, AFAIK, which is
> flexible or effective when you _don't_ know exactly how much memory
> the package is allowed to use, and don't want to use too little, i.e.
> when you want to say "I would like to use the memory I'm not using to
> cache stuff." OTOH, careless use would likely greatly increase GC
> overhead when memory is more constrained than the developer expected.

This sounds closer to java's SoftReference + the ability to express
the maximum amount of memory the application should use. I think this
is also useful, but it's slightly different.

Strong reference: "hands off, gc"
Weak reference: "0 this if no strong reference exists"
Soft refernce: "0 this if no strong reference exists and we're low
on RAM"

--
dustin

Jan Mercl

unread,
Mar 5, 2013, 5:43:35 PM3/5/13
to Dustin Sallings, golang-nuts
On Tue, Mar 5, 2013 at 11:24 PM, Dustin Sallings <dsal...@gmail.com> wrote:
> Strong reference: "hands off, gc"
> Weak reference: "0 this if no strong reference exists"
> Soft refernce: "0 this if no strong reference exists and we're low
> on RAM"

This is magic, even though feasible by the runtime. Go excels in
explicitness, even though that may mean more user coding. My feeling
is that those two approaches don't mix well, to put it mildly.

-j

Steve McCoy

unread,
Mar 5, 2013, 6:13:05 PM3/5/13
to golan...@googlegroups.com, Dustin Sallings
It's not magic; it's not as though people won't know they have an instance of a weak reference on their hands.

Anyhow, I don't care if Go ever gets them, but there are other fun things that can be done with weak references that don't have anything to do with caches. I wrote a Java tool+package that keeps track of every object created while the program runs and asserts some preconditions whenever an object is accessed. Of course, memory would be extremely wasted if I kept them all in a map with strong references for the keys, so I instead used a WeakHashMap, allowing the garbage collector to clean things up in a typical fashion. Using explicit reference counting would've been painful for that.

Jens Alfke

unread,
Mar 5, 2013, 6:14:21 PM3/5/13
to Jan Mercl, Dustin Sallings, golang-nuts
On Mar 5, 2013, at 2:43 PM, Jan Mercl <0xjnml@gmail.com> wrote:

This is magic, even though feasible by the runtime.

Not any more than goroutine scheduling is magic or garbage collection is magic.

Go excels in
explicitness, even though that may mean more user coding. My feeling
is that those two approaches don't mix well, to put it mildly.

It’s not a matter of language design, it’s a matter of efficient memory usage. It’s not a built-in language feature in Java either — I’m fine if it ends up in a library in Go, as long as it’s available and supported. Otherwise memory bloat could be a serious issue for the use of Go in non-server environments (such as the Raspberry Pi I just got my little Go server running on, which has only 256MB of RAM, up to half of which can be grabbed by the GPU.)

—Jens

Jens Alfke

unread,
Mar 5, 2013, 6:21:29 PM3/5/13
to Steve McCoy, golan...@googlegroups.com, Dustin Sallings

On Mar 5, 2013, at 3:13 PM, Steve McCoy <mcc...@gmail.com> wrote:

Anyhow, I don't care if Go ever gets them, but there are other fun things that can be done with weak references that don't have anything to do with caches.

They’re also useful for uniquing objects, of which Java’s String.intern is a simple example. Unique objects have nice characteristics, such as being able to compare them using pointer equality, and avoiding any need for coordinating state changes among multiple objects representing the same thing. (For example, I like this pattern for objects representing database rows/records.)

These objects are generally managed with a central map that will return an existing object or generate a new one if it doesn’t exist yet. Without weak references, this map grows monotonically as no uniqued object will ever get collected. (Unless you resort to workaround like a retain/release API, i.e. building ref-counting on top of a GC system, which seems perverse.)

—Jens

bryanturley

unread,
Mar 5, 2013, 6:41:52 PM3/5/13
to golan...@googlegroups.com, Steve McCoy, Dustin Sallings


On Tuesday, March 5, 2013 5:21:29 PM UTC-6, Jens Alfke wrote:

On Mar 5, 2013, at 3:13 PM, Steve McCoy <mcc...@gmail.com> wrote:

Anyhow, I don't care if Go ever gets them, but there are other fun things that can be done with weak references that don't have anything to do with caches.

They’re also useful for uniquing objects, of which Java’s String.intern is a simple example. Unique objects have nice characteristics, such as being able to compare them using pointer equality, and avoiding any need for coordinating state changes among multiple objects representing the same thing. (For example, I like this pattern for objects representing database rows/records.)


Test on pointer equality are exactly why I don't like weak refs
What if it gets collected and something of the exact same type gets allocated exactly where it used to live?
To detect that you would need two kinds of references defined for each object of that type, a pointer and some unique key.
 
These objects are generally managed with a central map that will return an existing object or generate a new one if it doesn’t exist yet. Without weak references, this map grows monotonically as no uniqued object will ever get collected. (Unless you resort to workaround like a retain/release API, i.e. building ref-counting on top of a GC system, which seems perverse.)


I use a map[X] structs this way now, but with a TTL value in the struct for forgetting and eventual normal gc collection.
Probably not the best if you get a large number of entries in the map but I don't so it works for me.
Not that you couldn't use some other data structures to back this style.

—Jens

For real small memory embedded systems you usually preallocate everything since you operate in somewhat controlled way and the system doesn't have enough resources to really multitask anyways.
For modern smartphone-esque systems I would just treat them like a desktop PC from the early 2000s which fits the current go model.

Rémy Oudompheng

unread,
Mar 5, 2013, 6:51:15 PM3/5/13
to Dustin, golang-nuts

I understand what a soft reference can be useful for, but what is the use for a weak reference since GC could happen anytime?

Remy

Kyle Lemons

unread,
Mar 5, 2013, 7:03:27 PM3/5/13
to Rémy Oudompheng, Dustin, golang-nuts
The most compelling thing I've heard is to provide a map of e.g. filename to in-memory buffer containing the file contents.  In other words, if someone else read it in, you can reuse their buffer, otherwise you read it in yourself.  So far this is like a normal cache; the difference with using weak references would be that you leave it to be collected by the garbage collector, which gives other processes running alongside or shortly after you a chance to make use of the data.  Depending on environmental factors, the GC may be reaping faster or slower than normal, and you don't have to try to infer such behavior in user-space code to try to play nicely and know when to purge references yourself, it would all "just work."

I could certainly be convinced that this is useful, though I'm not sure I like the idea of these syntactically:

map[string]weak []byte or map[string]weak *Data

Remy

Ian Lance Taylor

unread,
Mar 5, 2013, 8:02:15 PM3/5/13
to Jens Alfke, Steve McCoy, golan...@googlegroups.com, Dustin Sallings
This can be done in Go using finalizers.

Ian

Kevin Gillette

unread,
Mar 5, 2013, 8:03:19 PM3/5/13
to golan...@googlegroups.com, na...@animats.com
I could see weak references working in a fully GC'd language, from a technical point of view... I think they'd have to semantically be weak-pointers in Go (rather than another "reference" type), regardless of whether any book-keeping is kept alongside the pointer or in some global location. On any GC sweep, if a weak pointer is encountered, its pointed-to address is added to a sweep table, and evicted if a corresponding normal pointer is found (to prevent the need for an additional sweep, you'd actually have to also add every normal pointer's pointed address to a table in case there could be corresponding weak pointers). At the end of the sweep, if there are any addresses held only by weak pointers, those would need to be set to nil (rather than leave dangling pointers).

This model treats all pointers as strong, except during a GC sweep, which is the only time weak pointers are treated weakly. A few (unfortunate) side effects of this design:

1) It favors a stop-the-world GC system, which we're trying to move away from, since the following code that's reasonable and safe for normal pointers in normal conditions, now becomes a blatant race condition for weak pointers:

if p == nil {
  // do something
} else {
  // do something with *p
}

2a) due to #1, it would suggest that a two-part assignment form for pointers would also be needed to make the check atomic:

if v, ok := *p; !ok {
  // p was nil
} else {
  // v == *p
}

2b) alternatively, every check of weak pointer would need to be wrapped by a mutex, or be explicitly converted to a normal pointer, with language guarantees that the operation is atomic (and that the GC will monitor or adapt to data that resurrects during the sweep).  An example:

sp := wp // assumed strong pointer
if sp == nil {
  // do something
} else {
  // do something with *sp
}

In this case, perhaps accessing the value of a weak pointer would need to be illegal.

3) syntactically, I'm not sure how we'd set up weak pointers -- a new builtin sounds most reasonable (since extra unary syntax for just this would, imo, be ridiculous):

sp1 := new(int)
wp1 := weak(sp1)
sp2 := wp1       // normal pointer
wp2 := weak(wp1) // another weak pointer

// two ways to declare the zero value of a weak *int
var nilwp1 *int = weak(nil)
nilwp2 := weak((*int)(nil))

In this design, the 'weak' builtin must take a pointer, but the input and output types for the above example are still `*int`, even though it would internally be tracked as a weak *int.

4) weak reference types may need special handling. Clearly a weak *map[string]string is less desirable to use in practice than a weak "map value".

5) the runtime can't make good (at least not portable) decisions about when and if to free memory -- in some scenarios, the runtime might notice it has no longer used pages to spare while at the same time starving the other processes on the system. In this case, the runtime is the worst thing to handle caching, since it can't know what the app designer wants (beyond what limited, if even applicable, static or possible runtime analysis information is available), and it cannot reasonably know the state of the system it's running in. Arranging values that could be pointed at weakly to be allocated in pages marked with the DONTNEED madvise flag once all normal pointers disappear could be used to allow the OS to mediate a cache. As mentioned before, this is not strictly the purpose of weak references, but it seems like that's what people want. 

Anyway, there are conventional alternatives. I personally prefer, when I need caching, to use a map with string (or whatever) keys, and keep all references to that cache via keys -- a managing goroutine could free non-recently-used keys periodically, and all holders of a given key act like weak refs -- they must request the value behind that key and possibly get a not-present result. Keeping the application logic in the application has the benefit of addressing application concerns flexibly: maybe the managing goroutine knows how to regenerate values, or perhaps the requesting goroutine must acquire a lock and generate the value itself. There may be further applications related to weakly held values that conventional weak references simply could not handle effectively.

Ian Lance Taylor

unread,
Mar 5, 2013, 8:05:47 PM3/5/13
to bryanturley, golan...@googlegroups.com, Steve McCoy, Dustin Sallings
On Tue, Mar 5, 2013 at 3:41 PM, bryanturley <bryan...@gmail.com> wrote:
>
> Test on pointer equality are exactly why I don't like weak refs
> What if it gets collected and something of the exact same type gets
> allocated exactly where it used to live?
> To detect that you would need two kinds of references defined for each
> object of that type, a pointer and some unique key.

The idea is that if the weak reference is discarded, it is set to
zero. You can never have a pointer to a freed weak reference; that
pointer would have been destroyed. So this error can not happen.

Ian

bryanturley

unread,
Mar 5, 2013, 8:19:17 PM3/5/13
to golan...@googlegroups.com, bryanturley, Steve McCoy, Dustin Sallings

Yeah in a gc world I can see this, it would always require a stop the world gc though wouldn't it?
I think the first implementation of weak refs I worked with was done very badly and I have been strongly biased against them since.

Ian Lance Taylor

unread,
Mar 5, 2013, 8:33:19 PM3/5/13
to bryanturley, golan...@googlegroups.com, Steve McCoy, Dustin Sallings
On Tue, Mar 5, 2013 at 5:19 PM, bryanturley <bryan...@gmail.com> wrote:
> On Tuesday, March 5, 2013 7:05:47 PM UTC-6, Ian Lance Taylor wrote:
>>
>> On Tue, Mar 5, 2013 at 3:41 PM, bryanturley <bryan...@gmail.com> wrote:
>> >
>> > Test on pointer equality are exactly why I don't like weak refs
>> > What if it gets collected and something of the exact same type gets
>> > allocated exactly where it used to live?
>> > To detect that you would need two kinds of references defined for each
>> > object of that type, a pointer and some unique key.
>>
>> The idea is that if the weak reference is discarded, it is set to
>> zero. You can never have a pointer to a freed weak reference; that
>> pointer would have been destroyed. So this error can not happen.
>>
>> Ian
>
>
> Yeah in a gc world I can see this, it would always require a stop the world
> gc though wouldn't it?

In my opinion weak references should not be added to the language. If
Go ever has them, they should only be implemented via a runtime API.
With that approach, a stop-the-world GC is not required. If anybody
is holding a pointer that they retrieved via the runtime API, the
object will not be freed. The only time it would be freed would be if
the only references were in the internal runtime structure. An entry
in that structure could be zeroed without a stop-the-world GC,
whenever the object itself was freed.

Ian

Jens Alfke

unread,
Mar 5, 2013, 9:08:06 PM3/5/13
to Rémy Oudompheng, Dustin, golang-nuts

On Mar 5, 2013, at 3:51 PM, Rémy Oudompheng <remyoud...@gmail.com> wrote:

> I understand what a soft reference can be useful for, but what is the use for a weak reference since GC could happen anytime?

See any of my messages on this thread. They mostly apply to either type of weak reference, especially the “unique objects” pattern.

—Jens

Jens Alfke

unread,
Mar 5, 2013, 9:12:03 PM3/5/13
to Ian Lance Taylor, bryanturley, golan...@googlegroups.com, Steve McCoy, Dustin Sallings

On Mar 5, 2013, at 5:33 PM, Ian Lance Taylor <ia...@golang.org> wrote:

In my opinion weak references should not be added to the language.  If
Go ever has them, they should only be implemented via a runtime API.
With that approach, a stop-the-world GC is not required.  If anybody
is holding a pointer that they retrieved via the runtime API, the
object will not be freed.

Sure, that makes sense to me. There’s some convenience to having this at the language level (a la the “__weak” attribute specifier in Clang) but for the most part it seems like it would work equally well via some special opaque type like WeakRef or WeakMap.

I know a few examples have been shown in this thread of how to do this by messing around with unsafe pointers and finalizers; are these things that could safely be used in real apps, or are they dangerously unreliable?

—Jens

Rémy Oudompheng

unread,
Mar 6, 2013, 2:36:21 AM3/6/13
to Jens Alfke, Steve McCoy, golan...@googlegroups.com, Dustin Sallings
The interning example is the most convincing use for weak references.
But freeing the pointed object does not delete the key from the map,
so it is not purely a GC issue. The map must be treated specially
otherwise it will still grow indefinitely (but filled with nil
pointers)

Rémy.

GreatOdinsRaven

unread,
Mar 6, 2013, 4:14:56 PM3/6/13
to golan...@googlegroups.com, Dustin, Jens Alfke
In other GC languages I've used, e.g. Java and C#, a weak reference means "Release the memory if no strong (any regular reference that isn't weak) references are left pointing to it, but I still want a (potentially null) reference to it". I can count the number of times I've had to use them on the fingers of one hand, but they can be pretty useful , e.g. if you're trying to cache large amounts of data and you don't want it to stick around for too long. It's an edge-case optimization technique that can be abused, for sure. 

C#:

Java:


On Tuesday, March 5, 2013 12:58:53 AM UTC-7, Dmitry Vyukov wrote:
On Tue, Mar 5, 2013 at 9:46 AM, Dustin <dsal...@gmail.com> wrote:
>
> On Monday, March 4, 2013 10:56:05 PM UTC-8, Dmitry Vyukov wrote:
>>
>> Why it is impossible to explicitly manage the cache in this case? I
>> mean explicitly decide what to cache, when to cache and for how long.
>
>
>   It's also possible to explicitly manage memory, but we have a GC.
>
>   I have objects that I don't *need* to be in memory for long periods of
> time, but it can be awfully convenient to hint to the GC that an object is
> less important than another object during GC.  I convert a strongref to a
> weakref when I'm not using the object at all, and copy out a strongref for
> the times when I need it.  When I need it and it's not there, I have a more
> expensive path to get it.  When that becomes a really common thing, I have a
> fairly consistent degradation path.


I am not sure how one can want something along the lines of "discard
on the following GC" w/o any knowledge about what it actually means
(can be right now, never or at random time). What you are asking about
seems to be timed and/or memory limited cache component with some
tunable policy on when to discard cached objects. Such a component can
be implemented using some fancy references provided that it has some
intimate channels to and understanding of current GC, but that's an
implementation detail as well.



>   In general, it seems like the kind of thing that one would expect from a
> gc (e.g. those coming from java, python, ruby, smalltalk, etc...), so at the
> very least, it seems like a canned response for why it isn't/shouldn't be in
> go might be a good thing to point to.  It's fine if the answer is something
> along the lines of, "we think this is a bad idea!"
>
>   e.g. Kyle's argument for a straight cache is a good one, though I'm
> thinking about something closer to Jens' example where I am actually using a
> lot of the objects and may continue to use them, but my "central" reference
> is weak, only held by all of the live activity around the object until it's
> no longer available.

tgpti...@gmail.com

unread,
Jun 13, 2019, 11:52:40 AM6/13/19
to golang-nuts
I find WeakReferences when implementing observer pattern: A service generate events and signal listeners that the event was triggered. This usually requires the service to have a list to all of the listeners. Now, without weak refernece it means that any temporary listener will not be garbage collected and thus we will have memory grow without limits, since the listeners will not be collected as they are referenced by the service. When it comes to this, WeakReferences are mandatory and unfortunately Go doesn't support them :'(

Ian Lance Taylor

unread,
Jun 13, 2019, 12:03:46 PM6/13/19
to tgpti...@gmail.com, golang-nuts
On Thu, Jun 13, 2019 at 8:52 AM <tgpti...@gmail.com> wrote:
>
> I find WeakReferences when implementing observer pattern: A service generate events and signal listeners that the event was triggered. This usually requires the service to have a list to all of the listeners. Now, without weak refernece it means that any temporary listener will not be garbage collected and thus we will have memory grow without limits, since the listeners will not be collected as they are referenced by the service. When it comes to this, WeakReferences are mandatory and unfortunately Go doesn't support them :'(

Go has finalizers (https://golang.org/pkg/runtime/#SetFinalizer). A
finalizer on a handle object can be used to implement what you
describe.

Ian


> terça-feira, 5 de Março de 2013 às 23:13:05 UTC, Steve McCoy escreveu:
>>
>>
>>
>> On Tuesday, March 5, 2013 5:43:35 PM UTC-5, Jan Mercl wrote:
>>>
>>> On Tue, Mar 5, 2013 at 11:24 PM, Dustin Sallings <dsal...@gmail.com> wrote:
>>> > Strong reference: "hands off, gc"
>>> > Weak reference: "0 this if no strong reference exists"
>>> > Soft refernce: "0 this if no strong reference exists and we're low
>>> > on RAM"
>>>
>>> This is magic, even though feasible by the runtime. Go excels in
>>> explicitness, even though that may mean more user coding. My feeling
>>> is that those two approaches don't mix well, to put it mildly.
>>>
>>> -j
>>
>>
>> It's not magic; it's not as though people won't know they have an instance of a weak reference on their hands.
>>
>> Anyhow, I don't care if Go ever gets them, but there are other fun things that can be done with weak references that don't have anything to do with caches. I wrote a Java tool+package that keeps track of every object created while the program runs and asserts some preconditions whenever an object is accessed. Of course, memory would be extremely wasted if I kept them all in a map with strong references for the keys, so I instead used a WeakHashMap, allowing the garbage collector to clean things up in a typical fashion. Using explicit reference counting would've been painful for that.
>
> --
> 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.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/19d2f189-4dc6-406b-bd1e-45c1deeef054%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Robert Engels

unread,
Jun 13, 2019, 12:29:37 PM6/13/19
to tgpti...@gmail.com, golang-nuts
Isn't the "Go Way" for something like this to have the listeners explicitly unregister themselves ?

It does make certain event listener patterns more difficult, but I don't think it requires WeakRefs to implement.


-----Original Message-----
From: tgpti...@gmail.com
Sent: Jun 13, 2019 5:39 AM
To: golang-nuts
Subject: Re: [go-nuts] Re: weak references

I find WeakReferences when implementing observer pattern: A service generate events and signal listeners that the event was triggered. This usually requires the service to have a list to all of the listeners. Now, without weak refernece it means that any temporary listener will not be garbage collected and thus we will have memory grow without limits, since the listeners will not be collected as they are referenced by the service. When it comes to this, WeakReferences are mandatory and unfortunately Go doesn't support them :'(

terça-feira, 5 de Março de 2013 às 23:13:05 UTC, Steve McCoy escreveu:


On Tuesday, March 5, 2013 5:43:35 PM UTC-5, Jan Mercl wrote:
On Tue, Mar 5, 2013 at 11:24 PM, Dustin Sallings <dsal...@gmail.com> wrote:
>   Strong reference:  "hands off, gc"
>   Weak reference:    "0 this if no strong reference exists"
>   Soft refernce:     "0 this if no strong reference exists and we're low
>                       on RAM"

This is magic, even though feasible by the runtime. Go excels in
explicitness, even though that may mean more user coding. My feeling
is that those two approaches don't mix well, to put it mildly.

-j

It's not magic; it's not as though people won't know they have an instance of a weak reference on their hands.

Anyhow, I don't care if Go ever gets them, but there are other fun things that can be done with weak references that don't have anything to do with caches. I wrote a Java tool+package that keeps track of every object created while the program runs and asserts some preconditions whenever an object is accessed. Of course, memory would be extremely wasted if I kept them all in a map with strong references for the keys, so I instead used a WeakHashMap, allowing the garbage collector to clean things up in a typical fashion. Using explicit reference counting would've been painful for that.

tgpti...@gmail.com

unread,
Jun 13, 2019, 12:44:09 PM6/13/19
to golang-nuts
Yes, that is the "workaround". I mean, the lack of WeakReference makes Go (when implementing this pattern) at the same level as C/C++, since I have to know when I need to "delete" the resource in order to unsubscribe the event. I will have a look at SetFinalizer though, I haven't heard of it but I literally started programming Go yesterday x'D
To unsubscribe from this group and stop receiving emails from it, send an email to golan...@googlegroups.com.

Robert Engels

unread,
Jun 13, 2019, 3:10:17 PM6/13/19
to tgpti...@gmail.com, golang-nuts
That doesn't sound right. Whatever calls 'addListener', should be calling 'removeListener' - nothing needs to be detected.

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/5f6c64a4-f45b-4547-9e14-6039b97f26cb%40googlegroups.com.

Robert Engels

unread,
Jun 13, 2019, 3:35:45 PM6/13/19
to Ricardo Alves, tgpti...@gmail.com, golang-nuts
But using weak references for listeners is fraught with problems. The most common being that listeners are often anonymous inner classes (in something like Java) or at the point instantiated (closure), so if a direct reference is not held, the listener is would be immediately removed. Holding the reference to the inner instance is cumbersome - often requiring the developer to create an instance variable and assign to that. 

But you still need to manage the lifecycle of the object that was listening... so why not remove the listener then.

I don't think a finalizer helps in this case - since the service will have a hard reference to the listener, it will never be finalized... or GC'd, unless the removeListener is called anyway.

That being said, if the service is no longer referenced, all listeners will be GC'd - there will be no memory leak.

For example, Java Swing doesn't use weak listeners, but it does have weak references in some cases to the Window (i.e. Service), which contains references to all of the components (which are the listeners). When the Window goes out of scope, everything will be destroyed (cleaned up by GC).


-----Original Message-----
From: Ricardo Alves
Sent: Jun 13, 2019 2:12 PM
To: "tgpti...@gmail.com" , golang-nuts , Robert Engels
Subject: Re: [go-nuts] Re: weak references

Yes, I understand. But if I have a productive language that doesn't need me to call it better. In C/C++ it's also easy, whoever calls new should call delete, but trust me, I've had my troubles finding memory leaks in other people's code. With a large enterprise application made in Go and working with other people and observer pattern, sooner or later I will have the same trouble finding memory this "memory leak".


From: Robert Engels <ren...@ix.netcom.com>
Sent: Thursday, June 13, 2019 8:08:41 PM
To: tgpti...@gmail.com; golang-nuts

Ricardo Alves

unread,
Jun 13, 2019, 4:05:36 PM6/13/19
to tgpti...@gmail.com, golang-nuts, Robert Engels
Thank you for your response again. In the case where anonymous classes are used, the anonymous class would add it self as listeners as you said, but what happens is that the service where the listener will be added will have a weak reference to the this listener. This means that the listener can be collected anytime without calling the RemoveListener. I worked with projects which all the arquitechture was event based, and sometimes even small things like a field of a class are listening to something to keep it's value updated. Sometimes you end up with lots of listeners. Regarding this last case, imagine you have a class A that inside has a class B. Object B is listening to something but it will not be collected until object A is collected. So, Object B never really knows when he needs to stop listening. In this case I will have to come up with methods to communicate between the two objects or instead I will have to turn object A as a listener to act as a gateway to object B. This is not productive at all in these cases, that's why I like to have WeakReferences.

From: Robert Engels <ren...@ix.netcom.com>
Sent: Thursday, June 13, 2019 8:35:15 PM
To: Ricardo Alves; tgpti...@gmail.com; golang-nuts

Ricardo Alves

unread,
Jun 13, 2019, 4:05:36 PM6/13/19
to tgpti...@gmail.com, golang-nuts, Robert Engels
Yes, I understand. But if I have a productive language that doesn't need me to call it better. In C/C++ it's also easy, whoever calls new should call delete, but trust me, I've had my troubles finding memory leaks in other people's code. With a large enterprise application made in Go and working with other people and observer pattern, sooner or later I will have the same trouble finding memory this "memory leak".


From: Robert Engels <ren...@ix.netcom.com>
Sent: Thursday, June 13, 2019 8:08:41 PM
To: tgpti...@gmail.com; golang-nuts

Robert Engels

unread,
Jun 13, 2019, 4:21:24 PM6/13/19
to Ricardo Alves, tgpti...@gmail.com, golang-nuts
But if nothing else has a reference to the anonymous listener that was added it will be immediately collected - and since it is created at the invocation point usually - it's a problem.

here is the pseudo code:

s = Server() // Server uses weak listener references...

s.addListener(new Listener())

the listener will be immediately collected...

so try this

s = Server()

new ChildService(s)

with

ctor ChildService(Service s):
   s.addListener(new BaseListener(){// some overridden method})

The listener will also be immediately removed

The only way to prevent it is to use:

ctor ChildService(Service s):
   BaseListener sl = new BaseListener(){ with overridden method}; // sl must be named instance var
   s.addListener(sl);

That was the point I was trying to make - you can't use anonymous listener classes easily - which are often the bulk in many event driven designs.

But YES, having weak references at some top-level makes things much easier, but the "explicit nature" of Go, means knowing the life-cycle and manually removing the observers. For simple callbacks this is usually not necessary.




-----Original Message-----
From: Ricardo Alves
Sent: Jun 13, 2019 2:46 PM
To: "tgpti...@gmail.com" , golang-nuts , Robert Engels
Subject: Re: [go-nuts] Re: weak references

Thank you for your response again. In the case where anonymous classes are used, the anonymous class would add it self as listeners as you said, but what happens is that the service where the listener will be added will have a weak reference to the this listener. This means that the listener can be collected anytime without calling the RemoveListener. I worked with projects which all the arquitechture was event based, and sometimes even small things like a field of a class are listening to something to keep it's value updated. Sometimes you end up with lots of listeners. Regarding this last case, imagine you have a class A that inside has a class B. Object B is listening to something but it will not be collected until object A is collected. So, Object B never really knows when he needs to stop listening. In this case I will have to come up with methods to communicate between the two objects or instead I will have to turn object A as a listener to act as a gateway to object B. This is not productive at all in these cases, that's why I like to have WeakReferences.

From: Robert Engels <ren...@ix.netcom.com>

Sent: Thursday, June 13, 2019 8:35:15 PM
To: Ricardo Alves; tgpti...@gmail.com; golang-nuts

Subject: Re: [go-nuts] Re: weak references
But using weak references for listeners is fraught with problems. The most common being that listeners are often anonymous inner classes (in something like Java) or at the point instantiated (closure), so if a direct reference is not held, the listener is would be immediately removed. Holding the reference to the inner instance is cumbersome - often requiring the developer to create an instance variable and assign to that. 

But you still need to manage the lifecycle of the object that was listening... so why not remove the listener then.

I don't think a finalizer helps in this case - since the service will have a hard reference to the listener, it will never be finalized... or GC'd, unless the removeListener is called anyway.

That being said, if the service is no longer referenced, all listeners will be GC'd - there will be no memory leak.

For example, Java Swing doesn't use weak listeners, but it does have weak references in some cases to the Window (i.e. Service), which contains references to all of the components (which are the listeners). When the Window goes out of scope, everything will be destroyed (cleaned up by GC).

-----Original Message-----
From: Ricardo Alves
Sent: Jun 13, 2019 2:12 PM
To: "tgpti...@gmail.com" , golang-nuts , Robert Engels

ki...@nexedi.com

unread,
Jan 16, 2020, 9:04:45 AM1/16/20
to golang-nuts
For the reference: https://godoc.org/lab.nexedi.com/kirr/neo/go/zodb/internal/weak

Kirill


вторник, 5 марта 2013 г., 3:49:02 UTC+4 пользователь Dustin написал:

  I've been looking around for some information on weak references and,
while I've found a few open threads, I haven't seen much of a closure on
this, nor an issue opened on the project.

  I'd like to have a weak reference.  It seems like this requires GC
cooperation, though I'm open to a supportable strategy that doesn't
require any core changes.

  A friendly irc user suggested something like this could theoretically
work:  http://play.golang.org/p/IHRU2TwgF9

  But in general, being able to build a map[thing]WeakReference would be
very useful for many caching scenarios.  WeakReference.Get() ->
interface{} would be fine.  Something typesafe would be even better.

  Is there a writeup about this?  Should I file an issue?

--
dustin


Reply all
Reply to author
Forward
0 new messages