.. sources:

    `<https://info.ravenbrook.com/project/mps/master/design/message-gc/>`_
    `<https://info.ravenbrook.com/project/mps/doc/2002-06-18/obsolete-mminfo/mminfo/strategy/lisp-machine/>`_


.. index::
   single: collection
   single: garbage collection

.. _topic-collection:

Garbage collection
==================

The :term:`arena` contains a :term:`garbage collector` that
coordinates the collection of garbage in all of its
:term:`automatically managed <automatic memory management>`
:term:`pools`. The collector efficiently traces references between
:term:`roots` and pools, and between objects in different pools. It is
capable of collecting many automatically managed pools simultaneously.


.. index::
   single: chain; generation
   single: generation chain

Generation chains
-----------------

A :term:`generation chain` describes a sequence of :term:`generations`
used by a set of :term:`automatically managed <automatic memory
management>` :term:`pools`.

A generation is a set of blocks that are managed together by the
:term:`garbage collector`: they are :term:`condemned <condemned set>`
together, and in :term:`moving <moving memory manager>` pools they are
moved together. If two pools allocate blocks that are expected to live
and die together, then it is efficient for them to share a chain.

Typically blocks are allocated in the first generation in the chain,
the :term:`nursery generation` (though you can change this using the
:c:macro:`MPS_KEY_GEN` keyword argument to
:c:func:`mps_pool_create_k`), and each time a block survives one
collection then it is :term:`promoted <promotion>` to the next
generation. Thus a generation contains a set of blocks of similar
ages.

By default, all pools in an arena share the same generation chain
("the arena's default generation chain"), but if this doesn't meet
your requirements, then when creating an automatically managed pool,
you can choose which chain it should use by passing the
:c:macro:`MPS_KEY_CHAIN` keyword argument to
:c:func:`mps_pool_create_k`.

Create a generation chain by preparing an array of
:c:type:`mps_gen_param_s` structures giving the *capacity* (in
kilobytes) and *initial predicted mortality* (between 0 and 1
inclusive) of each generation, and passing them to
:c:func:`mps_chain_create`.

When the *new size* of a generation exceeds its capacity, the MPS will
be prepared to start collecting the chain to which the generation
belongs. See :ref:`topic-collection-schedule` below.

For example::

    mps_gen_param_s gen_params[] = {
        { 1024, 0.8 },
        { 2048, 0.4 },
    };

    mps_chain_t chain;
    mps_res_t res;
    res = mps_chain_create(&chain, arena,
                           sizeof(gen_params) / sizeof(gen_params[0]),
                           gen_params);
    if (res != MPS_RES_OK) error("Couldn't create chain");


.. c:type:: mps_chain_t

    The type of :term:`generation chains`. A generation chain
    describes the structure of :term:`generations` in a set of
    :term:`pools`.


.. c:type:: mps_gen_param_s

    The type of the structure used to specify a :term:`generation` in
    a :term:`generation chain`. ::

        typedef struct mps_gen_param_s {
            size_t mps_capacity;
            double mps_mortality;
        } mps_gen_param_s;

    ``mps_capacity`` is the capacity of the generation, in
    :term:`kilobytes`. When the size of the generation exceeds this,
    the MPS will be prepared to start collecting it.

    .. note::

        The name *capacity* is somewhat misleading. When a generation
        reaches its capacity the MPS may not be able to collect it
        immediately (for example because some other generation is
        being collected), but this does not prevent allocation into
        the generation, and so the size of a generation will often
        exceed its capacity.

    ``mps_mortality`` is the initial predicted mortality of the
    generation: the proportion (between 0 and 1 inclusive) of bytes in
    the generation that are expected to be :term:`dead` when the
    generation is collected.

    .. note::

        This value is only used as an initial estimate. The MPS
        measures the mortality each time it collects the generation,
        and maintains a moving average. So it is not important to
        provide an accurate estimate here.


.. c:function:: mps_res_t mps_chain_create(mps_chain_t *chain_o, mps_arena_t arena, size_t gen_count, mps_gen_param_s *gen_params)

    Create a :term:`generation chain`.

    ``chain_o`` points to a location that will hold a pointer to the
    new generation chain.

    ``arena`` is the arena to which the generation chain will belong.

    ``gen_count`` is the number of :term:`generations` in
    the chain.

    ``gen_params`` points to an array describing the generations.

    Returns :c:macro:`MPS_RES_OK` if the generation chain is created
    successfully, or another :term:`result code` if it fails.

    The generation chain persists until it is destroyed by calling
    :c:func:`mps_chain_destroy`.


.. c:function:: void mps_chain_destroy(mps_chain_t chain)

    Destroy a :term:`generation chain`.

    ``chain`` is the generation chain.

    It is an error to destroy a generation chain if there is a garbage
    collection in progress on the chain, or if there are any
    :term:`pools` using the chain. Before calling this function, the
    arena should be parked (by calling :c:func:`mps_arena_park`) to
    ensure that there are no collections in progress, and pools using
    the chain must be destroyed.


.. index::
   single: collection; scheduling
   single: garbage collection; scheduling

.. _topic-collection-schedule:

Scheduling of collections
-------------------------

.. note::

    It's likely that the algorithm the MPS uses to schedule its
    collections will change in future releases. There's a lot of room
    for improvement here.

The :dfn:`new size` of a generation is the total size of the newly
allocated (in generation 0) or newly promoted (in other generations)
blocks in that generation. These are the blocks that have not been
:term:`condemned <condemned set>` since they were allocated or
promoted into this generation. In pools like :ref:`pool-amc` where the
survivors get promoted to the next generation in the chain, the *new
size* of each generation (other than the topmost) is the same as its
total size, but in pools like :ref:`pool-ams` where survivors do not
get promoted, the two sizes can be different.

When a generation's *new size* exceeds its capacity, the MPS considers
collecting the chain to which the generation belongs. (How long it
takes to get around to it depends on which other collections are in
progress.)

.. note::

    You can affect the decision as to when to collect the chain by
    using the :ref:`ramp allocation pattern <topic-pattern-ramp>`.

If the MPS decides to collect a chain, all generations are collected
up to, and including, the highest generation whose *new size* exceeds
its capacity.

In pools such as :ref:`pool-amc`, blocks in generation *g* that
survive collection get promoted to generation *g*\+1. If the last
generation in the chain is collected, the survivors are promoted into
an :term:`arena`\-wide "top" generation.


.. index::
   single: garbage collection; start message
   single: message; garbage collection start

Garbage collection start messages
---------------------------------

.. c:function:: mps_message_type_t mps_message_type_gc_start(void)

    Return the :term:`message type` of garbage collection start
    messages.

    Garbage collection start messages contain information about why
    the :term:`garbage collection` started.

    The access method specific to a :term:`message` of this message
    type is:

    * :c:func:`mps_message_gc_start_why` returns a string that
      describes why the garbage collection started.

    .. seealso::

        :ref:`topic-message`.


.. c:function:: const char *mps_message_gc_start_why(mps_arena_t arena, mps_message_t message)

    Return a string that describes why the :term:`garbage collection`
    that posted a :term:`message` started.

    ``arena`` is the arena which posted the message.

    ``message`` is a message retrieved by :c:func:`mps_message_get` and
    not yet discarded.  It must be a garbage collection message: see
    :c:func:`mps_message_type_gc`.

    Returns a pointer to a string that is describes (in English) why
    this collection started. The contents of the string must not be
    modified by the client. The string and the pointer are valid until
    the message is discarded with :c:func:`mps_message_discard`.

    .. seealso::

        :ref:`topic-message`.


.. index::
   pair: garbage collection; message

Garbage collection messages
---------------------------

.. c:function:: mps_message_type_t mps_message_type_gc(void)

    Return the :term:`message type` of garbage collection statistic
    messages.

    Garbage collection statistic messages are used by the MPS to give
    the :term:`client program` information about a :term:`garbage
    collection` that has taken place. Such information may be useful in
    analysing the client program's memory usage over time.

    The access methods specific to a message of this type are:

    * :c:func:`mps_message_gc_live_size` returns the total size of the
      :term:`condemned set` that survived the garbage collection that
      generated the message;

    * :c:func:`mps_message_gc_condemned_size` returns the approximate
      size of :term:`condemned set` in the garbage collection that
      generated the message;

    * :c:func:`mps_message_gc_not_condemned_size` returns the
      approximate size of the set of blocks that were in collected
      :term:`pools`, but were not condemned in the garbage
      collection that generated the message.

    .. seealso::

        :ref:`topic-message`.


.. c:function:: size_t mps_message_gc_condemned_size(mps_arena_t arena, mps_message_t message)

    Return the "condemned size" property of a :term:`message`.

    ``arena`` is the arena which posted the message.

    ``message`` is a message retrieved by :c:func:`mps_message_get` and
    not yet discarded.  It must be a garbage collection message: see
    :c:func:`mps_message_type_gc`.

    The "condemned size" property is the approximate :term:`size` of
    the :term:`condemned set` in the :term:`garbage collection` that
    generated the message.

    .. seealso::

        :ref:`topic-message`.


.. c:function:: size_t mps_message_gc_live_size(mps_arena_t arena, mps_message_t message)

    Return the "live size" property of a :term:`message`.

    ``arena`` is the arena which posted the message.

    ``message`` is a message retrieved by :c:func:`mps_message_get` and
    not yet discarded.  It must be a garbage collection message: see
    :c:func:`mps_message_type_gc`.

    The "live size" property is the total size of the set of blocks
    that survived the :term:`garbage collection` that generated the
    message.

    .. seealso::

        :ref:`topic-message`.


.. c:function:: size_t mps_message_gc_not_condemned_size(mps_arena_t arena, mps_message_t message)

    Return the "not condemned size" property of a :term:`message`.

    ``arena`` is the arena which posted the message.

    ``message`` is a message retrieved by :c:func:`mps_message_get` and
    not yet discarded.  It must be a garbage collection message: see
    :c:func:`mps_message_type_gc`.

    The "not condemned size" property is the approximate size of the
    set of blocks that were in collected :term:`pools`, but
    were not in the :term:`condemned set` in the :term:`garbage
    collection` that generated the message.

    .. seealso::

        :ref:`topic-message`.