Weighted reference counting is more efficient than traditional reference counting because you don't have to update the reference counted object every time a reference is taken (potentially meaning updating the object on the heap and blowing the processor cache). Weighted reference counting only updates the referenced object when a reference is dropped or when weight is exhausted. Weight Reference Counting is also really good for concurrent systems because the number of synchronisations needed is much lower than normal reference counting.
Allocate a new reference counted object with a default weight (a tunable power of two - usually around 1 << 16
for a real implementation). An initial reference is allocated which points to the object and the weight value is copied into it. Every time the reference is cloned, the weight is split in two (shifting one bit to the right is the same as dividing by two) and one half allocated to the existing reference and one half allocated to the new reference.
If the a reference needs to be cloned and it's weight is only 1, then more weight is allocated on the referred to object and a new reference allocated and the whole splitting-and-copying behaviour starts over.
Whenever a reference is dropped it's weight is subtracted from the weight of the referred to object. When the object's weight drops to zero it is freed.
Weight reference counting (indeed all reference counting) stops working in the presence of circular references. Either a tracing garbage collector or a "weak" reference should be used to break cycles.
This example is implemented in Ruby, not because you'd ever need to do such a thing in Ruby, but because it's nice and easy to illustrate the concepts without a lot of boilerplate getting in the way. Also, I like Ruby.
Running the example code below outputs:
Created ReferenceCountedObject with weight 8
Created reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=8>, weight: 8
Dropping my weight from 8 to 4
Created reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=8>, weight: 4
Dropping my weight from 4 to 2
Created reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=8>, weight: 2
Dropping my weight from 2 to 1
Created reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=8>, weight: 1
Allocating more weight
Created reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=16>, weight: 8
Dropping reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=16>
Dropping reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=8>
Dropping reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=4>
Dropping reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=3>
Dropping reference to #<ReferenceCountedObject:0x007fd12489e278 @weight=1>
Weight is 0, drop.