Fetching contributors…
Cannot retrieve contributors at this time
378 lines (292 sloc) 18.6 KB
Automatic Garbage Collection.
Copyright (c) 2002-2010 Apple Inc. All rights reserved.
#ifndef __AUTO_ZONE__
#define __AUTO_ZONE__
#include <stdint.h>
#include <stdio.h>
#include <sys/types.h>
#include <malloc/malloc.h>
typedef malloc_zone_t auto_zone_t;
// an auto zone carries a little more state but can be cast into a malloc_zone_t
extern auto_zone_t *auto_zone_create(const char *name);
// create an garbage collected zone. Can be (theoretically) done more than once.
// memory can be allocated by malloc_zone_malloc(result, size)
// by default, this memory must be malloc_zone_free(result, ptr) as well (or generic free())
extern struct malloc_introspection_t auto_zone_introspection();
// access the zone introspection functions independent of any particular auto zone instance.
// this is used by tools to be able to introspect a zone in another process.
// the introspection functions returned are required to do version checking on the zone.
#define AUTO_RETAINED_BLOCK_TYPE 0x100 /* zone enumerator returns only blocks with nonzero retain count */
/********* External (Global) Use counting ************/
extern void auto_zone_retain(auto_zone_t *zone, void *ptr);
extern unsigned int auto_zone_release(auto_zone_t *zone, void *ptr);
extern unsigned int auto_zone_retain_count(auto_zone_t *zone, const void *ptr);
// All pointer in the auto zone have an explicit retain count
// Objects will not be collected when the retain count is non-zero
/********* Object information ************/
extern const void *auto_zone_base_pointer(auto_zone_t *zone, const void *ptr);
// return base of interior pointer (or NULL).
extern boolean_t auto_zone_is_valid_pointer(auto_zone_t *zone, const void *ptr);
// is this a pointer to the base of an allocated block?
extern size_t auto_zone_size(auto_zone_t *zone, const void *ptr);
/********* Write-barrier ************/
extern boolean_t auto_zone_set_write_barrier(auto_zone_t *zone, const void *dest, const void *new_value);
// must be used when an object field/slot in the auto zone is set to another object in the auto zone
// returns true if the dest was a valid target whose write-barrier was set
boolean_t auto_zone_atomicCompareAndSwap(auto_zone_t *zone, void *existingValue, void *newValue, void *volatile *location, boolean_t isGlobal, boolean_t issueBarrier);
// Atomically update a location with a new GC value. These use OSAtomicCompareAndSwapPtr{Barrier} with appropriate write-barrier interlocking logic.
boolean_t auto_zone_atomicCompareAndSwapPtr(auto_zone_t *zone, void *existingValue, void *newValue, void *volatile *location, boolean_t issueBarrier);
// Atomically update a location with a new GC value. These use OSAtomicCompareAndSwapPtr{Barrier} with appropriate write-barrier interlocking logic.
// This version checks location, and if it points into global storage, registers a root.
extern void *auto_zone_write_barrier_memmove(auto_zone_t *zone, void *dst, const void *src, size_t size);
// copy content from an arbitrary source area to an arbitrary destination area
// marking write barrier if necessary
/********* Statistics ************/
typedef uint64_t auto_date_t;
typedef struct {
auto_date_t total_duration;
auto_date_t scan_duration;
auto_date_t enlivening_duration;
auto_date_t finalize_duration;
auto_date_t reclaim_duration;
} auto_collection_durations_t;
typedef struct {
/* Memory usage */
malloc_statistics_t malloc_statistics;
/* GC stats */
// version 0
uint32_t version; // set to 1 before calling
/* When there is an array, 0 stands for full collection, 1 for generational */
size_t num_collections[2];
boolean_t last_collection_was_generational;
size_t bytes_in_use_after_last_collection[2];
size_t bytes_allocated_after_last_collection[2];
size_t bytes_freed_during_last_collection[2];
// durations
auto_collection_durations_t total[2]; // running total of each field
auto_collection_durations_t last[2]; // most recent result
auto_collection_durations_t maximum[2]; // on a per item basis, the max. Thus, total != scan + finalize ...
// version 1 additions
size_t thread_collections_total;
size_t thread_blocks_recovered_total;
size_t thread_bytes_recovered_total;
} auto_statistics_t;
extern void auto_zone_statistics(auto_zone_t *zone, auto_statistics_t *stats); // set version to 0
/********* Garbage Collection ************/
enum {
AUTO_COLLECT_RATIO_COLLECTION = (0 << 0), // run generational or full depending on applying AUTO_COLLECTION_RATIO
AUTO_COLLECT_GENERATIONAL_COLLECTION = (1 << 0), // collect young objects. Internal only.
AUTO_COLLECT_FULL_COLLECTION = (2 << 0), // collect entire heap. Internal only.
AUTO_COLLECT_EXHAUSTIVE_COLLECTION = (3 << 0), // run full collections until object count stabilizes.
AUTO_COLLECT_SYNCHRONOUS = (1 << 2), // block caller until scanning is finished.
AUTO_COLLECT_IF_NEEDED = (1 << 3), // only collect if AUTO_COLLECTION_THRESHOLD exceeded.
typedef uint32_t auto_collection_mode_t;
enum {
AUTO_LOG_COLLECTIONS = (1 << 1), // log whenever a collection occurs
AUTO_LOG_REGIONS = (1 << 4), // log whenever a new region is allocated
AUTO_LOG_UNUSUAL = (1 << 5), // log unusual circumstances
AUTO_LOG_WEAK = (1 << 6), // log weak reference manipulation
AUTO_LOG_ALL = (~0u),
typedef uint32_t auto_log_mask_t;
enum {
AUTO_HEAP_HOLES_SHRINKING = 1, // total size of holes is approaching zero
AUTO_HEAP_HOLES_EXHAUSTED = 2, // all holes exhausted, will use hitherto unused memory in "subzone"
AUTO_HEAP_SUBZONE_EXHAUSTED = 3, // will add subzone
AUTO_HEAP_REGION_EXHAUSTED = 4, // no more subzones available, need to add region
AUTO_HEAP_ARENA_EXHAUSTED = 5, // arena exhausted. (64-bit only)
typedef uint32_t auto_heap_growth_info_t;
typedef struct auto_zone_cursor *auto_zone_cursor_t;
typedef void (*auto_zone_foreach_object_t) (auto_zone_cursor_t cursor, void (*op) (void *ptr, void *data), void* data);
typedef struct {
uint32_t version; // reserved - 0 for now
void (*batch_invalidate) (auto_zone_t *zone, auto_zone_foreach_object_t foreach, auto_zone_cursor_t cursor, size_t cursor_size);
// After unreached objects are found, collector calls this routine with internal context.
// Typically, one enters a try block to call back into the collector with a function pointer to be used to
// invalidate each object. This amortizes the cost of the try block as well as allows the collector to use
// efficient contexts.
void (*resurrect) (auto_zone_t *zone, void *ptr);
// Objects on the garbage list may be assigned into live objects in an attempted resurrection. This is not allowed.
// This function, if supplied, is called for these objects to turn them into zombies. The zombies may well hold
// pointers to other objects on the garbage list. No attempt is made to preserved these objects beyond this collection.
const unsigned char* (*layout_for_address)(auto_zone_t *zone, void *ptr);
// The collector assumes that the first word of every "object" is a class pointer.
// For each class pointer discovered this function is called to return a layout, or NULL
// if the object should be scanned conservatively.
// The layout format is nibble pairs {skipcount, scancount} XXX
const unsigned char* (*weak_layout_for_address)(auto_zone_t *zone, void *ptr);
// called once for each allocation encountered for which we don't know the weak layout
// the callee returns a weak layout for the allocation or NULL if the allocation has no weak references.
char* (*name_for_address) (auto_zone_t *zone, vm_address_t base, vm_address_t offset);
// if supplied, is used during logging for errors such as resurrections
auto_log_mask_t log;
// set to auto_log_mask_t bits as desired
boolean_t disable_generational;
// if true, ignores requests to do generational GC.
boolean_t malloc_stack_logging;
// if true, logs allocations for malloc stack logging. Automatically set if MallocStackLogging{NoCompact} is set
void (*scan_external_callout)(void *context, void (*scanner)(void *context, void *start, void *end));
// an external function that is passed a memory scanner entry point
// if set, the function will be called during scanning so that the
// function the collector supplies will be called on all external memory that might
// have references. Useful, for example, for green thread systems.
void (*will_grow)(auto_zone_t *zone, auto_heap_growth_info_t);
// collector calls this when it is about to grow the heap. Advise if memory was returned to the collector, or not.
// if memory was returned, return 0 and the allocation will be attempted again, otherwise the heap will be grown.
size_t collection_threshold;
// if_needed threshold: collector will initiate a collection after this number of bytes is allocated.
size_t full_vs_gen_frequency;
// after full_vs_gen_frequency generational collections, a full collection will occur, if the if_needed threshold exceeded
} auto_collection_control_t;
extern auto_collection_control_t *auto_collection_parameters(auto_zone_t *zone);
// FIXME: API is to get the control struct and slam it
// sets a parameter that decides when callback gets called
extern void auto_collector_disable(auto_zone_t *zone);
extern void auto_collector_reenable(auto_zone_t *zone);
// these two functions turn off/on the collector
// default is on
// use with great care.
extern boolean_t auto_zone_is_enabled(auto_zone_t *zone);
extern boolean_t auto_zone_is_collecting(auto_zone_t *zone);
extern void auto_collect(auto_zone_t *zone, auto_collection_mode_t mode, void *collection_context);
// request a collection. By default, the collection will occur only on the main thread.
extern void auto_collect_multithreaded(auto_zone_t *zone);
// start a dedicated thread to do collections. The invalidate callback will subsequently be called from this new thread.
/********* Object layout for compaction ************/
// For compaction of the zone, we need to know for sure where are the pointers
// each object is assumed to have a class pointer as word 0 (the "isa")
// This layout information is also used for collection (for "tracing" pointers)
// Exact layout knowledge is also important for ignoring weak references
enum {
AUTO_TYPE_UNKNOWN = -1, // this is an error value
AUTO_MEMORY_SCANNED = 0, // holds conservatively scanned pointers
AUTO_MEMORY_UNSCANNED = AUTO_UNSCANNED, // holds unscanned memory (bits)
AUTO_OBJECT_SCANNED = AUTO_OBJECT, // first word is 'isa', may have 'exact' layout info elsewhere
AUTO_OBJECT_UNSCANNED = AUTO_OBJECT | AUTO_UNSCANNED, // first word is 'isa', good for bits or auto_zone_retain'ed items
typedef int auto_memory_type_t;
extern auto_memory_type_t auto_zone_get_layout_type(auto_zone_t *zone, void *ptr);
extern void* auto_zone_allocate_object(auto_zone_t *zone, size_t size, auto_memory_type_t type, boolean_t initial_refcount_to_one, boolean_t clear);
// Create copy of AUTO_MEMORY object preserving "scanned" attribute
// If not auto memory then create unscanned memory copy
void *auto_zone_create_copy(auto_zone_t *zone, void *ptr);
extern void auto_zone_register_thread(auto_zone_t *zone);
extern void auto_zone_unregister_thread(auto_zone_t *zone);
extern void auto_zone_assert_thread_registered(auto_zone_t *zone);
extern void auto_zone_register_datasegment(auto_zone_t *zone, void *address, size_t size);
extern void auto_zone_unregister_datasegment(auto_zone_t *zone, void *address, size_t size);
// Weak references
// The collector maintains a weak reference system.
// Essentially, locations in which references are stored are registered along with the reference itself.
// The location should not be within scanned GC memory.
// After a collection, before finalization, all registered locations are examined and any containing references to
// newly discovered garbage will be "zeroed" and the registration cancelled.
// Reading values from locations must be done through the weak read function because there is a race with such
// reads and the collector having just determined that that value read is in fact otherwise garbage.
// The address of a callback block may be supplied optionally. If supplied, if the location is zeroed, the callback
// block is queued to be called later with the arguments supplied in the callback block. The same callback block both
// can and should be used as an aggregation point. A table of weak locations could supply each registration with the
// same pointer to a callback block that will call that table if items are zerod. The callbacks are made before
// finalization. Note that only thread-safe operations may be performed by this callback.
// It is important to cancel all registrations before deallocating the memory containing locations or callback blocks.
// Cancellation is done by calling the registration function with a NULL "reference" parameter for that location.
typedef struct auto_weak_callback_block {
struct auto_weak_callback_block *next; // must be set to zero before first use
void (*callback_function)(void *arg1, void *arg2);
void *arg1;
void *arg2;
} auto_weak_callback_block_t;
extern void auto_assign_weak_reference(auto_zone_t *zone, const void *value, const void **location, auto_weak_callback_block_t *block);
// Read a weak-reference, informing the collector that it is now strongly referenced.
extern void* auto_read_weak_reference(auto_zone_t *zone, void **referrer);
extern void auto_zone_add_root(auto_zone_t *zone, void *address_of_root_ptr, void *value);
extern void auto_zone_remove_root(auto_zone_t *zone, void *address_of_root_ptr);
extern void auto_zone_root_write_barrier(auto_zone_t *zone, void *address_of_possible_root_ptr, void *value);
// Associative references.
// This informs the collector that an object A wishes to associate one or more secondary objects with object A's lifetime.
// This can be used to implement GC-safe associations that will neither cause uncollectable cycles, nor suffer the limitations
// of weak references.
extern void auto_zone_set_associative_ref(auto_zone_t *zone, void *object, void *key, void *value);
extern void *auto_zone_get_associative_ref(auto_zone_t *zone, void *object, void *key);
extern void auto_zone_erase_associative_refs(auto_zone_t *zone, void *object);
/***** SPI ******/
extern void auto_zone_start_monitor(boolean_t force);
extern void auto_zone_set_class_list(int (*get_class_list)(void **buffer, int count));
extern boolean_t auto_zone_is_finalized(auto_zone_t *zone, const void *ptr);
extern void auto_zone_stats(void); // write stats to stdout
extern void auto_zone_write_stats(FILE *f); // write stats to the given stream
extern char *auto_zone_stats_string(); // return a char * containing the stats string, which should be free()'d
extern void auto_zone_set_nofinalize(auto_zone_t *zone, void *ptr);
extern void auto_zone_set_unscanned(auto_zone_t *zone, void *ptr);
extern void auto_zone_clear_stack(auto_zone_t *zone, unsigned long options);
// Reference count logging support for ObjectAlloc et. al.
enum {
extern void (*__auto_reference_logger)(uint32_t eventtype, void *ptr, uintptr_t data);
// Reference tracing
// referrer_base[referrer_offset] -> referent
typedef struct
vm_address_t referent;
vm_address_t referrer_base;
intptr_t referrer_offset;
} auto_reference_t;
typedef void (*auto_reference_recorder_t)(auto_zone_t *zone, void *ctx,
auto_reference_t reference);
extern void auto_enumerate_references(auto_zone_t *zone, void *referent,
auto_reference_recorder_t callback,
void *stack_bottom, void *ctx);
void **auto_weak_find_first_referrer(auto_zone_t *zone, void **location, unsigned long count);
/************ DEPRECATED ***********/
extern auto_zone_t *auto_zone(void);
// returns a pointer to the first garbage collected zone created.
extern unsigned auto_zone_touched_size(auto_zone_t *zone);
// conservative (upper bound) on memory touched by the allocator itself.
extern double auto_zone_utilization(auto_zone_t *zone);
// conservative measure of utilization of allocator touched memory.
/************* EXPERIMENTAL *********/
#ifdef __BLOCKS__
typedef void (^auto_zone_stack_dump)(const void *base, unsigned long byte_size);
typedef void (^auto_zone_register_dump)(const void *base, unsigned long byte_size);
typedef void (^auto_zone_node_dump)(const void *address, unsigned long size, unsigned int layout, unsigned long refcount);
typedef void (^auto_zone_root_dump)(const void **address);
typedef void (^auto_zone_weak_dump)(const void **address, const void *item);
// utility; causes significant disruption.
// This is SPI for Apple's use only. Can and likely will change without regard to 3rd party use.
void auto_zone_dump(auto_zone_t *zone,
auto_zone_stack_dump stack_dump,
auto_zone_register_dump register_dump,
auto_zone_node_dump thread_local_node_dump, // unsupported
auto_zone_root_dump root_dump,
auto_zone_node_dump global_node_dump,
auto_zone_weak_dump weak_dump
enum {
auto_is_not_auto = 0,
auto_is_auto = (1 << 1), // always on for a start of a node
auto_is_local = (1 << 2), // is/was node local
typedef int auto_probe_results_t;
// utility; causes significant disruption.
// This is SPI for Apple's use only. Can and likely will change without regard to 3rd party use.
auto_probe_results_t auto_zone_probe_unlocked(auto_zone_t *zone, void *address);
#ifdef __BLOCKS__
void auto_zone_scan_exact(auto_zone_t *zone, void *address, void (^callback)(void *base, unsigned long byte_offset, void *candidate));
#endif /* __AUTO_ZONE__ */