I'm mulling over a couple of design considerations for allocators, and was thinking of the following restriction:

1. No out-of-bounds tricks and no pointer arithmetic. Consider:

int[] a = new int[1000];
a = a[250 .. 750];
int* p = a[500 .. $].ptr;

Subsequently the GC should be within its rights to deallocate any memory within the first and last 250 integers allocated, even though in theory the user may get to them by using pointer arithmetic.

In particular that means once a slice is shrunk, there's no growing back unless another slice is around.

I think the current GC already does that.

2. The same may be the case for classes WITHOUT destructors. Consider:

class A
{
   int[1000] a;
   int b;
   int[1000] c;
}
int* p = &(new A).b;

The collector should be allowed to deallocate any memory except b's own, even though that means the class has "holes" in it. The current GC does not do that.

2. However, the same shall not be the case for classes with destructors. Consider:

class B
{
   int[1000] a;
   int b;
   int[1000] c;
   ~this() { ... }
}
int* p = &(new B).b;

This class has a destructor, so it will be kept around in its entirety if an internal pointer is held.

3. Classes meant to have destructors called at collection will ALWAYS have been allocated with new (i.e. won't start in the middle of some other allocation). In other words, only class objects created with new will be properly collected. Those forced in odd places with emplace() are the responsibility of the user.

4. Currently, struct objects created with new do NOT have their destructors called during collection. I think this may continue, meaning that structs created with new are somewhat low-level and are meant to be destroyed and deallocated manually.

5. This brings up arrays of structs. As far as I understand, destructors will never be called for these, even after all references are gone:

struct S { ~this() { ... } }
auto a = new S[100];

Unlike (4), arrays of structs are high-level and frequently used. I think we must do something about it, so I plan to support calling destructors for arrays of structs.

6. The point above brings to mind more radical possibilities, such as making all arrays reference-counted and allowing compulsive deallocation when the reference counter goes down to zero. That would rule out things like escaping pointers to data inside arrays, which is quite extreme. But probably worth bringing up in a brainstorming. If we disallow statically constructs that take addresses we may get away with it.

Please chime in with your thoughts.

Andrei

Reply via email to