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"
28 #include "st-descriptor.h"
29 #include "st-object.h"
30 #include "st-behavior.h"
32 #include "st-context.h"
33 #include "st-method.h"
34 #include "st-association.h"
36 #include "st-system.h"
46 static inline st_oop
remap_oop (st_oop ref
);
47 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 256 MB worth of virtual address space
67 #define RESERVED_SIZE (1000 * 1024 * 1024)
68 #define INITIAL_COMMIT_SIZE (64 * 1024 * 1024)
70 // 500 KB for mark stack
71 #define MARK_STACK_SIZE (500 * 1024)
72 #define MARK_STACK_SIZE_OOPS (MARK_STACK_SIZE / sizeof (st_oop))
74 #define BLOCK_SIZE 256
75 #define BLOCK_SIZE_OOPS (BLOCK_SIZE / sizeof (st_oop))
78 verify (st_oop object
)
80 st_assert (st_object_is_mark (ST_HEADER (object
)->mark
));
84 round_pagesize (st_uint size
)
86 return ((size
+ st_system_pagesize () - 1) / st_system_pagesize ()) * st_system_pagesize ();
90 ensure_metadata (void)
92 /* The bit arrays are implemented using bytes as smallest element of storage.
93 * If there are N oops in the heap, then we need ((N + 7) / 8) bytes
94 * for a bit array with 32 elements. We need to reserve space for
95 * two bit arrays (mark, alloc), as well as the offsets array.
97 st_uint size
, bits_size
, offsets_size
;
98 st_uchar
*metadata_start
;
100 size
= memory
->end
- memory
->start
;
101 bits_size
= ((size
+ 7) / 8);
102 offsets_size
= (size
/ BLOCK_SIZE_OOPS
) * sizeof (st_oop
*);
104 st_free (memory
->mark_bits
);
105 st_free (memory
->alloc_bits
);
106 st_free (memory
->offsets
);
108 memory
->mark_bits
= st_malloc (bits_size
);
109 memory
->alloc_bits
= st_malloc (bits_size
);
110 memory
->bits_size
= bits_size
;
112 memory
->offsets
= st_malloc (offsets_size
);
113 memory
->offsets_size
= offsets_size
;
119 /* we grow the heap by roughly a quarter (0.4) */
121 st_uint size
, grow_size
;
125 size
= heap
->p
- heap
->start
;
126 grow_size
= size
/ 4;
128 st_heap_grow (heap
, grow_size
);
130 memory
->start
= (st_oop
*) heap
->start
;
131 memory
->end
= (st_oop
*) heap
->p
;
143 heap
= st_heap_new (RESERVED_SIZE
);
147 if (!st_heap_grow (heap
, INITIAL_COMMIT_SIZE
))
150 memory
= st_new0 (st_memory
);
153 memory
->start
= (st_oop
*) heap
->start
;
154 memory
->end
= (st_oop
*) heap
->p
;
155 memory
->p
= memory
->start
;
157 memory
->roots
= ptr_array_new (15);
159 memory
->total_pause_time
.tv_sec
= 0;
160 memory
->total_pause_time
.tv_nsec
= 0;
163 memory
->mark_stack
= st_malloc (MARK_STACK_SIZE
);
165 memory
->mark_bits
= NULL
;
166 memory
->alloc_bits
= NULL
;
167 memory
->offsets
= NULL
;
175 st_memory_destroy (void)
181 st_memory_add_root (st_oop object
)
183 ptr_array_append (memory
->roots
, (st_pointer
) object
);
187 st_memory_remove_root (st_oop object
)
189 ptr_array_remove_fast (memory
->roots
, (st_pointer
) object
);
193 st_memory_allocate (st_uint size
)
197 st_assert (size
>= 2);
199 if (memory
->counter
> ST_COLLECTION_THRESHOLD
)
201 if ((memory
->p
+ size
) >= memory
->end
)
207 memory
->counter
+= (size
* sizeof (st_oop
));
208 memory
->bytes_allocated
+= (size
* sizeof (st_oop
));
210 return ST_OOP (chunk
);
219 static inline st_oop
*
222 return (st_oop
*) ST_POINTER (oop
);
226 get_bit (st_uchar
*bits
, st_uint index
)
228 return (bits
[index
>> 3] >> (index
& 0x7)) & 1;
232 set_bit (st_uchar
*bits
, st_uint index
)
234 bits
[index
>> 3] |= 1 << (index
& 0x7);
237 static inline st_uint
238 bit_index (st_memory
*memory
, st_oop object
)
240 return detag (object
) - memory
->start
;
244 ismarked (st_memory
*memory
, st_oop object
)
246 return get_bit (memory
->mark_bits
, bit_index (memory
, object
));
250 set_marked (st_memory
*memory
, st_oop object
)
252 set_bit (memory
->mark_bits
, bit_index (memory
, object
));
256 get_alloc_bit (st_memory
*memory
, st_oop object
)
258 return get_bit (memory
->alloc_bits
, bit_index (memory
, object
));
262 set_alloc_bit (st_memory
*memory
, st_oop object
)
264 set_bit (memory
->alloc_bits
, bit_index (memory
, object
));
267 static inline st_uint
268 get_block_index (st_memory
*memory
, st_oop
*object
)
270 return (object
- memory
->start
) / BLOCK_SIZE_OOPS
;
273 static inline st_uint
274 compute_ordinal_number (st_memory
*memory
, st_oop ref
)
276 st_uint i
, ordinal
= 0;
279 b
= bit_index (memory
, ref
) & ~(BLOCK_SIZE_OOPS
- 1);
280 k
= bit_index (memory
, ref
);
282 for (i
= 0; i
< BLOCK_SIZE_OOPS
; i
++) {
283 if (get_bit (memory
->mark_bits
, b
+ i
)) {
295 remap_oop (st_oop ref
)
297 st_uint b
, i
= 0, j
= 0;
301 if (!st_object_is_heap (ref
) || ref
== st_nil
)
304 ordinal
= compute_ordinal_number (memory
, ref
);
305 offset
= memory
->offsets
[get_block_index (memory
, detag (ref
))];
306 b
= bit_index (memory
, tag (offset
));
309 if (get_bit (memory
->alloc_bits
, b
+ i
)) {
310 if (++j
== ordinal
) {
311 return tag (offset
+ i
);
328 while (p
< memory
->p
) {
330 /* remap class field */
331 p
[2] = remap_oop (p
[2]);
333 st_object_contents (tag (p
), &oops
, &size
);
334 for (st_uint i
= 0; i
< size
; i
++) {
335 oops
[i
] = remap_oop (oops
[i
]);
337 p
+= st_object_size (tag (p
));
344 st_oop
*p
, *from
, *to
;
350 while (ismarked (memory
, tag (p
)) && p
< memory
->p
) {
351 set_alloc_bit (memory
, tag (p
));
352 if (block
< (get_block_index (memory
, p
) + 1)) {
353 block
= get_block_index (memory
, p
);
354 memory
->offsets
[block
] = p
;
357 p
+= st_object_size (tag (p
));
361 while (!ismarked (memory
, tag (p
)) && p
< memory
->p
) {
362 st_assert (st_object_is_mark (*p
));
363 p
+= st_object_size (tag (p
));
367 if (from
== to
&& p
>= memory
->p
)
370 while (from
< memory
->p
) {
372 if (ismarked (memory
, tag (from
))) {
374 set_alloc_bit (memory
, tag (to
));
375 size
= st_object_size (tag (from
));
376 st_oops_move (to
, from
, size
);
378 if (block
< (get_block_index (memory
, from
) + 1)) {
379 block
= get_block_index (memory
, from
);
380 memory
->offsets
[block
] = to
;
387 from
+= st_object_size (tag (from
));
393 memory
->bytes_collected
= (memory
->p
- to
) * sizeof (st_oop
);
394 memory
->bytes_allocated
-= memory
->bytes_collected
;
402 st_oop
*oops
, *stack
;
406 stack
= memory
->mark_stack
;
408 for (st_uint i
= 0; i
< memory
->roots
->length
; i
++)
409 stack
[sp
++] = (st_oop
) ptr_array_get_index (memory
->roots
, i
);
410 stack
[sp
++] = proc
->context
;
414 object
= stack
[--sp
];
415 if (!st_object_is_heap (object
) || ismarked (memory
, object
))
418 set_marked (memory
, object
);
419 st_object_contents (object
, &oops
, &size
);
420 if (ST_UNLIKELY ((sp
+ size
+ 1) >= MARK_STACK_SIZE_OOPS
))
423 stack
[sp
++] = ST_HEADER (object
)->class;
424 for (st_uint i
= 0; i
< size
; i
++) {
425 if (oops
[i
] != st_nil
) {
426 stack
[sp
++] = oops
[i
];
434 fprintf (stderr
, "panda: error: marking stack overflowed\n");
439 remap_processor (st_processor
*pr
)
441 st_oop context
, home
;
443 context
= remap_oop (pr
->context
);
444 if (ST_HEADER (context
)->class == st_block_context_class
) {
445 home
= ST_BLOCK_CONTEXT (context
)->home
;
446 pr
->method
= ST_METHOD_CONTEXT (home
)->method
;
447 pr
->receiver
= ST_METHOD_CONTEXT (home
)->receiver
;
448 pr
->literals
= st_array_elements (ST_METHOD (pr
->method
)->literals
);
449 pr
->temps
= ST_METHOD_CONTEXT_TEMPORARY_FRAME (home
);
450 pr
->stack
= ST_BLOCK_CONTEXT (context
)->stack
;
452 pr
->method
= ST_METHOD_CONTEXT (context
)->method
;
453 pr
->receiver
= ST_METHOD_CONTEXT (context
)->receiver
;
454 pr
->literals
= st_array_elements (ST_METHOD (pr
->method
)->literals
);
455 pr
->temps
= ST_METHOD_CONTEXT_TEMPORARY_FRAME (context
);
456 pr
->stack
= ST_METHOD_CONTEXT_STACK (context
);
459 pr
->context
= context
;
460 pr
->bytecode
= st_method_bytecode_bytes (pr
->method
);
461 pr
->message_receiver
= remap_oop (pr
->message_receiver
);
462 pr
->message_selector
= remap_oop (pr
->message_selector
);
463 pr
->new_method
= remap_oop (pr
->new_method
);
471 for (i
= 0; i
< ST_N_ELEMENTS (globals
); i
++)
472 globals
[i
] = remap_oop (globals
[i
]);
474 for (i
= 0; i
< ST_N_ELEMENTS (st_specials
); i
++)
475 st_specials
[i
] = remap_oop (st_specials
[i
]);
478 for (i
= 0; i
< memory
->roots
->length
; i
++) {
479 ptr_array_set_index (memory
->roots
, i
,
480 (st_pointer
) remap_oop ((st_oop
) ptr_array_get_index (memory
->roots
, i
)));
485 clear_metadata (void)
487 memset (memory
->mark_bits
, 0, memory
->bits_size
);
488 memset (memory
->alloc_bits
, 0, memory
->bits_size
);
489 memset (memory
->offsets
, 0, memory
->offsets_size
);
493 garbage_collect (void)
505 times
[0] = st_timespec_to_double_seconds (&tm
);
506 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
513 times
[1] = st_timespec_to_double_seconds (&tm
);
514 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
520 remap_processor (proc
);
523 times
[2] = st_timespec_to_double_seconds (&tm
);
524 st_timespec_add (&memory
->total_pause_time
, &tm
, &memory
->total_pause_time
);
526 st_processor_clear_caches (proc
);
529 if (st_verbose_mode ()) {
530 fprintf (stderr
, "** gc: collected: %uK, heapSize: %uK, marking: %.6fs, compaction: %.6fs, remapping: %.6fs\n",
531 memory
->bytes_collected
/ 1024,
532 (memory
->bytes_collected
+ memory
->bytes_allocated
) / 1024,
533 times
[0], times
[1], times
[2]);
538 st_heap_new (st_uint reserved_size
)
540 /* Create a new heap with a reserved address space.
541 * Returns NULL if address space could not be reserved
547 st_assert (reserved_size
> 0);
548 size
= round_pagesize (reserved_size
);
550 result
= st_system_reserve_memory (NULL
, size
);
554 heap
= st_new0 (st_heap
);
556 heap
->start
= result
;
557 heap
->end
= result
+ size
;
564 st_heap_grow (st_heap
*heap
, st_uint grow_size
)
566 /* Grows the heap by the specified amount (in bytes).
567 * The primitive will not succeed if the heap runs out
568 * of reserved address space.
573 st_assert (grow_size
> 0);
574 size
= round_pagesize (grow_size
);
576 if ((heap
->p
+ size
) >= heap
->end
)
579 result
= st_system_commit_memory (heap
->p
, size
);
589 st_heap_shrink (st_heap
*heap
, st_uint shrink_size
)
591 /* Shrinks the heap by the specified amount (in bytes).
596 st_assert (shrink_size
> 0);
597 size
= round_pagesize (shrink_size
);
599 if ((heap
->p
- size
) < heap
->start
)
602 result
= st_system_decommit_memory (heap
->p
- size
, size
);
612 st_heap_destroy (st_heap
*heap
)
614 st_system_release_memory (heap
->start
, heap
->end
- heap
->start
);