2 * sgen-cardtable.c: Card table implementation for sgen
5 * Rodrigo Kumpera (rkumpera@novell.com)
7 * Copyright 2001-2003 Ximian, Inc
8 * Copyright 2003-2010 Novell, Inc.
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10 * Copyright (C) 2012 Xamarin Inc
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License 2.0 as published by the Free Software Foundation;
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public
22 * License 2.0 along with this library; if not, write to the Free
23 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include "metadata/sgen-gc.h"
30 #include "metadata/sgen-cardtable.h"
31 #include "metadata/sgen-memory-governor.h"
32 #include "metadata/sgen-protocol.h"
33 #include "metadata/sgen-layout-stats.h"
34 #include "utils/mono-counters.h"
35 #include "utils/mono-time.h"
36 #include "utils/mono-memory-model.h"
38 //#define CARDTABLE_STATS
43 #ifdef HAVE_SYS_MMAN_H
46 #include <sys/types.h>
48 #define ARRAY_OBJ_INDEX(ptr,array,elem_size) (((char*)(ptr) - ((char*)(array) + G_STRUCT_OFFSET (MonoArray, vector))) / (elem_size))
50 guint8
*sgen_cardtable
;
52 static gboolean need_mod_union
;
54 #ifdef HEAVY_STATISTICS
56 guint64 scanned_cards
;
57 guint64 scanned_objects
;
58 guint64 remarked_cards
;
60 static guint64 los_marked_cards
;
61 static guint64 large_objects
;
62 static guint64 bloby_objects
;
63 static guint64 los_array_cards
;
64 static guint64 los_array_remsets
;
67 static guint64 major_card_scan_time
;
68 static guint64 los_card_scan_time
;
70 static guint64 last_major_scan_time
;
71 static guint64 last_los_scan_time
;
73 static void sgen_card_tables_collect_stats (gboolean begin
);
76 /*WARNING: This function returns the number of cards regardless of overflow in case of overlapping cards.*/
78 cards_in_range (mword address
, mword size
)
80 mword end
= address
+ MAX (1, size
) - 1;
81 return (end
>> CARD_BITS
) - (address
>> CARD_BITS
) + 1;
85 sgen_card_table_wbarrier_set_field (MonoObject
*obj
, gpointer field_ptr
, MonoObject
* value
)
87 *(void**)field_ptr
= value
;
88 if (need_mod_union
|| sgen_ptr_in_nursery (value
))
89 sgen_card_table_mark_address ((mword
)field_ptr
);
90 sgen_dummy_use (value
);
94 sgen_card_table_wbarrier_set_arrayref (MonoArray
*arr
, gpointer slot_ptr
, MonoObject
* value
)
96 *(void**)slot_ptr
= value
;
97 if (need_mod_union
|| sgen_ptr_in_nursery (value
))
98 sgen_card_table_mark_address ((mword
)slot_ptr
);
99 sgen_dummy_use (value
);
103 sgen_card_table_wbarrier_arrayref_copy (gpointer dest_ptr
, gpointer src_ptr
, int count
)
105 gpointer
*dest
= dest_ptr
;
106 gpointer
*src
= src_ptr
;
108 /*overlapping that required backward copying*/
109 if (src
< dest
&& (src
+ count
) > dest
) {
110 gpointer
*start
= dest
;
114 for (; dest
>= start
; --src
, --dest
) {
115 gpointer value
= *src
;
116 SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest
, value
);
117 if (need_mod_union
|| sgen_ptr_in_nursery (value
))
118 sgen_card_table_mark_address ((mword
)dest
);
119 sgen_dummy_use (value
);
122 gpointer
*end
= dest
+ count
;
123 for (; dest
< end
; ++src
, ++dest
) {
124 gpointer value
= *src
;
125 SGEN_UPDATE_REFERENCE_ALLOW_NULL (dest
, value
);
126 if (need_mod_union
|| sgen_ptr_in_nursery (value
))
127 sgen_card_table_mark_address ((mword
)dest
);
128 sgen_dummy_use (value
);
134 sgen_card_table_wbarrier_value_copy (gpointer dest
, gpointer src
, int count
, MonoClass
*klass
)
136 size_t element_size
= mono_class_value_size (klass
, NULL
);
137 size_t size
= count
* element_size
;
139 #ifdef DISABLE_CRITICAL_REGION
143 ENTER_CRITICAL_REGION
;
145 mono_gc_memmove_atomic (dest
, src
, size
);
146 sgen_card_table_mark_range ((mword
)dest
, size
);
147 #ifdef DISABLE_CRITICAL_REGION
150 EXIT_CRITICAL_REGION
;
155 sgen_card_table_wbarrier_object_copy (MonoObject
* obj
, MonoObject
*src
)
157 int size
= mono_object_class (obj
)->instance_size
;
159 #ifdef DISABLE_CRITICAL_REGION
163 ENTER_CRITICAL_REGION
;
165 mono_gc_memmove_aligned ((char*)obj
+ sizeof (MonoObject
), (char*)src
+ sizeof (MonoObject
),
166 size
- sizeof (MonoObject
));
167 sgen_card_table_mark_range ((mword
)obj
, size
);
168 #ifdef DISABLE_CRITICAL_REGION
171 EXIT_CRITICAL_REGION
;
176 sgen_card_table_wbarrier_generic_nostore (gpointer ptr
)
178 sgen_card_table_mark_address ((mword
)ptr
);
181 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
183 guint8
*sgen_shadow_cardtable
;
185 #define SGEN_SHADOW_CARDTABLE_END (sgen_shadow_cardtable + CARD_COUNT_IN_BYTES)
186 #define SGEN_CARDTABLE_END (sgen_cardtable + CARD_COUNT_IN_BYTES)
189 sgen_card_table_region_begin_scanning (mword start
, mword end
)
191 /*XXX this can be improved to work on words and have a single loop induction var */
192 while (start
<= end
) {
193 if (sgen_card_table_card_begin_scanning (start
))
195 start
+= CARD_SIZE_IN_BYTES
;
203 sgen_card_table_region_begin_scanning (mword start
, mword size
)
205 gboolean res
= FALSE
;
206 guint8
*card
= sgen_card_table_get_card_address (start
);
207 guint8
*end
= card
+ cards_in_range (start
, size
);
209 /*XXX this can be improved to work on words and have a branchless body */
210 while (card
!= end
) {
217 memset (sgen_card_table_get_card_address (start
), 0, size
>> CARD_BITS
);
224 /*FIXME this assumes that major blocks are multiple of 4K which is pretty reasonable */
226 sgen_card_table_get_card_data (guint8
*data_dest
, mword address
, mword cards
)
228 mword
*start
= (mword
*)sgen_card_table_get_card_scan_address (address
);
229 mword
*dest
= (mword
*)data_dest
;
230 mword
*end
= (mword
*)(data_dest
+ cards
);
233 for (; dest
< end
; ++dest
, ++start
) {
238 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
247 sgen_card_table_align_pointer (void *ptr
)
249 return (void*)((mword
)ptr
& ~(CARD_SIZE_IN_BYTES
- 1));
253 sgen_card_table_mark_range (mword address
, mword size
)
255 memset (sgen_card_table_get_card_address (address
), 1, cards_in_range (address
, size
));
259 sgen_card_table_is_range_marked (guint8
*cards
, mword address
, mword size
)
261 guint8
*end
= cards
+ cards_in_range (address
, size
);
263 /*This is safe since this function is only called by code that only passes continuous card blocks*/
264 while (cards
!= end
) {
273 sgen_card_table_record_pointer (gpointer address
)
275 *sgen_card_table_get_card_address ((mword
)address
) = 1;
279 sgen_card_table_find_address (char *addr
)
281 return sgen_card_table_address_is_marked ((mword
)addr
);
285 sgen_card_table_find_address_with_cards (char *cards_start
, guint8
*cards
, char *addr
)
287 cards_start
= sgen_card_table_align_pointer (cards_start
);
288 return cards
[(addr
- cards_start
) >> CARD_BITS
];
292 update_mod_union (guint8
*dest
, gboolean init
, guint8
*start_card
, size_t num_cards
)
295 memcpy (dest
, start_card
, num_cards
);
298 for (i
= 0; i
< num_cards
; ++i
)
299 dest
[i
] |= start_card
[i
];
304 alloc_mod_union (size_t num_cards
)
306 return sgen_alloc_internal_dynamic (num_cards
, INTERNAL_MEM_CARDTABLE_MOD_UNION
, TRUE
);
310 sgen_card_table_update_mod_union_from_cards (guint8
*dest
, guint8
*start_card
, size_t num_cards
)
312 gboolean init
= dest
== NULL
;
315 dest
= alloc_mod_union (num_cards
);
317 update_mod_union (dest
, init
, start_card
, num_cards
);
323 sgen_card_table_update_mod_union (guint8
*dest
, char *obj
, mword obj_size
, size_t *out_num_cards
)
325 guint8
*start_card
= sgen_card_table_get_card_address ((mword
)obj
);
326 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
327 guint8
*end_card
= sgen_card_table_get_card_address ((mword
)obj
+ obj_size
- 1) + 1;
330 guint8
*result
= NULL
;
332 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
335 rest
= num_cards
= cards_in_range ((mword
) obj
, obj_size
);
337 while (start_card
+ rest
> SGEN_CARDTABLE_END
) {
338 size_t count
= SGEN_CARDTABLE_END
- start_card
;
339 dest
= sgen_card_table_update_mod_union_from_cards (dest
, start_card
, count
);
344 start_card
= sgen_cardtable
;
348 num_cards
= end_card
- start_card
;
351 dest
= sgen_card_table_update_mod_union_from_cards (dest
, start_card
, num_cards
);
356 *out_num_cards
= num_cards
;
361 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
364 move_cards_to_shadow_table (mword start
, mword size
)
366 guint8
*from
= sgen_card_table_get_card_address (start
);
367 guint8
*to
= sgen_card_table_get_shadow_card_address (start
);
368 size_t bytes
= cards_in_range (start
, size
);
370 if (bytes
>= CARD_COUNT_IN_BYTES
) {
371 memcpy (sgen_shadow_cardtable
, sgen_cardtable
, CARD_COUNT_IN_BYTES
);
372 } else if (to
+ bytes
> SGEN_SHADOW_CARDTABLE_END
) {
373 size_t first_chunk
= SGEN_SHADOW_CARDTABLE_END
- to
;
374 size_t second_chunk
= MIN (CARD_COUNT_IN_BYTES
, bytes
) - first_chunk
;
376 memcpy (to
, from
, first_chunk
);
377 memcpy (sgen_shadow_cardtable
, sgen_cardtable
, second_chunk
);
379 memcpy (to
, from
, bytes
);
384 clear_cards (mword start
, mword size
)
386 guint8
*addr
= sgen_card_table_get_card_address (start
);
387 size_t bytes
= cards_in_range (start
, size
);
389 if (bytes
>= CARD_COUNT_IN_BYTES
) {
390 memset (sgen_cardtable
, 0, CARD_COUNT_IN_BYTES
);
391 } else if (addr
+ bytes
> SGEN_CARDTABLE_END
) {
392 size_t first_chunk
= SGEN_CARDTABLE_END
- addr
;
394 memset (addr
, 0, first_chunk
);
395 memset (sgen_cardtable
, 0, bytes
- first_chunk
);
397 memset (addr
, 0, bytes
);
405 clear_cards (mword start
, mword size
)
407 memset (sgen_card_table_get_card_address (start
), 0, cards_in_range (start
, size
));
414 sgen_card_table_prepare_for_major_collection (void)
416 /*XXX we could do this in 2 ways. using mincore or iterating over all sections/los objects */
417 sgen_major_collector_iterate_live_block_ranges (clear_cards
);
418 sgen_los_iterate_live_block_ranges (clear_cards
);
422 sgen_card_table_finish_minor_collection (void)
424 sgen_card_tables_collect_stats (FALSE
);
428 sgen_card_table_finish_scan_remsets (void *start_nursery
, void *end_nursery
, SgenGrayQueue
*queue
)
430 SGEN_TV_DECLARE (atv
);
431 SGEN_TV_DECLARE (btv
);
433 sgen_card_tables_collect_stats (TRUE
);
435 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
436 /*FIXME we should have a bit on each block/los object telling if the object have marked cards.*/
438 sgen_major_collector_iterate_live_block_ranges (move_cards_to_shadow_table
);
439 sgen_los_iterate_live_block_ranges (move_cards_to_shadow_table
);
442 sgen_card_table_prepare_for_major_collection ();
444 SGEN_TV_GETTIME (atv
);
445 sgen_major_collector_scan_card_table (queue
);
446 SGEN_TV_GETTIME (btv
);
447 last_major_scan_time
= SGEN_TV_ELAPSED (atv
, btv
);
448 major_card_scan_time
+= last_major_scan_time
;
449 sgen_los_scan_card_table (FALSE
, queue
);
450 SGEN_TV_GETTIME (atv
);
451 last_los_scan_time
= SGEN_TV_ELAPSED (btv
, atv
);
452 los_card_scan_time
+= last_los_scan_time
;
456 mono_gc_get_card_table (int *shift_bits
, gpointer
*mask
)
458 #ifndef MANAGED_WBARRIER
464 *shift_bits
= CARD_BITS
;
465 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
466 *mask
= (gpointer
)CARD_MASK
;
471 return sgen_cardtable
;
476 mono_gc_card_table_nursery_check (void)
478 return !major_collector
.is_concurrent
;
483 sgen_card_table_dump_obj_card (char *object
, size_t size
, void *dummy
)
485 guint8
*start
= sgen_card_table_get_card_scan_address (object
);
486 guint8
*end
= start
+ cards_in_range (object
, size
);
488 printf ("--obj %p %d cards [%p %p]--", object
, size
, start
, end
);
489 for (; start
< end
; ++start
) {
491 printf ("\n\t[%p] ", start
);
492 printf ("%x ", *start
);
501 #define MWORD_MASK (sizeof (mword) - 1)
504 find_card_offset (mword card
)
506 /*XXX Use assembly as this generates some pretty bad code */
507 #if defined(__i386__) && defined(__GNUC__)
508 return (__builtin_ffs (card
) - 1) / 8;
509 #elif defined(__x86_64__) && defined(__GNUC__)
510 return (__builtin_ffsll (card
) - 1) / 8;
511 #elif defined(__s390x__)
512 return (__builtin_ffsll (GUINT64_TO_LE(card
)) - 1) / 8;
515 guint8
*ptr
= (guint8
*) &card
;
516 for (i
= 0; i
< sizeof (mword
); ++i
) {
525 find_next_card (guint8
*card_data
, guint8
*end
)
527 mword
*cards
, *cards_end
;
530 while ((((mword
)card_data
) & MWORD_MASK
) && card_data
< end
) {
536 if (card_data
== end
)
539 cards
= (mword
*)card_data
;
540 cards_end
= (mword
*)((mword
)end
& ~MWORD_MASK
);
541 while (cards
< cards_end
) {
544 return (guint8
*)cards
+ find_card_offset (card
);
548 card_data
= (guint8
*)cards_end
;
549 while (card_data
< end
) {
559 sgen_cardtable_scan_object (char *obj
, mword block_obj_size
, guint8
*cards
, gboolean mod_union
, SgenGrayQueue
*queue
)
561 MonoVTable
*vt
= (MonoVTable
*)SGEN_LOAD_VTABLE (obj
);
562 MonoClass
*klass
= vt
->klass
;
564 HEAVY_STAT (++large_objects
);
566 if (!SGEN_VTABLE_HAS_REFERENCES (vt
)) {
567 sgen_object_layout_scanned_bitmap (0);
572 guint8
*card_data
, *card_base
;
573 guint8
*card_data_end
;
574 char *obj_start
= sgen_card_table_align_pointer (obj
);
575 mword obj_size
= sgen_par_object_get_size (vt
, (MonoObject
*)obj
);
576 char *obj_end
= obj
+ obj_size
;
578 size_t extra_idx
= 0;
580 MonoArray
*arr
= (MonoArray
*)obj
;
581 mword desc
= (mword
)klass
->element_class
->gc_descr
;
582 int elem_size
= mono_array_element_size (klass
);
584 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
585 guint8
*overflow_scan_end
= NULL
;
588 #ifdef SGEN_OBJECT_LAYOUT_STATISTICS
589 if (klass
->element_class
->valuetype
)
590 sgen_object_layout_scanned_vtype_array ();
592 sgen_object_layout_scanned_ref_array ();
598 card_data
= sgen_card_table_get_card_scan_address ((mword
)obj
);
600 card_base
= card_data
;
601 card_count
= cards_in_range ((mword
)obj
, obj_size
);
602 card_data_end
= card_data
+ card_count
;
605 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
606 /*Check for overflow and if so, setup to scan in two steps*/
607 if (!cards
&& card_data_end
>= SGEN_SHADOW_CARDTABLE_END
) {
608 overflow_scan_end
= sgen_shadow_cardtable
+ (card_data_end
- SGEN_SHADOW_CARDTABLE_END
);
609 card_data_end
= SGEN_SHADOW_CARDTABLE_END
;
615 card_data
= find_next_card (card_data
, card_data_end
);
616 for (; card_data
< card_data_end
; card_data
= find_next_card (card_data
+ 1, card_data_end
)) {
618 size_t idx
= (card_data
- card_base
) + extra_idx
;
619 char *start
= (char*)(obj_start
+ idx
* CARD_SIZE_IN_BYTES
);
620 char *card_end
= start
+ CARD_SIZE_IN_BYTES
;
621 char *first_elem
, *elem
;
623 HEAVY_STAT (++los_marked_cards
);
626 sgen_card_table_prepare_card_for_scanning (card_data
);
628 card_end
= MIN (card_end
, obj_end
);
630 if (start
<= (char*)arr
->vector
)
633 index
= ARRAY_OBJ_INDEX (start
, obj
, elem_size
);
635 elem
= first_elem
= (char*)mono_array_addr_with_size_fast ((MonoArray
*)obj
, elem_size
, index
);
636 if (klass
->element_class
->valuetype
) {
637 ScanVTypeFunc scan_vtype_func
= sgen_get_current_object_ops ()->scan_vtype
;
639 for (; elem
< card_end
; elem
+= elem_size
)
640 scan_vtype_func (elem
, desc
, queue
BINARY_PROTOCOL_ARG (elem_size
));
642 CopyOrMarkObjectFunc copy_func
= sgen_get_current_object_ops ()->copy_or_mark_object
;
644 HEAVY_STAT (++los_array_cards
);
645 for (; elem
< card_end
; elem
+= SIZEOF_VOID_P
) {
646 gpointer
new, old
= *(gpointer
*)elem
;
647 if ((mod_union
&& old
) || G_UNLIKELY (sgen_ptr_in_nursery (old
))) {
648 HEAVY_STAT (++los_array_remsets
);
649 copy_func ((void**)elem
, queue
);
650 new = *(gpointer
*)elem
;
651 if (G_UNLIKELY (sgen_ptr_in_nursery (new)))
652 sgen_add_to_global_remset (elem
, new);
657 binary_protocol_card_scan (first_elem
, elem
- first_elem
);
660 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
661 if (overflow_scan_end
) {
662 extra_idx
= card_data
- card_base
;
663 card_base
= card_data
= sgen_shadow_cardtable
;
664 card_data_end
= overflow_scan_end
;
665 overflow_scan_end
= NULL
;
671 HEAVY_STAT (++bloby_objects
);
673 if (sgen_card_table_is_range_marked (cards
, (mword
)obj
, block_obj_size
))
674 sgen_get_current_object_ops ()->scan_object (obj
, sgen_obj_get_descriptor (obj
), queue
);
675 } else if (sgen_card_table_region_begin_scanning ((mword
)obj
, block_obj_size
)) {
676 sgen_get_current_object_ops ()->scan_object (obj
, sgen_obj_get_descriptor (obj
), queue
);
679 binary_protocol_card_scan (obj
, sgen_safe_object_get_size ((MonoObject
*)obj
));
683 #ifdef CARDTABLE_STATS
686 int total
, marked
, remarked
, gc_marked
;
689 static card_stats major_stats
, los_stats
;
690 static card_stats
*cur_stats
;
693 count_marked_cards (mword start
, mword size
)
695 mword end
= start
+ size
;
696 while (start
<= end
) {
697 guint8 card
= *sgen_card_table_get_card_address (start
);
702 ++cur_stats
->gc_marked
;
703 start
+= CARD_SIZE_IN_BYTES
;
708 count_remarked_cards (mword start
, mword size
)
710 mword end
= start
+ size
;
711 while (start
<= end
) {
712 if (sgen_card_table_address_is_marked (start
)) {
713 ++cur_stats
->remarked
;
714 *sgen_card_table_get_card_address (start
) = 2;
716 start
+= CARD_SIZE_IN_BYTES
;
723 sgen_card_tables_collect_stats (gboolean begin
)
725 #ifdef CARDTABLE_STATS
727 memset (&major_stats
, 0, sizeof (card_stats
));
728 memset (&los_stats
, 0, sizeof (card_stats
));
729 cur_stats
= &major_stats
;
730 sgen_major_collector_iterate_live_block_ranges (count_marked_cards
);
731 cur_stats
= &los_stats
;
732 sgen_los_iterate_live_block_ranges (count_marked_cards
);
734 cur_stats
= &major_stats
;
735 sgen_major_collector_iterate_live_block_ranges (count_remarked_cards
);
736 cur_stats
= &los_stats
;
737 sgen_los_iterate_live_block_ranges (count_remarked_cards
);
738 printf ("cards major (t %d m %d g %d r %d) los (t %d m %d g %d r %d) major_scan %.2fms los_scan %.2fms\n",
739 major_stats
.total
, major_stats
.marked
, major_stats
.gc_marked
, major_stats
.remarked
,
740 los_stats
.total
, los_stats
.marked
, los_stats
.gc_marked
, los_stats
.remarked
,
741 last_major_scan_time
/ 10000.0f
, last_los_scan_time
/ 10000.0f
);
747 sgen_card_table_init (SgenRemeberedSet
*remset
)
749 sgen_cardtable
= sgen_alloc_os_memory (CARD_COUNT_IN_BYTES
, SGEN_ALLOC_INTERNAL
| SGEN_ALLOC_ACTIVATE
, "card table");
751 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
752 sgen_shadow_cardtable
= sgen_alloc_os_memory (CARD_COUNT_IN_BYTES
, SGEN_ALLOC_INTERNAL
| SGEN_ALLOC_ACTIVATE
, "shadow card table");
755 #ifdef HEAVY_STATISTICS
756 mono_counters_register ("marked cards", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &marked_cards
);
757 mono_counters_register ("scanned cards", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &scanned_cards
);
758 mono_counters_register ("remarked cards", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &remarked_cards
);
760 mono_counters_register ("los marked cards", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &los_marked_cards
);
761 mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &los_array_cards
);
762 mono_counters_register ("los array remsets", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &los_array_remsets
);
763 mono_counters_register ("cardtable scanned objects", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &scanned_objects
);
764 mono_counters_register ("cardtable large objects", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &large_objects
);
765 mono_counters_register ("cardtable bloby objects", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &bloby_objects
);
767 mono_counters_register ("cardtable major scan time", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
| MONO_COUNTER_TIME
, &major_card_scan_time
);
768 mono_counters_register ("cardtable los scan time", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
| MONO_COUNTER_TIME
, &los_card_scan_time
);
771 remset
->wbarrier_set_field
= sgen_card_table_wbarrier_set_field
;
772 remset
->wbarrier_set_arrayref
= sgen_card_table_wbarrier_set_arrayref
;
773 remset
->wbarrier_arrayref_copy
= sgen_card_table_wbarrier_arrayref_copy
;
774 remset
->wbarrier_value_copy
= sgen_card_table_wbarrier_value_copy
;
775 remset
->wbarrier_object_copy
= sgen_card_table_wbarrier_object_copy
;
776 remset
->wbarrier_generic_nostore
= sgen_card_table_wbarrier_generic_nostore
;
777 remset
->record_pointer
= sgen_card_table_record_pointer
;
779 remset
->finish_scan_remsets
= sgen_card_table_finish_scan_remsets
;
781 remset
->finish_minor_collection
= sgen_card_table_finish_minor_collection
;
782 remset
->prepare_for_major_collection
= sgen_card_table_prepare_for_major_collection
;
784 remset
->find_address
= sgen_card_table_find_address
;
785 remset
->find_address_with_cards
= sgen_card_table_find_address_with_cards
;
787 need_mod_union
= sgen_get_major_collector ()->is_concurrent
;
790 #endif /*HAVE_SGEN_GC*/