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"
38 #include "st-handle.h"
48 static inline st_oop
remap_oop (st_oop ref
);
49 static void garbage_collect ();
52 timer_start (struct timespec
*spec
)
54 clock_gettime (CLOCK_PROCESS_CPUTIME_ID
, spec
);
58 timer_stop (struct timespec
*spec
)
61 clock_gettime (CLOCK_PROCESS_CPUTIME_ID
, &tmp
);
62 st_timespec_difference (spec
, &tmp
, spec
);
66 // RESERVE 1000 MB worth of virtual address space
67 #define RESERVED_SIZE (1000 * 1024 * 1024)
68 #define INITIAL_COMMIT_SIZE (1 * 1024 * 1024)
70 #define MARK_STACK_SIZE (256 * 1024)
71 #define MARK_STACK_SIZE_OOPS (MARK_STACK_SIZE / sizeof (st_oop))
73 #define BLOCK_SIZE 256
74 #define BLOCK_SIZE_OOPS (BLOCK_SIZE / sizeof (st_oop))
77 verify (st_oop object
)
79 st_assert (st_object_is_mark (ST_OBJECT_MARK (object
)));
83 ensure_metadata (void)
85 /* The bit arrays are implemented using bytes as smallest element of storage.
86 * If there are N oops in the heap, then we need ((N + 7) / 8) bytes
87 * for a bit array with 32 elements. We need to reserve space for
88 * two bit arrays (mark, alloc), as well as the offsets array.
90 st_uint size
, bits_size
, offsets_size
;
91 st_uchar
*metadata_start
;
93 size
= memory
->end
- memory
->start
;
94 bits_size
= ((size
+ 7) / 8);
95 offsets_size
= (size
/ BLOCK_SIZE_OOPS
) * sizeof (st_oop
*);
97 st_free (memory
->mark_bits
);
98 st_free (memory
->alloc_bits
);
99 st_free (memory
->offsets
);
101 memory
->mark_bits
= st_malloc (bits_size
);
102 memory
->alloc_bits
= st_malloc (bits_size
);
103 memory
->bits_size
= bits_size
;
105 memory
->offsets
= st_malloc (offsets_size
);
106 memory
->offsets_size
= offsets_size
;
110 grow_heap (st_uint min_size_oops
)
112 /* we grow the heap by roughly 0.25 or size_oops, whichever is larger */
114 st_uint size
, grow_size
;
118 size
= heap
->p
- heap
->start
;
119 grow_size
= MAX (size
/ 4, min_size_oops
* sizeof (st_oop
));
121 st_heap_grow (heap
, grow_size
);
123 memory
->start
= (st_oop
*) heap
->start
;
124 memory
->end
= (st_oop
*) heap
->p
;
136 heap
= st_heap_new (RESERVED_SIZE
);
140 if (!st_heap_grow (heap
, INITIAL_COMMIT_SIZE
))
143 memory
= st_new0 (st_memory
);
146 memory
->start
= (st_oop
*) heap
->start
;
147 memory
->end
= (st_oop
*) heap
->p
;
148 memory
->p
= memory
->start
;
150 memory
->roots
= ptr_array_new (15);
152 memory
->total_pause_time
.tv_sec
= 0;
153 memory
->total_pause_time
.tv_nsec
= 0;
156 memory
->mark_stack
= st_malloc (MARK_STACK_SIZE
);
157 memory
->mark_stack_size
= MARK_STACK_SIZE
;
159 memory
->mark_bits
= NULL
;
160 memory
->alloc_bits
= NULL
;
161 memory
->offsets
= NULL
;
163 memory
->free_context
= 0;
164 memory
->free_context_large
= 0;
166 memory
->ht
= st_identity_hashtable_new ();
174 st_memory_destroy (void)
180 st_memory_add_root (st_oop object
)
182 ptr_array_append (memory
->roots
, (st_pointer
) object
);
186 st_memory_remove_root (st_oop object
)
188 ptr_array_remove_fast (memory
->roots
, (st_pointer
) object
);
192 st_memory_allocate (st_uint size
)
196 st_assert (size
>= 2);
198 if (memory
->counter
> ST_COLLECTION_THRESHOLD
)
200 if ((memory
->p
+ size
) >= memory
->end
)
205 memory
->counter
+= (size
* sizeof (st_oop
));
207 return st_tag_pointer (chunk
);
211 st_memory_allocate_context (bool large
)
216 if (ST_LIKELY (memory
->free_context_large
)) {
217 context
= memory
->free_context_large
;
218 memory
->free_context_large
= ST_CONTEXT_PART_SENDER (memory
->free_context_large
);
222 if (ST_LIKELY (memory
->free_context
)) {
223 context
= memory
->free_context
;
224 memory
->free_context
= ST_CONTEXT_PART_SENDER (memory
->free_context
);
229 context
= st_memory_allocate (ST_SIZE_OOPS (struct st_method_context
) + (large
? 32 : 12));
231 st_memory_perform_gc ();
232 context
= st_memory_allocate (ST_SIZE_OOPS (struct st_method_context
) + (large
? 32 : 12));
233 st_assert (context
!= 0);
235 st_object_initialize_header (context
, ST_METHOD_CONTEXT_CLASS
);
236 st_object_set_large_context (context
, large
);
242 st_memory_recycle_context (st_oop context
)
244 if (st_object_large_context (context
)) {
245 ST_CONTEXT_PART_SENDER (context
) = memory
->free_context_large
;
246 memory
->free_context_large
= context
;
248 ST_CONTEXT_PART_SENDER (context
) = memory
->free_context
;
249 memory
->free_context
= context
;
254 get_bit (st_uchar
*bits
, st_uint index
)
256 return (bits
[index
>> 3] >> (index
& 0x7)) & 1;
260 set_bit (st_uchar
*bits
, st_uint index
)
262 bits
[index
>> 3] |= 1 << (index
& 0x7);
265 static inline st_uint
266 bit_index (st_oop object
)
268 return st_detag_pointer (object
) - memory
->start
;
272 ismarked (st_oop object
)
274 return get_bit (memory
->mark_bits
, bit_index (object
));
278 set_marked (st_oop object
)
280 set_bit (memory
->mark_bits
, bit_index (object
));
284 get_alloc_bit (st_oop object
)
286 return get_bit (memory
->alloc_bits
, bit_index (object
));
290 set_alloc_bit (st_oop object
)
292 set_bit (memory
->alloc_bits
, bit_index (object
));
295 static inline st_uint
296 get_block_index (st_oop
*object
)
298 return (object
- memory
->start
) / BLOCK_SIZE_OOPS
;
302 object_size (st_oop object
)
304 switch (st_object_format (object
)) {
305 case ST_FORMAT_OBJECT
:
306 return ST_SIZE_OOPS (struct st_header
) + st_object_instance_size (object
);
307 case ST_FORMAT_FLOAT
:
308 return ST_SIZE_OOPS (struct st_float
);
309 case ST_FORMAT_LARGE_INTEGER
:
310 return ST_SIZE_OOPS (struct st_large_integer
);
311 case ST_FORMAT_HANDLE
:
312 return ST_SIZE_OOPS (struct st_handle
);
313 case ST_FORMAT_ARRAY
:
314 return ST_SIZE_OOPS (struct st_arrayed_object
) + st_smi_value (st_arrayed_object_size (object
));
315 case ST_FORMAT_BYTE_ARRAY
:
316 return ST_SIZE_OOPS (struct st_arrayed_object
) + ST_ROUNDED_UP_OOPS (st_smi_value (st_arrayed_object_size (object
)) + 1);
317 case ST_FORMAT_WORD_ARRAY
:
318 return ST_SIZE_OOPS (struct st_arrayed_object
)
319 + (st_smi_value (st_arrayed_object_size (object
)) / (sizeof (st_oop
) / sizeof (st_uint
)));
320 case ST_FORMAT_FLOAT_ARRAY
:
321 return ST_SIZE_OOPS (struct st_arrayed_object
) + (st_smi_value (st_arrayed_object_size (object
)) * ST_SIZE_OOPS (double));
322 case ST_FORMAT_INTEGER_ARRAY
:
323 /* object format not used yet */
326 case ST_FORMAT_CONTEXT
:
327 return ST_SIZE_OOPS (struct st_header
) + st_object_instance_size (object
) + (st_object_large_context (object
) ? 32 : 12);
329 /* should not reach */
335 object_contents (st_oop object
, st_oop
**oops
, st_uint
*size
)
337 switch (st_object_format (object
)) {
338 case ST_FORMAT_OBJECT
:
339 *oops
= ST_OBJECT_FIELDS (object
);
340 *size
= st_object_instance_size (object
);
342 case ST_FORMAT_ARRAY
:
343 *oops
= st_array_elements (object
);
344 *size
= st_smi_value (st_arrayed_object_size (object
));
346 case ST_FORMAT_CONTEXT
:
347 *oops
= ST_OBJECT_FIELDS (object
);
348 *size
= st_object_instance_size (object
) + st_smi_value (ST_CONTEXT_PART_SP (object
));
350 case ST_FORMAT_FLOAT
:
351 case ST_FORMAT_LARGE_INTEGER
:
352 case ST_FORMAT_HANDLE
:
353 case ST_FORMAT_BYTE_ARRAY
:
354 case ST_FORMAT_WORD_ARRAY
:
355 case ST_FORMAT_FLOAT_ARRAY
:
356 case ST_FORMAT_INTEGER_ARRAY
:
361 /* should not reach */
366 static inline st_uint
367 compute_ordinal_number (st_memory
*memory
, st_oop ref
)
369 st_uint i
, k
, ordinal
;
373 i
= k
& ~(BLOCK_SIZE_OOPS
- 1);
376 if (get_bit (memory
->mark_bits
, i
)) {
386 remap_oop (st_oop ref
)
388 st_uint b
, i
= 0, j
= 0;
392 if (!st_object_is_heap (ref
) || ref
== ST_NIL
)
395 ordinal
= compute_ordinal_number (memory
, ref
);
396 offset
= memory
->offsets
[get_block_index (st_detag_pointer (ref
))];
397 b
= bit_index (st_tag_pointer (offset
));
400 if (get_bit (memory
->alloc_bits
, b
+ i
)) {
401 if (++j
== ordinal
) {
402 return st_tag_pointer (offset
+ i
);
413 st_memory_remap (void)
415 /* Remaps all object references in the heap */
420 while (p
< memory
->p
) {
421 p
[1] = remap_oop (p
[1]);
422 object_contents (st_tag_pointer (p
), &oops
, &size
);
423 for (st_uint i
= 0; i
< size
; i
++) {
424 oops
[i
] = remap_oop (oops
[i
]);
426 p
+= object_size (st_tag_pointer (p
));
431 basic_finalize (st_oop object
)
433 if (ST_UNLIKELY (st_object_format (object
) == ST_FORMAT_LARGE_INTEGER
))
434 mp_clear (st_large_integer_value (object
));
439 st_memory_compact (void)
441 st_oop
*p
, *from
, *to
;
447 while (ismarked (st_tag_pointer (p
)) && p
< memory
->p
) {
448 set_alloc_bit (st_tag_pointer (p
));
449 if (block
< (get_block_index (p
) + 1)) {
450 block
= get_block_index (p
);
451 memory
->offsets
[block
] = p
;
454 p
+= object_size (st_tag_pointer (p
));
458 while (!ismarked (st_tag_pointer (p
)) && p
< memory
->p
) {
459 basic_finalize (st_tag_pointer (p
));
460 p
+= object_size (st_tag_pointer (p
));
464 if (from
== to
&& p
>= memory
->p
)
467 while (from
< memory
->p
) {
469 if (ismarked (st_tag_pointer (from
))) {
471 if (st_object_is_hashed (st_tag_pointer (from
)))
472 st_identity_hashtable_rehash_object (memory
->ht
, st_tag_pointer (from
), st_tag_pointer (to
));
474 set_alloc_bit (st_tag_pointer (to
));
475 size
= object_size (st_tag_pointer (from
));
476 st_oops_move (to
, from
, size
);
478 if (block
< (get_block_index (from
) + 1)) {
479 block
= get_block_index (from
);
480 memory
->offsets
[block
] = to
;
487 basic_finalize (st_tag_pointer (from
));
488 if (st_object_is_hashed (st_tag_pointer (from
)))
489 st_identity_hashtable_remove (memory
->ht
, st_tag_pointer (from
));
490 from
+= object_size (st_tag_pointer (from
));
496 memory
->bytes_collected
= (memory
->p
- to
) * sizeof (st_oop
);
497 memory
->bytes_allocated
-= memory
->bytes_collected
;
502 grow_marking_stack (void)
504 memory
->mark_stack_size
*= 2;
505 memory
->mark_stack
= st_realloc (memory
->mark_stack
, memory
->mark_stack_size
);
507 return memory
->mark_stack_size
/ sizeof (st_oop
);
511 st_memory_mark (void)
514 st_oop
*oops
, *stack
;
515 st_uint size
, stack_size
, sp
;
518 stack
= memory
->mark_stack
;
519 stack_size
= memory
->mark_stack_size
/ sizeof (st_oop
);
521 for (st_uint i
= 0; i
< memory
->roots
->length
; i
++)
522 stack
[sp
++] = (st_oop
) ptr_array_get_index (memory
->roots
, i
);
523 stack
[sp
++] = __machine
.context
;
526 object
= stack
[--sp
];
527 if (!st_object_is_heap (object
) || ismarked (object
))
531 if (ST_UNLIKELY (sp
>= stack_size
)) {
532 stack_size
= grow_marking_stack ();
533 stack
= memory
->mark_stack
;
534 st_log ("gc", "increased size of marking stack");
536 stack
[sp
++] = ST_OBJECT_CLASS (object
);
537 object_contents (object
, &oops
, &size
);
538 for (st_uint i
= 0; i
< size
; i
++) {
539 if (ST_UNLIKELY (sp
>= stack_size
)) {
540 stack_size
= grow_marking_stack ();
541 stack
= memory
->mark_stack
;
542 st_log ("gc", "increased size of marking stack");
544 if (oops
[i
] != ST_NIL
) {
545 stack
[sp
++] = oops
[i
];
552 remap_machine (struct st_machine
*machine
)
554 st_oop context
, home
;
556 context
= remap_oop (machine
->context
);
557 if (ST_OBJECT_CLASS (context
) == ST_BLOCK_CONTEXT_CLASS
) {
558 home
= ST_BLOCK_CONTEXT_HOME (context
);
559 machine
->method
= ST_METHOD_CONTEXT_METHOD (home
);
560 machine
->receiver
= ST_METHOD_CONTEXT_RECEIVER (home
);
561 machine
->temps
= ST_METHOD_CONTEXT_STACK (home
);
562 machine
->stack
= ST_BLOCK_CONTEXT_STACK (context
);
564 machine
->method
= ST_METHOD_CONTEXT_METHOD (context
);
565 machine
->receiver
= ST_METHOD_CONTEXT_RECEIVER (context
);
566 machine
->temps
= ST_METHOD_CONTEXT_STACK (context
);
567 machine
->stack
= ST_METHOD_CONTEXT_STACK (context
);
570 machine
->context
= context
;
571 machine
->bytecode
= st_method_bytecode_bytes (machine
->method
);
572 machine
->message_receiver
= remap_oop (machine
->message_receiver
);
573 machine
->message_selector
= remap_oop (machine
->message_selector
);
574 machine
->new_method
= remap_oop (machine
->new_method
);
582 for (i
= 0; i
< ST_N_ELEMENTS (__machine
.globals
); i
++)
583 __machine
.globals
[i
] = remap_oop (__machine
.globals
[i
]);
585 for (i
= 0; i
< ST_N_ELEMENTS (__machine
.selectors
); i
++)
586 __machine
.selectors
[i
] = remap_oop (__machine
.selectors
[i
]);
588 for (i
= 0; i
< memory
->roots
->length
; i
++) {
589 ptr_array_set_index (memory
->roots
, i
,
590 (st_pointer
) remap_oop ((st_oop
) ptr_array_get_index (memory
->roots
, i
)));
595 clear_metadata (void)
597 memset (memory
->mark_bits
, 0, memory
->bits_size
);
598 memset (memory
->alloc_bits
, 0, memory
->bits_size
);
599 memset (memory
->offsets
, 0, memory
->offsets_size
);
604 st_memory_perform_gc (void)
610 garbage_collect (void)
615 /* clear context pool */
616 memory
->free_context
= 0;
617 memory
->free_context_large
= 0;
619 memory
->bytes_allocated
+= memory
->counter
;
628 times
[0] = st_timespec_to_double_seconds (&tm
);
629 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
633 st_memory_compact ();
636 times
[1] = st_timespec_to_double_seconds (&tm
);
637 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
643 remap_machine (&__machine
);
646 times
[2] = st_timespec_to_double_seconds (&tm
);
647 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
649 st_machine_clear_caches (&__machine
);
655 "marking time: %.6fs\n"
656 "compaction time: %.6fs\n"
657 "remapping time: %.6fs\n",
658 memory
->bytes_collected
/ 1024,
659 (memory
->bytes_collected
+ memory
->bytes_allocated
) / 1024,
660 times
[0], times
[1], times
[2]);
664 st_memory_remap_reference (st_oop reference
)
666 return remap_oop (reference
);