2010-01-17 01:53:50 +08:00
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef JEMALLOC_H_TYPES
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized
|
|
|
|
* as small as possible such that this setting is still honored, without
|
|
|
|
* violating other constraints. The goal is to make runs as small as possible
|
|
|
|
* without exceeding a per run external fragmentation threshold.
|
|
|
|
*
|
|
|
|
* We use binary fixed point math for overhead computations, where the binary
|
|
|
|
* point is implicitly RUN_BFP bits to the left.
|
|
|
|
*
|
|
|
|
* Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be
|
2010-12-18 10:07:53 +08:00
|
|
|
* honored for some/all object sizes, since when heap profiling is enabled
|
|
|
|
* there is one pointer of header overhead per object (plus a constant). This
|
|
|
|
* constraint is relaxed (ignored) for runs that are so small that the
|
|
|
|
* per-region overhead is greater than:
|
2010-01-17 01:53:50 +08:00
|
|
|
*
|
|
|
|
* (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP))
|
|
|
|
*/
|
|
|
|
#define RUN_BFP 12
|
|
|
|
/* \/ Implicit binary fixed point. */
|
|
|
|
#define RUN_MAX_OVRHD 0x0000003dU
|
|
|
|
#define RUN_MAX_OVRHD_RELAX 0x00001800U
|
|
|
|
|
2011-03-23 00:00:56 +08:00
|
|
|
/* Maximum number of regions in one run. */
|
|
|
|
#define LG_RUN_MAXREGS 11
|
|
|
|
#define RUN_MAXREGS (1U << LG_RUN_MAXREGS)
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
/*
|
|
|
|
* The minimum ratio of active:dirty pages per arena is computed as:
|
|
|
|
*
|
|
|
|
* (nactive >> opt_lg_dirty_mult) >= ndirty
|
|
|
|
*
|
|
|
|
* So, supposing that opt_lg_dirty_mult is 5, there can be no less than 32
|
|
|
|
* times as many active pages as dirty pages.
|
|
|
|
*/
|
|
|
|
#define LG_DIRTY_MULT_DEFAULT 5
|
|
|
|
|
|
|
|
typedef struct arena_chunk_map_s arena_chunk_map_t;
|
|
|
|
typedef struct arena_chunk_s arena_chunk_t;
|
|
|
|
typedef struct arena_run_s arena_run_t;
|
2011-03-16 04:59:15 +08:00
|
|
|
typedef struct arena_bin_info_s arena_bin_info_t;
|
2010-01-17 01:53:50 +08:00
|
|
|
typedef struct arena_bin_s arena_bin_t;
|
|
|
|
typedef struct arena_s arena_t;
|
|
|
|
|
|
|
|
#endif /* JEMALLOC_H_TYPES */
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef JEMALLOC_H_STRUCTS
|
|
|
|
|
|
|
|
/* Each element of the chunk map corresponds to one page within the chunk. */
|
|
|
|
struct arena_chunk_map_s {
|
2012-02-11 12:22:09 +08:00
|
|
|
#ifndef JEMALLOC_PROF
|
|
|
|
/*
|
|
|
|
* Overlay prof_ctx in order to allow it to be referenced by dead code.
|
|
|
|
* Such antics aren't warranted for per arena data structures, but
|
|
|
|
* chunk map overhead accounts for a percentage of memory, rather than
|
|
|
|
* being just a fixed cost.
|
|
|
|
*/
|
|
|
|
union {
|
|
|
|
#endif
|
2010-03-15 08:36:10 +08:00
|
|
|
union {
|
|
|
|
/*
|
|
|
|
* Linkage for run trees. There are two disjoint uses:
|
|
|
|
*
|
2010-03-19 11:36:40 +08:00
|
|
|
* 1) arena_t's runs_avail_{clean,dirty} trees.
|
2010-03-15 08:36:10 +08:00
|
|
|
* 2) arena_run_t conceptually uses this linkage for in-use
|
|
|
|
* non-full runs, rather than directly embedding linkage.
|
|
|
|
*/
|
|
|
|
rb_node(arena_chunk_map_t) rb_link;
|
|
|
|
/*
|
|
|
|
* List of runs currently in purgatory. arena_chunk_purge()
|
|
|
|
* temporarily allocates runs that contain dirty pages while
|
|
|
|
* purging, so that other threads cannot use the runs while the
|
|
|
|
* purging thread is operating without the arena lock held.
|
|
|
|
*/
|
|
|
|
ql_elm(arena_chunk_map_t) ql_link;
|
|
|
|
} u;
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2010-02-11 02:37:56 +08:00
|
|
|
/* Profile counters, used for large object runs. */
|
2010-04-14 07:13:54 +08:00
|
|
|
prof_ctx_t *prof_ctx;
|
2012-02-11 12:22:09 +08:00
|
|
|
#ifndef JEMALLOC_PROF
|
|
|
|
}; /* union { ... }; */
|
2010-02-11 02:37:56 +08:00
|
|
|
#endif
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
/*
|
|
|
|
* Run address (or size) and various flags are stored together. The bit
|
|
|
|
* layout looks like (assuming 32-bit system):
|
|
|
|
*
|
2010-12-18 10:07:53 +08:00
|
|
|
* ???????? ???????? ????---- ----dula
|
2010-01-17 01:53:50 +08:00
|
|
|
*
|
|
|
|
* ? : Unallocated: Run address for first/last pages, unset for internal
|
|
|
|
* pages.
|
2010-03-19 11:36:40 +08:00
|
|
|
* Small: Run page offset.
|
2010-01-17 01:53:50 +08:00
|
|
|
* Large: Run size for first page, unset for trailing pages.
|
|
|
|
* - : Unused.
|
|
|
|
* d : dirty?
|
2010-12-18 10:07:53 +08:00
|
|
|
* u : unzeroed?
|
2010-01-17 01:53:50 +08:00
|
|
|
* l : large?
|
|
|
|
* a : allocated?
|
|
|
|
*
|
|
|
|
* Following are example bit patterns for the three types of runs.
|
|
|
|
*
|
|
|
|
* p : run page offset
|
|
|
|
* s : run size
|
2010-10-23 01:45:59 +08:00
|
|
|
* c : (binind+1) for size class (used only if prof_promote is true)
|
2010-01-17 01:53:50 +08:00
|
|
|
* x : don't care
|
|
|
|
* - : 0
|
2010-04-01 07:45:04 +08:00
|
|
|
* + : 1
|
2010-10-02 08:53:37 +08:00
|
|
|
* [DULA] : bit set
|
|
|
|
* [dula] : bit unset
|
2010-01-17 01:53:50 +08:00
|
|
|
*
|
2010-03-19 11:36:40 +08:00
|
|
|
* Unallocated (clean):
|
2010-12-18 10:07:53 +08:00
|
|
|
* ssssssss ssssssss ssss---- ----du-a
|
2010-10-02 08:53:37 +08:00
|
|
|
* xxxxxxxx xxxxxxxx xxxx---- -----Uxx
|
2010-12-18 10:07:53 +08:00
|
|
|
* ssssssss ssssssss ssss---- ----dU-a
|
2010-03-19 11:36:40 +08:00
|
|
|
*
|
|
|
|
* Unallocated (dirty):
|
2010-12-18 10:07:53 +08:00
|
|
|
* ssssssss ssssssss ssss---- ----D--a
|
2010-03-19 11:36:40 +08:00
|
|
|
* xxxxxxxx xxxxxxxx xxxx---- ----xxxx
|
2010-12-18 10:07:53 +08:00
|
|
|
* ssssssss ssssssss ssss---- ----D--a
|
2010-01-17 01:53:50 +08:00
|
|
|
*
|
2010-03-18 07:27:39 +08:00
|
|
|
* Small:
|
2010-12-18 10:07:53 +08:00
|
|
|
* pppppppp pppppppp pppp---- ----d--A
|
|
|
|
* pppppppp pppppppp pppp---- -------A
|
|
|
|
* pppppppp pppppppp pppp---- ----d--A
|
2010-01-17 01:53:50 +08:00
|
|
|
*
|
|
|
|
* Large:
|
2010-12-18 10:07:53 +08:00
|
|
|
* ssssssss ssssssss ssss---- ----D-LA
|
2010-03-19 11:36:40 +08:00
|
|
|
* xxxxxxxx xxxxxxxx xxxx---- ----xxxx
|
2010-12-18 10:07:53 +08:00
|
|
|
* -------- -------- -------- ----D-LA
|
2010-04-01 07:45:04 +08:00
|
|
|
*
|
|
|
|
* Large (sampled, size <= PAGE_SIZE):
|
2010-12-18 10:07:53 +08:00
|
|
|
* ssssssss ssssssss sssscccc ccccD-LA
|
2010-04-01 07:45:04 +08:00
|
|
|
*
|
|
|
|
* Large (not sampled, size == PAGE_SIZE):
|
2010-12-18 10:07:53 +08:00
|
|
|
* ssssssss ssssssss ssss---- ----D-LA
|
2010-01-17 01:53:50 +08:00
|
|
|
*/
|
|
|
|
size_t bits;
|
2010-04-01 07:45:04 +08:00
|
|
|
#define CHUNK_MAP_CLASS_SHIFT 4
|
|
|
|
#define CHUNK_MAP_CLASS_MASK ((size_t)0xff0U)
|
|
|
|
#define CHUNK_MAP_FLAGS_MASK ((size_t)0xfU)
|
|
|
|
#define CHUNK_MAP_DIRTY ((size_t)0x8U)
|
2010-10-02 08:53:37 +08:00
|
|
|
#define CHUNK_MAP_UNZEROED ((size_t)0x4U)
|
2010-04-01 07:45:04 +08:00
|
|
|
#define CHUNK_MAP_LARGE ((size_t)0x2U)
|
|
|
|
#define CHUNK_MAP_ALLOCATED ((size_t)0x1U)
|
|
|
|
#define CHUNK_MAP_KEY CHUNK_MAP_ALLOCATED
|
2010-01-17 01:53:50 +08:00
|
|
|
};
|
|
|
|
typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t;
|
|
|
|
typedef rb_tree(arena_chunk_map_t) arena_run_tree_t;
|
|
|
|
|
|
|
|
/* Arena chunk header. */
|
|
|
|
struct arena_chunk_s {
|
|
|
|
/* Arena that owns the chunk. */
|
|
|
|
arena_t *arena;
|
|
|
|
|
2010-03-05 13:35:07 +08:00
|
|
|
/* Linkage for the arena's chunks_dirty list. */
|
|
|
|
ql_elm(arena_chunk_t) link_dirty;
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/*
|
2010-03-05 13:35:07 +08:00
|
|
|
* True if the chunk is currently in the chunks_dirty list, due to
|
2010-01-17 01:53:50 +08:00
|
|
|
* having at some point contained one or more dirty pages. Removal
|
|
|
|
* from chunks_dirty is lazy, so (dirtied && ndirty == 0) is possible.
|
|
|
|
*/
|
|
|
|
bool dirtied;
|
|
|
|
|
|
|
|
/* Number of dirty pages. */
|
|
|
|
size_t ndirty;
|
2010-01-31 19:57:29 +08:00
|
|
|
|
2010-10-02 08:35:43 +08:00
|
|
|
/*
|
|
|
|
* Map of pages within chunk that keeps track of free/large/small. The
|
|
|
|
* first map_bias entries are omitted, since the chunk header does not
|
|
|
|
* need to be tracked in the map. This omission saves a header page
|
|
|
|
* for common chunk sizes (e.g. 4 MiB).
|
|
|
|
*/
|
2010-01-17 01:53:50 +08:00
|
|
|
arena_chunk_map_t map[1]; /* Dynamically sized. */
|
|
|
|
};
|
|
|
|
typedef rb_tree(arena_chunk_t) arena_chunk_tree_t;
|
|
|
|
|
|
|
|
struct arena_run_s {
|
|
|
|
/* Bin this run is associated with. */
|
|
|
|
arena_bin_t *bin;
|
|
|
|
|
Use bitmaps to track small regions.
The previous free list implementation, which embedded singly linked
lists in available regions, had the unfortunate side effect of causing
many cache misses during thread cache fills. Fix this in two places:
- arena_run_t: Use a new bitmap implementation to track which regions
are available. Furthermore, revert to preferring the
lowest available region (as jemalloc did with its old
bitmap-based approach).
- tcache_t: Move read-only tcache_bin_t metadata into
tcache_bin_info_t, and add a contiguous array of pointers
to tcache_t in order to track cached objects. This
substantially increases the size of tcache_t, but results
in much higher data locality for common tcache operations.
As a side benefit, it is again possible to efficiently
flush the least recently used cached objects, so this
change changes flushing from MRU to LRU.
The new bitmap implementation uses a multi-level summary approach to
make finding the lowest available region very fast. In practice,
bitmaps only have one or two levels, though the implementation is
general enough to handle extremely large bitmaps, mainly so that large
page sizes can still be entertained.
Fix tcache_bin_flush_large() to always flush statistics, in the same way
that tcache_bin_flush_small() was recently fixed.
Use JEMALLOC_DEBUG rather than NDEBUG.
Add dassert(), and use it for debug-only asserts.
2011-03-17 01:30:13 +08:00
|
|
|
/* Index of next region that has never been allocated, or nregs. */
|
|
|
|
uint32_t nextind;
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/* Number of free regions in run. */
|
|
|
|
unsigned nfree;
|
|
|
|
};
|
|
|
|
|
2011-03-16 04:59:15 +08:00
|
|
|
/*
|
Use bitmaps to track small regions.
The previous free list implementation, which embedded singly linked
lists in available regions, had the unfortunate side effect of causing
many cache misses during thread cache fills. Fix this in two places:
- arena_run_t: Use a new bitmap implementation to track which regions
are available. Furthermore, revert to preferring the
lowest available region (as jemalloc did with its old
bitmap-based approach).
- tcache_t: Move read-only tcache_bin_t metadata into
tcache_bin_info_t, and add a contiguous array of pointers
to tcache_t in order to track cached objects. This
substantially increases the size of tcache_t, but results
in much higher data locality for common tcache operations.
As a side benefit, it is again possible to efficiently
flush the least recently used cached objects, so this
change changes flushing from MRU to LRU.
The new bitmap implementation uses a multi-level summary approach to
make finding the lowest available region very fast. In practice,
bitmaps only have one or two levels, though the implementation is
general enough to handle extremely large bitmaps, mainly so that large
page sizes can still be entertained.
Fix tcache_bin_flush_large() to always flush statistics, in the same way
that tcache_bin_flush_small() was recently fixed.
Use JEMALLOC_DEBUG rather than NDEBUG.
Add dassert(), and use it for debug-only asserts.
2011-03-17 01:30:13 +08:00
|
|
|
* Read-only information associated with each element of arena_t's bins array
|
2011-03-16 04:59:15 +08:00
|
|
|
* is stored separately, partly to reduce memory usage (only one copy, rather
|
|
|
|
* than one per arena), but mainly to avoid false cacheline sharing.
|
|
|
|
*/
|
|
|
|
struct arena_bin_info_s {
|
|
|
|
/* Size of regions in a run for this bin's size class. */
|
|
|
|
size_t reg_size;
|
|
|
|
|
|
|
|
/* Total size of a run for this bin's size class. */
|
|
|
|
size_t run_size;
|
|
|
|
|
|
|
|
/* Total number of regions in a run for this bin's size class. */
|
|
|
|
uint32_t nregs;
|
|
|
|
|
Use bitmaps to track small regions.
The previous free list implementation, which embedded singly linked
lists in available regions, had the unfortunate side effect of causing
many cache misses during thread cache fills. Fix this in two places:
- arena_run_t: Use a new bitmap implementation to track which regions
are available. Furthermore, revert to preferring the
lowest available region (as jemalloc did with its old
bitmap-based approach).
- tcache_t: Move read-only tcache_bin_t metadata into
tcache_bin_info_t, and add a contiguous array of pointers
to tcache_t in order to track cached objects. This
substantially increases the size of tcache_t, but results
in much higher data locality for common tcache operations.
As a side benefit, it is again possible to efficiently
flush the least recently used cached objects, so this
change changes flushing from MRU to LRU.
The new bitmap implementation uses a multi-level summary approach to
make finding the lowest available region very fast. In practice,
bitmaps only have one or two levels, though the implementation is
general enough to handle extremely large bitmaps, mainly so that large
page sizes can still be entertained.
Fix tcache_bin_flush_large() to always flush statistics, in the same way
that tcache_bin_flush_small() was recently fixed.
Use JEMALLOC_DEBUG rather than NDEBUG.
Add dassert(), and use it for debug-only asserts.
2011-03-17 01:30:13 +08:00
|
|
|
/*
|
|
|
|
* Offset of first bitmap_t element in a run header for this bin's size
|
|
|
|
* class.
|
|
|
|
*/
|
|
|
|
uint32_t bitmap_offset;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Metadata used to manipulate bitmaps for runs associated with this
|
|
|
|
* bin.
|
|
|
|
*/
|
|
|
|
bitmap_info_t bitmap_info;
|
|
|
|
|
2011-03-16 04:59:15 +08:00
|
|
|
/*
|
|
|
|
* Offset of first (prof_ctx_t *) in a run header for this bin's size
|
2012-02-11 12:22:09 +08:00
|
|
|
* class, or 0 if (config_prof == false || opt_prof == false).
|
2011-03-16 04:59:15 +08:00
|
|
|
*/
|
|
|
|
uint32_t ctx0_offset;
|
|
|
|
|
|
|
|
/* Offset of first region in a run for this bin's size class. */
|
|
|
|
uint32_t reg0_offset;
|
|
|
|
};
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
struct arena_bin_s {
|
2010-03-14 12:32:56 +08:00
|
|
|
/*
|
|
|
|
* All operations on runcur, runs, and stats require that lock be
|
|
|
|
* locked. Run allocation/deallocation are protected by the arena lock,
|
|
|
|
* which may be acquired while holding one or more bin locks, but not
|
|
|
|
* vise versa.
|
|
|
|
*/
|
|
|
|
malloc_mutex_t lock;
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
/*
|
|
|
|
* Current run being used to service allocations of this bin's size
|
|
|
|
* class.
|
|
|
|
*/
|
|
|
|
arena_run_t *runcur;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tree of non-full runs. This tree is used when looking for an
|
|
|
|
* existing run when runcur is no longer usable. We choose the
|
|
|
|
* non-full run that is lowest in memory; this policy tends to keep
|
|
|
|
* objects packed well, and it can also help reduce the number of
|
|
|
|
* almost-empty chunks.
|
|
|
|
*/
|
|
|
|
arena_run_tree_t runs;
|
|
|
|
|
|
|
|
/* Bin statistics. */
|
|
|
|
malloc_bin_stats_t stats;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct arena_s {
|
2010-02-11 02:37:56 +08:00
|
|
|
/* This arena's index within the arenas array. */
|
|
|
|
unsigned ind;
|
|
|
|
|
2010-03-14 12:32:56 +08:00
|
|
|
/*
|
2011-03-19 04:41:33 +08:00
|
|
|
* Number of threads currently assigned to this arena. This field is
|
|
|
|
* protected by arenas_lock.
|
|
|
|
*/
|
|
|
|
unsigned nthreads;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There are three classes of arena operations from a locking
|
|
|
|
* perspective:
|
|
|
|
* 1) Thread asssignment (modifies nthreads) is protected by
|
|
|
|
* arenas_lock.
|
|
|
|
* 2) Bin-related operations are protected by bin locks.
|
|
|
|
* 3) Chunk- and run-related operations are protected by this mutex.
|
2010-03-14 12:32:56 +08:00
|
|
|
*/
|
2010-01-17 01:53:50 +08:00
|
|
|
malloc_mutex_t lock;
|
|
|
|
|
|
|
|
arena_stats_t stats;
|
|
|
|
/*
|
|
|
|
* List of tcaches for extant threads associated with this arena.
|
|
|
|
* Stats from these are merged incrementally, and at exit.
|
|
|
|
*/
|
|
|
|
ql_head(tcache_t) tcache_ql;
|
|
|
|
|
2010-02-12 05:19:21 +08:00
|
|
|
uint64_t prof_accumbytes;
|
|
|
|
|
2010-03-05 13:35:07 +08:00
|
|
|
/* List of dirty-page-containing chunks this arena manages. */
|
|
|
|
ql_head(arena_chunk_t) chunks_dirty;
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In order to avoid rapid chunk allocation/deallocation when an arena
|
|
|
|
* oscillates right on the cusp of needing a new chunk, cache the most
|
|
|
|
* recently freed chunk. The spare is left in the arena's chunk trees
|
|
|
|
* until it is deleted.
|
|
|
|
*
|
|
|
|
* There is one spare chunk per arena, rather than one spare total, in
|
|
|
|
* order to avoid interactions between multiple threads that could make
|
|
|
|
* a single spare inadequate.
|
|
|
|
*/
|
|
|
|
arena_chunk_t *spare;
|
|
|
|
|
|
|
|
/* Number of pages in active runs. */
|
2010-01-25 08:41:01 +08:00
|
|
|
size_t nactive;
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Current count of pages within unused runs that are potentially
|
|
|
|
* dirty, and for which madvise(... MADV_DONTNEED) has not been called.
|
|
|
|
* By tracking this, we can institute a limit on how much dirty unused
|
|
|
|
* memory is mapped for each arena.
|
|
|
|
*/
|
|
|
|
size_t ndirty;
|
|
|
|
|
2010-03-15 08:36:10 +08:00
|
|
|
/*
|
2010-04-09 11:31:58 +08:00
|
|
|
* Approximate number of pages being purged. It is possible for
|
|
|
|
* multiple threads to purge dirty pages concurrently, and they use
|
|
|
|
* npurgatory to indicate the total number of pages all threads are
|
|
|
|
* attempting to purge.
|
2010-03-15 08:36:10 +08:00
|
|
|
*/
|
2010-04-09 11:31:58 +08:00
|
|
|
size_t npurgatory;
|
2010-03-15 08:36:10 +08:00
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
/*
|
2010-03-19 11:36:40 +08:00
|
|
|
* Size/address-ordered trees of this arena's available runs. The trees
|
|
|
|
* are used for first-best-fit run allocation. The dirty tree contains
|
|
|
|
* runs with dirty pages (i.e. very likely to have been touched and
|
|
|
|
* therefore have associated physical pages), whereas the clean tree
|
|
|
|
* contains runs with pages that either have no associated physical
|
|
|
|
* pages, or have pages that the kernel may recycle at any time due to
|
|
|
|
* previous madvise(2) calls. The dirty tree is used in preference to
|
|
|
|
* the clean tree for allocations, because using dirty pages reduces
|
|
|
|
* the amount of dirty purging necessary to keep the active:dirty page
|
|
|
|
* ratio below the purge threshold.
|
2010-01-17 01:53:50 +08:00
|
|
|
*/
|
2010-03-19 11:36:40 +08:00
|
|
|
arena_avail_tree_t runs_avail_clean;
|
|
|
|
arena_avail_tree_t runs_avail_dirty;
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2012-02-29 08:50:47 +08:00
|
|
|
/* bins is used to store trees of free regions. */
|
|
|
|
arena_bin_t bins[NBINS];
|
2010-01-17 01:53:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif /* JEMALLOC_H_STRUCTS */
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef JEMALLOC_H_EXTERNS
|
|
|
|
|
Use bitmaps to track small regions.
The previous free list implementation, which embedded singly linked
lists in available regions, had the unfortunate side effect of causing
many cache misses during thread cache fills. Fix this in two places:
- arena_run_t: Use a new bitmap implementation to track which regions
are available. Furthermore, revert to preferring the
lowest available region (as jemalloc did with its old
bitmap-based approach).
- tcache_t: Move read-only tcache_bin_t metadata into
tcache_bin_info_t, and add a contiguous array of pointers
to tcache_t in order to track cached objects. This
substantially increases the size of tcache_t, but results
in much higher data locality for common tcache operations.
As a side benefit, it is again possible to efficiently
flush the least recently used cached objects, so this
change changes flushing from MRU to LRU.
The new bitmap implementation uses a multi-level summary approach to
make finding the lowest available region very fast. In practice,
bitmaps only have one or two levels, though the implementation is
general enough to handle extremely large bitmaps, mainly so that large
page sizes can still be entertained.
Fix tcache_bin_flush_large() to always flush statistics, in the same way
that tcache_bin_flush_small() was recently fixed.
Use JEMALLOC_DEBUG rather than NDEBUG.
Add dassert(), and use it for debug-only asserts.
2011-03-17 01:30:13 +08:00
|
|
|
extern ssize_t opt_lg_dirty_mult;
|
2011-03-07 14:56:36 +08:00
|
|
|
/*
|
|
|
|
* small_size2bin is a compact lookup table that rounds request sizes up to
|
|
|
|
* size classes. In order to reduce cache footprint, the table is compressed,
|
|
|
|
* and all accesses are via the SMALL_SIZE2BIN macro.
|
|
|
|
*/
|
2012-02-29 08:50:47 +08:00
|
|
|
extern uint8_t const small_size2bin[];
|
2011-03-07 14:56:36 +08:00
|
|
|
#define SMALL_SIZE2BIN(s) (small_size2bin[(s-1) >> LG_TINY_MIN])
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2012-02-29 08:50:47 +08:00
|
|
|
extern arena_bin_info_t arena_bin_info[NBINS];
|
2010-01-17 01:53:50 +08:00
|
|
|
|
2012-02-29 08:50:47 +08:00
|
|
|
/* Number of large size classes. */
|
2010-10-02 08:35:43 +08:00
|
|
|
#define nlclasses (chunk_npages - map_bias)
|
2010-01-28 05:10:55 +08:00
|
|
|
|
2010-10-01 07:55:08 +08:00
|
|
|
void arena_purge_all(arena_t *arena);
|
|
|
|
void arena_prof_accum(arena_t *arena, uint64_t accumbytes);
|
2010-03-18 07:27:39 +08:00
|
|
|
void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin,
|
2012-02-11 12:22:09 +08:00
|
|
|
size_t binind, uint64_t prof_accumbytes);
|
2010-01-17 01:53:50 +08:00
|
|
|
void *arena_malloc_small(arena_t *arena, size_t size, bool zero);
|
2010-03-18 07:27:39 +08:00
|
|
|
void *arena_malloc_large(arena_t *arena, size_t size, bool zero);
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
void *arena_palloc(arena_t *arena, size_t size, size_t alloc_size,
|
|
|
|
size_t alignment, bool zero);
|
2010-01-17 01:53:50 +08:00
|
|
|
size_t arena_salloc(const void *ptr);
|
2010-04-01 07:45:04 +08:00
|
|
|
void arena_prof_promoted(const void *ptr, size_t size);
|
|
|
|
size_t arena_salloc_demote(const void *ptr);
|
2010-01-17 01:53:50 +08:00
|
|
|
void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
|
|
|
|
arena_chunk_map_t *mapelm);
|
|
|
|
void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr);
|
2010-01-18 09:35:19 +08:00
|
|
|
void arena_stats_merge(arena_t *arena, size_t *nactive, size_t *ndirty,
|
|
|
|
arena_stats_t *astats, malloc_bin_stats_t *bstats,
|
|
|
|
malloc_large_stats_t *lstats);
|
Add {,r,s,d}allocm().
Add allocm(), rallocm(), sallocm(), and dallocm(), which are a
functional superset of malloc(), calloc(), posix_memalign(),
malloc_usable_size(), and free().
2010-09-18 06:46:18 +08:00
|
|
|
void *arena_ralloc_no_move(void *ptr, size_t oldsize, size_t size,
|
|
|
|
size_t extra, bool zero);
|
|
|
|
void *arena_ralloc(void *ptr, size_t oldsize, size_t size, size_t extra,
|
|
|
|
size_t alignment, bool zero);
|
2010-01-17 01:53:50 +08:00
|
|
|
bool arena_new(arena_t *arena, unsigned ind);
|
2012-02-29 08:50:47 +08:00
|
|
|
void arena_boot(void);
|
2012-03-14 07:31:41 +08:00
|
|
|
void arena_prefork(arena_t *arena);
|
|
|
|
void arena_postfork_parent(arena_t *arena);
|
|
|
|
void arena_postfork_child(arena_t *arena);
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
#endif /* JEMALLOC_H_EXTERNS */
|
|
|
|
/******************************************************************************/
|
|
|
|
#ifdef JEMALLOC_H_INLINES
|
|
|
|
|
|
|
|
#ifndef JEMALLOC_ENABLE_INLINE
|
2011-03-16 04:59:15 +08:00
|
|
|
size_t arena_bin_index(arena_t *arena, arena_bin_t *bin);
|
|
|
|
unsigned arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info,
|
2011-03-16 13:19:45 +08:00
|
|
|
const void *ptr);
|
2010-10-21 11:52:00 +08:00
|
|
|
prof_ctx_t *arena_prof_ctx_get(const void *ptr);
|
2010-10-23 01:45:59 +08:00
|
|
|
void arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx);
|
2012-02-14 04:29:49 +08:00
|
|
|
void *arena_malloc(size_t size, bool zero);
|
2012-03-22 09:33:03 +08:00
|
|
|
void *arena_malloc_prechosen(arena_t *arena, size_t size, bool zero);
|
2010-01-17 01:53:50 +08:00
|
|
|
void arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_))
|
2011-03-16 04:59:15 +08:00
|
|
|
JEMALLOC_INLINE size_t
|
|
|
|
arena_bin_index(arena_t *arena, arena_bin_t *bin)
|
|
|
|
{
|
|
|
|
size_t binind = bin - arena->bins;
|
2012-02-29 08:50:47 +08:00
|
|
|
assert(binind < NBINS);
|
2011-03-16 04:59:15 +08:00
|
|
|
return (binind);
|
|
|
|
}
|
|
|
|
|
2010-10-21 11:52:00 +08:00
|
|
|
JEMALLOC_INLINE unsigned
|
2011-03-16 13:19:45 +08:00
|
|
|
arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, const void *ptr)
|
2010-10-21 11:52:00 +08:00
|
|
|
{
|
|
|
|
unsigned shift, diff, regind;
|
2011-03-16 13:19:45 +08:00
|
|
|
size_t size;
|
2010-10-21 11:52:00 +08:00
|
|
|
|
Use bitmaps to track small regions.
The previous free list implementation, which embedded singly linked
lists in available regions, had the unfortunate side effect of causing
many cache misses during thread cache fills. Fix this in two places:
- arena_run_t: Use a new bitmap implementation to track which regions
are available. Furthermore, revert to preferring the
lowest available region (as jemalloc did with its old
bitmap-based approach).
- tcache_t: Move read-only tcache_bin_t metadata into
tcache_bin_info_t, and add a contiguous array of pointers
to tcache_t in order to track cached objects. This
substantially increases the size of tcache_t, but results
in much higher data locality for common tcache operations.
As a side benefit, it is again possible to efficiently
flush the least recently used cached objects, so this
change changes flushing from MRU to LRU.
The new bitmap implementation uses a multi-level summary approach to
make finding the lowest available region very fast. In practice,
bitmaps only have one or two levels, though the implementation is
general enough to handle extremely large bitmaps, mainly so that large
page sizes can still be entertained.
Fix tcache_bin_flush_large() to always flush statistics, in the same way
that tcache_bin_flush_small() was recently fixed.
Use JEMALLOC_DEBUG rather than NDEBUG.
Add dassert(), and use it for debug-only asserts.
2011-03-17 01:30:13 +08:00
|
|
|
/*
|
|
|
|
* Freeing a pointer lower than region zero can cause assertion
|
|
|
|
* failure.
|
|
|
|
*/
|
|
|
|
assert((uintptr_t)ptr >= (uintptr_t)run +
|
|
|
|
(uintptr_t)bin_info->reg0_offset);
|
2010-10-21 11:52:00 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Avoid doing division with a variable divisor if possible. Using
|
|
|
|
* actual division here can reduce allocator throughput by over 20%!
|
|
|
|
*/
|
2011-03-16 04:59:15 +08:00
|
|
|
diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run -
|
|
|
|
bin_info->reg0_offset);
|
2010-10-21 11:52:00 +08:00
|
|
|
|
|
|
|
/* Rescale (factor powers of 2 out of the numerator and denominator). */
|
2011-03-16 13:19:45 +08:00
|
|
|
size = bin_info->reg_size;
|
2010-10-21 11:52:00 +08:00
|
|
|
shift = ffs(size) - 1;
|
|
|
|
diff >>= shift;
|
|
|
|
size >>= shift;
|
|
|
|
|
|
|
|
if (size == 1) {
|
|
|
|
/* The divisor was a power of 2. */
|
|
|
|
regind = diff;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* To divide by a number D that is not a power of two we
|
|
|
|
* multiply by (2^21 / D) and then right shift by 21 positions.
|
|
|
|
*
|
|
|
|
* X / D
|
|
|
|
*
|
|
|
|
* becomes
|
|
|
|
*
|
|
|
|
* (X * size_invs[D - 3]) >> SIZE_INV_SHIFT
|
|
|
|
*
|
|
|
|
* We can omit the first three elements, because we never
|
|
|
|
* divide by 0, and 1 and 2 are both powers of two, which are
|
|
|
|
* handled above.
|
|
|
|
*/
|
2011-03-23 00:00:56 +08:00
|
|
|
#define SIZE_INV_SHIFT ((sizeof(unsigned) << 3) - LG_RUN_MAXREGS)
|
|
|
|
#define SIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s)) + 1)
|
2010-10-21 11:52:00 +08:00
|
|
|
static const unsigned size_invs[] = {
|
|
|
|
SIZE_INV(3),
|
|
|
|
SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7),
|
|
|
|
SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11),
|
|
|
|
SIZE_INV(12), SIZE_INV(13), SIZE_INV(14), SIZE_INV(15),
|
|
|
|
SIZE_INV(16), SIZE_INV(17), SIZE_INV(18), SIZE_INV(19),
|
|
|
|
SIZE_INV(20), SIZE_INV(21), SIZE_INV(22), SIZE_INV(23),
|
|
|
|
SIZE_INV(24), SIZE_INV(25), SIZE_INV(26), SIZE_INV(27),
|
|
|
|
SIZE_INV(28), SIZE_INV(29), SIZE_INV(30), SIZE_INV(31)
|
|
|
|
};
|
|
|
|
|
|
|
|
if (size <= ((sizeof(size_invs) / sizeof(unsigned)) + 2))
|
|
|
|
regind = (diff * size_invs[size - 3]) >> SIZE_INV_SHIFT;
|
|
|
|
else
|
|
|
|
regind = diff / size;
|
|
|
|
#undef SIZE_INV
|
|
|
|
#undef SIZE_INV_SHIFT
|
|
|
|
}
|
|
|
|
assert(diff == regind * size);
|
2011-03-16 04:59:15 +08:00
|
|
|
assert(regind < bin_info->nregs);
|
2010-10-21 11:52:00 +08:00
|
|
|
|
|
|
|
return (regind);
|
|
|
|
}
|
|
|
|
|
|
|
|
JEMALLOC_INLINE prof_ctx_t *
|
|
|
|
arena_prof_ctx_get(const void *ptr)
|
|
|
|
{
|
|
|
|
prof_ctx_t *ret;
|
|
|
|
arena_chunk_t *chunk;
|
|
|
|
size_t pageind, mapbits;
|
|
|
|
|
2012-02-11 12:22:09 +08:00
|
|
|
cassert(config_prof);
|
2010-10-21 11:52:00 +08:00
|
|
|
assert(ptr != NULL);
|
|
|
|
assert(CHUNK_ADDR2BASE(ptr) != ptr);
|
|
|
|
|
|
|
|
chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
|
|
|
|
pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT;
|
|
|
|
mapbits = chunk->map[pageind-map_bias].bits;
|
|
|
|
assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
|
|
|
|
if ((mapbits & CHUNK_MAP_LARGE) == 0) {
|
|
|
|
if (prof_promote)
|
|
|
|
ret = (prof_ctx_t *)(uintptr_t)1U;
|
|
|
|
else {
|
|
|
|
arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
|
|
|
|
(uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) <<
|
|
|
|
PAGE_SHIFT));
|
2011-03-16 04:59:15 +08:00
|
|
|
size_t binind = arena_bin_index(chunk->arena, run->bin);
|
|
|
|
arena_bin_info_t *bin_info = &arena_bin_info[binind];
|
2010-10-21 11:52:00 +08:00
|
|
|
unsigned regind;
|
|
|
|
|
2011-03-16 13:19:45 +08:00
|
|
|
regind = arena_run_regind(run, bin_info, ptr);
|
2010-10-21 11:52:00 +08:00
|
|
|
ret = *(prof_ctx_t **)((uintptr_t)run +
|
2011-03-16 04:59:15 +08:00
|
|
|
bin_info->ctx0_offset + (regind *
|
2010-10-21 11:52:00 +08:00
|
|
|
sizeof(prof_ctx_t *)));
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
ret = chunk->map[pageind-map_bias].prof_ctx;
|
|
|
|
|
|
|
|
return (ret);
|
|
|
|
}
|
2010-10-23 01:45:59 +08:00
|
|
|
|
|
|
|
JEMALLOC_INLINE void
|
|
|
|
arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)
|
|
|
|
{
|
|
|
|
arena_chunk_t *chunk;
|
|
|
|
size_t pageind, mapbits;
|
|
|
|
|
2012-02-11 12:22:09 +08:00
|
|
|
cassert(config_prof);
|
2010-10-23 01:45:59 +08:00
|
|
|
assert(ptr != NULL);
|
|
|
|
assert(CHUNK_ADDR2BASE(ptr) != ptr);
|
|
|
|
|
|
|
|
chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
|
|
|
|
pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT;
|
|
|
|
mapbits = chunk->map[pageind-map_bias].bits;
|
|
|
|
assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
|
|
|
|
if ((mapbits & CHUNK_MAP_LARGE) == 0) {
|
|
|
|
if (prof_promote == false) {
|
|
|
|
arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
|
|
|
|
(uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) <<
|
|
|
|
PAGE_SHIFT));
|
|
|
|
arena_bin_t *bin = run->bin;
|
2011-03-16 04:59:15 +08:00
|
|
|
size_t binind;
|
|
|
|
arena_bin_info_t *bin_info;
|
2011-03-16 13:19:45 +08:00
|
|
|
unsigned regind;
|
2010-10-23 01:45:59 +08:00
|
|
|
|
2011-03-16 04:59:15 +08:00
|
|
|
binind = arena_bin_index(chunk->arena, bin);
|
|
|
|
bin_info = &arena_bin_info[binind];
|
2011-03-16 13:19:45 +08:00
|
|
|
regind = arena_run_regind(run, bin_info, ptr);
|
2010-10-23 01:45:59 +08:00
|
|
|
|
2011-03-16 04:59:15 +08:00
|
|
|
*((prof_ctx_t **)((uintptr_t)run + bin_info->ctx0_offset
|
2010-10-23 01:45:59 +08:00
|
|
|
+ (regind * sizeof(prof_ctx_t *)))) = ctx;
|
|
|
|
} else
|
|
|
|
assert((uintptr_t)ctx == (uintptr_t)1U);
|
|
|
|
} else
|
|
|
|
chunk->map[pageind-map_bias].prof_ctx = ctx;
|
|
|
|
}
|
2010-10-21 11:52:00 +08:00
|
|
|
|
2012-02-14 04:29:49 +08:00
|
|
|
JEMALLOC_INLINE void *
|
|
|
|
arena_malloc(size_t size, bool zero)
|
|
|
|
{
|
2012-02-14 06:30:52 +08:00
|
|
|
tcache_t *tcache;
|
2012-02-14 04:29:49 +08:00
|
|
|
|
|
|
|
assert(size != 0);
|
2012-03-22 09:33:03 +08:00
|
|
|
assert(size <= arena_maxclass);
|
2012-02-14 04:29:49 +08:00
|
|
|
|
2012-02-29 08:50:47 +08:00
|
|
|
if (size <= SMALL_MAXCLASS) {
|
2012-02-14 06:30:52 +08:00
|
|
|
if ((tcache = tcache_get()) != NULL)
|
2012-02-14 04:29:49 +08:00
|
|
|
return (tcache_alloc_small(tcache, size, zero));
|
|
|
|
else
|
|
|
|
return (arena_malloc_small(choose_arena(), size, zero));
|
|
|
|
} else {
|
2012-02-14 07:18:19 +08:00
|
|
|
/*
|
|
|
|
* Initialize tcache after checking size in order to avoid
|
|
|
|
* infinite recursion during tcache initialization.
|
|
|
|
*/
|
2012-02-14 06:30:52 +08:00
|
|
|
if (size <= tcache_maxclass && (tcache = tcache_get()) != NULL)
|
2012-02-14 04:29:49 +08:00
|
|
|
return (tcache_alloc_large(tcache, size, zero));
|
|
|
|
else
|
|
|
|
return (arena_malloc_large(choose_arena(), size, zero));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-22 09:33:03 +08:00
|
|
|
JEMALLOC_INLINE void *
|
|
|
|
arena_malloc_prechosen(arena_t *arena, size_t size, bool zero)
|
|
|
|
{
|
|
|
|
|
|
|
|
assert(size != 0);
|
|
|
|
assert(size <= arena_maxclass);
|
|
|
|
|
|
|
|
if (size <= SMALL_MAXCLASS)
|
|
|
|
return (arena_malloc_small(arena, size, zero));
|
|
|
|
else
|
|
|
|
return (arena_malloc_large(arena, size, zero));
|
|
|
|
}
|
|
|
|
|
2010-01-17 01:53:50 +08:00
|
|
|
JEMALLOC_INLINE void
|
|
|
|
arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr)
|
|
|
|
{
|
|
|
|
size_t pageind;
|
|
|
|
arena_chunk_map_t *mapelm;
|
2012-02-14 04:29:49 +08:00
|
|
|
tcache_t *tcache = tcache_get();
|
2010-01-17 01:53:50 +08:00
|
|
|
|
|
|
|
assert(arena != NULL);
|
|
|
|
assert(chunk->arena == arena);
|
|
|
|
assert(ptr != NULL);
|
|
|
|
assert(CHUNK_ADDR2BASE(ptr) != ptr);
|
|
|
|
|
2010-10-02 08:35:43 +08:00
|
|
|
pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT;
|
|
|
|
mapelm = &chunk->map[pageind-map_bias];
|
2010-01-17 01:53:50 +08:00
|
|
|
assert((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0);
|
|
|
|
if ((mapelm->bits & CHUNK_MAP_LARGE) == 0) {
|
|
|
|
/* Small allocation. */
|
2012-02-14 04:29:49 +08:00
|
|
|
if (tcache != NULL)
|
2010-03-18 07:27:39 +08:00
|
|
|
tcache_dalloc_small(tcache, ptr);
|
2010-01-17 01:53:50 +08:00
|
|
|
else {
|
2010-03-14 12:32:56 +08:00
|
|
|
arena_run_t *run;
|
|
|
|
arena_bin_t *bin;
|
|
|
|
|
|
|
|
run = (arena_run_t *)((uintptr_t)chunk +
|
2010-03-19 11:36:40 +08:00
|
|
|
(uintptr_t)((pageind - (mapelm->bits >>
|
|
|
|
PAGE_SHIFT)) << PAGE_SHIFT));
|
2010-03-14 12:32:56 +08:00
|
|
|
bin = run->bin;
|
2012-02-11 12:22:09 +08:00
|
|
|
if (config_debug) {
|
2011-03-16 04:59:15 +08:00
|
|
|
size_t binind = arena_bin_index(arena, bin);
|
2012-02-11 12:22:09 +08:00
|
|
|
UNUSED arena_bin_info_t *bin_info =
|
2011-03-16 04:59:15 +08:00
|
|
|
&arena_bin_info[binind];
|
|
|
|
assert(((uintptr_t)ptr - ((uintptr_t)run +
|
|
|
|
(uintptr_t)bin_info->reg0_offset)) %
|
|
|
|
bin_info->reg_size == 0);
|
|
|
|
}
|
2010-03-14 12:32:56 +08:00
|
|
|
malloc_mutex_lock(&bin->lock);
|
2010-01-17 01:53:50 +08:00
|
|
|
arena_dalloc_bin(arena, chunk, ptr, mapelm);
|
2010-03-14 12:32:56 +08:00
|
|
|
malloc_mutex_unlock(&bin->lock);
|
2010-01-17 01:53:50 +08:00
|
|
|
}
|
2010-03-16 07:38:27 +08:00
|
|
|
} else {
|
2012-02-14 04:29:49 +08:00
|
|
|
size_t size = mapelm->bits & ~PAGE_MASK;
|
|
|
|
|
|
|
|
assert(((uintptr_t)ptr & PAGE_MASK) == 0);
|
|
|
|
|
|
|
|
if (size <= tcache_maxclass && tcache != NULL) {
|
|
|
|
tcache_dalloc_large(tcache, ptr, size);
|
2010-03-18 07:27:39 +08:00
|
|
|
} else {
|
|
|
|
malloc_mutex_lock(&arena->lock);
|
|
|
|
arena_dalloc_large(arena, chunk, ptr);
|
|
|
|
malloc_mutex_unlock(&arena->lock);
|
|
|
|
}
|
2010-03-16 07:38:27 +08:00
|
|
|
}
|
2010-01-17 01:53:50 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif /* JEMALLOC_H_INLINES */
|
|
|
|
/******************************************************************************/
|