4 * Copyright (C) 2008 Vincent Geddes
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 #include "st-memory.h"
29 #include "st-large-integer.h"
30 #include "st-object.h"
31 #include "st-behavior.h"
33 #include "st-context.h"
34 #include "st-method.h"
35 #include "st-association.h"
37 #include "st-system.h"
47 static inline st_oop
remap_oop (st_oop ref
);
48 static void garbage_collect ();
51 timer_start (struct timespec
*spec
)
53 clock_gettime (CLOCK_PROCESS_CPUTIME_ID
, spec
);
57 timer_stop (struct timespec
*spec
)
60 clock_gettime (CLOCK_PROCESS_CPUTIME_ID
, &tmp
);
61 st_timespec_difference (spec
, &tmp
, spec
);
65 // RESERVE 1000 MB worth of virtual address space
66 #define RESERVED_SIZE (1000 * 1024 * 1024)
67 #define INITIAL_COMMIT_SIZE (1 * 1024 * 1024)
69 #define MARK_STACK_SIZE (256 * 1024)
70 #define MARK_STACK_SIZE_OOPS (MARK_STACK_SIZE / sizeof (st_oop))
72 #define BLOCK_SIZE 256
73 #define BLOCK_SIZE_OOPS (BLOCK_SIZE / sizeof (st_oop))
76 verify (st_oop object
)
78 st_assert (st_object_is_mark (ST_OBJECT_MARK (object
)));
82 ensure_metadata (void)
84 /* The bit arrays are implemented using bytes as smallest element of storage.
85 * If there are N oops in the heap, then we need ((N + 7) / 8) bytes
86 * for a bit array with 32 elements. We need to reserve space for
87 * two bit arrays (mark, alloc), as well as the offsets array.
89 st_uint size
, bits_size
, offsets_size
;
90 st_uchar
*metadata_start
;
92 size
= memory
->end
- memory
->start
;
93 bits_size
= ((size
+ 7) / 8);
94 offsets_size
= (size
/ BLOCK_SIZE_OOPS
) * sizeof (st_oop
*);
96 st_free (memory
->mark_bits
);
97 st_free (memory
->alloc_bits
);
98 st_free (memory
->offsets
);
100 memory
->mark_bits
= st_malloc (bits_size
);
101 memory
->alloc_bits
= st_malloc (bits_size
);
102 memory
->bits_size
= bits_size
;
104 memory
->offsets
= st_malloc (offsets_size
);
105 memory
->offsets_size
= offsets_size
;
109 grow_heap (st_uint min_size_oops
)
111 /* we grow the heap by roughly 0.25 or size_oops, whichever is larger */
113 st_uint size
, grow_size
;
117 size
= heap
->p
- heap
->start
;
118 grow_size
= MAX (size
/ 4, min_size_oops
* sizeof (st_oop
));
120 st_heap_grow (heap
, grow_size
);
122 memory
->start
= (st_oop
*) heap
->start
;
123 memory
->end
= (st_oop
*) heap
->p
;
135 heap
= st_heap_new (RESERVED_SIZE
);
139 if (!st_heap_grow (heap
, INITIAL_COMMIT_SIZE
))
142 memory
= st_new0 (st_memory
);
145 memory
->start
= (st_oop
*) heap
->start
;
146 memory
->end
= (st_oop
*) heap
->p
;
147 memory
->p
= memory
->start
;
149 memory
->roots
= ptr_array_new (15);
151 memory
->total_pause_time
.tv_sec
= 0;
152 memory
->total_pause_time
.tv_nsec
= 0;
155 memory
->mark_stack
= st_malloc (MARK_STACK_SIZE
);
156 memory
->mark_stack_size
= MARK_STACK_SIZE
;
158 memory
->mark_bits
= NULL
;
159 memory
->alloc_bits
= NULL
;
160 memory
->offsets
= NULL
;
162 memory
->free_context
= 0;
163 memory
->free_context_large
= 0;
165 memory
->ht
= st_identity_hashtable_new ();
173 st_memory_destroy (void)
179 st_memory_add_root (st_oop object
)
181 ptr_array_append (memory
->roots
, (st_pointer
) object
);
185 st_memory_remove_root (st_oop object
)
187 ptr_array_remove_fast (memory
->roots
, (st_pointer
) object
);
191 st_memory_allocate (st_uint size
)
195 st_assert (size
>= 2);
197 if (memory
->counter
> ST_COLLECTION_THRESHOLD
)
199 if ((memory
->p
+ size
) >= memory
->end
)
204 memory
->counter
+= (size
* sizeof (st_oop
));
206 return ST_OOP (chunk
);
210 st_memory_allocate_context (bool large
)
215 if (ST_LIKELY (memory
->free_context_large
)) {
216 context
= memory
->free_context_large
;
217 memory
->free_context_large
= ST_CONTEXT_PART_SENDER (memory
->free_context_large
);
221 if (ST_LIKELY (memory
->free_context
)) {
222 context
= memory
->free_context
;
223 memory
->free_context
= ST_CONTEXT_PART_SENDER (memory
->free_context
);
228 context
= st_memory_allocate (ST_SIZE_OOPS (struct st_method_context
) + (large
? 32 : 12));
230 st_memory_perform_gc ();
231 context
= st_memory_allocate (ST_SIZE_OOPS (struct st_method_context
) + (large
? 32 : 12));
232 st_assert (context
!= 0);
234 st_object_initialize_header (context
, ST_METHOD_CONTEXT_CLASS
);
235 st_object_set_large_context (context
, large
);
241 st_memory_recycle_context (st_oop context
)
243 if (st_object_large_context (context
)) {
244 ST_CONTEXT_PART_SENDER (context
) = memory
->free_context_large
;
245 memory
->free_context_large
= context
;
247 ST_CONTEXT_PART_SENDER (context
) = memory
->free_context
;
248 memory
->free_context
= context
;
258 static inline st_oop
*
261 return (st_oop
*) ST_POINTER (oop
);
265 get_bit (st_uchar
*bits
, st_uint index
)
267 return (bits
[index
>> 3] >> (index
& 0x7)) & 1;
271 set_bit (st_uchar
*bits
, st_uint index
)
273 bits
[index
>> 3] |= 1 << (index
& 0x7);
276 static inline st_uint
277 bit_index (st_oop object
)
279 return detag (object
) - memory
->start
;
283 ismarked (st_oop object
)
285 return get_bit (memory
->mark_bits
, bit_index (object
));
289 set_marked (st_oop object
)
291 set_bit (memory
->mark_bits
, bit_index (object
));
295 get_alloc_bit (st_oop object
)
297 return get_bit (memory
->alloc_bits
, bit_index (object
));
301 set_alloc_bit (st_oop object
)
303 set_bit (memory
->alloc_bits
, bit_index (object
));
306 static inline st_uint
307 get_block_index (st_oop
*object
)
309 return (object
- memory
->start
) / BLOCK_SIZE_OOPS
;
313 object_size (st_oop object
)
315 switch (st_object_format (object
)) {
316 case ST_FORMAT_OBJECT
:
317 return ST_SIZE_OOPS (struct st_header
) + st_object_instance_size (object
);
318 case ST_FORMAT_FLOAT
:
319 return ST_SIZE_OOPS (struct st_float
);
320 case ST_FORMAT_LARGE_INTEGER
:
321 return ST_SIZE_OOPS (struct st_large_integer
);
322 case ST_FORMAT_HANDLE
:
323 return ST_SIZE_OOPS (struct st_handle
);
324 case ST_FORMAT_ARRAY
:
325 return ST_SIZE_OOPS (struct st_arrayed_object
) + st_smi_value (st_arrayed_object_size (object
));
326 case ST_FORMAT_BYTE_ARRAY
:
327 return ST_SIZE_OOPS (struct st_arrayed_object
) + ST_ROUNDED_UP_OOPS (st_smi_value (st_arrayed_object_size (object
)) + 1);
328 case ST_FORMAT_WORD_ARRAY
:
329 return ST_SIZE_OOPS (struct st_arrayed_object
)
330 + (st_smi_value (st_arrayed_object_size (object
)) / (sizeof (st_oop
) / sizeof (st_uint
)));
331 case ST_FORMAT_FLOAT_ARRAY
:
332 return ST_SIZE_OOPS (struct st_arrayed_object
) + (st_smi_value (st_arrayed_object_size (object
)) * ST_SIZE_OOPS (double));
333 case ST_FORMAT_INTEGER_ARRAY
:
334 /* object format not used yet */
337 case ST_FORMAT_CONTEXT
:
338 return ST_SIZE_OOPS (struct st_header
) + st_object_instance_size (object
) + (st_object_large_context (object
) ? 32 : 12);
340 /* should not reach */
346 object_contents (st_oop object
, st_oop
**oops
, st_uint
*size
)
348 switch (st_object_format (object
)) {
349 case ST_FORMAT_OBJECT
:
350 *oops
= ST_OBJECT_FIELDS (object
);
351 *size
= st_object_instance_size (object
);
353 case ST_FORMAT_ARRAY
:
354 *oops
= st_array_elements (object
);
355 *size
= st_smi_value (st_arrayed_object_size (object
));
357 case ST_FORMAT_CONTEXT
:
358 *oops
= ST_OBJECT_FIELDS (object
);
359 *size
= st_object_instance_size (object
) + st_smi_value (ST_CONTEXT_PART_SP (object
));
361 case ST_FORMAT_FLOAT
:
362 case ST_FORMAT_LARGE_INTEGER
:
363 case ST_FORMAT_HANDLE
:
364 case ST_FORMAT_BYTE_ARRAY
:
365 case ST_FORMAT_WORD_ARRAY
:
366 case ST_FORMAT_FLOAT_ARRAY
:
367 case ST_FORMAT_INTEGER_ARRAY
:
372 /* should not reach */
377 static inline st_uint
378 compute_ordinal_number (st_memory
*memory
, st_oop ref
)
380 st_uint i
, ordinal
= 0;
383 b
= bit_index (ref
) & ~(BLOCK_SIZE_OOPS
- 1);
386 for (i
= 0; i
< BLOCK_SIZE_OOPS
; i
++) {
387 if (get_bit (memory
->mark_bits
, b
+ i
)) {
399 remap_oop (st_oop ref
)
401 st_uint b
, i
= 0, j
= 0;
405 if (!st_object_is_heap (ref
) || ref
== ST_NIL
)
408 ordinal
= compute_ordinal_number (memory
, ref
);
409 offset
= memory
->offsets
[get_block_index (detag (ref
))];
410 b
= bit_index (tag (offset
));
413 if (get_bit (memory
->alloc_bits
, b
+ i
)) {
414 if (++j
== ordinal
) {
415 return tag (offset
+ i
);
426 st_memory_remap (void)
428 /* Remaps all object references in the heap */
433 while (p
< memory
->p
) {
434 p
[1] = remap_oop (p
[1]);
435 object_contents (tag (p
), &oops
, &size
);
436 for (st_uint i
= 0; i
< size
; i
++) {
437 oops
[i
] = remap_oop (oops
[i
]);
439 p
+= object_size (tag (p
));
444 basic_finalize (st_oop object
)
446 if (ST_UNLIKELY (st_object_format (object
) == ST_FORMAT_LARGE_INTEGER
))
447 mp_clear (st_large_integer_value (object
));
452 st_memory_compact (void)
454 st_oop
*p
, *from
, *to
;
460 while (ismarked (tag (p
)) && p
< memory
->p
) {
461 set_alloc_bit (tag (p
));
462 if (block
< (get_block_index (p
) + 1)) {
463 block
= get_block_index (p
);
464 memory
->offsets
[block
] = p
;
467 p
+= object_size (tag (p
));
471 while (!ismarked (tag (p
)) && p
< memory
->p
) {
472 basic_finalize (tag (p
));
473 p
+= object_size (tag (p
));
477 if (from
== to
&& p
>= memory
->p
)
480 while (from
< memory
->p
) {
482 if (ismarked (tag (from
))) {
484 if (st_object_is_hashed (tag (from
)))
485 st_identity_hashtable_rehash_object (memory
->ht
, tag (from
), tag (to
));
487 set_alloc_bit (tag (to
));
488 size
= object_size (tag (from
));
489 st_oops_move (to
, from
, size
);
491 if (block
< (get_block_index (from
) + 1)) {
492 block
= get_block_index (from
);
493 memory
->offsets
[block
] = to
;
500 basic_finalize (tag (from
));
501 if (st_object_is_hashed (tag (from
)))
502 st_identity_hashtable_remove (memory
->ht
, tag (from
));
503 from
+= object_size (tag (from
));
509 memory
->bytes_collected
= (memory
->p
- to
) * sizeof (st_oop
);
510 memory
->bytes_allocated
-= memory
->bytes_collected
;
515 grow_marking_stack (void)
517 memory
->mark_stack_size
*= 2;
518 memory
->mark_stack
= st_realloc (memory
->mark_stack
, memory
->mark_stack_size
);
520 return memory
->mark_stack_size
/ sizeof (st_oop
);
524 st_memory_mark (void)
527 st_oop
*oops
, *stack
;
528 st_uint size
, stack_size
, sp
;
531 stack
= memory
->mark_stack
;
532 stack_size
= memory
->mark_stack_size
/ sizeof (st_oop
);
534 for (st_uint i
= 0; i
< memory
->roots
->length
; i
++)
535 stack
[sp
++] = (st_oop
) ptr_array_get_index (memory
->roots
, i
);
536 stack
[sp
++] = __cpu
.context
;
539 object
= stack
[--sp
];
540 if (!st_object_is_heap (object
) || ismarked (object
))
544 if (ST_UNLIKELY (sp
>= stack_size
)) {
545 stack_size
= grow_marking_stack ();
546 stack
= memory
->mark_stack
;
547 st_log ("gc", "increased size of marking stack");
549 stack
[sp
++] = ST_OBJECT_CLASS (object
);
550 object_contents (object
, &oops
, &size
);
551 for (st_uint i
= 0; i
< size
; i
++) {
552 if (ST_UNLIKELY (sp
>= stack_size
)) {
553 stack_size
= grow_marking_stack ();
554 stack
= memory
->mark_stack
;
555 st_log ("gc", "increased size of marking stack");
557 if (oops
[i
] != ST_NIL
) {
558 stack
[sp
++] = oops
[i
];
565 remap_cpu (struct st_cpu
*cpu
)
567 st_oop context
, home
;
569 context
= remap_oop (cpu
->context
);
570 if (ST_OBJECT_CLASS (context
) == ST_BLOCK_CONTEXT_CLASS
) {
571 home
= ST_BLOCK_CONTEXT_HOME (context
);
572 cpu
->method
= ST_METHOD_CONTEXT_METHOD (home
);
573 cpu
->receiver
= ST_METHOD_CONTEXT_RECEIVER (home
);
574 cpu
->literals
= st_array_elements (ST_METHOD_LITERALS (cpu
->method
));
575 cpu
->temps
= ST_METHOD_CONTEXT_STACK (home
);
576 cpu
->stack
= ST_BLOCK_CONTEXT_STACK (context
);
578 cpu
->method
= ST_METHOD_CONTEXT_METHOD (context
);
579 cpu
->receiver
= ST_METHOD_CONTEXT_RECEIVER (context
);
580 cpu
->literals
= st_array_elements (ST_METHOD_LITERALS (cpu
->method
));
581 cpu
->temps
= ST_METHOD_CONTEXT_STACK (context
);
582 cpu
->stack
= ST_METHOD_CONTEXT_STACK (context
);
585 cpu
->context
= context
;
586 cpu
->bytecode
= st_method_bytecode_bytes (cpu
->method
);
587 cpu
->message_receiver
= remap_oop (cpu
->message_receiver
);
588 cpu
->message_selector
= remap_oop (cpu
->message_selector
);
589 cpu
->new_method
= remap_oop (cpu
->new_method
);
597 for (i
= 0; i
< ST_N_ELEMENTS (__cpu
.globals
); i
++)
598 __cpu
.globals
[i
] = remap_oop (__cpu
.globals
[i
]);
600 for (i
= 0; i
< ST_N_ELEMENTS (__cpu
.selectors
); i
++)
601 __cpu
.selectors
[i
] = remap_oop (__cpu
.selectors
[i
]);
603 for (i
= 0; i
< memory
->roots
->length
; i
++) {
604 ptr_array_set_index (memory
->roots
, i
,
605 (st_pointer
) remap_oop ((st_oop
) ptr_array_get_index (memory
->roots
, i
)));
610 clear_metadata (void)
612 memset (memory
->mark_bits
, 0, memory
->bits_size
);
613 memset (memory
->alloc_bits
, 0, memory
->bits_size
);
614 memset (memory
->offsets
, 0, memory
->offsets_size
);
619 st_memory_perform_gc (void)
625 garbage_collect (void)
630 /* clear context pool */
631 memory
->free_context
= 0;
632 memory
->free_context_large
= 0;
634 memory
->bytes_allocated
+= memory
->counter
;
643 times
[0] = st_timespec_to_double_seconds (&tm
);
644 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
648 st_memory_compact ();
651 times
[1] = st_timespec_to_double_seconds (&tm
);
652 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
661 times
[2] = st_timespec_to_double_seconds (&tm
);
662 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
664 st_cpu_clear_caches ();
667 st_log ("gc", "collected: %uK\n"
669 "marking time: %.6fs\n"
670 "compaction time: %.6fs\n"
671 "remapping time: %.6fs\n",
672 memory
->bytes_collected
/ 1024,
673 (memory
->bytes_collected
+ memory
->bytes_allocated
) / 1024,
674 times
[0], times
[1], times
[2]);
678 st_memory_remap_reference (st_oop reference
)
680 return remap_oop (reference
);