1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla Communicator client code, released
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 * Lifetime-based fast allocation, inspired by much prior art, including
42 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
43 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
50 #include "jsarenainlines.h"
54 static JSArenaStats
*arena_stats_list
;
55 #define COUNT(pool,what) (pool)->stats.what++
57 #define COUNT(pool,what) /* nothing */
60 #define JS_ARENA_DEFAULT_ALIGN sizeof(double)
63 JSArenaPool::init(const char *name
, size_t size
, size_t align
, size_t *quotap
)
66 align
= JS_ARENA_DEFAULT_ALIGN
;
67 mask
= JS_BITMASK(JS_CeilingLog2(align
));
69 first
.base
= first
.avail
= first
.limit
= this->align(jsuword(&first
+ 1));
72 this->quotap
= quotap
;
74 stats
.init(name
, arena_stats_list
);
75 arena_stats_list
= &stats
;
80 * An allocation that consumes more than pool->arenasize also has a header
81 * pointing back to its previous arena's next member. This header is not
82 * included in [a->base, a->limit), so its space can't be wrongly claimed.
84 * As the header is a pointer, it must be well-aligned. If pool->mask is
85 * greater than or equal to POINTER_MASK, the header just preceding a->base
86 * for an oversized arena a is well-aligned, because a->base is well-aligned.
87 * However, we may need to add more space to pad the JSArena ** back-pointer
88 * so that it lies just behind a->base, because a might not be aligned such
89 * that (jsuword)(a + 1) is on a pointer boundary.
91 * By how much must we pad? Let M be the alignment modulus for pool and P
92 * the modulus for a pointer. Given M >= P, the base of an oversized arena
93 * that satisfies M is well-aligned for P.
95 * On the other hand, if M < P, we must include enough space in the header
96 * size to align the back-pointer on a P boundary so that it can be found by
97 * subtracting P from a->base. This means a->base must be on a P boundary,
98 * even though subsequent allocations from a may be aligned on a lesser (M)
99 * boundary. Given powers of two M and P as above, the extra space needed
100 * when M < P is P-M or POINTER_MASK - pool->mask.
102 * The size of a header including padding is given by the headerSize method
103 * for any pool (for any value of M).
107 JSArenaPool::allocate(size_t nb
, bool limitCheck
)
110 size_t alignedNB
= align(nb
);
111 jsuword p
= current
->avail
;
113 * NB: always subtract nb from limit rather than adding nb to p to avoid overflowing a
114 * 32-bit address space. See bug 279273.
116 if ((limitCheck
&& alignedNB
> current
->limit
) || p
> current
->limit
- alignedNB
)
117 p
= jsuword(allocateInternal(alignedNB
));
119 current
->avail
= p
+ alignedNB
;
124 JSArenaPool::allocateInternal(size_t nb
)
127 * Search pool from current forward till we find or make enough space.
129 * NB: subtract nb from a->limit in the loop condition, instead of adding
130 * nb to a->avail, to avoid overflowing a 32-bit address space (possible
131 * when running a 32-bit program on a 64-bit system where the kernel maps
132 * the heap up against the top of the 32-bit address space).
134 * Thanks to Juergen Kreileder <jk@blackdown.de>, who brought this up in
135 * https://bugzilla.mozilla.org/show_bug.cgi?id=279273.
137 JS_ASSERT((nb
& mask
) == 0);
139 for (a
= current
; nb
> a
->limit
|| a
->avail
> a
->limit
- nb
; current
= a
) {
140 JSArena
**ap
= &a
->next
;
142 /* Not enough space in pool, so we must malloc. */
143 jsuword extra
= (nb
> arenasize
) ? headerSize() : 0;
144 jsuword hdrsz
= sizeof *a
+ extra
+ mask
;
145 jsuword gross
= hdrsz
+ JS_MAX(nb
, arenasize
);
152 b
= (JSArena
*) js_malloc(gross
);
157 b
= (JSArena
*) js_malloc(gross
);
163 b
->limit
= (jsuword
)b
+ gross
;
165 COUNT(this, nmallocs
);
167 /* If oversized, store ap in the header, just before a->base. */
169 JS_ASSERT(gross
<= JS_UPTRDIFF(a
->limit
, a
));
172 ((jsuword
)a
+ hdrsz
) & ~headerBaseMask();
175 a
->base
= a
->avail
= align(jsuword(a
+ 1));
179 a
= *ap
; /* move to next arena */
182 void *p
= (void *) a
->avail
;
184 JS_ASSERT(a
->base
<= a
->avail
&& a
->avail
<= a
->limit
);
189 JSArenaPool::reallocInternal(void *p
, size_t size
, size_t incr
)
191 JSArena
**ap
, *a
, *b
;
192 jsuword boff
, aoff
, extra
, hdrsz
, gross
, growth
;
195 * Use the oversized-single-allocation header to avoid searching for ap.
196 * See JS_ArenaAllocate, the SET_HEADER call.
198 if (size
> arenasize
) {
199 ap
= *ptrToHeader(p
);
203 while ((a
= *ap
) != current
)
207 JS_ASSERT(a
->base
== (jsuword
)p
);
208 boff
= JS_UPTRDIFF(a
->base
, a
);
209 aoff
= align(size
+ incr
);
210 JS_ASSERT(aoff
> arenasize
);
211 extra
= headerSize(); /* oversized header holds ap */
212 hdrsz
= sizeof *a
+ extra
+ mask
; /* header and alignment slop */
213 gross
= hdrsz
+ aoff
;
214 JS_ASSERT(gross
> aoff
);
216 growth
= gross
- (a
->limit
- (jsuword
) a
);
217 if (growth
> *quotap
)
219 a
= (JSArena
*) js_realloc(a
, gross
);
224 a
= (JSArena
*) js_realloc(a
, gross
);
231 /* Oops, realloc moved the allocation: update other pointers to a. */
235 if (b
&& b
->avail
- b
->base
> arenasize
) {
236 JS_ASSERT(getHeader(b
) == &(*ap
)->next
);
237 setHeader(b
, &a
->next
);
240 /* Now update *ap, the next link of the arena before a. */
244 a
->base
= ((jsuword
)a
+ hdrsz
) & ~headerBaseMask();
245 a
->limit
= (jsuword
)a
+ gross
;
246 a
->avail
= a
->base
+ aoff
;
247 JS_ASSERT(a
->base
<= a
->avail
&& a
->avail
<= a
->limit
);
249 /* Check whether realloc aligned differently, and copy if necessary. */
250 if (boff
!= JS_UPTRDIFF(a
->base
, a
))
251 memmove((void *) a
->base
, (char *) a
+ boff
, size
);
253 /* Store ap in the oversized-load arena header. */
255 return (void *) a
->base
;
259 JSArenaPool::finish()
261 freeArenaList(&first
);
264 JSArenaStats
*stats
, **statsp
;
266 this->stats
.finish();
267 for (statsp
= &arena_stats_list
; (stats
= *statsp
) != 0;
268 statsp
= &stats
->next
) {
269 if (stats
== &this->stats
) {
270 *statsp
= stats
->next
;
280 JSArenaPool::countAllocation(size_t nb
)
284 if (nb
> stats
.maxalloc
)
286 stats
.variance
+= nb
* nb
;
290 JSArenaPool::countGrowth(size_t size
, size_t incr
)
293 stats
.nbytes
+= incr
;
294 stats
.variance
-= size
* size
;
296 if (size
> stats
.maxalloc
)
297 stats
.maxalloc
= size
;
298 stats
.variance
+= size
* size
;
304 const char *filename
= getenv("JS_ARENA_STATFILE");
307 FILE *arenaStatFile
= strcmp(filename
, "stdout")
308 ? stdout
: strcmp(filename
, "stderr")
309 ? stderr
: fopen(filename
, "w");
310 for (const JSArenaStats
*stats
= arena_stats_list
; stats
; stats
= stats
->getNext())
311 stats
->dump(arenaStatFile
);
312 fclose(arenaStatFile
);
316 JSArenaStats::dump(FILE *fp
) const
319 double mean
= JS_MeanAndStdDev(nallocs
, nbytes
, variance
, &sigma
);
321 fprintf(fp
, "\n%s allocation statistics:\n", name
);
322 fprintf(fp
, " number of arenas: %u\n", narenas
);
323 fprintf(fp
, " number of allocations: %u\n", nallocs
);
324 fprintf(fp
, " number of malloc calls: %u\n", nmallocs
);
325 fprintf(fp
, " number of deallocations: %u\n", ndeallocs
);
326 fprintf(fp
, " number of allocation growths: %u\n", ngrows
);
327 fprintf(fp
, " number of in-place growths: %u\n", ninplace
);
328 fprintf(fp
, " number of realloc'ing growths: %u\n", nreallocs
);
329 fprintf(fp
, "number of released allocations: %u\n", nreleases
);
330 fprintf(fp
, " number of fast releases: %u\n", nfastrels
);
331 fprintf(fp
, " total bytes allocated: %u\n", unsigned(nbytes
));
332 fprintf(fp
, " mean allocation size: %g\n", mean
);
333 fprintf(fp
, " standard deviation: %g\n", sigma
);
334 fprintf(fp
, " maximum allocation size: %u\n", unsigned(maxalloc
));
338 /* Backwards compatibility. */
340 JS_FRIEND_API(void *)
341 JS_ARENA_MARK(const JSArenaPool
*pool
)
343 return pool
->getMark();
347 JS_ARENA_RELEASE(JSArenaPool
*pool
, void *mark
)
352 JS_FRIEND_API(void *)
353 JS_ARENA_ALLOCATE_COMMON_SANE(jsuword p
, JSArenaPool
*pool
, size_t nb
, bool limitCheck
)
355 return pool
->allocate(nb
, limitCheck
);