📜 ⬆️ ⬇️

Weak links in various programming languages

In languages ​​with automatic memory management, the garbage collector deletes objects when they are no longer accessible by reference. Usually this is exactly what you need: the object exists as long as it is possible to access it.
Sometimes this behavior is not suitable. For example, the program needed to store some supporting information about instances of a certain class, but you cannot add your field to this class. In this case, you can create a mapping, in which the key is the object, and the value is auxiliary information.
This is where the problems begin. Since the mapping stores references to keys, those objects to which the auxiliary information was tied cease to be freed from memory. If the program in the course of its work creates many objects, the memory ends sooner or later.
Weak links help to avoid such problems. Weak references differ from the usual ones in that they do not prevent the object from being deleted from memory. When the memory occupied by the object is released, all weak references pointing to it are reset. In some implementations, in this case, the user-defined handler is also called - finalizer.
In the example above, you need to replace the normal display with a display that stores weak references to keys. When the key disappears, the entry in the display automatically disappears along with it, after which the value can also be released.
Another example - a function by complex calculations gets a great value by a set of parameters. In order not to calculate the same thing several times, you can make a cache of calculated values. If a value is an object, then there is no point in deleting it from the cache as long as it is referenced. Here you can use a mapping as a cache with weak references to values, not keys.
Let's return to the example with supporting information. Sometimes it happens that the supporting information is an object, and it contains a link to the key. Mapping with weak references to keys ceases to work: since the reference to the value is not weak, and the value has a reference to the key, the key is always reachable through normal links, and the memory is not released. This problem can be solved by making the link from the value too weak.
But in some languages ​​this problem can be solved with the help of a special kind of weak links: ephemeron . Ephemeron supports links to two objects: a key and a value. As long as the key is reachable by normal links, neither the key nor the value is deleted. If the key is no longer reachable, then both links are reset and the key is released. The whole point is that key references from a value are not taken into account when determining reachability, so if in the example above we replace the mapping with weak references to keys with a map that uses ephemerons, then everything will work again.
From the point of view of the garbage collector, the presence of ephemeron creates a weak link from ephemeron to the key and a strong link from key to value. In some languages, such as Haskell, there is also a strong link from the key to the ephemeron itself, so that the ephemeron continues to exist as long as the key exists, which allows you not to save the link to the ephemeron.

C #


In C #, weak references are implemented in two classes: WeakReference and WeakReference<T> in the System namespace. These classes are distinguished by the fact that the second one is a generic type, that is, it allows you to specify the type of the object to which the link points, in addition, they have different interfaces for accessing the object that the link points to: the first one uses this properties IsAlive and Target , and the second - methods TryGetTarget and SetTarget .
Unlike other languages, C # allows you to change what object a weak reference points to after it has been created. Also weak links have a constructor that accepts an additional parameter of type bool : if its value is false , then a normal weak link is created, and if true , the link will behave like a PhantomReference in Java. In C #, weak links have no finalizers, as well as other ways to quickly find that one of the many weak links has been reset.

C ++


In C ++, the std::weak_ptr class is responsible for weak references, which is intended to be used in conjunction with the std::shared_ptr class. To work with an object to which a weak link points, you first need to get a strong link using the lock method.
')
Code example:
 #include <iostream> #include <memory> using namespace std; void print_weak(weak_ptr<int> w) { if (auto s = w.lock()) { cout << *s << endl; } else { cout << "   " << endl; } } int main(int argc, char *argv[]) { shared_ptr<int> s(new int(123)); weak_ptr<int> w(s); print_weak(w); s = NULL; print_weak(w); } 


Haskell


Unlike many other languages, Haskell supports ephemerons. All weak links are of type Weak , which is defined in the System.Mem.Weak module. In Haskell, a weak link will exist at least as long as the key is reachable by reference, so you can create a weak link with the finalizer, “forget” about it, and the finalizer will still be called. The addFinalizer function addFinalizer just that. To create a weak link, there are functions mkWeak and mkWeakPtr .

Code example:
 import Control.Exception import System.Mem.Weak main = do l <- getLine mkWeakPtr l (Just $ putStrLn "   ") evaluate $ sum [1..100000] 


Java


In Java, a weak reference is a separate object, and in order to work with an object pointed to by a weak reference, you must first obtain a regular reference using the get method. There are as many as 3 kinds of weak links: “soft” links (class SoftReference in the java.lang.ref ), common weak links ( WeakReference ) and “phantom” links ( PhantomReference ). They differ in that when the garbage collector will release objects reachable by such links. If an object is reachable via soft links, it will be freed only if there is not enough free space on the heap. Such links are well suited for all kinds of caches. Normal weak links are different from “phantom” ones when the link is reset: the first ones are reset before the finalizer is called, and the second ones after, just before the memory is released. To prevent access to finalized objects, the read function for the PhantomReference always returns null . The only way to benefit from them is to use link queues.
If the program uses a lot of weak links, then checking each of them, if it is not nullified, it can be expensive. Java does not allow you to set finalizers for weak links, but instead you can create a special object — ReferenceQueue — and set up weak links so that they add themselves when they are zeroed out. Now you can check not all links, but only one queue.
Also in Java there is a class WeakHashMap - an analogue of HashMap with weak references to keys.

Code example:
 import java.lang.ref.*; class WeakRefTest implements Runnable { //  ,    // ,     . public static void printWhenUnreachable(Object obj) { ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); WeakReference<Object> ref = new WeakReference<Object>(obj, queue); new Thread(new WeakRefTest(ref, queue)).start(); } private final WeakReference<Object> ref; private final ReferenceQueue<Object> queue; private WeakRefTest(WeakReference<Object> ref, ReferenceQueue<Object> queue) { this.ref = ref; this.queue = queue; } public void run() { try { queue.remove(); System.out.println("   "); } catch (InterruptedException e) { throw new RuntimeException(e); } } } 

Notice that the WeakRefTest class has a ref field that is not used. It is necessary so that the weak link itself is not released prematurely, otherwise it will not work.

Lua


Lua supports weak references through “weak tables” : in the corresponding metatable, the __mode key can be set to a string value, and then if it contains the k character, then the keys references will be weak, and if it contains the v character, then the references to values ​​will be weak . The type of the table can be changed after its creation, but the changes will take effect only at the next garbage collection. As in other implementations of tables with weak references, when a key or value is zeroed out, the entire record is deleted.
If the references to the keys are weak, and the values ​​are strong, then the records in the table will behave like ephemerons: a reference from the value to the key will not prevent the entry from being deleted.

Perl


In Perl, any link can be made weak using the weaken method of the Scalar::Util package. Unlike other languages, if you copy a weak link, the copy will be a regular link.

Python


Python supports weak references, but they can only be created on instances of those classes that support it. These include most custom classes and some built-in ones. In addition, in Python there are 2 classes for weak links: from instances of the class ref (package weakref ), the usual link to the object must be obtained explicitly, and with instances of the class proxy you can work directly.
When creating a weak reference, you can specify a finalizer. It is also possible for any object to get a list of weak links to it (normal, by the way, too).
Unlike Java, Python has as many as 3 types of collections with weak links: WeakSet , WeakKeyDictionary and WeakValueDictionary .

Code example:
 # -*- coding: utf8 -*- from weakref import * def finalizer(ref): print("   ") obj = set() reference = ref(obj, finalizer) del obj 

As in Java, if the reference is deleted before the object, the finalizer is not called.

Ruby


In Ruby, weak links are implemented in a single WeakRef class, which behaves similarly to the proxy class in Python, that is, you can work with a weak link in the same way as with the object itself. In addition to the methods of the object itself, weak links have a weakref_alive? method weakref_alive? , which returns true if the object to which the link points still exists, otherwise false .

UPD: sorted the languages ​​in alphabetical order, added C # and Lua, described the behavior of ephemerons in more detail.

Source: https://habr.com/ru/post/163679/


All Articles