rfc:weak_maps

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:weak_maps [2019/11/04 18:36] – Fix example nikicrfc:weak_maps [2020/01/03 09:56] (current) – Implemented nikic
Line 3: Line 3:
   * Author: Nikita Popov <nikic@php.net>   * Author: Nikita Popov <nikic@php.net>
   * Proposed Version: PHP 8.0   * Proposed Version: PHP 8.0
-  * Status: Under Discussion+  * Status: Implemented
   * Implementation: https://github.com/php/php-src/pull/4882   * Implementation: https://github.com/php/php-src/pull/4882
  
 ===== Introduction ===== ===== Introduction =====
  
-Weak maps allow creating a map from objects to arbitrary values (similar to SplObjectStorage) without preventing the objects that are used as keys from being garbage collected. If an object key is garbage collected, it will simply be removed from the map. This allows implementing caches and similar structures without causing memory leaks.+Weak maps allow creating a map from objects to arbitrary values (similar to SplObjectStorage) without preventing the objects that are used as keys from being garbage collected. If an object key is garbage collected, it will simply be removed from the map.
  
 In PHP 7.4 first-class support for [[rfc:weakrefs|weak references]] was already introduced. However, raw weak references are only of limited usefulness by themselves and weak maps are much more commonly used in practice. It is not possible to implement an efficient weak map on top of PHP weak references, because the ability to register a destruction callback is not provided. In PHP 7.4 first-class support for [[rfc:weakrefs|weak references]] was already introduced. However, raw weak references are only of limited usefulness by themselves and weak maps are much more commonly used in practice. It is not possible to implement an efficient weak map on top of PHP weak references, because the ability to register a destruction callback is not provided.
 +
 +The general use case for weak maps is to associate data with individual object instances, without forcing them to stay alive and thus effectively leak memory in long-running processes. For example, a weak map may be used to memoize a computation result:
 +
 +<PHP>
 +class FooBar {
 +    private WeakMap $cache;
 +    
 +    public function getSomethingWithCaching(object $obj) {
 +        return $this->cache[$obj] ??= $this->computeSomethingExpensive($obj);
 +    }
 +    
 +    // ...
 +}
 +</PHP>
 +
 +This will invoke the ''computeSomethingExpensive()'' method only once for each object. At the same time it will also drop the cached value from the map if the object is destroyed. Doing the same with a normal array (or rather ''SplObjectStorage'') would result in a memory leak.
  
 ===== Proposal ===== ===== Proposal =====
Line 67: Line 83:
 ===== Vote ===== ===== Vote =====
  
-Add WeakMap class in PHP 8.0? Yes/No.+Voting started 2019-12-18 and closes 2020-01-01. 
 + 
 +<doodle title="Add WeakMap class in PHP 8.0?" auth="nikic" voteType="single" closed="true"> 
 +   Yes 
 +   No 
 +</doodle> 
 + 
 +===== Differences to spl_object_id() and WeakReference ===== 
 + 
 +Weak maps require first-class language support and cannot be implemented using existing functionality provided by PHP. 
 + 
 +At first sight, it may seem that an array mapping from ''spl_object_id()'' to arbitrary values could serve the purpose of a weak map. This is not the case for multiple reasons: 
 + 
 +  * ''spl_object_id()'' values are reused after the object is destroyed. Two different objects can have the same object ID -- just not at the same time. 
 +  * The object ID cannot be converted back into an object, so iteration over the map is not possible. 
 +  * The value stored under the ID will not be released when the object is destroyed. 
 + 
 +Using the ''WeakReference'' class introduced in PHP 7.4, it is possible to avoid the first two issues, by using the following construction: 
 + 
 +<PHP> 
 +// Insertion 
 +$this->map[spl_object_id($object)] = [WeakReference::create($object), $data]; 
 + 
 +// Lookup 
 +$id = spl_object_id($object); 
 +if (isset($this->map[$id])) { 
 +    [$weakRef, $data] = $this->map[$id]; 
 +    if ($weakRef->get() === $object) { 
 +        return $data; 
 +    } 
 +    // This entry belongs to a destroyed object. 
 +    unset($this->map[$id]); 
 +
 +return null; 
 +</PHP> 
 + 
 +This makes use of the ''WeakReference'' to determine whether the object ID has been reused. However, this does not solve the third problem: The data will not be released when the object is destroyed. It will only be released on the next access with an object that has the same reused ID, or if a garbage collection mechanism, which performs regular sweeps of the whole map, is implemented.
  
 +A native weak map implementation will instead remove the value from the weak map as soon as the object key is destroyed.
rfc/weak_maps.1572892607.txt.gz · Last modified: 2019/11/04 18:36 by nikic