If the symbol is the null string, we would read an invalid byte

When the symbol string is a null string, we would read the byte at
position -1, which is invalid for the string. So we need to check
whether last is actually a valid string offset here.
Reworked finalizers.

The objects to be finalized are now grouped into "generations." All the
objects in a generation are kept alive until all the finalizers are run.

The Ruby finalizers are run first, then the C finalizers, and finally, the
objects are "released", which primarily involves cleaning up handle-related
aspects.

The Ruby finalizers are run first so that any objects that are being finalized
can access other objects that were alive at the same time they were alive.

Once all the objects in a generation have been processed, the list of objects
is dropped. Since the objects are kept alive by the list they are a member of,
all objects in a list that is dropped before the next GC cycle will be
collected in that cycle.
Fixed collecting handles.

A mature GC can run when no young GC has run. The handle cleanup code only
considered young objects when a young GC had run, thereby cleaning up handles
for young objects that necessarily must still be alive (since no GC ran for
the heap they were in).
Ignore finalizers when finishing finalizers.

The FFI gem creates new objects to call a function. When Ruby finalizers are
run at shutdown, calling that function creates new Data objects which have
finalizers.
Dirkjan found a bug!

Since Ruby finalizers are sometimes added after an object is in
the finalizers list, we have to check the objects separately. The
FinalizeObject entity is still moved to a dead list based on the
live-ness of the object itself.

The change to handle cleanup is a revert of a previous change that
was incorrect because we mark all objects in a mature collection.
This is conceptually a bit odd because it sets mark bits in a young
object.
Dirkjan found another bug!

Cleaning up inflated headers and handles depends on objects being marked in
the mature GC. If an object needing finalization is young, it will be moved to
a dead list when the young GC runs. However, if a mature GC follows in the
same cycle, the object is not iterated in the dead list. Consequently, if the
object has a handle or inflated header, it is not marked and those are
deallocated. Then all hell breaks loose and unicorns steal baby kittens to use
as gummy bears.
Don't leak memory.
Only mark or clear headers that have been in use.
Disable finishing Ruby finalizers at halt.
Don't break the loop when cleaning up handles

Otherwise we wouldn't clean up all the unused handles and we could have
increasing memory use for inflated headers.
Deallocate finalizer handler when shutting down

We weren't deallocating the finalizer handler. We also don't need
to clean up the process_list_ and live_list_ because they are part of
the list lists too and will be cleaned up there.
Explicitly zero memory for initialize name

Fixes a valgrind warning because of jumping on uninitialized memory.
Explicitly initialize complete InflatedHeader

This makes sure every inflated header is always zero on initialization
and that we don't see any invalid data.
Don't raise if self_ is set

There is a race condition here where if we exit immediately after for
example forking. What happens is that the finalizer thread has just
started, but is still waiting to become dependent in thread.cpp when we
are already shutting down.
Hydra branch is long since gone
Remove references to non existing GIL
Switch to list for auxiliary threads

This makes it easy to ensure the ordering. There is a dependency between
the finalizer and the agent thread, because the agent creates an IO
object, so the finalizer needs to be setup.

Using this order makes sure that we run run auxiliary threads setup in a
fifo fashion and teardown in a lifo fashion. We also move the setup of
the finalizer thread before the phase where the agent thread might be
started.