2 * sgen-los.c: Simple generational GC.
5 * Paolo Molaro (lupus@ximian.com)
7 * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
9 * Thread start/stop adapted from Boehm's GC:
10 * Copyright (c) 1994 by Xerox Corporation. All rights reserved.
11 * Copyright (c) 1996 by Silicon Graphics. All rights reserved.
12 * Copyright (c) 1998 by Fergus Henderson. All rights reserved.
13 * Copyright (c) 2000-2004 by Hewlett-Packard Company. All rights reserved.
15 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16 * OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
18 * Permission is hereby granted to use or copy this program
19 * for any purpose, provided the above notices are retained on all copies.
20 * Permission to modify the code and to distribute modified code is granted,
21 * provided the above notices are retained, and a notice that the code was
22 * modified is included with the above copyright notice.
25 * Copyright 2001-2003 Ximian, Inc
26 * Copyright 2003-2010 Novell, Inc.
28 * Permission is hereby granted, free of charge, to any person obtaining
29 * a copy of this software and associated documentation files (the
30 * "Software"), to deal in the Software without restriction, including
31 * without limitation the rights to use, copy, modify, merge, publish,
32 * distribute, sublicense, and/or sell copies of the Software, and to
33 * permit persons to whom the Software is furnished to do so, subject to
34 * the following conditions:
36 * The above copyright notice and this permission notice shall be
37 * included in all copies or substantial portions of the Software.
39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
52 #include "metadata/sgen-gc.h"
53 #include "metadata/sgen-protocol.h"
54 #include "metadata/sgen-cardtable.h"
55 #include "utils/mono-mmap.h"
57 #define LOS_SECTION_SIZE (1024 * 1024)
60 * This shouldn't be much smaller or larger than MAX_SMALL_OBJ_SIZE.
61 * Must be at least sizeof (LOSSection).
63 #define LOS_CHUNK_SIZE 4096
64 #define LOS_CHUNK_BITS 12
66 /* Largest object that can be allocated in a section. */
67 #define LOS_SECTION_OBJECT_LIMIT (LOS_SECTION_SIZE - LOS_CHUNK_SIZE - sizeof (LOSObject))
68 //#define LOS_SECTION_OBJECT_LIMIT 0
69 #define LOS_SECTION_NUM_CHUNKS ((LOS_SECTION_SIZE >> LOS_CHUNK_BITS) - 1)
71 #define LOS_SECTION_FOR_OBJ(obj) ((LOSSection*)((mword)(obj) & ~(mword)(LOS_SECTION_SIZE - 1)))
72 #define LOS_CHUNK_INDEX(obj,section) (((char*)(obj) - (char*)(section)) >> LOS_CHUNK_BITS)
74 #define LOS_NUM_FAST_SIZES 32
76 typedef struct _LOSFreeChunks LOSFreeChunks
;
77 struct _LOSFreeChunks
{
78 LOSFreeChunks
*next_size
;
82 typedef struct _LOSSection LOSSection
;
86 unsigned char *free_chunk_map
;
89 LOSObject
*los_object_list
= NULL
;
90 mword los_memory_usage
= 0;
92 static LOSSection
*los_sections
= NULL
;
93 static LOSFreeChunks
*los_fast_free_lists
[LOS_NUM_FAST_SIZES
]; /* 0 is for larger sizes */
94 static mword los_num_objects
= 0;
95 static int los_num_sections
= 0;
96 static mword next_los_collection
= 2*1024*1024; /* 2 MB, need to tune */
99 //#define LOS_CONSISTENCY_CHECK
103 #define LOS_SEGMENT_SIZE (4096 * 1024)
105 static char *los_segment
= NULL
;
106 static int los_segment_index
= 0;
109 #ifdef LOS_CONSISTENCY_CHECK
111 los_consistency_check (void)
116 mword memory_usage
= 0;
118 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
119 char *end
= obj
->data
+ obj
->size
;
120 int start_index
, num_chunks
;
122 memory_usage
+= obj
->size
;
124 if (obj
->size
> LOS_SECTION_OBJECT_LIMIT
)
127 section
= LOS_SECTION_FOR_OBJ (obj
);
129 g_assert (end
<= (char*)section
+ LOS_SECTION_SIZE
);
131 start_index
= LOS_CHUNK_INDEX (obj
, section
);
132 num_chunks
= (obj
->size
+ sizeof (LOSObject
) + LOS_CHUNK_SIZE
- 1) >> LOS_CHUNK_BITS
;
133 for (i
= start_index
; i
< start_index
+ num_chunks
; ++i
)
134 g_assert (!section
->free_chunk_map
[i
]);
137 for (i
= 0; i
< LOS_NUM_FAST_SIZES
; ++i
) {
138 LOSFreeChunks
*size_chunks
;
139 for (size_chunks
= los_fast_free_lists
[i
]; size_chunks
; size_chunks
= size_chunks
->next_size
) {
140 LOSSection
*section
= LOS_SECTION_FOR_OBJ (size_chunks
);
141 int j
, num_chunks
, start_index
;
144 g_assert (size_chunks
->size
>= LOS_NUM_FAST_SIZES
* LOS_CHUNK_SIZE
);
146 g_assert (size_chunks
->size
== i
* LOS_CHUNK_SIZE
);
148 num_chunks
= size_chunks
->size
>> LOS_CHUNK_BITS
;
149 start_index
= LOS_CHUNK_INDEX (size_chunks
, section
);
150 for (j
= start_index
; j
< start_index
+ num_chunks
; ++j
)
151 g_assert (section
->free_chunk_map
[j
]);
155 g_assert (los_memory_usage
== memory_usage
);
160 add_free_chunk (LOSFreeChunks
*free_chunks
, size_t size
)
162 int num_chunks
= size
>> LOS_CHUNK_BITS
;
164 free_chunks
->size
= size
;
166 if (num_chunks
>= LOS_NUM_FAST_SIZES
)
168 free_chunks
->next_size
= los_fast_free_lists
[num_chunks
];
169 los_fast_free_lists
[num_chunks
] = free_chunks
;
172 static LOSFreeChunks
*
173 get_from_size_list (LOSFreeChunks
**list
, size_t size
)
175 LOSFreeChunks
*free_chunks
= NULL
;
177 int num_chunks
, i
, start_index
;
179 g_assert ((size
& (LOS_CHUNK_SIZE
- 1)) == 0);
183 if (free_chunks
->size
>= size
)
185 list
= &(*list
)->next_size
;
191 *list
= free_chunks
->next_size
;
193 if (free_chunks
->size
> size
)
194 add_free_chunk ((LOSFreeChunks
*)((char*)free_chunks
+ size
), free_chunks
->size
- size
);
196 num_chunks
= size
>> LOS_CHUNK_BITS
;
198 section
= LOS_SECTION_FOR_OBJ (free_chunks
);
200 start_index
= LOS_CHUNK_INDEX (free_chunks
, section
);
201 for (i
= start_index
; i
< start_index
+ num_chunks
; ++i
) {
202 g_assert (section
->free_chunk_map
[i
]);
203 section
->free_chunk_map
[i
] = 0;
206 section
->num_free_chunks
-= size
>> LOS_CHUNK_BITS
;
207 g_assert (section
->num_free_chunks
>= 0);
213 get_los_section_memory (size_t size
)
216 LOSFreeChunks
*free_chunks
;
219 size
+= LOS_CHUNK_SIZE
- 1;
220 size
&= ~(LOS_CHUNK_SIZE
- 1);
222 num_chunks
= size
>> LOS_CHUNK_BITS
;
224 g_assert (size
> 0 && size
- sizeof (LOSObject
) <= LOS_SECTION_OBJECT_LIMIT
);
225 g_assert (num_chunks
> 0);
228 if (num_chunks
>= LOS_NUM_FAST_SIZES
) {
229 free_chunks
= get_from_size_list (&los_fast_free_lists
[0], size
);
232 for (i
= num_chunks
; i
< LOS_NUM_FAST_SIZES
; ++i
) {
233 free_chunks
= get_from_size_list (&los_fast_free_lists
[i
], size
);
238 free_chunks
= get_from_size_list (&los_fast_free_lists
[0], size
);
242 return (LOSObject
*)free_chunks
;
244 if (!mono_sgen_try_alloc_space (LOS_SECTION_SIZE
, SPACE_LOS
))
247 section
= mono_sgen_alloc_os_memory_aligned (LOS_SECTION_SIZE
, LOS_SECTION_SIZE
, TRUE
);
249 free_chunks
= (LOSFreeChunks
*)((char*)section
+ LOS_CHUNK_SIZE
);
250 free_chunks
->size
= LOS_SECTION_SIZE
- LOS_CHUNK_SIZE
;
251 free_chunks
->next_size
= los_fast_free_lists
[0];
252 los_fast_free_lists
[0] = free_chunks
;
254 section
->num_free_chunks
= LOS_SECTION_NUM_CHUNKS
;
256 section
->free_chunk_map
= (unsigned char*)section
+ sizeof (LOSSection
);
257 g_assert (sizeof (LOSSection
) + LOS_SECTION_NUM_CHUNKS
+ 1 <= LOS_CHUNK_SIZE
);
258 section
->free_chunk_map
[0] = 0;
259 memset (section
->free_chunk_map
+ 1, 1, LOS_SECTION_NUM_CHUNKS
);
261 section
->next
= los_sections
;
262 los_sections
= section
;
270 free_los_section_memory (LOSObject
*obj
, size_t size
)
272 LOSSection
*section
= LOS_SECTION_FOR_OBJ (obj
);
273 int num_chunks
, i
, start_index
;
275 size
+= LOS_CHUNK_SIZE
- 1;
276 size
&= ~(LOS_CHUNK_SIZE
- 1);
278 num_chunks
= size
>> LOS_CHUNK_BITS
;
280 g_assert (size
> 0 && size
- sizeof (LOSObject
) <= LOS_SECTION_OBJECT_LIMIT
);
281 g_assert (num_chunks
> 0);
283 section
->num_free_chunks
+= num_chunks
;
284 g_assert (section
->num_free_chunks
<= LOS_SECTION_NUM_CHUNKS
);
287 * We could free the LOS section here if it's empty, but we
288 * can't unless we also remove its free chunks from the fast
289 * free lists. Instead, we do it in los_sweep().
292 start_index
= LOS_CHUNK_INDEX (obj
, section
);
293 for (i
= start_index
; i
< start_index
+ num_chunks
; ++i
) {
294 g_assert (!section
->free_chunk_map
[i
]);
295 section
->free_chunk_map
[i
] = 1;
298 add_free_chunk ((LOSFreeChunks
*)obj
, size
);
304 mono_sgen_los_free_object (LOSObject
*obj
)
307 size_t size
= obj
->size
;
308 DEBUG (4, fprintf (gc_debug_file
, "Freed large object %p, size %lu\n", obj
->data
, (unsigned long)obj
->size
));
309 binary_protocol_empty (obj
->data
, obj
->size
);
311 los_memory_usage
-= size
;
317 if (size
> LOS_SECTION_OBJECT_LIMIT
) {
319 pagesize
= mono_pagesize ();
320 size
+= sizeof (LOSObject
);
321 size
+= pagesize
- 1;
322 size
&= ~(pagesize
- 1);
323 mono_sgen_free_os_memory (obj
, size
);
324 mono_sgen_release_space (size
, SPACE_LOS
);
326 free_los_section_memory (obj
, size
+ sizeof (LOSObject
));
327 #ifdef LOS_CONSISTENCY_CHECKS
328 los_consistency_check ();
336 * Objects with size >= MAX_SMALL_SIZE are allocated in the large object space.
337 * They are currently kept track of with a linked list.
338 * They don't move, so there is no need to pin them during collection
339 * and we avoid the memcpy overhead.
342 mono_sgen_los_alloc_large_inner (MonoVTable
*vtable
, size_t size
)
344 LOSObject
*obj
= NULL
;
347 g_assert (size
> SGEN_MAX_SMALL_OBJ_SIZE
);
351 los_segment
= mono_sgen_alloc_os_memory (LOS_SEGMENT_SIZE
, TRUE
);
352 los_segment_index
= ALIGN_UP (los_segment_index
);
354 obj
= (LOSObject
*)(los_segment
+ los_segment_index
);
355 los_segment_index
+= size
+ sizeof (LOSObject
);
356 g_assert (los_segment_index
<= LOS_SEGMENT_SIZE
);
358 if (mono_sgen_need_major_collection (size
)) {
359 DEBUG (4, fprintf (gc_debug_file
, "Should trigger major collection: req size %zd (los already: %lu, limit: %lu)\n", size
, (unsigned long)los_memory_usage
, (unsigned long)next_los_collection
));
360 sgen_collect_major_no_lock ("LOS overflow");
364 obj
= malloc (size
+ sizeof (LOSObject
));
365 memset (obj
, 0, size
+ sizeof (LOSObject
));
367 if (size
> LOS_SECTION_OBJECT_LIMIT
) {
368 size_t alloc_size
= size
;
370 pagesize
= mono_pagesize ();
371 alloc_size
+= sizeof (LOSObject
);
372 alloc_size
+= pagesize
- 1;
373 alloc_size
&= ~(pagesize
- 1);
374 if (mono_sgen_try_alloc_space (alloc_size
, SPACE_LOS
)) {
375 obj
= mono_sgen_alloc_os_memory (alloc_size
, TRUE
);
376 obj
->huge_object
= TRUE
;
379 obj
= get_los_section_memory (size
+ sizeof (LOSObject
));
381 memset (obj
, 0, size
+ sizeof (LOSObject
));
387 g_assert (!((mword
)obj
->data
& (SGEN_ALLOC_ALIGN
- 1)));
389 vtslot
= (void**)obj
->data
;
391 mono_sgen_update_heap_boundaries ((mword
)obj
->data
, (mword
)obj
->data
+ size
);
392 obj
->next
= los_object_list
;
393 los_object_list
= obj
;
394 los_memory_usage
+= size
;
396 DEBUG (4, fprintf (gc_debug_file
, "Allocated large object %p, vtable: %p (%s), size: %zd\n", obj
->data
, vtable
, vtable
->klass
->name
, size
));
397 binary_protocol_alloc (obj
->data
, vtable
, size
);
399 #ifdef LOS_CONSISTENCY_CHECK
400 los_consistency_check ();
407 mono_sgen_los_sweep (void)
409 LOSSection
*section
, *prev
;
411 int num_sections
= 0;
413 for (i
= 0; i
< LOS_NUM_FAST_SIZES
; ++i
)
414 los_fast_free_lists
[i
] = NULL
;
417 section
= los_sections
;
419 if (section
->num_free_chunks
== LOS_SECTION_NUM_CHUNKS
) {
420 LOSSection
*next
= section
->next
;
425 mono_sgen_free_os_memory (section
, LOS_SECTION_SIZE
);
426 mono_sgen_release_space (LOS_SECTION_SIZE
, SPACE_LOS
);
432 for (i
= 0; i
<= LOS_SECTION_NUM_CHUNKS
; ++i
) {
433 if (section
->free_chunk_map
[i
]) {
435 for (j
= i
+ 1; j
<= LOS_SECTION_NUM_CHUNKS
&& section
->free_chunk_map
[j
]; ++j
)
437 add_free_chunk ((LOSFreeChunks
*)((char*)section
+ (i
<< LOS_CHUNK_BITS
)), (j
- i
) << LOS_CHUNK_BITS
);
443 section
= section
->next
;
448 #ifdef LOS_CONSISTENCY_CHECK
449 los_consistency_check ();
453 g_print ("LOS sections: %d objects: %d usage: %d\n", num_sections, los_num_objects, los_memory_usage);
454 for (i = 0; i < LOS_NUM_FAST_SIZES; ++i) {
456 LOSFreeChunks *free_chunks;
457 for (free_chunks = los_fast_free_lists [i]; free_chunks; free_chunks = free_chunks->next_size)
459 g_print (" %d: %d\n", i, num_chunks);
463 g_assert (los_num_sections
== num_sections
);
467 mono_sgen_ptr_is_in_los (char *ptr
, char **start
)
472 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
473 char *end
= obj
->data
+ obj
->size
;
475 if (ptr
>= obj
->data
&& ptr
< end
) {
484 mono_sgen_los_iterate_objects (IterateObjectCallbackFunc cb
, void *user_data
)
488 for (obj
= los_object_list
; obj
; obj
= obj
->next
)
489 cb (obj
->data
, obj
->size
, user_data
);
493 mono_sgen_los_iterate_live_block_ranges (sgen_cardtable_block_callback callback
)
496 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
497 MonoVTable
*vt
= (MonoVTable
*)SGEN_LOAD_VTABLE (obj
->data
);
498 if (SGEN_VTABLE_HAS_REFERENCES (vt
))
499 callback ((mword
)obj
->data
, (mword
)obj
->size
);
503 #ifdef SGEN_HAVE_CARDTABLE
505 mono_sgen_los_scan_card_table (SgenGrayQueue
*queue
)
509 for (obj
= los_object_list
; obj
; obj
= obj
->next
) {
510 sgen_cardtable_scan_object (obj
->data
, obj
->size
, NULL
, queue
);
515 #endif /* HAVE_SGEN_GC */