On Thursday, 27 April 2017 at 22:43:56 UTC, Moritz Maxeiner wrote:
You replied to the wrong person here, seeing as I did not link
to the article you're referring to,
Sorry...
so I'll reply:
Expanding the continuous memory region a dynamic array consists
of (possibly moving it) once it overflows has absolutely
nothing to do with the GC, or even the language, it's how the
abstract data type "dynamic array" is defined. D just does this
transparently for you by default. If you already know the exact
or maximum size, you can allocate *once* (not 6 times) using
`new` and `.reserve` respectively *before* entering the loop,
like that article explains in depth.
You seem to be missing the fact that i pointed this out. The fact
that the GC might have done up to 6 collection rounds in that
loop is "ludicrous".
Um, what? Memory (de)allocation (in C often malloc/free) and
object (de)contruction (in C usually functions with some naming
conventions like `type_init`/`type_deinit`) are on two entirely
different layers! Granted, they are often combined in C to
functions with names like `type_new`/`type_free`, but they are
conceptually two distinct things. Just to be very clear, here
is a primitive diagram of how things work:
make object O of type T:
<OS> --(allocate N bytes)--> [memory chunk M] --(call
constructor T(args) on M)--> [O]
dispose of O:
[O] --(call destructor ~T() on O)--> [memory chunk M]
--(deallocate M)--> <OS>
D's garbage collector conceptually changes this to:
make object O of type T:
<OS> --(GC allocates)--> [GC memory pool] --(allocate N
bytes)--> [memory chunk M] --(call constructor T(args) on M)-->
[O]
dispose of O:
[O] --(call destructor ~T() on O)--> [memory chunk M]
--(deallocate M)--> [GC memory pool] --(GC deallocates)--> <OS>
with the specific restriction that you have *no* control over
'GC deallocates' and only indirect control over 'GC allocates'
(by allocating more memory from the GC than is available its
the pool).
Working on the memory chunk layer is memory management.
Working on the object layer is object lifetime management.
D offers you both automatic memory management and automatic
lifetime management via its GC.
What you describe is manual object lifetime management (which
is what std.conv.emplace and object.destroy exist for) and has
no effect on the automatic memory management the GC performs.
You *can* do manual memory management *on top* of the GC's
memory pool (using core.memory.GC.{alloc/free) or the newer,
generic Alloactor interface via
std.experimental.allocator.gc_allocator.GCAllocator.{allocate/deallocate}), but these operations will (generally) not yield any observable difference from the OS's perspective.
That is assuming the GC removes the memory reference when you
call it. I remember seeing in some other languages ( C#
possibly? ) that referring a variable to be freed only meant
the GC freed the memory when it felt like it, not the exact
spot when you told it.
Again, you seem to mix object lifetime management and memory
management. You cannot tell the GC to free memory back to the
operating system (which is what the free syscall does and what
you seem to be describing). You can *only* free memory you
allocated *from* the GC *back* to the GC. The GC decides when
(and if) any memory is ever being freed back to the OS (which
is kinda one major point of having a GC in the first place).
I know, i do not express myself very well in English.
In my experience most people's aversion to GCs can be aptly
described by the German proverb "Was der Bauer nicht kennt, das
frisst er nicht" (the meaning of which being that most people
generally like living in comfort zones and rarely - if ever -
dare to leave them of their own free will; that includes
developers, sadly).
Unfortunately i do have plenty of experience with GC kicking in
on the wrong moments ( or not at the right moments, people forget
that one ).
I am sure that the amount of people who develop in GC languages
is much bigger these days then manual managed languages ( what is
more or less a rarity these days among the new languages ). Even
Rust still has some background management going on ( like the
byte counter ).
Maybe its my opinion only but a good language will not put
anything in the way of the developer but will point out mistakes.
The Rust compiler is not a bad example but it can be taken a step
more. Is it so hard for developers when you declare a variable,
to later also clean it up???
var x = 1;
// Do work
x.free;
Easy ... Sure, it becomes a little bit more tricky with ownership
but that is where the compiler can help and simply state: "Hey,
you forgot this variable, it does not seem to be used beyond this
point". Just calling the x.free seem to be too much work for
developers these days.
Up to now i found very few languages that did this correctly. But
this is a offtopic discussion.