2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
10 // GC automatically manages memory allocated by managed code.
11 // The design doc for GC can be found at
12 // file:../../doc/BookOfTheRuntime/GC/GCDesign.doc
14 // This file includes both the code for GC and the allocator. The most common
15 // case for a GC to be triggered is from the allocator code. See
16 // code:#try_allocate_more_space where it calls GarbageCollectGeneration.
18 // Entry points for the allocate is GCHeap::Alloc* which are called by the
19 // allocation helpers in gcscan.cpp
26 // defines for ETW events.
27 #define ETW_TYPE_GC_MARK_1 21 // after marking stack roots
28 #define ETW_TYPE_GC_MARK_2 22 // after marking finalize queue roots
29 #define ETW_TYPE_GC_MARK_3 23 // after marking handles
30 #define ETW_TYPE_GC_MARK_4 24 // after marking cards
32 #define ETW_TYPE_BGC_BEGIN 25
33 #define ETW_TYPE_BGC_1ST_NONCON_END 26
34 #define ETW_TYPE_BGC_1ST_CON_END 27
35 #define ETW_TYPE_BGC_2ND_NONCON_BEGIN 28
36 #define ETW_TYPE_BGC_2ND_NONCON_END 29
37 #define ETW_TYPE_BGC_2ND_CON_BEGIN 30
38 #define ETW_TYPE_BGC_2ND_CON_END 31
39 #define ETW_TYPE_BGC_PLAN_END 32
40 #define ETW_TYPE_BGC_SWEEP_END 33
42 #define ETW_TYPE_BGC_DRAIN_MARK_LIST 34
43 #define ETW_TYPE_BGC_REVISIT 35
44 #define ETW_TYPE_BGC_OVERFLOW 36
46 #define ETW_TYPE_ALLOC_WAIT_BEGIN 37
47 #define ETW_TYPE_ALLOC_WAIT_END 38
49 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
50 inline BOOL ShouldTrackMovementForProfilerOrEtw()
53 if (CORProfilerTrackGC())
57 #ifdef FEATURE_EVENT_TRACE
58 if (ETW::GCLog::ShouldTrackMovementForEtw())
64 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
66 #if defined(FEATURE_REDHAWK)
67 #define MAYBE_UNUSED_VAR(v) v = v
69 #define MAYBE_UNUSED_VAR(v)
70 #endif // FEATURE_REDHAWK
72 #define MAX_PTR ((BYTE*)(~(SSIZE_T)0))
75 #define partial_size_th 100
76 #define num_partial_refs 64
78 #define partial_size_th 100
79 #define num_partial_refs 32
82 #define demotion_plug_len_th (6*1024*1024)
85 #define MARK_STACK_INITIAL_LENGTH 1024
87 #define MARK_STACK_INITIAL_LENGTH 128
90 #define LOH_PIN_QUEUE_LENGTH 100
91 #define LOH_PIN_DECAY 10
93 // Right now we support maximum 256 procs - meaning that we will create at most
94 // 256 GC threads and 256 GC heaps.
95 #define MAX_SUPPORTED_CPUS 256
97 #if defined (TRACE_GC) && !defined (DACCESS_COMPILE)
98 const char * const allocation_state_str[] = {
104 "try_fit_new_seg_after_cg",
108 "try_free_full_seg_in_bgc",
109 "try_free_after_bgc",
112 "acquire_seg_after_cg",
113 "acquire_seg_after_bgc",
114 "check_and_wait_for_bgc",
115 "trigger_full_compact_gc",
116 "trigger_ephemeral_gc",
117 "trigger_2nd_ephemeral_gc",
120 #endif //TRACE_GC && !DACCESS_COMPILE
123 // Keep this in sync with the definition of gc_reaon
124 #if (defined(DT_LOG) || defined(TRACE_GC)) && !defined (DACCESS_COMPILE)
125 static const char* const str_gc_reasons[] =
138 #endif // defined(DT_LOG) || defined(TRACE_GC)
141 BOOL is_induced (gc_reason reason)
143 return ((reason == reason_induced) ||
144 (reason == reason_induced_noforce) ||
145 (reason == reason_lowmemory) ||
146 (reason == reason_lowmemory_blocking) ||
147 (reason == reason_induced_compacting));
151 BOOL is_induced_blocking (gc_reason reason)
153 return ((reason == reason_induced) ||
154 (reason == reason_lowmemory_blocking) ||
155 (reason == reason_induced_compacting));
159 // There is a current and a prior copy of the statistics. This allows us to display deltas per reporting
160 // interval, as well as running totals. The 'min' and 'max' values require special treatment. They are
161 // Reset (zeroed) in the current statistics when we begin a new interval and they are updated via a
162 // comparison with the global min/max.
163 GCStatistics g_GCStatistics;
164 GCStatistics g_LastGCStatistics;
166 WCHAR* GCStatistics::logFileName = NULL;
167 FILE* GCStatistics::logFile = NULL;
169 void GCStatistics::AddGCStats(const gc_mechanisms& settings, size_t timeInMSec)
172 if (settings.concurrent)
174 bgc.Accumulate((DWORD)timeInMSec*1000);
177 else if (settings.background_p)
179 fgc.Accumulate((DWORD)timeInMSec*1000);
181 if (settings.compaction)
183 assert(settings.condemned_generation < max_generation);
184 cntFGCGen[settings.condemned_generation]++;
187 #endif // BACKGROUND_GC
189 ngc.Accumulate((DWORD)timeInMSec*1000);
191 if (settings.compaction)
193 cntNGCGen[settings.condemned_generation]++;
196 if (is_induced (settings.reason))
197 cntReasons[(int)reason_induced]++;
198 else if (settings.stress_induced)
199 cntReasons[(int)reason_gcstress]++;
201 cntReasons[(int)settings.reason]++;
204 if (settings.concurrent || !settings.background_p)
206 #endif // BACKGROUND_GC
210 #endif // BACKGROUND_GC
213 void GCStatistics::Initialize()
215 LIMITED_METHOD_CONTRACT;
216 // for efficiency sake we're taking a dependency on the layout of a C++ object
217 // with a vtable. protect against violations of our premise:
218 static_assert(offsetof(GCStatistics, cntDisplay) == sizeof(void*),
219 "The first field of GCStatistics follows the pointer sized vtable");
221 int podOffs = offsetof(GCStatistics, cntDisplay); // offset of the first POD field
222 memset((BYTE*)(&g_GCStatistics)+podOffs, 0, sizeof(g_GCStatistics)-podOffs);
223 memset((BYTE*)(&g_LastGCStatistics)+podOffs, 0, sizeof(g_LastGCStatistics)-podOffs);
226 void GCStatistics::DisplayAndUpdate()
228 LIMITED_METHOD_CONTRACT;
230 if (logFileName == NULL || logFile == NULL)
235 fprintf(logFile, "\nGCMix **** Initialize *****\n\n");
237 fprintf(logFile, "GCMix **** Summary ***** %d\n", cntDisplay);
239 // NGC summary (total, timing info)
240 ngc.DisplayAndUpdate(logFile, "NGC ", &g_LastGCStatistics.ngc, cntNGC, g_LastGCStatistics.cntNGC, msec);
242 // FGC summary (total, timing info)
243 fgc.DisplayAndUpdate(logFile, "FGC ", &g_LastGCStatistics.fgc, cntFGC, g_LastGCStatistics.cntFGC, msec);
246 bgc.DisplayAndUpdate(logFile, "BGC ", &g_LastGCStatistics.bgc, cntBGC, g_LastGCStatistics.cntBGC, msec);
248 // NGC/FGC break out by generation & compacting vs. sweeping
249 fprintf(logFile, "NGC ");
250 for (int i = max_generation; i >= 0; --i)
251 fprintf(logFile, "gen%d %d (%d). ", i, cntNGCGen[i]-g_LastGCStatistics.cntNGCGen[i], cntNGCGen[i]);
252 fprintf(logFile, "\n");
254 fprintf(logFile, "FGC ");
255 for (int i = max_generation-1; i >= 0; --i)
256 fprintf(logFile, "gen%d %d (%d). ", i, cntFGCGen[i]-g_LastGCStatistics.cntFGCGen[i], cntFGCGen[i]);
257 fprintf(logFile, "\n");
259 // Compacting vs. Sweeping break out
260 int _cntSweep = cntNGC-cntCompactNGC;
261 int _cntLastSweep = g_LastGCStatistics.cntNGC-g_LastGCStatistics.cntCompactNGC;
262 fprintf(logFile, "NGC Sweeping %d (%d) Compacting %d (%d)\n",
263 _cntSweep - _cntLastSweep, _cntSweep,
264 cntCompactNGC - g_LastGCStatistics.cntCompactNGC, cntCompactNGC);
266 _cntSweep = cntFGC-cntCompactFGC;
267 _cntLastSweep = g_LastGCStatistics.cntFGC-g_LastGCStatistics.cntCompactFGC;
268 fprintf(logFile, "FGC Sweeping %d (%d) Compacting %d (%d)\n",
269 _cntSweep - _cntLastSweep, _cntSweep,
270 cntCompactFGC - g_LastGCStatistics.cntCompactFGC, cntCompactFGC);
274 for (int reason=(int)reason_alloc_soh; reason <= (int)reason_gcstress; ++reason)
276 if (cntReasons[reason] != 0)
277 fprintf(logFile, "%s %d (%d). ", str_gc_reasons[reason],
278 cntReasons[reason]-g_LastGCStatistics.cntReasons[reason], cntReasons[reason]);
281 fprintf(logFile, "\n\n");
283 // flush the log file...
287 memcpy(&g_LastGCStatistics, this, sizeof(g_LastGCStatistics));
297 DWORD bgc_alloc_spin_count = 140;
298 DWORD bgc_alloc_spin_count_loh = 16;
299 DWORD bgc_alloc_spin = 2;
303 void c_write (DWORD& place, DWORD value)
305 FastInterlockExchange (&(LONG&)place, value);
309 // TODO - can't make it work with the syntax for Volatile<T>
311 void c_write_volatile (BOOL* place, DWORD value)
313 FastInterlockExchange ((LONG*)place, value);
317 #ifndef DACCESS_COMPILE
318 // If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC.
319 const size_t bgc_min_per_heap = 4*1024*1024;
321 int gc_heap::gchist_index = 0;
322 gc_mechanisms_store gc_heap::gchist[max_history_count];
324 #ifndef MULTIPLE_HEAPS
325 size_t gc_heap::total_promoted_bytes = 0;
326 VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process;
327 int gc_heap::gchist_index_per_heap = 0;
328 gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count];
329 #endif //MULTIPLE_HEAPS
331 void gc_heap::add_to_history_per_heap()
334 gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap];
335 current_hist->gc_index = settings.gc_index;
336 current_hist->current_bgc_state = current_bgc_state;
337 size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0));
338 current_hist->gc_time_ms = (DWORD)elapsed;
339 current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes);
340 current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1));
341 current_hist->gen0_start = generation_allocation_start (generation_of (0));
342 current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment);
344 current_hist->bgc_lowest = background_saved_lowest_address;
345 current_hist->bgc_highest = background_saved_highest_address;
346 #endif //BACKGROUND_GC
347 current_hist->fgc_lowest = lowest_address;
348 current_hist->fgc_highest = highest_address;
349 current_hist->g_lowest = g_lowest_address;
350 current_hist->g_highest = g_highest_address;
352 gchist_index_per_heap++;
353 if (gchist_index_per_heap == max_history_count)
355 gchist_index_per_heap = 0;
360 void gc_heap::add_to_history()
363 gc_mechanisms_store* current_settings = &gchist[gchist_index];
364 current_settings->store (&settings);
367 if (gchist_index == max_history_count)
374 #endif //DACCESS_COMPILE
375 #endif //BACKGROUND_GC
379 BOOL gc_log_on = TRUE;
380 HANDLE gc_log = INVALID_HANDLE_VALUE;
381 size_t gc_log_file_size = 0;
383 size_t gc_buffer_index = 0;
384 size_t max_gc_buffers = 0;
386 static MUTEX_COOKIE gc_log_lock = 0;
388 // we keep this much in a buffer and only flush when the buffer is full
389 #define gc_log_buffer_size (1024*1024)
390 BYTE* gc_log_buffer = 0;
391 size_t gc_log_buffer_offset = 0;
393 void log_va_msg(const char *fmt, va_list args)
395 DWORD status = ClrWaitForMutex(gc_log_lock, INFINITE, FALSE);
396 assert (WAIT_OBJECT_0 == status);
398 const int BUFFERSIZE = 512;
399 static char rgchBuffer[BUFFERSIZE];
400 char * pBuffer = &rgchBuffer[0];
404 int buffer_start = 2;
405 int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", GetCurrentThreadId());
406 buffer_start += pid_len;
407 memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
408 int msg_len = _vsnprintf(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, fmt, args );
411 msg_len = BUFFERSIZE - buffer_start;
414 msg_len += buffer_start;
416 if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12))
419 memset (index_str, '-', 8);
420 sprintf_s (index_str, _countof(index_str), "%d", (int)gc_buffer_index);
421 gc_log_buffer[gc_log_buffer_offset] = '\r';
422 gc_log_buffer[gc_log_buffer_offset + 1] = '\n';
423 memcpy (gc_log_buffer + (gc_log_buffer_offset + 2), index_str, 8);
426 if (gc_buffer_index > max_gc_buffers)
428 SetFilePointer (gc_log, 0, NULL, FILE_BEGIN);
431 DWORD written_to_log = 0;
432 WriteFile (gc_log, gc_log_buffer, (DWORD)gc_log_buffer_size, &written_to_log, NULL);
433 FlushFileBuffers (gc_log);
434 memset (gc_log_buffer, '*', gc_log_buffer_size);
435 gc_log_buffer_offset = 0;
438 memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len);
439 gc_log_buffer_offset += msg_len;
441 status = ClrReleaseMutex(gc_log_lock);
445 void GCLog (const char *fmt, ... )
447 if (gc_log_on && (gc_log != INVALID_HANDLE_VALUE))
450 va_start( args, fmt );
451 log_va_msg (fmt, args);
457 #ifdef SYNCHRONIZATION_STATS
459 // Number of GCs have we done since we last logged.
460 static unsigned int gc_count_during_log;
461 // In ms. This is how often we print out stats.
462 static const unsigned int log_interval = 5000;
463 // Time (in ms) when we start a new log interval.
464 static unsigned int log_start_tick;
465 static unsigned int gc_lock_contended;
466 // Cycles accumulated in SuspendEE during log_interval.
467 static ULONGLONG suspend_ee_during_log;
468 // Cycles accumulated in RestartEE during log_interval.
469 static ULONGLONG restart_ee_during_log;
470 static ULONGLONG gc_during_log;
472 #endif //SYNCHRONIZATION_STATS
475 init_sync_log_stats()
477 #ifdef SYNCHRONIZATION_STATS
478 if (gc_count_during_log == 0)
480 gc_heap::init_sync_stats();
481 suspend_ee_during_log = 0;
482 restart_ee_during_log = 0;
484 gc_lock_contended = 0;
486 log_start_tick = GetTickCount();
488 gc_count_during_log++;
489 #endif //SYNCHRONIZATION_STATS
493 process_sync_log_stats()
495 #ifdef SYNCHRONIZATION_STATS
497 unsigned int log_elapsed = GetTickCount() - log_start_tick;
499 if (log_elapsed > log_interval)
501 // Print out the cycles we spent on average in each suspend and restart.
502 printf("\n_________________________________________________________________________________\n"
503 "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
504 "SuspendEE: %8u; RestartEE: %8u\n",
508 (unsigned int)(gc_during_log / gc_count_during_log),
509 (unsigned int)(suspend_ee_during_log / gc_count_during_log),
510 (unsigned int)(restart_ee_during_log / gc_count_during_log));
511 gc_heap::print_sync_stats(gc_count_during_log);
513 gc_count_during_log = 0;
515 #endif //SYNCHRONIZATION_STATS
518 #ifdef MULTIPLE_HEAPS
522 gc_join_init_cpu_mapping = 0,
524 gc_join_generation_determined = 2,
525 gc_join_begin_mark_phase = 3,
526 gc_join_scan_dependent_handles = 4,
527 gc_join_rescan_dependent_handles = 5,
528 gc_join_scan_sizedref_done = 6,
529 gc_join_null_dead_short_weak = 7,
530 gc_join_scan_finalization = 8,
531 gc_join_null_dead_long_weak = 9,
532 gc_join_null_dead_syncblk = 10,
533 gc_join_decide_on_compaction = 11,
534 gc_join_rearrange_segs_compaction = 12,
535 gc_join_adjust_handle_age_compact = 13,
536 gc_join_adjust_handle_age_sweep = 14,
537 gc_join_begin_relocate_phase = 15,
538 gc_join_relocate_phase_done = 16,
539 gc_join_verify_objects_done = 17,
540 gc_join_start_bgc = 18,
541 gc_join_restart_ee = 19,
542 gc_join_concurrent_overflow = 20,
543 gc_join_suspend_ee = 21,
544 gc_join_bgc_after_ephemeral = 22,
545 gc_join_allow_fgc = 23,
546 gc_join_bgc_sweep = 24,
547 gc_join_suspend_ee_verify = 25,
548 gc_join_restart_ee_verify = 26,
549 gc_join_set_state_free = 27,
550 gc_r_join_update_card_bundle = 28,
551 gc_join_after_absorb = 29,
552 gc_join_verify_copy_table = 30,
553 gc_join_after_reset = 31,
554 gc_join_after_ephemeral_sweep = 32,
555 gc_join_after_profiler_heap_walk = 33,
556 gc_join_minimal_gc = 34
561 join_flavor_server_gc = 0,
565 #define first_thread_arrived 2
566 struct join_structure
568 CLREvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
569 VOLATILE(LONG) join_lock;
570 VOLATILE(LONG) r_join_lock;
571 VOLATILE(LONG) join_restart;
572 VOLATILE(LONG) r_join_restart; // only used by get_here_first and friends.
574 VOLATILE(BOOL) joined_p;
575 // avoid lock_color and join_lock being on same cache line
576 // make sure to modify this if adding/removing variables to layout
577 char cache_line_separator[HS_CACHE_LINE_SIZE - (3*sizeof(int) + sizeof(int) + sizeof(BOOL))];
578 VOLATILE(int) lock_color;
579 VOLATILE(BOOL) wait_done;
582 typedef enum _join_type {
583 type_last_join, type_join, type_restart
586 typedef enum _join_time {
599 join_structure join_struct;
602 gc_join_flavor flavor;
605 unsigned int start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
606 // remember join id and last thread to arrive so restart can use these
608 // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
610 // counters for joins, in 1000's of clock cycles
611 unsigned int elapsed_total[gc_join_max], seq_loss_total[gc_join_max], par_loss_total[gc_join_max], in_join_total[gc_join_max];
615 BOOL init (int n_th, gc_join_flavor f)
617 dprintf (JOIN_LOG, ("Initializing join structure"));
618 join_struct.n_threads = n_th;
619 join_struct.lock_color = 0;
620 for (int i = 0; i < 3; i++)
622 if (!join_struct.joined_event[i].IsValid())
624 join_struct.joined_p = FALSE;
625 dprintf (JOIN_LOG, ("Creating join event %d", i));
626 // TODO - changing this to a non OS event
627 // because this is also used by BGC threads which are
628 // managed threads and WaitEx does not allow you to wait
629 // for an OS event on a managed thread.
630 // But we are not sure if this plays well in the hosting
632 //join_struct.joined_event[i].CreateOSManualEvent(FALSE);
633 join_struct.joined_event[i].CreateManualEvent(FALSE);
634 if (!join_struct.joined_event[i].IsValid())
638 join_struct.join_lock = join_struct.n_threads;
639 join_struct.join_restart = join_struct.n_threads - 1;
640 join_struct.r_join_lock = join_struct.n_threads;
641 join_struct.r_join_restart = join_struct.n_threads - 1;
642 join_struct.wait_done = FALSE;
646 start_tick = GetTickCount();
654 dprintf (JOIN_LOG, ("Destroying join structure"));
655 for (int i = 0; i < 3; i++)
657 if (join_struct.joined_event[i].IsValid())
658 join_struct.joined_event[i].CloseEvent();
662 inline void fire_event (ULONG heap, join_time time, join_type type)
664 FireEtwGCJoin_V1(heap, time, type, GetClrInstanceId());
667 void join (gc_heap* gch, int join_id)
670 // parallel execution ends here
671 end[gch->heap_number] = GetCycleCount32();
674 assert (!join_struct.joined_p);
675 int color = join_struct.lock_color;
677 if (FastInterlockDecrement(&join_struct.join_lock) != 0)
679 dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d",
680 flavor, join_id, (LONG)(join_struct.join_lock)));
682 fire_event (gch->heap_number, time_start, type_join);
684 //busy wait around the color
685 if (color == join_struct.lock_color)
688 int spin_count = 4096 * g_SystemInfo.dwNumberOfProcessors;
689 for (int j = 0; j < spin_count; j++)
691 if (color != join_struct.lock_color)
695 YieldProcessor(); // indicate to the processor that we are spinning
698 // we've spun, and if color still hasn't changed, fall into hard wait
699 if (color == join_struct.lock_color)
701 dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d",
702 flavor, join_id, color, (LONG)(join_struct.join_lock)));
704 //Thread* current_thread = GetThread();
705 //BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
706 DWORD dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE);
707 //gc_heap::disable_preemptive (current_thread, cooperative_mode);
709 if (dwJoinWait != WAIT_OBJECT_0)
711 STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
716 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
717 if (color == join_struct.lock_color)
722 dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d",
723 flavor, join_id, (LONG)(join_struct.join_lock)));
726 fire_event (gch->heap_number, time_end, type_join);
728 // last thread out should reset event
729 if (FastInterlockDecrement(&join_struct.join_restart) == 0)
731 // the joined event must be set at this point, because the restarting must have done this
732 join_struct.join_restart = join_struct.n_threads - 1;
733 // printf("Reset joined_event %d\n", color);
737 // parallel execution starts here
738 start[gch->heap_number] = GetCycleCount32();
739 FastInterlockExchangeAdd((int*)&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
744 fire_event (gch->heap_number, time_start, type_last_join);
746 join_struct.joined_p = TRUE;
747 dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id", flavor, join_id));
748 join_struct.joined_event[!color].Reset();
750 // this one is alone so it can proceed
752 // remember the join id, the last thread arriving, the start of the sequential phase,
753 // and keep track of the cycles spent waiting in the join
754 thd = gch->heap_number;
755 start_seq = GetCycleCount32();
756 FastInterlockExchangeAdd((int*)&in_join_total[join_id], (start_seq - end[gch->heap_number])/1000);
761 // Reverse join - first thread gets here does the work; other threads will only proceed
762 // afte the work is done.
763 // Note that you cannot call this twice in a row on the same thread. Plus there's no
764 // need to call it twice in row - you should just merge the work.
765 BOOL r_join (gc_heap* gch, int join_id)
768 // parallel execution ends here
769 end[gch->heap_number] = GetCycleCount32();
772 if (join_struct.n_threads == 1)
777 if (FastInterlockDecrement(&join_struct.r_join_lock) != (join_struct.n_threads - 1))
779 if (!join_struct.wait_done)
781 dprintf (JOIN_LOG, ("r_join() Waiting..."));
783 fire_event (gch->heap_number, time_start, type_join);
785 //busy wait around the color
786 if (!join_struct.wait_done)
789 int spin_count = 2 * 4096 * g_SystemInfo.dwNumberOfProcessors;
790 for (int j = 0; j < spin_count; j++)
792 if (join_struct.wait_done)
796 YieldProcessor(); // indicate to the processor that we are spinning
799 // we've spun, and if color still hasn't changed, fall into hard wait
800 if (!join_struct.wait_done)
802 dprintf (JOIN_LOG, ("Join() hard wait on reset event %d", first_thread_arrived));
803 DWORD dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE);
804 if (dwJoinWait != WAIT_OBJECT_0)
806 STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
811 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
812 if (!join_struct.wait_done)
817 dprintf (JOIN_LOG, ("r_join() done"));
820 fire_event (gch->heap_number, time_end, type_join);
823 // parallel execution starts here
824 start[gch->heap_number] = GetCycleCount32();
825 FastInterlockExchangeAdd((volatile int *)&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
840 unsigned int elapsed_seq = GetCycleCount32() - start_seq;
841 unsigned int max = 0, sum = 0;
842 for (int i = 0; i < join_struct.n_threads; i++)
844 unsigned int elapsed = end[i] - start[i];
849 unsigned int seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
850 unsigned int par_loss = join_struct.n_threads*max - sum;
851 double efficiency = 0.0;
853 efficiency = sum*100.0/(join_struct.n_threads*max);
855 // enable this printf to get statistics on each individual join as it occurs
856 // printf("join #%3d seq_loss = %5d par_loss = %5d efficiency = %3.0f%%\n", join_id, seq_loss/1000, par_loss/1000, efficiency);
858 elapsed_total[join_id] += sum/1000;
859 seq_loss_total[join_id] += seq_loss/1000;
860 par_loss_total[join_id] += par_loss/1000;
862 // every 10 seconds, print a summary of the time spent in each type of join, in 1000's of clock cycles
863 if (GetTickCount() - start_tick > 10*1000)
865 printf("**** summary *****\n");
866 for (int i = 0; i < 16; i++)
868 printf("join #%3d seq_loss = %8u par_loss = %8u in_join_total = %8u\n", i, seq_loss_total[i], par_loss_total[i], in_join_total[i]);
869 elapsed_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
871 start_tick = GetTickCount();
875 fire_event (100, time_start, type_restart);
876 assert (join_struct.joined_p);
877 join_struct.joined_p = FALSE;
878 join_struct.join_lock = join_struct.n_threads;
879 dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (LONG)(join_struct.join_lock)));
880 // printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
881 int color = join_struct.lock_color;
882 join_struct.lock_color = !color;
883 join_struct.joined_event[color].Set();
885 // printf("Set joined_event %d\n", !join_struct.lock_color);
887 fire_event (100, time_end, type_restart);
890 start[thd] = GetCycleCount32();
896 dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d", flavor, id, (LONG)(join_struct.join_lock)));
897 return join_struct.joined_p;
902 if (join_struct.n_threads != 1)
904 join_struct.wait_done = TRUE;
905 join_struct.joined_event[first_thread_arrived].Set();
911 if (join_struct.n_threads != 1)
913 join_struct.r_join_lock = join_struct.n_threads;
914 join_struct.r_join_restart = join_struct.n_threads - 1;
915 join_struct.wait_done = FALSE;
916 join_struct.joined_event[first_thread_arrived].Reset();
925 #endif //BACKGROUND_GC
927 #endif //MULTIPLE_HEAPS
929 #define spin_and_switch(count_to_spin, expr) \
931 for (int j = 0; j < count_to_spin; j++) \
941 __SwitchToThread(0, CALLER_LIMITS_SPINNING); \
945 #ifndef DACCESS_COMPILE
948 #define max_pending_allocs 64
952 // TODO - verify that this is the right syntax for Volatile.
953 VOLATILE(BYTE*) rwp_object;
954 VOLATILE(LONG) needs_checking;
958 BYTE cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (LONG)];
960 // TODO - perhaps each object should be on its own cache line...
961 VOLATILE(BYTE*) alloc_objects[max_pending_allocs];
963 int find_free_index ()
965 for (int i = 0; i < max_pending_allocs; i++)
967 if (alloc_objects [i] == (BYTE*)0)
979 spin_count = 32 * (g_SystemInfo.dwNumberOfProcessors - 1);
982 for (int i = 0; i < max_pending_allocs; i++)
984 alloc_objects [i] = (BYTE*)0;
990 for (int i = 0; i < max_pending_allocs; i++)
992 if (alloc_objects [i] != (BYTE*)0)
999 void bgc_mark_set (BYTE* obj)
1001 dprintf (3, ("cm: probing %Ix", obj));
1003 if (FastInterlockExchange (&needs_checking, 1) == 0)
1005 // If we spend too much time spending all the allocs,
1006 // consider adding a high water mark and scan up
1007 // to that; we'll need to interlock in done when
1008 // we update the high watermark.
1009 for (int i = 0; i < max_pending_allocs; i++)
1011 if (obj == alloc_objects[i])
1014 dprintf (3, ("cm: will spin", obj));
1015 spin_and_switch (spin_count, (obj != alloc_objects[i]));
1022 dprintf (3, ("cm: set %Ix", obj));
1027 spin_and_switch (spin_count, (needs_checking == 0));
1032 int loh_alloc_set (BYTE* obj)
1034 if (!gc_heap::cm_in_progress)
1040 dprintf (3, ("loh alloc: probing %Ix", obj));
1042 if (FastInterlockExchange (&needs_checking, 1) == 0)
1044 if (obj == rwp_object)
1047 spin_and_switch (spin_count, (obj != rwp_object));
1052 int cookie = find_free_index();
1056 alloc_objects[cookie] = obj;
1063 dprintf (3, ("loh alloc: set %Ix at %d", obj, cookie));
1069 dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index", obj));
1070 spin_and_switch (spin_count, (find_free_index () != -1));
1077 dprintf (3, ("loh alloc: will spin on checking", obj));
1078 spin_and_switch (spin_count, (needs_checking == 0));
1083 void bgc_mark_done ()
1085 dprintf (3, ("cm: release lock on %Ix", (BYTE *)rwp_object));
1089 void loh_alloc_done_with_index (int index)
1091 dprintf (3, ("loh alloc: release lock on %Ix based on %d", (BYTE *)alloc_objects[index], index));
1092 assert ((index >= 0) && (index < max_pending_allocs));
1093 alloc_objects[index] = (BYTE*)0;
1096 void loh_alloc_done (BYTE* obj)
1098 #ifdef BACKGROUND_GC
1099 if (!gc_heap::cm_in_progress)
1104 for (int i = 0; i < max_pending_allocs; i++)
1106 if (alloc_objects [i] == obj)
1108 dprintf (3, ("loh alloc: release lock on %Ix at %d", (BYTE *)alloc_objects[i], i));
1109 alloc_objects[i] = (BYTE*)0;
1113 #endif //BACKGROUND_GC
1117 // Note that this class was written assuming just synchronization between
1118 // one background GC thread and multiple user threads that might request
1119 // an FGC - it does not take into account what kind of locks the multiple
1120 // user threads might be holding at the time (eg, there could only be one
1121 // user thread requesting an FGC because it needs to take gc_lock first)
1122 // so you'll see checks that may not be necessary if you take those conditions
1123 // into consideration.
1125 // With the introduction of Server Background GC we no longer use this
1126 // class to do synchronization between FGCs and BGC.
1127 class recursive_gc_sync
1129 static VOLATILE(LONG) foreground_request_count;//initial state 0
1130 static VOLATILE(BOOL) gc_background_running; //initial state FALSE
1131 static VOLATILE(LONG) foreground_count; // initial state 0;
1132 static VOLATILE(DWORD) foreground_gate; // initial state FALSE;
1133 static CLREvent foreground_complete;//Auto Reset
1134 static CLREvent foreground_allowed;//Auto Reset
1136 static void begin_background();
1137 static void end_background();
1138 static void begin_foreground();
1139 static void end_foreground();
1140 BOOL allow_foreground ();
1142 static void shutdown();
1143 static BOOL background_running_p() {return gc_background_running;}
1146 VOLATILE(LONG) recursive_gc_sync::foreground_request_count = 0;//initial state 0
1147 VOLATILE(LONG) recursive_gc_sync::foreground_count = 0; // initial state 0;
1148 VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
1149 VOLATILE(DWORD) recursive_gc_sync::foreground_gate = 0;
1150 CLREvent recursive_gc_sync::foreground_complete;//Auto Reset
1151 CLREvent recursive_gc_sync::foreground_allowed;//Manual Reset
1153 BOOL recursive_gc_sync::init ()
1155 foreground_request_count = 0;
1156 foreground_count = 0;
1157 gc_background_running = FALSE;
1158 foreground_gate = 0;
1160 foreground_complete.CreateOSAutoEvent(FALSE);
1161 if (!foreground_complete.IsValid())
1165 foreground_allowed.CreateManualEvent(FALSE);
1166 if (!foreground_allowed.IsValid())
1178 void recursive_gc_sync::shutdown()
1180 if (foreground_complete.IsValid())
1181 foreground_complete.CloseEvent();
1182 if (foreground_allowed.IsValid())
1183 foreground_allowed.CloseEvent();
1186 void recursive_gc_sync::begin_background()
1188 dprintf (2, ("begin background"));
1189 foreground_request_count = 1;
1190 foreground_count = 1;
1191 foreground_allowed.Reset();
1192 gc_background_running = TRUE;
1194 void recursive_gc_sync::end_background()
1196 dprintf (2, ("end background"));
1197 gc_background_running = FALSE;
1198 foreground_gate = 1;
1199 foreground_allowed.Set();
1202 void recursive_gc_sync::begin_foreground()
1204 dprintf (2, ("begin_foreground"));
1206 BOOL cooperative_mode = FALSE;
1207 Thread* current_thread = 0;
1209 if (gc_background_running)
1211 gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc);
1212 gc_heap::alloc_wait_event_p = TRUE;
1216 FastInterlockIncrement (&foreground_request_count);
1219 dprintf(2, ("Waiting sync gc point"));
1220 assert (foreground_allowed.IsValid());
1221 assert (foreground_complete.IsValid());
1223 current_thread = GetThread();
1224 cooperative_mode = gc_heap::enable_preemptive (current_thread);
1226 foreground_allowed.Wait(INFINITE, FALSE);
1228 dprintf(2, ("Waiting sync gc point is done"));
1230 gc_heap::disable_preemptive (current_thread, cooperative_mode);
1232 if (foreground_gate)
1234 FastInterlockIncrement (&foreground_count);
1235 dprintf (2, ("foreground_count: %d", (LONG)foreground_count));
1236 if (foreground_gate)
1238 gc_heap::settings.concurrent = FALSE;
1249 goto try_again_no_inc;
1254 void recursive_gc_sync::end_foreground()
1256 dprintf (2, ("end_foreground"));
1257 if (gc_background_running)
1259 FastInterlockDecrement (&foreground_request_count);
1260 dprintf (2, ("foreground_count before decrement: %d", (LONG)foreground_count));
1261 if (FastInterlockDecrement (&foreground_count) == 0)
1263 //c_write_volatile ((BOOL*)&foreground_gate, 0);
1264 // TODO - couldn't make the syntax work with Volatile<T>
1265 foreground_gate = 0;
1266 if (foreground_count == 0)
1268 foreground_allowed.Reset ();
1269 dprintf(2, ("setting foreground complete event"));
1270 foreground_complete.Set();
1277 BOOL recursive_gc_sync::allow_foreground()
1279 assert (gc_heap::settings.concurrent);
1280 dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d",
1281 (LONG)foreground_request_count, (LONG)foreground_count));
1283 BOOL did_fgc = FALSE;
1285 //if we have suspended the EE, just return because
1286 //some thread could be waiting on this to proceed.
1287 if (!GCHeap::GcInProgress)
1289 //TODO BACKGROUND_GC This is to stress the concurrency between
1290 //background and foreground
1291 // gc_heap::disallow_new_allocation (0);
1293 //__SwitchToThread(0, CALLER_LIMITS_SPINNING);
1296 if (foreground_request_count != 0)
1298 //foreground wants to run
1299 //save the important settings
1300 //TODO BACKGROUND_GC be more selective about the important settings.
1301 gc_mechanisms saved_settings = gc_heap::settings;
1305 //c_write_volatile ((BOOL*)&foreground_gate, 1);
1306 // TODO - couldn't make the syntax work with Volatile<T>
1307 foreground_gate = 1;
1308 foreground_allowed.Set ();
1309 foreground_complete.Wait (INFINITE, FALSE);
1310 }while (/*foreground_request_count ||*/ foreground_gate);
1312 assert (!foreground_gate);
1314 //restore the important settings
1315 gc_heap::settings = saved_settings;
1316 GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
1317 //the background GC shouldn't be using gc_high and gc_low
1318 //gc_low = lowest_address;
1319 //gc_high = highest_address;
1322 //TODO BACKGROUND_GC This is to stress the concurrency between
1323 //background and foreground
1324 // gc_heap::allow_new_allocation (0);
1328 dprintf (100, ("leave allow_foreground"));
1329 assert (gc_heap::settings.concurrent);
1333 #endif //BACKGROUND_GC
1334 #endif //DACCESS_COMPILE
1337 // disable new LKG8 warning
1338 #pragma warning(disable:4293)
1341 #if defined(COUNT_CYCLES) || defined(JOIN_STATS) || defined(SYNCHRONIZATION_STATS)
1343 #pragma warning(disable:4035)
1347 unsigned GetCycleCount32() // enough for about 40 seconds
1355 #pragma warning(default:4035)
1357 #endif //COUNT_CYCLES || JOIN_STATS || SYNCHRONIZATION_STATS
1363 int mark_time, plan_time, sweep_time, reloc_time, compact_time;
1366 #ifndef MULTIPLE_HEAPS
1368 #define ephemeral_low g_ephemeral_low
1369 #define ephemeral_high g_ephemeral_high
1371 #endif // MULTIPLE_HEAPS
1375 int print_level = DEFAULT_GC_PRN_LVL; //level of detail of the debug trace
1376 BOOL trace_gc = FALSE;
1377 int gc_trace_fac = 0;
1378 hlet* hlet::bindings = 0;
1382 void reset_memory (BYTE* o, size_t sizeo);
1386 #define MEM_WRITE_WATCH 0x200000
1388 static DWORD mem_reserve = MEM_RESERVE;
1390 #ifndef FEATURE_REDHAWK
1391 BOOL write_watch_capability = FALSE;
1394 #ifndef DACCESS_COMPILE
1396 //check if the write watch APIs are supported.
1398 void write_watch_api_supported()
1400 #ifndef FEATURE_REDHAWK
1401 // check if the OS will accept the MEM_WRITE_WATCH flag at runtime.
1402 // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
1403 // Otherwise, all currently supported OSes do support write-watch.
1404 void* mem = VirtualAlloc (0, g_SystemInfo.dwAllocationGranularity, MEM_WRITE_WATCH|MEM_RESERVE,
1408 dprintf (2,("WriteWatch not supported"));
1412 write_watch_capability = TRUE;
1413 dprintf (2, ("WriteWatch supported"));
1414 VirtualFree (mem, 0, MEM_RELEASE);
1416 #endif //FEATURE_REDHAWK
1419 #endif //!DACCESS_COMPILE
1421 inline BOOL can_use_write_watch()
1423 #ifdef FEATURE_REDHAWK
1424 return PalHasCapability(WriteWatchCapability);
1425 #else //FEATURE_REDHAWK
1426 return write_watch_capability;
1427 #endif //FEATURE_REDHAWK
1431 #define mem_reserve (MEM_RESERVE)
1432 #endif //WRITE_WATCH
1434 //check if the low memory notification is supported
1436 #ifndef DACCESS_COMPILE
1438 void WaitLongerNoInstru (int i)
1440 // every 8th attempt:
1441 Thread *pCurThread = GetThread();
1442 BOOL bToggleGC = FALSE;
1445 bToggleGC = pCurThread->PreemptiveGCDisabled();
1447 pCurThread->EnablePreemptiveGC();
1450 // if we're waiting for gc to finish, we should block immediately
1451 if (!g_TrapReturningThreads)
1453 if (g_SystemInfo.dwNumberOfProcessors > 1)
1455 YieldProcessor(); // indicate to the processor that we are spining
1457 __SwitchToThread (0, CALLER_LIMITS_SPINNING);
1459 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1462 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1465 // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1466 // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1467 // It is important that the thread is going to wait for GC. Otherwise the thread
1468 // is in a tight loop. If the thread has high priority, the perf is going to be very BAD.
1471 if (bToggleGC || g_TrapReturningThreads)
1473 pCurThread->DisablePreemptiveGC();
1476 pCurThread->EnablePreemptiveGC();
1480 else if (g_TrapReturningThreads)
1482 GCHeap::GetGCHeap()->WaitUntilGCComplete();
1487 static void safe_switch_to_thread()
1489 Thread* current_thread = GetThread();
1490 BOOL cooperative_mode = gc_heap::enable_preemptive(current_thread);
1492 __SwitchToThread(0, CALLER_LIMITS_SPINNING);
1494 gc_heap::disable_preemptive(current_thread, cooperative_mode);
1498 // We need the following methods to have volatile arguments, so that they can accept
1499 // raw pointers in addition to the results of the & operator on Volatile<T>.
1502 static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) LONG* lock)
1506 if (FastInterlockExchange (lock, 0) >= 0)
1509 while (VolatileLoad(lock) >= 0)
1511 if ((++i & 7) && !GCHeap::IsGCInProgress())
1513 if (g_SystemInfo.dwNumberOfProcessors > 1)
1515 #ifndef MULTIPLE_HEAPS
1516 int spin_count = 1024 * g_SystemInfo.dwNumberOfProcessors;
1517 #else //!MULTIPLE_HEAPS
1518 int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
1519 #endif //!MULTIPLE_HEAPS
1520 for (int j = 0; j < spin_count; j++)
1522 if (VolatileLoad(lock) < 0 || GCHeap::IsGCInProgress())
1524 YieldProcessor(); // indicate to the processor that we are spining
1526 if (VolatileLoad(lock) >= 0 && !GCHeap::IsGCInProgress())
1528 safe_switch_to_thread();
1533 safe_switch_to_thread();
1538 WaitLongerNoInstru(i);
1546 static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) LONG* lock)
1548 return (FastInterlockExchange (&*lock, 0) < 0);
1552 static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) LONG* lock)
1554 VolatileStore<LONG>((LONG*)lock, -1);
1560 static void enter_spin_lock(GCSpinLock *pSpinLock)
1562 enter_spin_lock_noinstru(&pSpinLock->lock);
1563 assert (pSpinLock->holding_thread == (Thread*)-1);
1564 pSpinLock->holding_thread = GetThread();
1568 static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
1570 BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock);
1572 pSpinLock->holding_thread = GetThread();
1577 static void leave_spin_lock(GCSpinLock *pSpinLock)
1579 BOOL gc_thread_p = IsGCSpecialThread();
1580 // _ASSERTE((pSpinLock->holding_thread == GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
1581 pSpinLock->released_by_gc_p = gc_thread_p;
1582 pSpinLock->holding_thread = (Thread*) -1;
1583 if (pSpinLock->lock != -1)
1584 leave_spin_lock_noinstru(&pSpinLock->lock);
1587 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \
1588 _ASSERTE((pSpinLock)->holding_thread == GetThread());
1590 #define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \
1591 _ASSERTE((pSpinLock)->holding_thread != GetThread());
1595 //In the concurrent version, the Enable/DisablePreemptiveGC is optional because
1596 //the gc thread call WaitLonger.
1597 void WaitLonger (int i
1598 #ifdef SYNCHRONIZATION_STATS
1599 , VOLATILE(GCSpinLock)* spin_lock
1600 #endif //SYNCHRONIZATION_STATS
1603 #ifdef SYNCHRONIZATION_STATS
1604 (spin_lock->num_wait_longer)++;
1605 #endif //SYNCHRONIZATION_STATS
1607 // every 8th attempt:
1608 Thread *pCurThread = GetThread();
1609 BOOL bToggleGC = FALSE;
1612 bToggleGC = pCurThread->PreemptiveGCDisabled();
1615 pCurThread->EnablePreemptiveGC();
1619 assert (!"bToggleGC == TRUE");
1623 // if we're waiting for gc to finish, we should block immediately
1624 if (!gc_heap::gc_started)
1626 #ifdef SYNCHRONIZATION_STATS
1627 (spin_lock->num_switch_thread_w)++;
1628 #endif //SYNCHRONIZATION_STATS
1629 if (g_SystemInfo.dwNumberOfProcessors > 1)
1631 YieldProcessor(); // indicate to the processor that we are spining
1633 __SwitchToThread (0, CALLER_LIMITS_SPINNING);
1635 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1638 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1641 // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1642 // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1643 // It is important that the thread is going to wait for GC. Otherwise the thread
1644 // is in a tight loop. If the thread has high priority, the perf is going to be very BAD.
1647 if (bToggleGC || gc_heap::gc_started)
1649 if (gc_heap::gc_started)
1651 gc_heap::wait_for_gc_done();
1654 #ifdef SYNCHRONIZATION_STATS
1655 (spin_lock->num_disable_preemptive_w)++;
1656 #endif //SYNCHRONIZATION_STATS
1657 pCurThread->DisablePreemptiveGC();
1663 static void enter_spin_lock (GCSpinLock* spin_lock)
1667 if (FastInterlockExchange (&spin_lock->lock, 0) >= 0)
1670 while (spin_lock->lock >= 0)
1672 if ((++i & 7) && !gc_heap::gc_started)
1674 if (g_SystemInfo.dwNumberOfProcessors > 1)
1676 #ifndef MULTIPLE_HEAPS
1677 int spin_count = 1024 * g_SystemInfo.dwNumberOfProcessors;
1678 #else //!MULTIPLE_HEAPS
1679 int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
1680 #endif //!MULTIPLE_HEAPS
1681 for (int j = 0; j < spin_count; j++)
1683 if (spin_lock->lock < 0 || gc_heap::gc_started)
1685 YieldProcessor(); // indicate to the processor that we are spining
1687 if (spin_lock->lock >= 0 && !gc_heap::gc_started)
1689 #ifdef SYNCHRONIZATION_STATS
1690 (spin_lock->num_switch_thread)++;
1691 #endif //SYNCHRONIZATION_STATS
1692 Thread* current_thread = GetThread();
1693 BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
1695 __SwitchToThread(0, CALLER_LIMITS_SPINNING);
1697 gc_heap::disable_preemptive (current_thread, cooperative_mode);
1701 __SwitchToThread(0, CALLER_LIMITS_SPINNING);
1706 #ifdef SYNCHRONIZATION_STATS
1708 #endif //SYNCHRONIZATION_STATS
1716 inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
1718 return (FastInterlockExchange (&spin_lock->lock, 0) < 0);
1722 static void leave_spin_lock (GCSpinLock * spin_lock)
1724 spin_lock->lock = -1;
1727 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock)
1731 #endif // !DACCESS_COMPILE
1733 BOOL gc_heap::enable_preemptive (Thread* current_thread)
1735 BOOL cooperative_mode = FALSE;
1738 cooperative_mode = current_thread->PreemptiveGCDisabled();
1739 if (cooperative_mode)
1741 current_thread->EnablePreemptiveGC();
1745 return cooperative_mode;
1748 void gc_heap::disable_preemptive (Thread* current_thread, BOOL restore_cooperative)
1752 if (restore_cooperative)
1754 current_thread->DisablePreemptiveGC();
1759 typedef void ** PTR_PTR;
1760 //This function clears a piece of memory
1761 // size has to be Dword aligned
1764 void memclr ( BYTE* mem, size_t size)
1766 dprintf (3, ("MEMCLR: %Ix, %d", mem, size));
1767 assert ((size & (sizeof(PTR_PTR)-1)) == 0);
1768 assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1771 // The compiler will recognize this pattern and replace it with memset call. We can as well just call
1772 // memset directly to make it obvious what's going on.
1773 PTR_PTR m = (PTR_PTR) mem;
1774 for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
1778 memset (mem, 0, size);
1781 void memcopy (BYTE* dmem, BYTE* smem, size_t size)
1783 const size_t sz4ptr = sizeof(PTR_PTR)*4;
1784 const size_t sz2ptr = sizeof(PTR_PTR)*2;
1785 const size_t sz1ptr = sizeof(PTR_PTR)*1;
1787 // size must be a multiple of the pointer size
1788 assert ((size & (sizeof (PTR_PTR)-1)) == 0);
1789 assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1791 // copy in groups of four pointer sized things at a time
1796 ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1797 ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1798 ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2];
1799 ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3];
1803 while ((size -= sz4ptr) >= sz4ptr);
1806 // still two pointer sized things or more left to copy?
1809 ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1810 ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1815 // still one pointer sized thing left to copy?
1818 ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1826 ptrdiff_t round_down (ptrdiff_t add, int pitch)
1828 return ((add / pitch) * pitch);
1831 #if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT)
1832 // FEATURE_STRUCTALIGN allows the compiler to dictate the alignment,
1833 // i.e, if a larger alignment matters or is beneficial, the compiler
1834 // generated info tells us so. RESPECT_LARGE_ALIGNMENT is just the
1835 // converse - it's a heuristic for the GC to use a larger alignment.
1836 #error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT
1839 #if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION)
1840 #error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive
1843 #if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE)
1844 #error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined
1848 BOOL same_large_alignment_p (BYTE* p1, BYTE* p2)
1850 #ifdef RESPECT_LARGE_ALIGNMENT
1851 return ((((size_t)p1 ^ (size_t)p2) & 7) == 0);
1854 #endif //RESPECT_LARGE_ALIGNMENT
1858 size_t switch_alignment_size (BOOL already_padded_p)
1860 if (already_padded_p)
1861 return DATA_ALIGNMENT;
1863 return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT));
1867 #ifdef FEATURE_STRUCTALIGN
1868 void set_node_aligninfo (BYTE *node, int requiredAlignment, ptrdiff_t pad);
1869 void clear_node_aligninfo (BYTE *node);
1870 #else // FEATURE_STRUCTALIGN
1871 void set_node_realigned (BYTE* node);
1872 #endif // FEATURE_STRUCTALIGN
1875 size_t AlignQword (size_t nbytes)
1877 #ifdef FEATURE_STRUCTALIGN
1878 // This function is used to align everything on the large object
1879 // heap to an 8-byte boundary, to reduce the number of unaligned
1880 // accesses to (say) arrays of doubles. With FEATURE_STRUCTALIGN,
1881 // the compiler dictates the optimal alignment instead of having
1882 // a heuristic in the GC.
1883 return Align (nbytes);
1884 #else // FEATURE_STRUCTALIGN
1885 return (nbytes + 7) & ~7;
1886 #endif // FEATURE_STRUCTALIGN
1890 BOOL Aligned (size_t n)
1892 return (n & ALIGNCONST) == 0;
1895 #define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *))
1897 #ifdef FEATURE_STRUCTALIGN
1898 #define MAX_STRUCTALIGN OS_PAGE_SIZE
1899 #else // FEATURE_STRUCTALIGN
1900 #define MAX_STRUCTALIGN 0
1901 #endif // FEATURE_STRUCTALIGN
1903 #ifdef FEATURE_STRUCTALIGN
1905 ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment)
1907 // The resulting alignpad must be either 0 or at least min_obj_size.
1908 // Note that by computing the following difference on unsigned types,
1909 // we can do the range check 0 < alignpad < min_obj_size with a
1910 // single conditional branch.
1911 if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT)
1913 return requiredAlignment;
1919 BYTE* StructAlign (BYTE* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
1921 // required alignment must be a power of two
1922 _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0);
1923 _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0);
1924 _ASSERTE(requiredAlignment >= sizeof(void *));
1925 _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN);
1927 // When this method is invoked for individual objects (i.e., alignmentOffset
1928 // is just the size of the PostHeader), what needs to be aligned when
1929 // we're done is the pointer to the payload of the object (which means
1930 // the actual resulting object pointer is typically not aligned).
1932 BYTE* result = (BYTE*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset;
1933 ptrdiff_t alignpad = result - origPtr;
1935 return result + AdjustmentForMinPadSize (alignpad, requiredAlignment);
1939 ptrdiff_t ComputeStructAlignPad (BYTE* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
1941 return StructAlign (plug, requiredAlignment, alignmentOffset) - plug;
1944 BOOL IsStructAligned (BYTE *ptr, int requiredAlignment)
1946 return StructAlign (ptr, requiredAlignment) == ptr;
1950 ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment)
1952 if (requiredAlignment == DATA_ALIGNMENT)
1954 // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the
1955 // alignment padding object), the worst-case alignment padding is correspondingly larger
1956 // than the required alignment.
1957 return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT;
1961 ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment)
1963 if (requiredAlignment <= get_alignment_constant (TRUE)+1)
1965 // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space
1966 // for padding before the actual object, it also leaves space for filling a gap after the
1967 // actual object. This is needed on the large object heap, as the outer allocation functions
1968 // don't operate on an allocation context (which would have left space for the final gap).
1969 return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT;
1972 BYTE* gc_heap::pad_for_alignment (BYTE* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext)
1974 BYTE* alignedPtr = StructAlign (newAlloc, requiredAlignment);
1975 if (alignedPtr != newAlloc) {
1976 make_unused_array (newAlloc, alignedPtr - newAlloc);
1978 acontext->alloc_ptr = alignedPtr + Align (size);
1982 BYTE* gc_heap::pad_for_alignment_large (BYTE* newAlloc, int requiredAlignment, size_t size)
1984 BYTE* alignedPtr = StructAlign (newAlloc, requiredAlignment);
1985 if (alignedPtr != newAlloc) {
1986 make_unused_array (newAlloc, alignedPtr - newAlloc);
1988 if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) {
1989 make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr);
1993 #else // FEATURE_STRUCTALIGN
1994 #define ComputeMaxStructAlignPad(requiredAlignment) 0
1995 #define ComputeMaxStructAlignPadLarge(requiredAlignment) 0
1996 #endif // FEATURE_STRUCTALIGN
1998 //CLR_SIZE is the max amount of bytes from gen0 that is set to 0 in one chunk
2000 #define CLR_SIZE ((size_t)(8*1024))
2002 #define CLR_SIZE ((size_t)(8*1024))
2005 #define END_SPACE_AFTER_GC (LARGE_OBJECT_SIZE + MAX_STRUCTALIGN)
2007 #ifdef BACKGROUND_GC
2008 #define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE)
2010 #define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE)
2011 #endif //BACKGROUND_GC
2017 #define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024))
2018 #define LHEAP_ALLOC ((size_t)(1024*1024*256))
2022 #define INITIAL_ALLOC ((size_t)(1024*1024*64))
2023 #define LHEAP_ALLOC ((size_t)(1024*1024*32))
2031 #define INITIAL_ALLOC ((size_t)(1024*1024*256))
2032 #define LHEAP_ALLOC ((size_t)(1024*1024*128))
2036 #define INITIAL_ALLOC ((size_t)(1024*1024*16))
2037 #define LHEAP_ALLOC ((size_t)(1024*1024*16))
2043 //amount in bytes of the etw allocation tick
2044 const size_t etw_allocation_tick = 100*1024;
2046 const size_t low_latency_alloc = 256*1024;
2048 const size_t fgn_check_quantum = 2*1024*1024;
2051 const int max_snoop_level = 128;
2056 //threshold of heap size to turn on card bundles.
2057 #define SH_TH_CARD_BUNDLE (40*1024*1024)
2058 #define MH_TH_CARD_BUNDLE (180*1024*1024)
2059 #endif //CARD_BUNDLE
2061 #define page_size OS_PAGE_SIZE
2063 #define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000
2066 size_t align_on_page (size_t add)
2068 return ((add + page_size - 1) & ~(page_size - 1));
2072 BYTE* align_on_page (BYTE* add)
2074 return (BYTE*)align_on_page ((size_t) add);
2078 size_t align_lower_page (size_t add)
2080 return (add & ~(page_size - 1));
2084 BYTE* align_lower_page (BYTE* add)
2086 return (BYTE*)align_lower_page ((size_t)add);
2090 BOOL power_of_two_p (size_t integer)
2092 return !(integer & (integer-1));
2096 BOOL oddp (size_t integer)
2098 return (integer & 1) != 0;
2101 // we only ever use this for WORDs.
2102 size_t logcount (size_t word)
2104 //counts the number of high bits in a 16 bit word.
2105 assert (word < 0x10000);
2107 count = (word & 0x5555) + ( (word >> 1 ) & 0x5555);
2108 count = (count & 0x3333) + ( (count >> 2) & 0x3333);
2109 count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F);
2110 count = (count & 0x00FF) + ( (count >> 8) & 0x00FF);
2115 int log2(unsigned int n)
2118 if (n >= 1<<16) { n >>= 16; pos += 16; }
2119 if (n >= 1<< 8) { n >>= 8; pos += 8; }
2120 if (n >= 1<< 4) { n >>= 4; pos += 4; }
2121 if (n >= 1<< 2) { n >>= 2; pos += 2; }
2122 if (n >= 1<< 1) { pos += 1; }
2126 //extract the low bits [0,low[ of a DWORD
2127 #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1))
2128 //extract the high bits [high, 32] of a DWORD
2129 #define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1))
2135 class CObjectHeader;
2139 class c_synchronize;
2141 #ifdef FEATURE_PREMORTEM_FINALIZATION
2142 #ifndef DACCESS_COMPILE
2144 HRESULT AllocateCFinalize(CFinalize **pCFinalize);
2145 #endif //!DACCESS_COMPILE
2146 #endif // FEATURE_PREMORTEM_FINALIZATION
2148 BYTE* tree_search (BYTE* tree, BYTE* old_address);
2151 #ifdef USE_INTROSORT
2152 #define _sort introsort::sort
2153 #else //USE_INTROSORT
2154 #define _sort qsort1
2155 void qsort1(BYTE** low, BYTE** high, unsigned int depth);
2156 #endif //USE_INTROSORT
2158 void* virtual_alloc (size_t size);
2159 void virtual_free (void* add, size_t size);
2161 /* per heap static initialization */
2163 #ifndef MULTIPLE_HEAPS
2164 SPTR_IMPL_NS(DWORD, WKS, gc_heap, mark_array);
2165 #endif //!MULTIPLE_HEAPS
2169 BYTE** gc_heap::g_mark_list;
2171 #ifdef PARALLEL_MARK_LIST_SORT
2172 BYTE** gc_heap::g_mark_list_copy;
2173 #endif //PARALLEL_MARK_LIST_SORT
2175 size_t gc_heap::mark_list_size;
2178 #ifdef SEG_MAPPING_TABLE
2179 seg_mapping* seg_mapping_table;
2180 #endif //SEG_MAPPING_TABLE
2182 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2183 sorted_table* gc_heap::seg_table;
2184 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
2186 #ifdef MULTIPLE_HEAPS
2187 CLREvent gc_heap::ee_suspend_event;
2188 #endif //MULTIPLE_HEAPS
2190 VOLATILE(BOOL) gc_heap::gc_started;
2192 #ifdef MULTIPLE_HEAPS
2194 CLREvent gc_heap::gc_start_event;
2196 SVAL_IMPL_NS(int, SVR, gc_heap, n_heaps);
2197 SPTR_IMPL_NS(PTR_gc_heap, SVR, gc_heap, g_heaps);
2199 HANDLE* gc_heap::g_gc_threads;
2201 size_t* gc_heap::g_promoted;
2204 BOOL* gc_heap::g_mark_stack_busy;
2208 #ifdef BACKGROUND_GC
2209 size_t* gc_heap::g_bpromoted;
2210 #endif //BACKGROUND_GC
2212 #else //MULTIPLE_HEAPS
2214 size_t gc_heap::g_promoted;
2216 #ifdef BACKGROUND_GC
2217 size_t gc_heap::g_bpromoted;
2218 #endif //BACKGROUND_GC
2220 #endif //MULTIPLE_HEAPS
2222 size_t gc_heap::reserved_memory = 0;
2223 size_t gc_heap::reserved_memory_limit = 0;
2224 BOOL gc_heap::g_low_memory_status;
2226 #ifndef DACCESS_COMPILE
2227 static gc_reason gc_trigger_reason = reason_empty;
2228 #endif //DACCESS_COMPILE
2230 gc_mechanisms gc_heap::settings;
2232 gc_history_global gc_heap::gc_data_global;
2234 size_t gc_heap::gc_last_ephemeral_decommit_time = 0;
2236 size_t gc_heap::gc_gen0_desired_high;
2239 #define MAX_ALLOWED_MEM_LOAD 85
2241 // consider putting this in dynamic data -
2242 // we may want different values for workstation
2244 #define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024)
2246 size_t gc_heap::youngest_gen_desired_th;
2248 size_t gc_heap::mem_one_percent;
2250 ULONGLONG gc_heap::total_physical_mem;
2252 ULONGLONG gc_heap::available_physical_mem;
2255 #ifdef BACKGROUND_GC
2256 CLREvent gc_heap::bgc_start_event;
2258 gc_mechanisms gc_heap::saved_bgc_settings;
2260 CLREvent gc_heap::background_gc_done_event;
2262 CLREvent gc_heap::ee_proceed_event;
2264 BOOL gc_heap::gc_can_use_concurrent = FALSE;
2266 BOOL gc_heap::temp_disable_concurrent_p = FALSE;
2268 DWORD gc_heap::cm_in_progress = FALSE;
2270 BOOL gc_heap::dont_restart_ee_p = FALSE;
2272 BOOL gc_heap::keep_bgc_threads_p = FALSE;
2274 CLREvent gc_heap::bgc_threads_sync_event;
2276 BOOL gc_heap::do_ephemeral_gc_p = FALSE;
2278 BOOL gc_heap::do_concurrent_p = FALSE;
2280 size_t gc_heap::ephemeral_fgc_counts[max_generation];
2282 BOOL gc_heap::alloc_wait_event_p = FALSE;
2284 #if defined (DACCESS_COMPILE) && !defined (MULTIPLE_HEAPS)
2285 SVAL_IMPL_NS_INIT(gc_heap::c_gc_state, WKS, gc_heap, current_c_gc_state, c_gc_state_free);
2287 VOLATILE(gc_heap::c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free;
2288 #endif //DACCESS_COMPILE && !MULTIPLE_HEAPS
2290 #endif //BACKGROUND_GC
2292 #ifndef MULTIPLE_HEAPS
2293 #ifdef SPINLOCK_HISTORY
2294 int gc_heap::spinlock_info_index = 0;
2295 spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8];
2296 #endif //SPINLOCK_HISTORY
2298 size_t gc_heap::fgn_last_alloc = 0;
2300 int gc_heap::generation_skip_ratio = 100;
2302 unsigned __int64 gc_heap::loh_alloc_since_cg = 0;
2304 BOOL gc_heap::elevation_requested = FALSE;
2306 BOOL gc_heap::last_gc_before_oom = FALSE;
2308 #ifdef BACKGROUND_GC
2309 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, background_saved_lowest_address, 0);
2310 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, background_saved_highest_address, 0);
2311 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, next_sweep_obj, 0);
2312 BYTE* gc_heap::current_sweep_pos = 0;
2313 exclusive_sync* gc_heap::bgc_alloc_lock;
2314 #endif //BACKGROUND_GC
2316 SVAL_IMPL_NS(oom_history, WKS, gc_heap, oom_info);
2318 fgm_history gc_heap::fgm_result;
2320 BOOL gc_heap::ro_segments_in_range;
2322 size_t gc_heap::gen0_big_free_spaces = 0;
2324 BYTE* gc_heap::lowest_address;
2326 BYTE* gc_heap::highest_address;
2328 BOOL gc_heap::ephemeral_promotion;
2330 BYTE* gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1];
2331 size_t gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1];
2333 short* gc_heap::brick_table;
2335 DWORD* gc_heap::card_table;
2338 DWORD* gc_heap::card_bundle_table;
2339 #endif //CARD_BUNDLE
2341 BYTE* gc_heap::gc_low;
2343 BYTE* gc_heap::gc_high;
2345 BYTE* gc_heap::demotion_low;
2347 BYTE* gc_heap::demotion_high;
2349 BOOL gc_heap::demote_gen1_p = TRUE;
2351 BYTE* gc_heap::last_gen1_pin_end;
2353 gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;
2355 size_t gc_heap::etw_allocation_running_amount[2];
2357 int gc_heap::gc_policy = 0;
2359 size_t gc_heap::allocation_running_time;
2361 size_t gc_heap::allocation_running_amount;
2363 SPTR_IMPL_NS_INIT(heap_segment, WKS, gc_heap, ephemeral_heap_segment, 0);
2365 BOOL gc_heap::blocking_collection = FALSE;
2367 heap_segment* gc_heap::freeable_large_heap_segment = 0;
2369 size_t gc_heap::time_bgc_last = 0;
2371 size_t gc_heap::mark_stack_tos = 0;
2373 size_t gc_heap::mark_stack_bos = 0;
2375 size_t gc_heap::mark_stack_array_length = 0;
2377 mark* gc_heap::mark_stack_array = 0;
2379 BOOL gc_heap::verify_pinned_queue_p = FALSE;
2381 BYTE* gc_heap::oldest_pinned_plug = 0;
2383 #ifdef FEATURE_LOH_COMPACTION
2384 size_t gc_heap::loh_pinned_queue_tos = 0;
2386 size_t gc_heap::loh_pinned_queue_bos = 0;
2388 size_t gc_heap::loh_pinned_queue_length = 0;
2390 mark* gc_heap::loh_pinned_queue = 0;
2392 BOOL gc_heap::loh_compacted_p = FALSE;
2393 #endif //FEATURE_LOH_COMPACTION
2395 #ifdef BACKGROUND_GC
2397 DWORD gc_heap::bgc_thread_id = 0;
2399 BYTE* gc_heap::background_written_addresses [array_size+2];
2401 heap_segment* gc_heap::freeable_small_heap_segment = 0;
2403 size_t gc_heap::bgc_overflow_count = 0;
2405 size_t gc_heap::bgc_begin_loh_size = 0;
2406 size_t gc_heap::end_loh_size = 0;
2408 DWORD gc_heap::bgc_alloc_spin_loh = 0;
2410 size_t gc_heap::bgc_loh_size_increased = 0;
2412 size_t gc_heap::bgc_loh_allocated_in_free = 0;
2414 size_t gc_heap::background_soh_alloc_count = 0;
2416 size_t gc_heap::background_loh_alloc_count = 0;
2418 BYTE** gc_heap::background_mark_stack_tos = 0;
2420 BYTE** gc_heap::background_mark_stack_array = 0;
2422 size_t gc_heap::background_mark_stack_array_length = 0;
2424 BYTE* gc_heap::background_min_overflow_address =0;
2426 BYTE* gc_heap::background_max_overflow_address =0;
2428 BOOL gc_heap::processed_soh_overflow_p = FALSE;
2430 BYTE* gc_heap::background_min_soh_overflow_address =0;
2432 BYTE* gc_heap::background_max_soh_overflow_address =0;
2434 SPTR_IMPL_NS_INIT(heap_segment, WKS, gc_heap, saved_sweep_ephemeral_seg, 0);
2435 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, saved_sweep_ephemeral_start, 0);
2437 heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0;
2439 Thread* gc_heap::bgc_thread = 0;
2441 BOOL gc_heap::expanded_in_fgc = FALSE;
2443 BYTE** gc_heap::c_mark_list = 0;
2445 size_t gc_heap::c_mark_list_length = 0;
2447 size_t gc_heap::c_mark_list_index = 0;
2449 gc_history_per_heap gc_heap::saved_bgc_data_per_heap;
2451 BOOL gc_heap::bgc_data_saved_p = FALSE;
2453 BOOL gc_heap::bgc_thread_running;
2455 CLREvent gc_heap::background_gc_create_event;
2457 CRITICAL_SECTION gc_heap::bgc_threads_timeout_cs;
2459 CLREvent gc_heap::gc_lh_block_event;
2461 #endif //BACKGROUND_GC
2464 BYTE** gc_heap::mark_list;
2465 BYTE** gc_heap::mark_list_index;
2466 BYTE** gc_heap::mark_list_end;
2470 snoop_stats_data gc_heap::snoop_stat;
2471 #endif //SNOOP_STATS
2473 BYTE* gc_heap::min_overflow_address = MAX_PTR;
2475 BYTE* gc_heap::max_overflow_address = 0;
2477 BYTE* gc_heap::shigh = 0;
2479 BYTE* gc_heap::slow = MAX_PTR;
2481 size_t gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS];
2483 size_t gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS];
2485 size_t gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS];
2487 size_t gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS];
2489 BOOL gc_heap::ordered_plug_indices_init = FALSE;
2491 BOOL gc_heap::use_bestfit = FALSE;
2493 BYTE* gc_heap::bestfit_first_pin = 0;
2495 BOOL gc_heap::commit_end_of_seg = FALSE;
2497 size_t gc_heap::max_free_space_items = 0;
2499 size_t gc_heap::free_space_buckets = 0;
2501 size_t gc_heap::free_space_items = 0;
2503 int gc_heap::trimmed_free_space_index = 0;
2505 size_t gc_heap::total_ephemeral_plugs = 0;
2507 seg_free_spaces* gc_heap::bestfit_seg = 0;
2509 size_t gc_heap::total_ephemeral_size = 0;
2513 size_t gc_heap::internal_root_array_length = initial_internal_roots;
2515 SPTR_IMPL_NS_INIT(PTR_BYTE, WKS, gc_heap, internal_root_array, 0);
2516 SVAL_IMPL_NS_INIT(size_t, WKS, gc_heap, internal_root_array_index, 0);
2517 SVAL_IMPL_NS_INIT(BOOL, WKS, gc_heap, heap_analyze_success, TRUE);
2519 BYTE* gc_heap::current_obj = 0;
2520 size_t gc_heap::current_obj_size = 0;
2522 #endif //HEAP_ANALYZE
2524 #endif //MULTIPLE_HEAPS
2526 GCSpinLock gc_heap::gc_lock;
2528 size_t gc_heap::eph_gen_starts_size = 0;
2529 heap_segment* gc_heap::segment_standby_list;
2530 size_t gc_heap::last_gc_index = 0;
2531 size_t gc_heap::min_segment_size = 0;
2533 #ifdef FEATURE_LOH_COMPACTION
2534 BOOL gc_heap::loh_compaction_always_p = FALSE;
2535 gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default;
2536 int gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
2538 #endif //FEATURE_LOH_COMPACTION
2540 CLREvent gc_heap::full_gc_approach_event;
2542 CLREvent gc_heap::full_gc_end_event;
2544 DWORD gc_heap::fgn_maxgen_percent = 0;
2546 DWORD gc_heap::fgn_loh_percent = 0;
2548 #ifdef BACKGROUND_GC
2549 BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE;
2550 #endif //BACKGROUND_GC
2552 VOLATILE(bool) gc_heap::full_gc_approach_event_set;
2554 size_t gc_heap::full_gc_counts[gc_type_max];
2556 BOOL gc_heap::should_expand_in_full_gc = FALSE;
2559 BOOL gc_heap::heap_analyze_enabled = FALSE;
2560 #endif //HEAP_ANALYZE
2562 #ifndef MULTIPLE_HEAPS
2564 #ifndef DACCESS_COMPILE
2566 #endif //!DACCESS_COMPILE
2567 GARY_IMPL(generation, generation_table,NUMBERGENERATIONS+1);
2568 #ifndef DACCESS_COMPILE
2570 #endif //!DACCESS_COMPILE
2572 #endif //MULTIPLE_HEAPS
2574 #ifndef MULTIPLE_HEAPS
2576 alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1];
2577 alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1];
2579 dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1];
2580 gc_history_per_heap gc_heap::gc_data_per_heap;
2582 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, alloc_allocated, 0);
2584 size_t gc_heap::allocation_quantum = CLR_SIZE;
2586 GCSpinLock gc_heap::more_space_lock;
2588 #ifdef SYNCHRONIZATION_STATS
2589 unsigned int gc_heap::good_suspension = 0;
2590 unsigned int gc_heap::bad_suspension = 0;
2591 ULONGLONG gc_heap::total_msl_acquire = 0;
2592 unsigned int gc_heap::num_msl_acquired = 0;
2593 unsigned int gc_heap::num_high_msl_acquire = 0;
2594 unsigned int gc_heap::num_low_msl_acquire = 0;
2595 #endif //SYNCHRONIZATION_STATS
2597 size_t gc_heap::alloc_contexts_used = 0;
2599 #endif //MULTIPLE_HEAPS
2601 #ifndef MULTIPLE_HEAPS
2603 BOOL gc_heap::gen0_bricks_cleared = FALSE;
2606 int gc_heap::gen0_must_clear_bricks = 0;
2607 #endif //FFIND_OBJECT
2609 #ifdef FEATURE_PREMORTEM_FINALIZATION
2610 SPTR_IMPL_NS_INIT(CFinalize, WKS, gc_heap, finalize_queue, 0);
2611 #endif // FEATURE_PREMORTEM_FINALIZATION
2613 #endif // MULTIPLE_HEAPS
2615 /* end of per heap static initialization */
2617 /* end of static initialization */
2619 #ifndef DACCESS_COMPILE
2621 void gen_to_condemn_tuning::print (int heap_num)
2624 dprintf (DT_LOG_0, ("condemned reasons"));
2625 dprintf (DT_LOG_0, ("%s", record_condemn_reasons_gen_header));
2626 gc_condemn_reason_gen r_gen;
2627 for (int i = 0; i < gcrg_max; i++)
2629 r_gen = (gc_condemn_reason_gen)(i);
2630 str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen));
2632 dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_gen));
2634 dprintf (DT_LOG_0, ("%s", record_condemn_reasons_condition_header));
2635 gc_condemn_reason_condition r_condition;
2636 for (int i = 0; i < gcrc_max; i++)
2638 r_condition = (gc_condemn_reason_condition)(i);
2639 str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition));
2642 dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_condition));
2646 void gc_generation_data::print (int heap_num, int gen_num)
2648 #ifdef SIMPLE_DPRINTF
2650 dprintf (DT_LOG_0, ("[%2d]gen%d beg %Id fl %Id fo %Id end %Id fl %Id fo %Id in %Id out %Id surv %Id alloc %Id",
2653 free_list_space_before, free_obj_space_before,
2655 free_list_space_after, free_obj_space_after,
2660 #endif //SIMPLE_DPRINTF
2663 void gc_history_per_heap::print (int heap_num)
2666 for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++)
2668 gen_data[i].print (heap_num, i);
2670 dprintf (DT_LOG_0, ("[%2d]mp %d", heap_num, mem_pressure));
2673 gc_mechanism_descr* descr = 0;
2675 for (int i = 0; i < max_mechanism_per_heap; i++)
2677 mechanism = get_mechanism ((gc_mechanism_per_heap)i);
2681 descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i];
2682 dprintf (DT_LOG_0, ("[%2d]%s%s",
2685 (descr->descr)[mechanism]));
2691 void gc_history_global::print()
2694 char str_settings[64];
2695 memset (str_settings, '|', sizeof (char) * 64);
2696 str_settings[max_global_mechanism*2] = 0;
2698 for (int i = 0; i < max_global_mechanism; i++)
2700 str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N');
2703 dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|"));
2704 dprintf (DT_LOG_0, ("%4d|%s", num_heaps, str_settings));
2705 dprintf (DT_LOG_0, ("Condemned gen%d(%s), youngest budget %Id(%d)",
2706 condemned_generation,
2707 str_gc_reasons[reason],
2708 final_youngest_desired,
2709 gen0_reduction_count));
2713 void gc_heap::fire_pevents()
2716 settings.record (&gc_data_global);
2717 gc_data_global.print();
2719 FireEtwGCGlobalHeapHistory_V1(gc_data_global.final_youngest_desired,
2720 gc_data_global.num_heaps,
2721 gc_data_global.condemned_generation,
2722 gc_data_global.gen0_reduction_count,
2723 gc_data_global.reason,
2724 gc_data_global.global_mechanims_p,
2725 GetClrInstanceId());
2727 #ifdef MULTIPLE_HEAPS
2728 for (int i = 0; i < gc_heap::n_heaps; i++)
2730 gc_heap* hp = gc_heap::g_heaps[i];
2731 gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
2732 current_gc_data_per_heap->print (i);
2733 current_gc_data_per_heap->gen_to_condemn_reasons.print (i);
2734 FireEtwGCPerHeapHistorySpecial(*current_gc_data_per_heap, sizeof(hp->gc_data_per_heap), (UINT8)GetClrInstanceId());
2737 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
2738 FireEtwGCPerHeapHistorySpecial(*current_gc_data_per_heap, sizeof(gc_data_per_heap), (UINT8)GetClrInstanceId());
2739 current_gc_data_per_heap->print (0);
2740 current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_number);
2746 gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp)
2752 case tuning_deciding_condemned_gen:
2753 case tuning_deciding_compaction:
2754 case tuning_deciding_expansion:
2755 case tuning_deciding_full_gc:
2757 ret = (!ephemeral_gen_fit_p (tp));
2760 case tuning_deciding_promote_ephemeral:
2762 size_t new_gen0size = approximate_new_allocation();
2763 ptrdiff_t plan_ephemeral_size = total_ephemeral_size;
2765 dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id",
2766 heap_number, plan_ephemeral_size, new_gen0size));
2767 ret = ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - (heap_segment_mem (ephemeral_heap_segment))) <
2768 (plan_ephemeral_size + new_gen0size));
2779 gc_heap::dt_high_frag_p (gc_tuning_point tp,
2787 case tuning_deciding_condemned_gen:
2789 dynamic_data* dd = dynamic_data_of (gen_number);
2790 float fragmentation_burden = 0;
2794 ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd));
2795 dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id",
2796 heap_number, dd_fragmentation (dd), dd_max_size(dd)));
2800 #ifndef MULTIPLE_HEAPS
2801 if (gen_number == max_generation)
2803 float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation);
2804 if (frag_ratio > 0.65)
2806 dprintf (GTC_LOG, ("g2 FR: %d%%", (int)(frag_ratio*100)));
2810 #endif //!MULTIPLE_HEAPS
2811 size_t fr = generation_unusable_fragmentation (generation_of (gen_number));
2812 ret = (fr > dd_fragmentation_limit(dd));
2815 fragmentation_burden = (float)fr / generation_size (gen_number);
2816 ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd));
2818 dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d",
2819 heap_number, gen_number, dd_fragmentation (dd),
2820 (int)(100*generation_allocator_efficiency (generation_of (gen_number))),
2821 fr, (int)(fragmentation_burden*100)));
2833 gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number, ULONGLONG total_mem)
2839 case tuning_deciding_condemned_gen:
2841 if (gen_number == max_generation)
2843 dynamic_data* dd = dynamic_data_of (gen_number);
2844 size_t maxgen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
2845 size_t maxgen_total_size = maxgen_allocated + dd_current_size (dd);
2846 size_t est_maxgen_surv = (size_t)((float) (maxgen_total_size) * dd_surv (dd));
2847 size_t est_maxgen_free = maxgen_total_size - est_maxgen_surv + dd_fragmentation (dd);
2849 #ifdef SIMPLE_DPRINTF
2850 dprintf (GTC_LOG, ("h%d: Total gen2 size: %Id(s: %d%%), est gen2 dead space: %Id (s: %d, allocated: %Id), frag: %Id, 3%% of physical mem is %Id bytes",
2853 (int)(100*dd_surv (dd)),
2855 (int)(dd_surv (dd) * 100),
2857 dd_fragmentation (dd),
2858 (size_t)((float)total_mem * 0.03)));
2859 #endif //SIMPLE_DPRINTF
2860 DWORD num_heaps = 1;
2862 #ifdef MULTIPLE_HEAPS
2863 num_heaps = gc_heap::n_heaps;
2864 #endif //MULTIPLE_HEAPS
2866 size_t min_frag_th = min_reclaim_fragmentation_threshold(total_mem, num_heaps);
2867 dprintf (GTC_LOG, ("h%d, min frag is %Id", heap_number, min_frag_th));
2868 ret = (est_maxgen_free >= min_frag_th);
2884 // DTREVIEW: Right now we only estimate gen2 fragmentation.
2885 // on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
2888 gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, ULONGLONG available_mem)
2894 case tuning_deciding_condemned_gen:
2896 if (gen_number == max_generation)
2898 dynamic_data* dd = dynamic_data_of (gen_number);
2899 float est_frag_ratio = 0;
2900 if (dd_current_size (dd) == 0)
2904 else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0))
2910 est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd));
2913 size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio));
2914 dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id",
2917 dd_current_size (dd),
2918 dd_fragmentation (dd),
2919 (int)(est_frag_ratio*100),
2922 DWORD num_heaps = 1;
2924 #ifdef MULTIPLE_HEAPS
2925 num_heaps = gc_heap::n_heaps;
2926 #endif //MULTIPLE_HEAPS
2927 ULONGLONG min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps);
2928 //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th));
2929 ret = (est_frag >= min_frag_th);
2946 gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp)
2952 case tuning_deciding_condemned_gen:
2954 /* promote into max-generation if the card table has too many
2955 * generation faults besides the n -> 0
2957 ret = (generation_skip_ratio < 30);
2969 in_range_for_segment(BYTE* add, heap_segment* seg)
2971 return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg)));
2974 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2975 // The array we allocate is organized as follows:
2976 // 0th element is the address of the last array we allocated.
2977 // starting from the 1st element are the segment addresses, that's
2978 // what buckets() returns.
2990 bk* buckets() { return (slots + 1); }
2991 BYTE*& last_slot (bk* arr) { return arr[0].add; }
2994 static sorted_table* make_sorted_table ();
2995 BOOL insert (BYTE* add, size_t val);;
2996 size_t lookup (BYTE*& add);
2997 void remove (BYTE* add);
2999 void delete_sorted_table();
3000 void delete_old_slots();
3001 void enqueue_old_slot(bk* sl);
3002 BOOL insure_space_for_insert();
3006 sorted_table::make_sorted_table ()
3010 // allocate one more bk to store the older slot address.
3011 sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)];
3015 res->slots = (bk*)(res + 1);
3022 sorted_table::delete_sorted_table()
3024 if (slots != (bk*)(this+1))
3032 sorted_table::delete_old_slots()
3034 BYTE* sl = (BYTE*)old_slots;
3038 sl = last_slot ((bk*)sl);
3044 sorted_table::enqueue_old_slot(bk* sl)
3046 last_slot (sl) = (BYTE*)old_slots;
3052 sorted_table::lookup (BYTE*& add)
3054 ptrdiff_t high = (count-1);
3058 bk* buck = buckets();
3061 mid = ((low + high)/2);
3063 if (buck[ti].add > add)
3065 if ((ti > 0) && (buck[ti-1].add <= add))
3067 add = buck[ti-1].add;
3074 if (buck[ti+1].add > add)
3087 sorted_table::insure_space_for_insert()
3091 size = (size * 3)/2;
3092 assert((size * sizeof (bk)) > 0);
3093 bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)];
3098 last_slot (res) = 0;
3099 memcpy (((bk*)res + 1), buckets(), count * sizeof (bk));
3100 bk* last_old_slots = slots;
3102 if (last_old_slots != (bk*)(this + 1))
3103 enqueue_old_slot (last_old_slots);
3109 sorted_table::insert (BYTE* add, size_t val)
3111 //val is ignored for non concurrent gc
3113 //grow if no more room
3114 assert (count < size);
3117 ptrdiff_t high = (count-1);
3121 bk* buck = buckets();
3124 mid = ((low + high)/2);
3126 if (buck[ti].add > add)
3128 if ((ti == 0) || (buck[ti-1].add <= add))
3130 // found insertion point
3131 for (ptrdiff_t k = count; k > ti;k--)
3133 buck [k] = buck [k-1];
3143 if (buck[ti+1].add > add)
3145 //found the insertion point
3146 for (ptrdiff_t k = count; k > ti+1;k--)
3148 buck [k] = buck [k-1];
3150 buck[ti+1].add = add;
3163 sorted_table::remove (BYTE* add)
3165 ptrdiff_t high = (count-1);
3169 bk* buck = buckets();
3172 mid = ((low + high)/2);
3174 if (buck[ti].add > add)
3176 if (buck[ti-1].add <= add)
3178 // found the guy to remove
3179 for (ptrdiff_t k = ti; k < count; k++)
3180 buck[k-1] = buck[k];
3188 if (buck[ti+1].add > add)
3190 // found the guy to remove
3191 for (ptrdiff_t k = ti+1; k < count; k++)
3192 buck[k-1] = buck[k];
3203 sorted_table::clear()
3206 buckets()[0].add = MAX_PTR;
3208 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
3210 #ifdef SEG_MAPPING_TABLE
3211 #ifdef GROWABLE_SEG_MAPPING_TABLE
3213 BYTE* align_on_segment (BYTE* add)
3215 return (BYTE*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1));
3219 BYTE* align_lower_segment (BYTE* add)
3221 return (BYTE*)((size_t)(add) & ~(gc_heap::min_segment_size - 1));
3224 size_t size_seg_mapping_table_of (BYTE* from, BYTE* end)
3226 from = align_lower_segment (from);
3227 end = align_on_segment (end);
3228 dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((end - from) / gc_heap::min_segment_size))));
3229 return sizeof (seg_mapping)*((end - from) / gc_heap::min_segment_size);
3233 size_t seg_mapping_word_of (BYTE* add)
3235 return (size_t)add / gc_heap::min_segment_size;
3237 #else //GROWABLE_SEG_MAPPING_TABLE
3238 BOOL seg_mapping_table_init()
3241 ULONGLONG total_address_space = (ULONGLONG)8*1024*1024*1024*1024;
3243 ULONGLONG total_address_space = (ULONGLONG)4*1024*1024*1024;
3246 size_t num_entries = (size_t)(total_address_space / gc_heap::min_segment_size);
3247 seg_mapping_table = new seg_mapping[num_entries];
3249 if (seg_mapping_table)
3251 memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping));
3252 dprintf (1, ("created %d entries for heap mapping (%Id bytes)",
3253 num_entries, (num_entries * sizeof (seg_mapping))));
3258 dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)",
3259 num_entries, (num_entries * sizeof (seg_mapping))));
3263 #endif //GROWABLE_SEG_MAPPING_TABLE
3265 #ifdef FEATURE_BASICFREEZE
3267 size_t ro_seg_begin_index (heap_segment* seg)
3269 size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3270 begin_index = max (begin_index, (size_t)g_lowest_address / gc_heap::min_segment_size);
3275 size_t ro_seg_end_index (heap_segment* seg)
3277 size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) / gc_heap::min_segment_size;
3278 end_index = min (end_index, (size_t)g_highest_address / gc_heap::min_segment_size);
3282 void seg_mapping_table_add_ro_segment (heap_segment* seg)
3284 #ifdef GROWABLE_SEG_MAPPING_TABLE
3285 if ((heap_segment_reserved (seg) <= g_lowest_address) || (heap_segment_mem (seg) >= g_highest_address))
3287 #endif //GROWABLE_SEG_MAPPING_TABLE
3289 for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++)
3290 seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry);
3293 void seg_mapping_table_remove_ro_segment (heap_segment* seg)
3296 // POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves
3297 // to be a perf problem, we can search in the current ro segs and see if any lands in this range and only
3298 // remove the flag if none lands in this range.
3302 heap_segment* ro_segment_lookup (BYTE* o)
3305 gc_heap::seg_table->lookup (ro_seg);
3307 if (ro_seg && in_range_for_segment (o, (heap_segment*)ro_seg))
3308 return (heap_segment*)ro_seg;
3313 #endif //FEATURE_BASICFREEZE
3315 void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
3317 size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3318 size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3319 seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3320 size_t end_index = seg_end / gc_heap::min_segment_size;
3321 seg_mapping* end_entry = &seg_mapping_table[end_index];
3322 dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)",
3323 seg, begin_index, heap_segment_reserved (seg), end_index));
3325 dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3326 begin_index, (seg_mapping_table[begin_index].boundary + 1),
3327 end_index, (seg_mapping_table[end_index].boundary + 1)));
3329 #ifdef MULTIPLE_HEAPS
3330 #ifdef SIMPLE_DPRINTF
3331 dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)",
3332 begin_index, (BYTE*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3333 (BYTE*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3334 end_index, (BYTE*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3335 (BYTE*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3336 #endif //SIMPLE_DPRINTF
3337 assert (end_entry->boundary == 0);
3338 assert (end_entry->h0 == 0);
3340 assert (begin_entry->h1 == 0);
3341 begin_entry->h1 = hp;
3342 #endif //MULTIPLE_HEAPS
3344 end_entry->boundary = (BYTE*)seg_end;
3346 dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix", begin_index, end_index, seg));
3347 assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry));
3348 begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg);
3349 end_entry->seg0 = seg;
3351 // for every entry inbetween we need to set its heap too.
3352 for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3354 assert (seg_mapping_table[entry_index].boundary == 0);
3355 #ifdef MULTIPLE_HEAPS
3356 assert (seg_mapping_table[entry_index].h0 == 0);
3357 seg_mapping_table[entry_index].h1 = hp;
3358 #endif //MULTIPLE_HEAPS
3359 seg_mapping_table[entry_index].seg1 = seg;
3362 dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3363 begin_index, (seg_mapping_table[begin_index].boundary + 1),
3364 end_index, (seg_mapping_table[end_index].boundary + 1)));
3365 #if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF)
3366 dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)",
3367 begin_index, (BYTE*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3368 (BYTE*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3369 end_index, (BYTE*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3370 (BYTE*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3371 #endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF
3374 void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
3376 size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3377 size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3378 seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3379 size_t end_index = seg_end / gc_heap::min_segment_size;
3380 seg_mapping* end_entry = &seg_mapping_table[end_index];
3381 dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)",
3382 seg, begin_index, heap_segment_reserved (seg), end_index));
3384 assert (end_entry->boundary == (BYTE*)seg_end);
3385 end_entry->boundary = 0;
3387 #ifdef MULTIPLE_HEAPS
3388 gc_heap* hp = heap_segment_heap (seg);
3389 assert (end_entry->h0 == hp);
3391 assert (begin_entry->h1 == hp);
3392 begin_entry->h1 = 0;
3393 #endif //MULTIPLE_HEAPS
3395 assert (begin_entry->seg1 != 0);
3396 begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry);
3397 end_entry->seg0 = 0;
3399 // for every entry inbetween we need to reset its heap too.
3400 for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3402 assert (seg_mapping_table[entry_index].boundary == 0);
3403 #ifdef MULTIPLE_HEAPS
3404 assert (seg_mapping_table[entry_index].h0 == 0);
3405 assert (seg_mapping_table[entry_index].h1 == hp);
3406 seg_mapping_table[entry_index].h1 = 0;
3407 #endif //MULTIPLE_HEAPS
3408 seg_mapping_table[entry_index].seg1 = 0;
3411 dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3412 begin_index, (seg_mapping_table[begin_index].boundary + 1),
3413 end_index, (seg_mapping_table[end_index].boundary + 1)));
3414 #ifdef MULTIPLE_HEAPS
3415 dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix",
3416 begin_index, (BYTE*)(begin_entry->h0), (BYTE*)(begin_entry->h1),
3417 end_index, (BYTE*)(end_entry->h0), (BYTE*)(end_entry->h1)));
3418 #endif //MULTIPLE_HEAPS
3421 #ifdef MULTIPLE_HEAPS
3423 gc_heap* seg_mapping_table_heap_of_worker (BYTE* o)
3425 size_t index = (size_t)o / gc_heap::min_segment_size;
3426 seg_mapping* entry = &seg_mapping_table[index];
3428 gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
3430 dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
3431 o, index, (entry->boundary + 1),
3432 (BYTE*)(entry->h0), (BYTE*)(entry->seg0),
3433 (BYTE*)(entry->h1), (BYTE*)(entry->seg1)));
3436 heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3437 #ifdef FEATURE_BASICFREEZE
3438 if ((size_t)seg & ro_in_entry)
3439 seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3440 #endif //FEATURE_BASICFREEZE
3444 if (in_range_for_segment (o, seg))
3446 dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, seg, (BYTE*)heap_segment_allocated (seg)));
3450 dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg",
3451 seg, (BYTE*)heap_segment_allocated (seg), o));
3456 dprintf (2, ("could not find obj %Ix in any existing segments", o));
3463 gc_heap* seg_mapping_table_heap_of (BYTE* o)
3465 #ifdef GROWABLE_SEG_MAPPING_TABLE
3466 if ((o < g_lowest_address) || (o >= g_highest_address))
3468 #endif //GROWABLE_SEG_MAPPING_TABLE
3470 return seg_mapping_table_heap_of_worker (o);
3473 gc_heap* seg_mapping_table_heap_of_gc (BYTE* o)
3475 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3476 if ((o < g_lowest_address) || (o >= g_highest_address))
3478 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3480 return seg_mapping_table_heap_of_worker (o);
3482 #endif //MULTIPLE_HEAPS
3484 // Only returns a valid seg if we can actually find o on the seg.
3485 heap_segment* seg_mapping_table_segment_of (BYTE* o)
3487 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3488 if ((o < g_lowest_address) || (o >= g_highest_address))
3489 #ifdef FEATURE_BASICFREEZE
3490 return ro_segment_lookup (o);
3493 #endif //FEATURE_BASICFREEZE
3494 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3496 size_t index = (size_t)o / gc_heap::min_segment_size;
3497 seg_mapping* entry = &seg_mapping_table[index];
3499 dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
3500 o, index, (entry->boundary + 1),
3501 (BYTE*)(entry->seg0), (BYTE*)(entry->seg1)));
3503 heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3504 #ifdef FEATURE_BASICFREEZE
3505 if ((size_t)seg & ro_in_entry)
3506 seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3507 #endif //FEATURE_BASICFREEZE
3511 // Can't assert this when it's callled by everyone (it's true when it's called by mark cards).
3512 //assert (in_range_for_segment (o, seg));
3513 if (in_range_for_segment (o, seg))
3515 dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, (BYTE*)heap_segment_mem(seg), (BYTE*)heap_segment_reserved(seg)));
3519 dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0",
3520 (BYTE*)heap_segment_mem(seg), (BYTE*)heap_segment_reserved(seg), o));
3526 dprintf (2, ("could not find obj %Ix in any existing segments", o));
3529 #ifdef FEATURE_BASICFREEZE
3530 if (!seg && (size_t)(entry->seg1) & ro_in_entry)
3532 seg = ro_segment_lookup (o);
3533 if (!in_range_for_segment (o, seg))
3536 #endif //FEATURE_BASICFREEZE
3540 #endif //SEG_MAPPING_TABLE
3542 size_t gcard_of ( BYTE*);
3543 void gset_card (size_t card);
3545 #define memref(i) *(BYTE**)(i)
3548 #define GC_MARKED (size_t)0x1
3549 #define slot(i, j) ((BYTE**)(i))[j+1]
3551 #define free_object_base_size (plug_skew + sizeof(ArrayBase))
3553 class CObjectHeader : public Object
3557 #ifdef FEATURE_REDHAWK
3558 // The GC expects the following methods that are provided by the Object class in the CLR but not provided
3559 // by Redhawk's version of Object.
3560 DWORD GetNumComponents()
3562 return ((ArrayBase *)this)->GetNumComponents();
3565 void Validate(BOOL bDeep=TRUE, BOOL bVerifyNextHeader = TRUE)
3570 BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE;
3571 fSmallObjectHeapPtr = GCHeap::GetGCHeap()->IsHeapPointer(this, TRUE);
3572 if (!fSmallObjectHeapPtr)
3573 fLargeObjectHeapPtr = GCHeap::GetGCHeap()->IsHeapPointer(this);
3575 _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr);
3576 _ASSERTE(GetMethodTable()->GetBaseSize() >= 8);
3578 #ifdef FEATURE_STRUCTALIGN
3579 _ASSERTE(IsStructAligned((BYTE *)this, GetMethodTable()->GetBaseAlignment()));
3580 #endif // FEATURE_STRUCTALIGN
3584 GCHeap::GetGCHeap()->ValidateObjectMember(this);
3588 void ValidatePromote(ScanContext *sc, DWORD flags)
3593 void ValidateHeap(Object *from, BOOL bDeep)
3595 Validate(bDeep, FALSE);
3598 ADIndex GetAppDomainIndex()
3600 return (ADIndex)RH_DEFAULT_DOMAIN_ID;
3602 #endif //FEATURE_REDHAWK
3606 // Header Status Information
3609 MethodTable *GetMethodTable() const
3611 return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED))));
3616 RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
3619 BOOL IsMarked() const
3621 return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
3626 assert (!(gc_heap::settings.concurrent));
3627 GetHeader()->SetGCBit();
3630 BOOL IsPinned() const
3632 return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE);
3637 RawSetMethodTable( GetMethodTable() );
3640 CGCDesc *GetSlotMap ()
3642 assert (GetMethodTable()->ContainsPointers());
3643 return CGCDesc::GetCGCDescFromMT(GetMethodTable());
3646 void SetFree(size_t size)
3648 assert (size >= free_object_base_size);
3650 assert (g_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size);
3651 assert (g_pFreeObjectMethodTable->RawGetComponentSize() == 1);
3653 RawSetMethodTable( g_pFreeObjectMethodTable );
3655 SIZE_T* numComponentsPtr = (SIZE_T*) &((BYTE*) this)[ArrayBase::GetOffsetOfNumComponents()];
3656 *numComponentsPtr = size - free_object_base_size;
3658 //This introduces a bug in the free list management.
3659 //((void**) this)[-1] = 0; // clear the sync block,
3660 assert (*numComponentsPtr >= 0);
3661 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
3662 memset (((BYTE*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr);
3663 #endif //VERIFY_HEAP
3668 size_t size = free_object_base_size - plug_skew;
3670 // since we only need to clear 2 ptr size, we do it manually
3671 PTR_PTR m = (PTR_PTR) this;
3672 for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
3676 BOOL IsFree () const
3678 return (GetMethodTable() == g_pFreeObjectMethodTable);
3681 #ifdef FEATURE_STRUCTALIGN
3682 int GetRequiredAlignment () const
3684 return GetMethodTable()->GetRequiredAlignment();
3686 #endif // FEATURE_STRUCTALIGN
3688 BOOL ContainsPointers() const
3690 return GetMethodTable()->ContainsPointers();
3693 #ifdef COLLECTIBLE_CLASS
3694 BOOL Collectible() const
3696 return GetMethodTable()->Collectible();
3699 FORCEINLINE BOOL ContainsPointersOrCollectible() const
3701 MethodTable *pMethodTable = GetMethodTable();
3702 return (pMethodTable->ContainsPointers() || pMethodTable->Collectible());
3704 #endif //COLLECTIBLE_CLASS
3706 Object* GetObjectBase() const
3708 return (Object*) this;
3712 #define header(i) ((CObjectHeader*)(i))
3714 #define free_list_slot(x) ((BYTE**)(x))[2]
3715 #define free_list_undo(x) ((BYTE**)(x))[-1]
3716 #define UNDO_EMPTY ((BYTE*)1)
3720 void set_plug_padded (BYTE* node)
3722 header(node)->SetMarked();
3725 void clear_plug_padded (BYTE* node)
3727 header(node)->ClearMarked();
3730 BOOL is_plug_padded (BYTE* node)
3732 return header(node)->IsMarked();
3735 inline void set_plug_padded (BYTE* node){}
3736 inline void clear_plug_padded (BYTE* node){}
3738 BOOL is_plug_padded (BYTE* node){return FALSE;}
3739 #endif //SHORT_PLUGS
3742 inline size_t unused_array_size(BYTE * p)
3744 assert(((CObjectHeader*)p)->IsFree());
3746 SIZE_T* numComponentsPtr = (SIZE_T*)(p + ArrayBase::GetOffsetOfNumComponents());
3747 return free_object_base_size + *numComponentsPtr;
3750 heap_segment* heap_segment_rw (heap_segment* ns)
3752 if ((ns == 0) || !heap_segment_read_only_p (ns))
3760 ns = heap_segment_next (ns);
3761 } while ((ns != 0) && heap_segment_read_only_p (ns));
3766 //returns the next non ro segment.
3767 heap_segment* heap_segment_next_rw (heap_segment* seg)
3769 heap_segment* ns = heap_segment_next (seg);
3770 return heap_segment_rw (ns);
3773 // returns the segment before seg.
3774 heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg)
3776 assert (begin != 0);
3777 heap_segment* prev = begin;
3778 heap_segment* current = heap_segment_next_rw (begin);
3780 while (current && current != seg)
3783 current = heap_segment_next_rw (current);
3796 // returns the segment before seg.
3797 heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg)
3799 assert (begin != 0);
3800 heap_segment* prev = begin;
3801 heap_segment* current = heap_segment_next (begin);
3803 while (current && current != seg)
3806 current = heap_segment_next (current);
3819 heap_segment* heap_segment_in_range (heap_segment* ns)
3821 if ((ns == 0) || heap_segment_in_range_p (ns))
3829 ns = heap_segment_next (ns);
3830 } while ((ns != 0) && !heap_segment_in_range_p (ns));
3835 heap_segment* heap_segment_next_in_range (heap_segment* seg)
3837 heap_segment* ns = heap_segment_next (seg);
3838 return heap_segment_in_range (ns);
3848 imemory_data *initial_memory;
3849 imemory_data *initial_normal_heap; // points into initial_memory_array
3850 imemory_data *initial_large_heap; // points into initial_memory_array
3852 size_t block_size_normal;
3853 size_t block_size_large;
3855 size_t block_count; // # of blocks in each
3856 size_t current_block_normal;
3857 size_t current_block_large;
3866 size_t allocation_pattern;
3867 } initial_memory_details;
3869 initial_memory_details memory_details;
3871 BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
3873 BOOL reserve_success = FALSE;
3875 // should only be called once
3876 assert (memory_details.initial_memory == 0);
3878 memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2];
3879 if (memory_details.initial_memory == 0)
3881 dprintf (2, ("failed to reserve %Id bytes for imemory_data", num_heaps*2*sizeof(imemory_data)));
3885 memory_details.initial_normal_heap = memory_details.initial_memory;
3886 memory_details.initial_large_heap = memory_details.initial_memory + num_heaps;
3887 memory_details.block_size_normal = normal_size;
3888 memory_details.block_size_large = large_size;
3889 memory_details.block_count = num_heaps;
3891 memory_details.current_block_normal = 0;
3892 memory_details.current_block_large = 0;
3894 g_lowest_address = MAX_PTR;
3895 g_highest_address = 0;
3897 // Try to get the data all at once
3898 ptrdiff_t allatonce_delta;
3900 if (((size_t)MAX_PTR - large_size) < normal_size)
3902 // we are already overflowing with just one heap.
3903 dprintf (2, ("0x%Ix + 0x%Ix already overflow", normal_size, large_size));
3907 if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size))
3909 dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow", normal_size, large_size, memory_details.block_count));
3913 size_t requestedMemory = memory_details.block_count * (normal_size + large_size);
3915 BYTE* allatonce_block = (BYTE*)virtual_alloc (requestedMemory);
3916 if (allatonce_block)
3918 g_lowest_address = allatonce_block;
3919 g_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size));
3920 memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
3922 for(size_t i = 0; i < memory_details.block_count; i++)
3924 memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size);
3925 memory_details.initial_large_heap[i].memory_base = allatonce_block +
3926 (memory_details.block_count*normal_size) + (i*large_size);
3927 reserve_success = TRUE;
3932 // try to allocate 2 blocks
3935 b1 = (BYTE*)virtual_alloc (memory_details.block_count * normal_size);
3938 b2 = (BYTE*)virtual_alloc (memory_details.block_count * large_size);
3941 memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
3942 g_lowest_address = min(b1,b2);
3943 g_highest_address = max(b1 + memory_details.block_count*normal_size,
3944 b2 + memory_details.block_count*large_size);
3945 for(size_t i = 0; i < memory_details.block_count; i++)
3947 memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size);
3948 memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size);
3949 reserve_success = TRUE;
3954 // b2 allocation failed, we'll go on to try allocating each block.
3955 // We could preserve the b1 alloc, but code complexity increases
3956 virtual_free (b1, memory_details.block_count * normal_size);
3960 if ((b2==NULL) && ( memory_details.block_count > 1))
3962 memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK;
3964 imemory_data *current_block = memory_details.initial_memory;
3965 for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
3967 size_t block_size = ((i < memory_details.block_count) ?
3968 memory_details.block_size_normal :
3969 memory_details.block_size_large);
3970 current_block->memory_base =
3971 (BYTE*)virtual_alloc (block_size);
3972 if (current_block->memory_base == 0)
3974 // Free the blocks that we've allocated so far
3975 current_block = memory_details.initial_memory;
3976 for(size_t j = 0; j < i; j++, current_block++){
3977 if (current_block->memory_base != 0){
3978 block_size = ((j < memory_details.block_count) ?
3979 memory_details.block_size_normal :
3980 memory_details.block_size_large);
3981 virtual_free (current_block->memory_base , block_size);
3984 reserve_success = FALSE;
3989 if (current_block->memory_base < g_lowest_address)
3990 g_lowest_address = current_block->memory_base;
3991 if (((BYTE *) current_block->memory_base + block_size) > g_highest_address)
3992 g_highest_address = (current_block->memory_base + block_size);
3994 reserve_success = TRUE;
3999 return reserve_success;
4002 void destroy_initial_memory()
4004 if (memory_details.initial_memory != NULL)
4006 if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4008 virtual_free(memory_details.initial_memory[0].memory_base,
4009 memory_details.block_count*(memory_details.block_size_normal +
4010 memory_details.block_size_large));
4012 else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE)
4014 virtual_free (memory_details.initial_normal_heap[0].memory_base,
4015 memory_details.block_count*memory_details.block_size_normal);
4017 virtual_free (memory_details.initial_large_heap[0].memory_base,
4018 memory_details.block_count*memory_details.block_size_large);
4022 assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK);
4023 imemory_data *current_block = memory_details.initial_memory;
4024 for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4026 size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal :
4027 memory_details.block_size_large;
4028 if (current_block->memory_base != NULL)
4030 virtual_free (current_block->memory_base, block_size);
4035 delete [] memory_details.initial_memory;
4036 memory_details.initial_memory = NULL;
4037 memory_details.initial_normal_heap = NULL;
4038 memory_details.initial_large_heap = NULL;
4042 void* next_initial_memory (size_t size)
4044 assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large));
4047 if ((size != memory_details.block_size_normal) ||
4048 ((memory_details.current_block_normal == memory_details.block_count) &&
4049 (memory_details.block_size_normal == memory_details.block_size_large)))
4051 // If the block sizes are the same, flow block requests from normal to large
4052 assert (memory_details.current_block_large < memory_details.block_count);
4053 assert (memory_details.initial_large_heap != 0);
4055 res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base;
4056 memory_details.current_block_large++;
4060 assert (memory_details.current_block_normal < memory_details.block_count);
4061 assert (memory_details.initial_normal_heap != NULL);
4063 res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base;
4064 memory_details.current_block_normal++;
4070 heap_segment* get_initial_segment (size_t size, int h_number)
4072 void* mem = next_initial_memory (size);
4073 heap_segment* res = gc_heap::make_heap_segment ((BYTE*)mem, size , h_number);
4078 void* virtual_alloc (size_t size)
4080 size_t requested_size = size;
4082 if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4084 gc_heap::reserved_memory_limit =
4085 CNameSpace::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size);
4086 if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4092 void* prgmem = ClrVirtualAllocAligned (0, requested_size, mem_reserve, PAGE_READWRITE, card_size * card_word_width);
4093 void *aligned_mem = prgmem;
4095 // We don't want (prgmem + size) to be right at the end of the address space
4096 // because we'd have to worry about that everytime we do (address + size).
4097 // We also want to make sure that we leave LARGE_OBJECT_SIZE at the end
4098 // so we allocate a small object we don't need to worry about overflow there
4099 // when we do alloc_ptr+size.
4102 BYTE* end_mem = (BYTE*)prgmem + requested_size;
4104 if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC))
4106 VirtualFree (prgmem, 0, MEM_RELEASE);
4107 dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding",
4108 requested_size, (size_t)prgmem, (size_t)((BYTE*)prgmem+requested_size)));
4116 gc_heap::reserved_memory += requested_size;
4119 dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[",
4120 requested_size, (size_t)prgmem, (size_t)((BYTE*)prgmem+requested_size)));
4125 void virtual_free (void* add, size_t size)
4127 VirtualFree (add, 0, MEM_RELEASE);
4128 gc_heap::reserved_memory -= size;
4129 dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[",
4130 size, (size_t)add, (size_t)((BYTE*)add+size)));
4133 // We have a few places that call this but the seg size doesn't change so call it
4134 // once and save the result.
4135 // TODO: move back after we do this.
4136 static size_t get_valid_segment_size (BOOL large_seg=FALSE)
4138 size_t seg_size, initial_seg_size;
4142 initial_seg_size = INITIAL_ALLOC;
4143 seg_size = g_pConfig->GetSegmentSize();
4147 initial_seg_size = LHEAP_ALLOC;
4148 seg_size = g_pConfig->GetSegmentSize() / 2;
4151 #ifdef MULTIPLE_HEAPS
4152 if (g_SystemInfo.dwNumberOfProcessors > 4)
4153 initial_seg_size /= 2;
4154 if (g_SystemInfo.dwNumberOfProcessors > 8)
4155 initial_seg_size /= 2;
4156 #endif //MULTIPLE_HEAPS
4158 // if seg_size is small but not 0 (0 is default if config not set)
4159 // then set the segment to the minimum size
4160 if (!GCHeap::IsValidSegmentSize(seg_size))
4162 // if requested size is between 1 byte and 4MB, use min
4163 if ((seg_size >> 1) && !(seg_size >> 22))
4164 seg_size = 1024*1024*4;
4166 seg_size = initial_seg_size;
4173 gc_heap::compute_new_ephemeral_size()
4175 int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0);
4176 size_t padding_size = 0;
4178 for (int i = 0; i <= eph_gen_max; i++)
4180 dynamic_data* dd = dynamic_data_of (i);
4181 total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd));
4182 #ifdef RESPECT_LARGE_ALIGNMENT
4183 total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE);
4184 #endif //RESPECT_LARGE_ALIGNMENT
4185 #ifdef FEATURE_STRUCTALIGN
4186 total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN;
4187 #endif //FEATURE_STRUCTALIGN
4190 padding_size += dd_padding_size (dd);
4191 #endif //SHORT_PLUGS
4194 total_ephemeral_size += eph_gen_starts_size;
4196 #ifdef RESPECT_LARGE_ALIGNMENT
4197 size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) -
4198 generation_plan_allocation_start (generation_of (max_generation-1));
4199 total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size);
4200 #endif //RESPECT_LARGE_ALIGNMENT
4203 float pad_ratio = (float)24 / (float)DESIRED_PLUG_LENGTH;
4204 total_ephemeral_size += (size_t)((float)total_ephemeral_size * pad_ratio) + Align (min_obj_size);
4205 #endif //SHORT_PLUGS
4207 dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)",
4208 total_ephemeral_size,
4209 padding_size, (total_ephemeral_size - padding_size)));
4213 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
4217 gc_heap::soh_get_segment_to_expand()
4219 size_t size = get_valid_segment_size();
4221 ordered_plug_indices_init = FALSE;
4222 use_bestfit = FALSE;
4224 //compute the size of the new ephemeral heap segment.
4225 compute_new_ephemeral_size();
4227 if ((settings.pause_mode != pause_low_latency)
4228 #ifdef BACKGROUND_GC
4229 && (!recursive_gc_sync::background_running_p())
4230 #endif //BACKGROUND_GC
4233 allocator* gen_alloc = ((settings.condemned_generation == max_generation) ? 0 :
4234 generation_allocator (generation_of (max_generation)));
4235 dprintf (2, ("(gen%d)soh_get_segment_to_expand", settings.condemned_generation));
4237 // try to find one in the gen 2 segment list, search backwards because the first segments
4238 // tend to be more compact than the later ones.
4239 heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
4241 PREFIX_ASSUME(fseg != NULL);
4243 #ifdef SEG_REUSE_STATS
4245 #endif //SEG_REUSE_STATS
4247 heap_segment* seg = ephemeral_heap_segment;
4248 while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg))
4250 #ifdef SEG_REUSE_STATS
4252 #endif //SEG_REUSE_STATS
4254 if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc))
4256 gc_data_per_heap.set_mechanism (gc_heap_expand,
4257 (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal));
4258 if (settings.condemned_generation == max_generation)
4262 build_ordered_free_spaces (seg);
4263 dprintf (GTC_LOG, ("can use best fit"));
4266 #ifdef SEG_REUSE_STATS
4267 dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse",
4268 settings.condemned_generation, try_reuse));
4269 #endif //SEG_REUSE_STATS
4270 dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix", (size_t)seg));
4275 #ifdef SEG_REUSE_STATS
4276 dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning",
4277 settings.condemned_generation, try_reuse));
4278 #endif //SEG_REUSE_STATS
4279 dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
4281 // If we return 0 here, the allocator will think since we are short on end
4282 // of seg we neeed to trigger a full compacting GC. So if sustained low latency
4283 // is set we should acquire a new seg instead, that way we wouldn't be short.
4284 // The real solution, of course, is to actually implement seg reuse in gen1.
4285 if (settings.pause_mode != pause_sustained_low_latency)
4287 dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg"));
4295 heap_segment* result = get_segment (size, FALSE);
4299 #ifdef BACKGROUND_GC
4300 if (current_c_gc_state == c_gc_state_planning)
4302 // When we expand heap during bgc sweep, we set the seg to be swept so
4303 // we'll always look at cards for objects on the new segment.
4304 result->flags |= heap_segment_flags_swept;
4306 #endif //BACKGROUND_GC
4308 FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(result),
4309 (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)),
4310 ETW::GCLog::ETW_GC_INFO::SMALL_OBJECT_HEAP,
4311 GetClrInstanceId());
4314 gc_data_per_heap.set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory));
4318 dprintf (2, ("h%d: failed to allocate a new segment!", heap_number));
4322 #ifdef MULTIPLE_HEAPS
4323 heap_segment_heap (result) = this;
4324 #endif //MULTIPLE_HEAPS
4327 dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix", settings.condemned_generation, result));
4332 #pragma warning(default:4706)
4335 //returns 0 in case of allocation failure
4337 gc_heap::get_segment (size_t size, BOOL loh_p)
4339 heap_segment* result = 0;
4341 if (segment_standby_list != 0)
4343 result = segment_standby_list;
4344 heap_segment* last = 0;
4347 size_t hs = (size_t)(heap_segment_reserved (result) - (BYTE*)result);
4348 if ((hs >= size) && ((hs / 2) < size))
4350 dprintf (2, ("Hoarded segment %Ix found", (size_t) result));
4353 heap_segment_next (last) = heap_segment_next (result);
4357 segment_standby_list = heap_segment_next (result);
4364 result = heap_segment_next (result);
4371 init_heap_segment (result);
4372 #ifdef BACKGROUND_GC
4373 if (should_commit_mark_array())
4375 dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix", result, mark_array));
4376 if (!commit_mark_array_new_seg (__this, result))
4378 dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg"));
4379 // If we can't use it we need to thread it back.
4380 if (segment_standby_list != 0)
4382 heap_segment_next (result) = segment_standby_list;
4383 segment_standby_list = result;
4387 segment_standby_list = result;
4393 #endif //BACKGROUND_GC
4395 #ifdef SEG_MAPPING_TABLE
4397 seg_mapping_table_add_segment (result, __this);
4398 #endif //SEG_MAPPING_TABLE
4403 #ifndef SEG_MAPPING_TABLE
4404 if (!seg_table->insure_space_for_insert ())
4406 #endif //SEG_MAPPING_TABLE
4407 void* mem = virtual_alloc (size);
4410 fgm_result.set_fgm (fgm_reserve_segment, size, loh_p);
4414 result = gc_heap::make_heap_segment ((BYTE*)mem, size, heap_number);
4420 if (mem < g_lowest_address)
4426 start = (BYTE*)g_lowest_address;
4429 if (((BYTE*)mem + size) > g_highest_address)
4431 end = (BYTE*)mem + size;
4435 end = (BYTE*)g_highest_address;
4438 if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0)
4440 virtual_free (mem, size);
4446 fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p);
4447 virtual_free (mem, size);
4452 #ifdef SEG_MAPPING_TABLE
4453 seg_mapping_table_add_segment (result, __this);
4454 #else //SEG_MAPPING_TABLE
4455 gc_heap::seg_table->insert ((BYTE*)result, delta);
4456 #endif //SEG_MAPPING_TABLE
4460 #ifdef BACKGROUND_GC
4463 ::record_changed_seg ((BYTE*)result, heap_segment_reserved (result),
4464 settings.gc_index, current_bgc_state,
4466 bgc_verify_mark_array_cleared (result);
4468 #endif //BACKGROUND_GC
4470 dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)", heap_number, result, ((BYTE*)result + size), size));
4474 void release_segment (heap_segment* sg)
4476 ptrdiff_t delta = 0;
4477 FireEtwGCFreeSegment_V1((size_t)heap_segment_mem(sg), GetClrInstanceId());
4478 virtual_free (sg, (BYTE*)heap_segment_reserved (sg)-(BYTE*)sg);
4481 heap_segment* gc_heap::get_segment_for_loh (size_t size
4482 #ifdef MULTIPLE_HEAPS
4484 #endif //MULTIPLE_HEAPS
4487 #ifndef MULTIPLE_HEAPS
4489 #endif //MULTIPLE_HEAPS
4490 heap_segment* res = hp->get_segment (size, TRUE);
4493 #ifdef MULTIPLE_HEAPS
4494 heap_segment_heap (res) = hp;
4495 #endif //MULTIPLE_HEAPS
4496 res->flags |= heap_segment_flags_loh;
4498 FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), ETW::GCLog::ETW_GC_INFO::LARGE_OBJECT_HEAP, GetClrInstanceId());
4501 if (CORProfilerTrackGC())
4502 UpdateGenerationBounds();
4503 #endif // GC_PROFILING
4505 #ifdef MULTIPLE_HEAPS
4506 hp->thread_loh_segment (res);
4508 thread_loh_segment (res);
4509 #endif //MULTIPLE_HEAPS
4515 void gc_heap::thread_loh_segment (heap_segment* new_seg)
4517 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
4519 while (heap_segment_next_rw (seg))
4520 seg = heap_segment_next_rw (seg);
4521 heap_segment_next (seg) = new_seg;
4525 gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc)
4527 *did_full_compact_gc = FALSE;
4528 size_t last_full_compact_gc_count = get_full_compact_gc_count();
4530 //access to get_segment needs to be serialized
4531 add_saved_spinlock_info (me_release, mt_get_large_seg);
4533 dprintf (SPINLOCK_LOG, ("[%d]Seg: Lmsl", heap_number));
4534 leave_spin_lock (&more_space_lock);
4535 enter_spin_lock (&gc_heap::gc_lock);
4536 dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc", heap_number));
4537 // if a GC happened between here and before we ask for a segment in
4538 // get_large_segment, we need to count that GC.
4539 size_t current_full_compact_gc_count = get_full_compact_gc_count();
4541 if (current_full_compact_gc_count > last_full_compact_gc_count)
4543 *did_full_compact_gc = TRUE;
4546 #ifdef BACKGROUND_GC
4547 while (current_c_gc_state == c_gc_state_planning)
4549 dprintf (3, ("lh state planning, waiting to get a large seg"));
4551 dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Lgc", heap_number));
4552 leave_spin_lock (&gc_lock);
4553 background_gc_wait_lh (awr_get_loh_seg);
4554 enter_spin_lock (&gc_lock);
4555 dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Egc", heap_number));
4557 assert ((current_c_gc_state == c_gc_state_free) ||
4558 (current_c_gc_state == c_gc_state_marking));
4559 #endif //BACKGROUND_GC
4561 heap_segment* res = get_segment_for_loh (size
4562 #ifdef MULTIPLE_HEAPS
4564 #endif //MULTIPLE_HEAPS
4567 dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc", heap_number));
4568 leave_spin_lock (&gc_heap::gc_lock);
4569 enter_spin_lock (&more_space_lock);
4570 dprintf (SPINLOCK_LOG, ("[%d]Seg: A Emsl", heap_number));
4571 add_saved_spinlock_info (me_acquire, mt_get_large_seg);
4573 #ifdef BACKGROUND_GC
4574 wait_for_background_planning (awr_get_loh_seg);
4575 #endif //BACKGROUND_GC
4580 BOOL gc_heap::unprotect_segment (heap_segment* seg)
4582 BYTE* start = align_lower_page (heap_segment_mem (seg));
4583 ptrdiff_t region_size = heap_segment_allocated (seg) - start;
4585 if (region_size != 0 )
4587 DWORD old_protection;
4588 dprintf (3, ("unprotecting segment %Ix:", (size_t)seg));
4590 BOOL status = VirtualProtect (start, region_size,
4591 PAGE_READWRITE, &old_protection);
4598 #ifdef MULTIPLE_HEAPS
4601 #pragma warning(disable:4035)
4602 static SSIZE_T get_cycle_count()
4606 #pragma warning(default:4035)
4607 #elif defined(__GNUC__)
4608 static SSIZE_T get_cycle_count()
4612 __asm__ __volatile__
4613 ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4617 #error Unknown compiler
4619 #elif defined(_TARGET_AMD64_)
4621 extern "C" unsigned __int64 __rdtsc();
4622 #pragma intrinsic(__rdtsc)
4623 static SSIZE_T get_cycle_count()
4625 return (SSIZE_T)__rdtsc();
4627 #elif defined(__clang__)
4628 static SSIZE_T get_cycle_count()
4632 __asm__ __volatile__
4633 ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4634 return (cyclesHi << 32) | cycles;
4637 extern "C" SSIZE_T get_cycle_count(void);
4639 #elif defined(_TARGET_ARM_)
4640 static SSIZE_T get_cycle_count()
4642 // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
4643 // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
4644 // all buffer access times being reported as equal in access_time().
4647 #elif defined(_TARGET_ARM64_)
4648 static SSIZE_T get_cycle_count()
4650 // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
4651 // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
4652 // all buffer access times being reported as equal in access_time().
4656 #error NYI platform: get_cycle_count
4657 #endif //_TARGET_X86_
4659 // The purpose of this whole class is to guess the right heap to use for a given thread.
4661 DWORD (WINAPI *GetCurrentProcessorNumber_t)(VOID);
4666 static BYTE* sniff_buffer;
4667 static unsigned n_sniff_buffers;
4668 static unsigned cur_sniff_index;
4670 static BYTE proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
4671 static BYTE heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
4672 static BYTE heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
4673 static BYTE heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
4674 static BYTE heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
4675 static BYTE numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
4677 static int access_time(BYTE *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers)
4679 SSIZE_T start_cycles = get_cycle_count();
4680 BYTE sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE];
4681 assert (sniff == 0);
4682 SSIZE_T elapsed_cycles = get_cycle_count() - start_cycles;
4683 // add sniff here just to defeat the optimizer
4684 elapsed_cycles += sniff;
4685 return (int) elapsed_cycles;
4689 GetCurrentProcessorNumber_t GCGetCurrentProcessorNumber;
4691 //check if the new APIs are supported.
4693 BOOL api_supported()
4695 #ifdef FEATURE_REDHAWK
4696 BOOL fSupported = PalHasCapability(GetCurrentProcessorNumberCapability);
4697 GCGetCurrentProcessorNumber = fSupported ? PalGetCurrentProcessorNumber : NULL;
4699 #elif !defined(FEATURE_PAL)
4700 // on all platforms we support this API exists.
4701 GCGetCurrentProcessorNumber = (GetCurrentProcessorNumber_t)&GetCurrentProcessorNumber;
4705 #endif //FEATURE_REDHAWK
4709 static BOOL init(int n_heaps)
4711 assert (sniff_buffer == NULL && n_sniff_buffers == 0);
4712 if (!api_supported())
4714 n_sniff_buffers = n_heaps*2+1;
4715 size_t sniff_buf_size = 0;
4716 #ifdef FEATURE_REDHAWK
4717 size_t n_cache_lines = 1 + n_heaps*n_sniff_buffers + 1;
4718 sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE;
4720 S_SIZE_T safe_sniff_buf_size = S_SIZE_T(1 + n_heaps*n_sniff_buffers + 1);
4721 safe_sniff_buf_size *= HS_CACHE_LINE_SIZE;
4722 if (safe_sniff_buf_size.IsOverflow())
4726 sniff_buf_size = safe_sniff_buf_size.Value();
4727 #endif //FEATURE_REDHAWK
4728 sniff_buffer = new (nothrow) BYTE[sniff_buf_size];
4729 if (sniff_buffer == 0)
4731 memset(sniff_buffer, 0, sniff_buf_size*sizeof(BYTE));
4734 //can not enable gc numa aware, force all heaps to be in
4735 //one numa node by filling the array with all 0s
4736 if (!NumaNodeInfo::CanEnableGCNumaAware())
4737 memset(heap_no_to_numa_node, 0, MAX_SUPPORTED_CPUS);
4742 static void init_cpu_mapping(gc_heap *heap, int heap_number)
4744 if (GCGetCurrentProcessorNumber != 0)
4746 DWORD proc_no = GCGetCurrentProcessorNumber() % gc_heap::n_heaps;
4747 // We can safely cast heap_number to a BYTE 'cause GetCurrentProcessCpuCount
4748 // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most
4749 // MAX_SUPPORTED_CPUS GC threads.
4750 proc_no_to_heap_no[proc_no] = (BYTE)heap_number;
4754 static void mark_heap(int heap_number)
4756 if (GCGetCurrentProcessorNumber != 0)
4759 for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++)
4760 sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
4763 static int select_heap(alloc_context* acontext, int hint)
4765 if (GCGetCurrentProcessorNumber)
4766 return proc_no_to_heap_no[GCGetCurrentProcessorNumber() % gc_heap::n_heaps];
4768 unsigned sniff_index = FastInterlockIncrement((LONG *)&cur_sniff_index);
4769 sniff_index %= n_sniff_buffers;
4772 int best_access_time = 1000*1000*1000;
4773 int second_best_access_time = best_access_time;
4775 BYTE *l_sniff_buffer = sniff_buffer;
4776 unsigned l_n_sniff_buffers = n_sniff_buffers;
4777 for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++)
4779 int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers);
4780 if (this_access_time < best_access_time)
4782 second_best_access_time = best_access_time;
4783 best_access_time = this_access_time;
4784 best_heap = heap_number;
4786 else if (this_access_time < second_best_access_time)
4788 second_best_access_time = this_access_time;
4792 if (best_access_time*2 < second_best_access_time)
4794 sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
4796 dprintf (3, ("select_heap yields crisp %d for context %p\n", best_heap, (void *)acontext));
4800 dprintf (3, ("select_heap yields vague %d for context %p\n", best_heap, (void *)acontext ));
4806 static BOOL can_find_heap_fast()
4808 if (GCGetCurrentProcessorNumber)
4814 static BYTE find_proc_no_from_heap_no(int heap_number)
4816 return heap_no_to_proc_no[heap_number];
4819 static void set_proc_no_for_heap(int heap_number, BYTE proc_no)
4821 heap_no_to_proc_no[heap_number] = proc_no;
4824 static BYTE find_numa_node_from_heap_no(int heap_number)
4826 return heap_no_to_numa_node[heap_number];
4829 static void set_numa_node_for_heap(int heap_number, BYTE numa_node)
4831 heap_no_to_numa_node[heap_number] = numa_node;
4834 static BYTE find_cpu_group_from_heap_no(int heap_number)
4836 return heap_no_to_cpu_group[heap_number];
4839 static void set_cpu_group_for_heap(int heap_number, BYTE group_number)
4841 heap_no_to_cpu_group[heap_number] = group_number;
4844 static BYTE find_group_proc_from_heap_no(int heap_number)
4846 return heap_no_to_group_proc[heap_number];
4849 static void set_group_proc_for_heap(int heap_number, BYTE group_proc)
4851 heap_no_to_group_proc[heap_number] = group_proc;
4854 static void init_numa_node_to_heap_map(int nheaps)
4855 { // called right after GCHeap::Init() for each heap is finished
4856 // when numa is not enabled, heap_no_to_numa_node[] are all filled
4857 // with 0s during initialization, and will be treated as one node
4858 numa_node_to_heap_map[0] = 0;
4861 for (int i=1; i < nheaps; i++)
4863 if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1])
4864 numa_node_to_heap_map[node_index++] = (BYTE)i;
4866 numa_node_to_heap_map[node_index] = (BYTE)nheaps; //mark the end with nheaps
4869 static void get_heap_range_for_heap(int hn, int* start, int* end)
4870 { // 1-tier/no numa case: heap_no_to_numa_node[] all zeros,
4871 // and treated as in one node. thus: start=0, end=n_heaps
4872 BYTE numa_node = heap_no_to_numa_node[hn];
4873 *start = (int)numa_node_to_heap_map[numa_node];
4874 *end = (int)(numa_node_to_heap_map[numa_node+1]);
4877 BYTE* heap_select::sniff_buffer;
4878 unsigned heap_select::n_sniff_buffers;
4879 unsigned heap_select::cur_sniff_index;
4880 GetCurrentProcessorNumber_t heap_select::GCGetCurrentProcessorNumber;
4881 BYTE heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
4882 BYTE heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
4883 BYTE heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
4884 BYTE heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
4885 BYTE heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
4886 BYTE heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
4888 BOOL gc_heap::create_thread_support (unsigned number_of_heaps)
4891 gc_start_event.CreateOSManualEvent (FALSE);
4892 if (!gc_start_event.IsValid())
4896 ee_suspend_event.CreateOSAutoEvent (FALSE);
4897 if (!ee_suspend_event.IsValid())
4901 if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc))
4912 destroy_thread_support();
4918 void gc_heap::destroy_thread_support ()
4920 if (ee_suspend_event.IsValid())
4922 ee_suspend_event.CloseEvent();
4924 if (gc_start_event.IsValid())
4926 gc_start_event.CloseEvent();
4930 void set_thread_group_affinity_for_heap(HANDLE gc_thread, int heap_number)
4932 #if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORECLR)
4936 CPUGroupInfo::GetGroupForProcessor((WORD)heap_number, &gn, &gpn);
4938 ga.Reserved[0] = 0; // reserve must be filled with zero
4939 ga.Reserved[1] = 0; // otherwise call may fail
4943 for (DWORD_PTR mask = 1; mask !=0; mask <<=1)
4945 if (bit_number == gpn)
4947 dprintf(3, ("using processor group %d, mask %x%Ix for heap %d\n", gn, mask, heap_number));
4949 CPUGroupInfo::SetThreadGroupAffinity(gc_thread, &ga, NULL);
4950 heap_select::set_cpu_group_for_heap(heap_number, (BYTE)gn);
4951 heap_select::set_group_proc_for_heap(heap_number, (BYTE)gpn);
4952 if (NumaNodeInfo::CanEnableGCNumaAware())
4954 PROCESSOR_NUMBER proc_no;
4956 proc_no.Number = (BYTE)gpn;
4957 proc_no.Reserved = 0;
4960 if (NumaNodeInfo::GetNumaProcessorNodeEx(&proc_no, &node_no))
4961 heap_select::set_numa_node_for_heap(heap_number, (BYTE)node_no);
4964 { // no numa setting, each cpu group is treated as a node
4965 heap_select::set_numa_node_for_heap(heap_number, (BYTE)gn);
4974 void set_thread_affinity_mask_for_heap(HANDLE gc_thread, int heap_number)
4976 #if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORECLR)
4977 DWORD_PTR pmask, smask;
4979 if (GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
4983 BYTE proc_number = 0;
4984 for (DWORD_PTR mask = 1; mask != 0; mask <<= 1)
4986 if ((mask & pmask) != 0)
4988 if (bit_number == heap_number)
4990 dprintf (3, ("Using processor mask 0x%Ix for heap %d\n", mask, heap_number));
4991 SetThreadAffinityMask(gc_thread, mask);
4992 heap_select::set_proc_no_for_heap(heap_number, proc_number);
4993 if (NumaNodeInfo::CanEnableGCNumaAware())
4994 { // have the processor number, find the numa node
4995 #if !defined(FEATURE_CORESYSTEM)
4997 if (NumaNodeInfo::GetNumaProcessorNode(proc_number, &node_no))
4998 heap_select::set_numa_node_for_heap(heap_number, node_no);
5002 CPUGroupInfo::GetGroupForProcessor((WORD)heap_number, &gn, &gpn);
5004 PROCESSOR_NUMBER proc_no;
5006 proc_no.Number = (BYTE)gpn;
5007 proc_no.Reserved = 0;
5008 if (NumaNodeInfo::GetNumaProcessorNodeEx(&proc_no, &node_no))
5010 heap_select::set_numa_node_for_heap(heap_number, (BYTE)node_no);
5024 HANDLE gc_heap::create_gc_thread ()
5027 dprintf (3, ("Creating gc thread\n"));
5029 #ifdef FEATURE_REDHAWK
5030 HANDLE gc_thread = CreateThread(0, 4096, gc_thread_stub,this, CREATE_SUSPENDED, &thread_id);
5031 #else //FEATURE_REDHAWK
5032 HANDLE gc_thread = Thread::CreateUtilityThread(Thread::StackSize_Medium, gc_thread_stub, this, CREATE_SUSPENDED, &thread_id);
5033 #endif //FEATURE_REDHAWK
5039 SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST );
5041 //We are about to set affinity for GC threads, it is a good place to setup NUMA and
5042 //CPU groups, because the process mask, processor number, group number are all
5043 //readyly available.
5044 if (CPUGroupInfo::CanEnableGCCPUGroups())
5045 set_thread_group_affinity_for_heap(gc_thread, heap_number);
5047 set_thread_affinity_mask_for_heap(gc_thread, heap_number);
5049 ResumeThread(gc_thread);
5054 #pragma warning(disable:4715) //IA64 xcompiler recognizes that without the 'break;' the while(1) will never end and therefore not return a value for that code path
5056 DWORD gc_heap::gc_thread_function ()
5058 assert (gc_done_event.IsValid());
5059 assert (gc_start_event.IsValid());
5060 dprintf (3, ("gc thread started"));
5062 heap_select::init_cpu_mapping(this, heap_number);
5066 assert (!gc_t_join.joined());
5068 if (heap_number == 0)
5070 gc_heap::ee_suspend_event.Wait(INFINITE, FALSE);
5072 BEGIN_TIMING(suspend_ee_during_log);
5073 GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC);
5074 END_TIMING(suspend_ee_during_log);
5076 settings.init_mechanisms();
5077 dprintf (3, ("%d gc thread waiting...", heap_number));
5078 gc_start_event.Set();
5082 gc_start_event.Wait(INFINITE, FALSE);
5083 dprintf (3, ("%d gc thread waiting... Done", heap_number));
5086 garbage_collect (GCHeap::GcCondemnedGeneration);
5088 if (heap_number == 0)
5090 if (!settings.concurrent)
5095 #ifdef BACKGROUND_GC
5096 recover_bgc_settings();
5097 #endif //BACKGROUND_GC
5099 #ifdef MULTIPLE_HEAPS
5100 for (int i = 0; i < gc_heap::n_heaps; i++)
5102 gc_heap* hp = gc_heap::g_heaps[i];
5103 hp->add_saved_spinlock_info (me_release, mt_block_gc);
5104 dprintf (SPINLOCK_LOG, ("[%d]GC Lmsl", i));
5105 leave_spin_lock(&hp->more_space_lock);
5107 #endif //MULTIPLE_HEAPS
5109 gc_heap::gc_started = FALSE;
5111 BEGIN_TIMING(restart_ee_during_log);
5112 GCToEEInterface::RestartEE(TRUE);
5113 END_TIMING(restart_ee_during_log);
5114 process_sync_log_stats();
5116 dprintf (SPINLOCK_LOG, ("GC Lgc"));
5117 leave_spin_lock (&gc_heap::gc_lock);
5119 gc_heap::internal_gc_done = true;
5125 int spin_count = 32 * (g_SystemInfo.dwNumberOfProcessors - 1);
5127 // wait until RestartEE has progressed to a stage where we can restart user threads
5128 while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads())
5130 spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads()));
5138 #pragma warning(default:4715) //IA64 xcompiler recognizes that without the 'break;' the while(1) will never end and therefore not return a value for that code path
5141 #endif //MULTIPLE_HEAPS
5143 void* virtual_alloc_commit_for_heap(void* addr, size_t size, DWORD type,
5144 DWORD prot, int h_number)
5146 #if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
5147 // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to
5148 // a host. This will need to be added later.
5149 if (!CLRMemoryHosted())
5151 if (NumaNodeInfo::CanEnableGCNumaAware())
5153 DWORD numa_node = heap_select::find_numa_node_from_heap_no(h_number);
5154 void * ret = NumaNodeInfo::VirtualAllocExNuma(GetCurrentProcess(), addr, size,
5155 type, prot, numa_node);
5162 //numa aware not enabled, or call failed --> fallback to VirtualAlloc()
5163 return VirtualAlloc(addr, size, type, prot);
5166 #ifndef SEG_MAPPING_TABLE
5168 heap_segment* gc_heap::segment_of (BYTE* add, ptrdiff_t& delta, BOOL verify_p)
5171 heap_segment* hs = 0;
5172 heap_segment* hs1 = 0;
5173 if (!((add >= g_lowest_address) && (add < g_highest_address)))
5178 //repeat in case there is a concurrent insertion in the table.
5183 seg_table->lookup (sadd);
5184 hs1 = (heap_segment*)sadd;
5185 } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1));
5190 (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta)))))
5194 #endif //SEG_MAPPING_TABLE
5202 // If we want to save space we can have a pool of plug_and_gap's instead of
5203 // always having 2 allocated for each pinned plug.
5204 gap_reloc_pair saved_pre_plug;
5205 // If we decide to not compact, we need to restore the original values.
5206 gap_reloc_pair saved_pre_plug_reloc;
5208 gap_reloc_pair saved_post_plug;
5210 // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke
5211 // frames. Also if it's an artificially pinned plug created by us, it can certainly
5213 // We know these cases will be rare so we can optimize this to be only allocated on decommand.
5214 gap_reloc_pair saved_post_plug_reloc;
5216 // We need to calculate this after we are done with plan phase and before compact
5217 // phase because compact phase will change the bricks so relocate_address will no
5219 BYTE* saved_pre_plug_info_reloc_start;
5221 // We need to save this because we will have no way to calculate it, unlike the
5222 // pre plug info start which is right before this plug.
5223 BYTE* saved_post_plug_info_start;
5226 BYTE* allocation_context_start_region;
5227 #endif //SHORT_PLUGS
5229 // How the bits in these bytes are organized:
5231 // bit to indicate whether it's a short obj | 3 bits for refs in this short obj | 2 unused bits | bit to indicate if it's collectible | last bit
5232 // last bit indicates if there's pre or post info associated with this plug. If it's not set all other bits will be 0.
5237 // We are seeing this is getting corrupted for a PP with a NP after.
5238 // Save it when we first set it and make sure it doesn't change.
5239 gap_reloc_pair saved_post_plug_debug;
5242 size_t get_max_short_bits()
5244 return (sizeof (gap_reloc_pair) / sizeof (BYTE*));
5248 size_t get_pre_short_start_bit ()
5250 return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (BYTE*)));
5255 return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1)));
5258 void set_pre_short()
5260 saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1));
5263 void set_pre_short_bit (size_t bit)
5265 saved_pre_p |= 1 << (get_pre_short_start_bit() + bit);
5268 BOOL pre_short_bit_p (size_t bit)
5270 return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit)));
5273 #ifdef COLLECTIBLE_CLASS
5274 void set_pre_short_collectible()
5279 BOOL pre_short_collectible_p()
5281 return (saved_pre_p & 2);
5283 #endif //COLLECTIBLE_CLASS
5286 size_t get_post_short_start_bit ()
5288 return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (BYTE*)));
5293 return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1)));
5296 void set_post_short()
5298 saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1));
5301 void set_post_short_bit (size_t bit)
5303 saved_post_p |= 1 << (get_post_short_start_bit() + bit);
5306 BOOL post_short_bit_p (size_t bit)
5308 return (saved_post_p & (1 << (get_post_short_start_bit() + bit)));
5311 #ifdef COLLECTIBLE_CLASS
5312 void set_post_short_collectible()
5317 BOOL post_short_collectible_p()
5319 return (saved_post_p & 2);
5321 #endif //COLLECTIBLE_CLASS
5323 BYTE* get_plug_address() { return first; }
5325 BOOL has_pre_plug_info() { return saved_pre_p; }
5326 BOOL has_post_plug_info() { return saved_post_p; }
5328 gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; }
5329 gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; }
5330 void set_pre_plug_info_reloc_start (BYTE* reloc) { saved_pre_plug_info_reloc_start = reloc; }
5331 BYTE* get_post_plug_info_start() { return saved_post_plug_info_start; }
5333 // We need to temporarily recover the shortened plugs for compact phase so we can
5334 // copy over the whole plug and their related info (mark bits/cards). But we will
5335 // need to set the artificial gap back so compact phase can keep reading the plug info.
5336 // We also need to recover the saved info because we'll need to recover it later.
5338 // So we would call swap_p*_plug_and_saved once to recover the object info; then call
5339 // it again to recover the artifical gap.
5340 void swap_pre_plug_and_saved()
5342 gap_reloc_pair temp;
5343 memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5344 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5345 saved_pre_plug_reloc = temp;
5348 void swap_post_plug_and_saved()
5350 gap_reloc_pair temp;
5351 memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5352 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5353 saved_post_plug_reloc = temp;
5356 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
5357 void swap_pre_plug_and_saved_for_profiler()
5359 gap_reloc_pair temp;
5360 memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5361 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5362 saved_pre_plug = temp;
5365 void swap_post_plug_and_saved_for_profiler()
5367 gap_reloc_pair temp;
5368 memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5369 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5370 saved_post_plug = temp;
5372 #endif //GC_PROFILING || //FEATURE_EVENT_TRACE
5374 // We should think about whether it's really necessary to have to copy back the pre plug
5375 // info since it was already copied during compacting plugs. But if a plug doesn't move
5376 // by < 3 ptr size, it means we'd have to recover pre plug info.
5377 void recover_plug_info()
5381 if (gc_heap::settings.compaction)
5383 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix",
5385 &saved_pre_plug_reloc,
5386 saved_pre_plug_info_reloc_start));
5387 memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5391 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix",
5394 (first - sizeof (plug_and_gap))));
5395 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5401 if (gc_heap::settings.compaction)
5403 dprintf (3, ("%Ix: REC Post: %Ix-%Ix",
5405 &saved_post_plug_reloc,
5406 saved_post_plug_info_start));
5407 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5411 dprintf (3, ("%Ix: REC Post: %Ix-%Ix",
5414 saved_post_plug_info_start));
5415 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5422 void gc_mechanisms::init_mechanisms()
5424 condemned_generation = 0;
5425 promotion = FALSE;//TRUE;
5427 #ifdef FEATURE_LOH_COMPACTION
5428 loh_compaction = gc_heap::should_compact_loh();
5430 loh_compaction = FALSE;
5431 #endif //FEATURE_LOH_COMPACTION
5432 heap_expansion = FALSE;
5435 found_finalizers = FALSE;
5436 #ifdef BACKGROUND_GC
5437 background_p = recursive_gc_sync::background_running_p() != FALSE;
5438 allocations_allowed = TRUE;
5439 #endif //BACKGROUND_GC
5442 entry_memory_load = 0;
5446 stress_induced = FALSE;
5447 #endif // STRESS_HEAP
5450 void gc_mechanisms::first_init()
5453 gen0_reduction_count = 0;
5454 should_lock_elevation = FALSE;
5455 elevation_locked_count = 0;
5456 reason = reason_empty;
5457 #ifdef BACKGROUND_GC
5458 pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch;
5460 int debug_pause_mode = g_pConfig->GetGCLatencyMode();
5461 if (debug_pause_mode >= 0)
5463 assert (debug_pause_mode <= pause_sustained_low_latency);
5464 pause_mode = (gc_pause_mode)debug_pause_mode;
5467 #else //BACKGROUND_GC
5468 pause_mode = pause_batch;
5469 #endif //BACKGROUND_GC
5474 void gc_mechanisms::record (gc_history_global* history)
5476 #ifdef MULTIPLE_HEAPS
5477 history->num_heaps = gc_heap::n_heaps;
5479 history->num_heaps = 1;
5480 #endif //MULTIPLE_HEAPS
5482 history->condemned_generation = condemned_generation;
5483 history->gen0_reduction_count = gen0_reduction_count;
5484 history->reason = reason;
5485 history->global_mechanims_p = 0;
5487 // start setting the boolean values.
5489 history->set_mechanism_p (global_concurrent);
5492 history->set_mechanism_p (global_compaction);
5495 history->set_mechanism_p (global_promotion);
5498 history->set_mechanism_p (global_demotion);
5501 history->set_mechanism_p (global_card_bundles);
5504 /**********************************
5505 called at the beginning of GC to fix the allocated size to
5506 what is really allocated, or to turn the free area into an unused object
5507 It needs to be called after all of the other allocation contexts have been
5508 fixed since it relies on alloc_allocated.
5509 ********************************/
5511 //for_gc_p indicates that the work is being done for GC,
5512 //as opposed to concurrent heap verification
5513 void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p)
5515 assert (alloc_allocated);
5516 alloc_context* acontext = generation_alloc_context (youngest_generation);
5517 dprintf (3, ("generation 0 alloc context: ptr: %Ix, limit %Ix",
5518 (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5519 fix_allocation_context (acontext, for_gc_p, get_alignment_constant (TRUE));
5522 acontext->alloc_ptr = alloc_allocated;
5523 acontext->alloc_limit = acontext->alloc_ptr;
5525 heap_segment_allocated (ephemeral_heap_segment) =
5529 void gc_heap::fix_large_allocation_area (BOOL for_gc_p)
5532 alloc_context* acontext =
5534 generation_alloc_context (large_object_generation);
5535 assert (acontext->alloc_ptr == 0);
5536 assert (acontext->alloc_limit == 0);
5538 dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix",
5539 (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5540 fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE));
5543 acontext->alloc_ptr = 0;
5544 acontext->alloc_limit = acontext->alloc_ptr;
5549 //for_gc_p indicates that the work is being done for GC,
5550 //as opposed to concurrent heap verification
5551 void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
5554 dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
5556 (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5558 if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) ||
5561 BYTE* point = acontext->alloc_ptr;
5564 size_t size = (acontext->alloc_limit - acontext->alloc_ptr);
5565 // the allocation area was from the free list
5566 // it was shortened by Align (min_obj_size) to make room for
5567 // at least the shortest unused object
5568 size += Align (min_obj_size, align_const);
5569 assert ((size >= Align (min_obj_size)));
5571 dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point,
5572 (size_t)point + size ));
5573 make_unused_array (point, size);
5577 generation_free_obj_space (generation_of (0)) += size;
5578 alloc_contexts_used ++;
5584 alloc_allocated = acontext->alloc_ptr;
5585 assert (heap_segment_allocated (ephemeral_heap_segment) <=
5586 heap_segment_committed (ephemeral_heap_segment));
5587 alloc_contexts_used ++;
5593 acontext->alloc_ptr = 0;
5594 acontext->alloc_limit = acontext->alloc_ptr;
5598 //used by the heap verification for concurrent gc.
5599 //it nulls out the words set by fix_allocation_context for heap_verification
5600 void repair_allocation (alloc_context* acontext)
5602 BYTE* point = acontext->alloc_ptr;
5606 dprintf (3, ("Clearing [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5607 (size_t)acontext->alloc_limit+Align(min_obj_size)));
5608 memclr (acontext->alloc_ptr - plug_skew,
5609 (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size));
5613 void void_allocation (alloc_context* acontext)
5615 BYTE* point = acontext->alloc_ptr;
5619 dprintf (3, ("Void [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5620 (size_t)acontext->alloc_limit+Align(min_obj_size)));
5621 acontext->alloc_ptr = 0;
5622 acontext->alloc_limit = acontext->alloc_ptr;
5626 void gc_heap::fix_allocation_contexts (BOOL for_gc_p)
5628 CNameSpace::GcFixAllocContexts ((void*)for_gc_p, __this);
5629 fix_youngest_allocation_area (for_gc_p);
5630 fix_large_allocation_area (for_gc_p);
5633 void gc_heap::repair_allocation_contexts (BOOL repair_p)
5635 CNameSpace::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation);
5637 alloc_context* acontext = generation_alloc_context (youngest_generation);
5639 repair_allocation (acontext);
5641 void_allocation (acontext);
5644 void gc_heap::fix_older_allocation_area (generation* older_gen)
5646 heap_segment* older_gen_seg = generation_allocation_segment (older_gen);
5647 if (generation_allocation_limit (older_gen) !=
5648 heap_segment_plan_allocated (older_gen_seg))
5650 BYTE* point = generation_allocation_pointer (older_gen);
5652 size_t size = (generation_allocation_limit (older_gen) -
5653 generation_allocation_pointer (older_gen));
5656 assert ((size >= Align (min_obj_size)));
5657 dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, (size_t)point+size));
5658 make_unused_array (point, size);
5663 assert (older_gen_seg != ephemeral_heap_segment);
5664 heap_segment_plan_allocated (older_gen_seg) =
5665 generation_allocation_pointer (older_gen);
5666 generation_allocation_limit (older_gen) =
5667 generation_allocation_pointer (older_gen);
5671 void gc_heap::set_allocation_heap_segment (generation* gen)
5673 BYTE* p = generation_allocation_start (gen);
5675 heap_segment* seg = generation_allocation_segment (gen);
5676 if (in_range_for_segment (p, seg))
5679 // try ephemeral heap segment in case of heap expansion
5680 seg = ephemeral_heap_segment;
5681 if (!in_range_for_segment (p, seg))
5683 seg = heap_segment_rw (generation_start_segment (gen));
5685 PREFIX_ASSUME(seg != NULL);
5687 while (!in_range_for_segment (p, seg))
5689 seg = heap_segment_next_rw (seg);
5690 PREFIX_ASSUME(seg != NULL);
5694 generation_allocation_segment (gen) = seg;
5697 void gc_heap::reset_allocation_pointers (generation* gen, BYTE* start)
5700 assert (Align ((size_t)start) == (size_t)start);
5701 generation_allocation_start (gen) = start;
5702 generation_allocation_pointer (gen) = 0;//start + Align (min_obj_size);
5703 generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen);
5704 set_allocation_heap_segment (gen);
5707 #ifdef BACKGROUND_GC
5708 //TODO BACKGROUND_GC this is for test only
5710 gc_heap::disallow_new_allocation (int gen_number)
5712 settings.allocations_allowed = FALSE;
5715 gc_heap::allow_new_allocation (int gen_number)
5717 settings.allocations_allowed = TRUE;
5720 #endif //BACKGROUND_GC
5722 bool gc_heap::new_allocation_allowed (int gen_number)
5724 #ifdef BACKGROUND_GC
5725 //TODO BACKGROUND_GC this is for test only
5726 if (!settings.allocations_allowed)
5728 dprintf (2, ("new allocation not allowed"));
5731 #endif //BACKGROUND_GC
5733 if (dd_new_allocation (dynamic_data_of (gen_number)) < 0)
5735 if (gen_number != 0)
5737 // For LOH we will give it more budget before we try a GC.
5738 if (settings.concurrent)
5740 dynamic_data* dd2 = dynamic_data_of (max_generation + 1 );
5742 if (dd_new_allocation (dd2) <= (SSIZE_T)(-2 * dd_desired_allocation (dd2)))
5750 #ifndef MULTIPLE_HEAPS
5751 else if ((gen_number == 0))
5753 dprintf (3, ("evaluating allocation rate"));
5754 dynamic_data* dd0 = dynamic_data_of (0);
5755 if ((allocation_running_amount - dd_new_allocation (dd0)) >
5756 dd_min_gc_size (dd0))
5758 DWORD ctime = GetTickCount();
5759 if ((ctime - allocation_running_time) > 1000)
5761 dprintf (2, (">1s since last gen0 gc"));
5766 allocation_running_amount = dd_new_allocation (dd0);
5770 #endif //MULTIPLE_HEAPS
5775 ptrdiff_t gc_heap::get_desired_allocation (int gen_number)
5777 return dd_desired_allocation (dynamic_data_of (gen_number));
5781 ptrdiff_t gc_heap::get_new_allocation (int gen_number)
5783 return dd_new_allocation (dynamic_data_of (gen_number));
5786 //return the amount allocated so far in gen_number
5788 ptrdiff_t gc_heap::get_allocation (int gen_number)
5790 dynamic_data* dd = dynamic_data_of (gen_number);
5792 return dd_desired_allocation (dd) - dd_new_allocation (dd);
5796 BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len)
5798 size_t new_size = max (init_len, 2*len);
5799 mark* tmp = new (nothrow) (mark [new_size]);
5802 memcpy (tmp, m, len * sizeof (mark));
5810 dprintf (1, ("Failed to allocate %Id bytes for mark stack", (len * sizeof (mark))));
5816 BYTE* pinned_plug (mark* m)
5822 size_t& pinned_len (mark* m)
5828 void set_new_pin_info (mark* m, BYTE* pin_free_space_start)
5830 m->len = pinned_plug (m) - pin_free_space_start;
5832 m->allocation_context_start_region = pin_free_space_start;
5833 #endif //SHORT_PLUGS
5838 BYTE*& pin_allocation_context_start_region (mark* m)
5840 return m->allocation_context_start_region;
5844 void set_padding_in_expand (BYTE* old_loc,
5845 BOOL set_padding_on_saved_p,
5846 mark* pinned_plug_entry)
5848 if (set_padding_on_saved_p)
5850 BYTE* saved_pre_plug_info = (BYTE*)(pinned_plug_entry->get_pre_plug_reloc_info());
5851 BYTE* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap)));
5852 //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix",
5853 // old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
5854 dprintf (1, ("EP: %Ix(%Ix)", old_loc, (BYTE)pinned_plug_entry));
5855 set_plug_padded (plug_start_in_saved);
5859 set_plug_padded (old_loc);
5862 #endif //SHORT_PLUGS
5864 void gc_heap::reset_pinned_queue()
5870 void gc_heap::reset_pinned_queue_bos()
5875 // last_pinned_plug is only for asserting purpose.
5876 void gc_heap::merge_with_last_pinned_plug (BYTE* last_pinned_plug, size_t plug_size)
5878 if (last_pinned_plug)
5880 mark& last_m = mark_stack_array[mark_stack_tos - 1];
5881 assert (last_pinned_plug == last_m.first);
5882 if (last_m.saved_post_p)
5884 last_m.saved_post_p = FALSE;
5885 dprintf (3, ("setting last plug %Ix post to false", last_m.first));
5886 // We need to recover what the gap has overwritten.
5887 memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair));
5889 last_m.len += plug_size;
5890 dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix", last_m.first, last_m.len));
5894 void gc_heap::set_allocator_next_pin (BYTE* alloc_pointer, BYTE*& alloc_limit)
5896 dprintf (3, ("sanp: ptr: %Ix, limit: %Ix", alloc_pointer, alloc_limit));
5897 dprintf (3, ("oldest %Id: %Ix", mark_stack_bos, pinned_plug (oldest_pin())));
5898 if (!(pinned_plug_que_empty_p()))
5900 mark* oldest_entry = oldest_pin();
5901 BYTE* plug = pinned_plug (oldest_entry);
5902 if ((plug >= alloc_pointer) && (plug < alloc_limit))
5904 alloc_limit = pinned_plug (oldest_entry);
5905 dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)",
5906 alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer)));
5911 void gc_heap::set_allocator_next_pin (generation* gen)
5913 dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix", gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen)));
5914 if (!(pinned_plug_que_empty_p()))
5916 mark* oldest_entry = oldest_pin();
5917 BYTE* plug = pinned_plug (oldest_entry);
5918 if ((plug >= generation_allocation_pointer (gen)) &&
5919 (plug < generation_allocation_limit (gen)))
5921 generation_allocation_limit (gen) = pinned_plug (oldest_entry);
5922 dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)",
5924 generation_allocation_pointer (gen), generation_allocation_limit (gen),
5925 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
5928 assert (!((plug < generation_allocation_pointer (gen)) &&
5929 (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
5933 // After we set the info, we increase tos.
5934 void gc_heap::set_pinned_info (BYTE* last_pinned_plug, size_t plug_len, BYTE* alloc_pointer, BYTE*& alloc_limit)
5936 mark& m = mark_stack_array[mark_stack_tos];
5937 assert (m.first == last_pinned_plug);
5941 set_allocator_next_pin (alloc_pointer, alloc_limit);
5944 // After we set the info, we increase tos.
5945 void gc_heap::set_pinned_info (BYTE* last_pinned_plug, size_t plug_len, generation* gen)
5947 mark& m = mark_stack_array[mark_stack_tos];
5948 assert (m.first == last_pinned_plug);
5953 // Why are we checking here? gen is never 0.
5956 set_allocator_next_pin (gen);
5960 size_t gc_heap::deque_pinned_plug ()
5962 dprintf (3, ("dequed: %Id", mark_stack_bos));
5963 size_t m = mark_stack_bos;
5969 mark* gc_heap::pinned_plug_of (size_t bos)
5971 return &mark_stack_array [ bos ];
5975 mark* gc_heap::oldest_pin ()
5977 return pinned_plug_of (mark_stack_bos);
5981 BOOL gc_heap::pinned_plug_que_empty_p ()
5983 return (mark_stack_bos == mark_stack_tos);
5987 mark* gc_heap::before_oldest_pin()
5989 if (mark_stack_bos >= 1)
5990 return pinned_plug_of (mark_stack_bos-1);
5996 BOOL gc_heap::ephemeral_pointer_p (BYTE* o)
5998 return ((o >= ephemeral_low) && (o < ephemeral_high));
6003 int& gc_heap::mark_stack_busy()
6005 return g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
6009 void gc_heap::make_mark_stack (mark* arr)
6011 reset_pinned_queue();
6012 mark_stack_array = arr;
6013 mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6015 mark_stack_busy() = 0;
6019 #ifdef BACKGROUND_GC
6021 size_t& gc_heap::bpromoted_bytes(int thread)
6023 #ifdef MULTIPLE_HEAPS
6024 return g_bpromoted [thread*16];
6025 #else //MULTIPLE_HEAPS
6028 #endif //MULTIPLE_HEAPS
6031 void gc_heap::make_background_mark_stack (BYTE** arr)
6033 background_mark_stack_array = arr;
6034 background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6035 background_mark_stack_tos = arr;
6038 void gc_heap::make_c_mark_list (BYTE** arr)
6041 c_mark_list_index = 0;
6042 c_mark_list_length = 1 + (page_size / MIN_OBJECT_SIZE);
6044 #endif //BACKGROUND_GC
6046 #if defined (_TARGET_AMD64_)
6047 #define brick_size ((size_t)4096)
6049 #define brick_size ((size_t)2048)
6050 #endif //_TARGET_AMD64_
6053 size_t gc_heap::brick_of (BYTE* add)
6055 return (size_t)(add - lowest_address) / brick_size;
6059 BYTE* gc_heap::brick_address (size_t brick)
6061 return lowest_address + (brick_size * brick);
6065 void gc_heap::clear_brick_table (BYTE* from, BYTE* end)
6067 for (size_t i = brick_of (from);i < brick_of (end); i++)
6071 //codes for the brick entries:
6072 //entry == 0 -> not assigned
6073 //entry >0 offset is entry-1
6074 //entry <0 jump back entry bricks
6078 void gc_heap::set_brick (size_t index, ptrdiff_t val)
6084 assert (val < 32767);
6086 brick_table [index] = (short)val+1;
6088 brick_table [index] = (short)val;
6092 int gc_heap::brick_entry (size_t index)
6094 int val = brick_table [index];
6109 BYTE* align_on_brick (BYTE* add)
6111 return (BYTE*)((size_t)(add + brick_size - 1) & ~(brick_size - 1));
6115 BYTE* align_lower_brick (BYTE* add)
6117 return (BYTE*)(((size_t)add) & ~(brick_size - 1));
6120 size_t size_brick_of (BYTE* from, BYTE* end)
6122 assert (((size_t)from & (brick_size-1)) == 0);
6123 assert (((size_t)end & (brick_size-1)) == 0);
6125 return ((end - from) / brick_size) * sizeof (short);
6129 BYTE* gc_heap::card_address (size_t card)
6131 return (BYTE*) (card_size * card);
6135 size_t gc_heap::card_of ( BYTE* object)
6137 return (size_t)(object) / card_size;
6141 size_t gc_heap::card_to_brick (size_t card)
6143 return brick_of (card_address (card));
6147 BYTE* align_on_card (BYTE* add)
6149 return (BYTE*)((size_t)(add + card_size - 1) & ~(card_size - 1 ));
6152 BYTE* align_on_card_word (BYTE* add)
6154 return (BYTE*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1));
6158 BYTE* align_lower_card (BYTE* add)
6160 return (BYTE*)((size_t)add & ~(card_size-1));
6164 void gc_heap::clear_card (size_t card)
6166 card_table [card_word (card)] =
6167 (card_table [card_word (card)] & ~(1 << card_bit (card)));
6168 dprintf (3,("Cleared card %Ix [%Ix, %Ix[", card, (size_t)card_address (card),
6169 (size_t)card_address (card+1)));
6173 void gc_heap::set_card (size_t card)
6175 card_table [card_word (card)] =
6176 (card_table [card_word (card)] | (1 << card_bit (card)));
6180 void gset_card (size_t card)
6182 g_card_table [card_word (card)] |= (1 << card_bit (card));
6186 BOOL gc_heap::card_set_p (size_t card)
6188 return ( card_table [ card_word (card) ] & (1 << card_bit (card)));
6191 // Returns the number of DWORDs in the card table that cover the
6192 // range of addresses [from, end[.
6193 size_t count_card_of (BYTE* from, BYTE* end)
6195 return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1;
6198 // Returns the number of bytes to allocate for a card table
6199 // that covers the range of addresses [from, end[.
6200 size_t size_card_of (BYTE* from, BYTE* end)
6202 return count_card_of (from, end) * sizeof(DWORD);
6207 //The card bundle keeps track of groups of card words
6208 #define card_bundle_word_width ((size_t)32)
6209 //how do we express the fact that 32 bits (card_word_width) is one DWORD?
6210 #define card_bundle_size ((size_t)(OS_PAGE_SIZE/(sizeof (DWORD)*card_bundle_word_width)))
6213 size_t card_bundle_word (size_t cardb)
6215 return cardb / card_bundle_word_width;
6219 DWORD card_bundle_bit (size_t cardb)
6221 return (DWORD)(cardb % card_bundle_word_width);
6224 size_t align_cardw_on_bundle (size_t cardw)
6226 return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 ));
6229 size_t cardw_card_bundle (size_t cardw)
6231 return cardw/card_bundle_size;
6234 size_t card_bundle_cardw (size_t cardb)
6236 return cardb*card_bundle_size;
6239 void gc_heap::card_bundle_clear(size_t cardb)
6241 card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb));
6242 dprintf (3,("Cleared card bundle %Ix [%Ix, %Ix[", cardb, (size_t)card_bundle_cardw (cardb),
6243 (size_t)card_bundle_cardw (cardb+1)));
6244 // printf ("Cleared card bundle %Ix\n", cardb);
6247 void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb)
6249 size_t start_word = card_bundle_word (start_cardb);
6250 size_t end_word = card_bundle_word (end_cardb);
6251 if (start_word < end_word)
6253 //set the partial words
6254 card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb));
6256 if (card_bundle_bit (end_cardb))
6257 card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb));
6259 for (size_t i = start_word+1; i < end_word; i++)
6260 card_bundle_table [i] = ~0u;
6265 card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) &
6266 lowbits (~0u, card_bundle_bit (end_cardb)));
6272 BOOL gc_heap::card_bundle_set_p (size_t cardb)
6274 return ( card_bundle_table [ card_bundle_word (cardb) ] & (1 << card_bundle_bit (cardb)));
6277 size_t size_card_bundle_of (BYTE* from, BYTE* end)
6279 //align from to lower
6280 from = (BYTE*)((size_t)from & ~(card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1));
6282 end = (BYTE*)((size_t)(end + (card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1)) &
6283 ~(card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1));
6285 assert (((size_t)from & ((card_size*card_word_width*card_bundle_size*card_bundle_word_width)-1)) == 0);
6286 assert (((size_t)end & ((card_size*card_word_width*card_bundle_size*card_bundle_word_width)-1)) == 0);
6288 return ((end - from) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (DWORD);
6291 DWORD* translate_card_bundle_table (DWORD* cb)
6293 return (DWORD*)((BYTE*)cb - ((((size_t)g_lowest_address) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (DWORD)));
6296 void gc_heap::enable_card_bundles ()
6298 if (can_use_write_watch() && (!card_bundles_enabled()))
6300 dprintf (3, ("Enabling card bundles"));
6301 //set all of the card bundles
6302 card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
6303 cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
6304 settings.card_bundles = TRUE;
6308 BOOL gc_heap::card_bundles_enabled ()
6310 return settings.card_bundles;
6313 #endif //CARD_BUNDLE
6315 // We don't store seg_mapping_table in card_table_info because there's only always one view.
6316 class card_table_info
6320 BYTE* lowest_address;
6321 BYTE* highest_address;
6325 DWORD* card_bundle_table;
6326 #endif //CARD_BUNDLE
6328 // mark_array is always at the end of the data structure because we
6329 // want to be able to make one commit call for everything before it.
6334 DWORD* next_card_table;
6337 //These are accessors on untranslated cardtable
6339 unsigned& card_table_refcount (DWORD* c_table)
6341 return *(unsigned*)((char*)c_table - sizeof (card_table_info));
6345 BYTE*& card_table_lowest_address (DWORD* c_table)
6347 return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->lowest_address;
6350 DWORD* translate_card_table (DWORD* ct)
6352 return (DWORD*)((BYTE*)ct - size_card_of (0, card_table_lowest_address( ct)));
6356 BYTE*& card_table_highest_address (DWORD* c_table)
6358 return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->highest_address;
6362 short*& card_table_brick_table (DWORD* c_table)
6364 return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->brick_table;
6369 DWORD*& card_table_card_bundle_table (DWORD* c_table)
6371 return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->card_bundle_table;
6373 #endif //CARD_BUNDLE
6376 /* Support for mark_array */
6379 DWORD*& card_table_mark_array (DWORD* c_table)
6381 return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->mark_array;
6384 #if defined (_TARGET_AMD64_)
6385 #define mark_bit_pitch ((size_t)16)
6387 #define mark_bit_pitch ((size_t)8)
6389 #define mark_word_width ((size_t)32)
6390 #define mark_word_size (mark_word_width * mark_bit_pitch)
6393 BYTE* align_on_mark_bit (BYTE* add)
6395 return (BYTE*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1));
6399 BYTE* align_lower_mark_bit (BYTE* add)
6401 return (BYTE*)((size_t)(add) & ~(mark_bit_pitch - 1));
6405 BOOL is_aligned_on_mark_word (BYTE* add)
6407 return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1)));
6411 BYTE* align_on_mark_word (BYTE* add)
6413 return (BYTE*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1));
6417 BYTE* align_lower_mark_word (BYTE* add)
6419 return (BYTE*)((size_t)(add) & ~(mark_word_size - 1));
6423 size_t mark_bit_of (BYTE* add)
6425 return ((size_t)add / mark_bit_pitch);
6429 unsigned int mark_bit_bit (size_t mark_bit)
6431 return (unsigned int)(mark_bit % mark_word_width);
6435 size_t mark_bit_word (size_t mark_bit)
6437 return (mark_bit / mark_word_width);
6441 size_t mark_word_of (BYTE* add)
6443 return ((size_t)add) / mark_word_size;
6446 BYTE* mark_word_address (size_t wd)
6448 return (BYTE*)(wd*mark_word_size);
6451 BYTE* mark_bit_address (size_t mark_bit)
6453 return (BYTE*)(mark_bit*mark_bit_pitch);
6457 size_t mark_bit_bit_of (BYTE* add)
6459 return (((size_t)add / mark_bit_pitch) % mark_word_width);
6463 unsigned int gc_heap::mark_array_marked(BYTE* add)
6465 return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add));
6469 BOOL gc_heap::is_mark_bit_set (BYTE* add)
6471 return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)));
6475 void gc_heap::mark_array_set_marked (BYTE* add)
6477 size_t index = mark_word_of (add);
6478 DWORD val = (1 << mark_bit_bit_of (add));
6479 #ifdef MULTIPLE_HEAPS
6480 InterlockedOr ((LONG*)&(mark_array [index]), val);
6482 mark_array [index] |= val;
6487 void gc_heap::mark_array_clear_marked (BYTE* add)
6489 mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add));
6492 size_t size_mark_array_of (BYTE* from, BYTE* end)
6494 assert (((size_t)from & ((mark_word_size)-1)) == 0);
6495 assert (((size_t)end & ((mark_word_size)-1)) == 0);
6496 return sizeof (DWORD)*(((end - from) / mark_word_size));
6499 //In order to eliminate the lowest_address in the mark array
6500 //computations (mark_word_of, etc) mark_array is offset
6501 // according to the lowest_address.
6502 DWORD* translate_mark_array (DWORD* ma)
6504 return (DWORD*)((BYTE*)ma - size_mark_array_of (0, g_lowest_address));
6507 // from and end must be page aligned addresses.
6508 void gc_heap::clear_mark_array (BYTE* from, BYTE* end, BOOL check_only/*=TRUE*/)
6510 if(!gc_can_use_concurrent)
6513 assert (from == align_on_mark_word (from));
6514 assert (end == align_on_mark_word (end));
6516 #ifdef BACKGROUND_GC
6517 BYTE* current_lowest_address = background_saved_lowest_address;
6518 BYTE* current_highest_address = background_saved_highest_address;
6520 BYTE* current_lowest_address = lowest_address;
6521 BYTE* current_highest_address = highest_address;
6522 #endif //BACKGROUND_GC
6524 //there is a possibility of the addresses to be
6525 //outside of the covered range because of a newly allocated
6526 //large object segment
6527 if ((end <= current_highest_address) && (from >= current_lowest_address))
6529 size_t beg_word = mark_word_of (align_on_mark_word (from));
6530 MAYBE_UNUSED_VAR(beg_word);
6531 //align end word to make sure to cover the address
6532 size_t end_word = mark_word_of (align_on_mark_word (end));
6533 MAYBE_UNUSED_VAR(end_word);
6534 dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)",
6535 (size_t)mark_word_address (beg_word),
6536 (size_t)mark_word_address (end_word),
6537 (size_t)from, (size_t)end,
6538 (check_only ? "check_only" : "clear")));
6542 while (op < mark_word_address (beg_word))
6544 mark_array_clear_marked (op);
6545 op += mark_bit_pitch;
6548 memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (DWORD));
6553 //Beware, it is assumed that the mark array word straddling
6554 //start has been cleared before
6555 //verify that the array is empty.
6556 size_t markw = mark_word_of (align_on_mark_word (from));
6557 size_t markw_end = mark_word_of (align_on_mark_word (end));
6558 while (markw < markw_end)
6560 assert (!(mark_array [markw]));
6563 BYTE* p = mark_word_address (markw_end);
6566 assert (!(mark_array_marked (p)));
6575 //These work on untranslated card tables
6577 DWORD*& card_table_next (DWORD* c_table)
6579 return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->next_card_table;
6582 void own_card_table (DWORD* c_table)
6584 card_table_refcount (c_table) += 1;
6587 void destroy_card_table (DWORD* c_table);
6589 void delete_next_card_table (DWORD* c_table)
6591 DWORD* n_table = card_table_next (c_table);
6594 if (card_table_next (n_table))
6596 delete_next_card_table (n_table);
6598 if (card_table_refcount (n_table) == 0)
6600 destroy_card_table (n_table);
6601 card_table_next (c_table) = 0;
6606 void release_card_table (DWORD* c_table)
6608 assert (card_table_refcount (c_table) >0);
6609 card_table_refcount (c_table) -= 1;
6610 if (card_table_refcount (c_table) == 0)
6612 delete_next_card_table (c_table);
6613 if (card_table_next (c_table) == 0)
6615 destroy_card_table (c_table);
6616 // sever the link from the parent
6617 if (&g_card_table[card_word (gcard_of(g_lowest_address))] == c_table)
6619 DWORD* p_table = &g_card_table[card_word (gcard_of(g_lowest_address))];
6622 while (p_table && (card_table_next (p_table) != c_table))
6623 p_table = card_table_next (p_table);
6624 card_table_next (p_table) = 0;
6630 void destroy_card_table (DWORD* c_table)
6632 // delete (DWORD*)&card_table_refcount(c_table);
6633 VirtualFree (&card_table_refcount(c_table), 0, MEM_RELEASE);
6634 dprintf (2, ("Table Virtual Free : %Ix", (size_t)&card_table_refcount(c_table)));
6637 DWORD* gc_heap::make_card_table (BYTE* start, BYTE* end)
6639 assert (g_lowest_address == start);
6640 assert (g_highest_address == end);
6642 DWORD mem_flags = MEM_RESERVE;
6644 size_t bs = size_brick_of (start, end);
6645 size_t cs = size_card_of (start, end);
6647 size_t ms = (gc_can_use_concurrent ?
6648 size_mark_array_of (start, end) :
6657 if (can_use_write_watch())
6659 mem_flags |= MEM_WRITE_WATCH;
6660 cb = size_card_bundle_of (g_lowest_address, g_highest_address);
6662 #endif //CARD_BUNDLE
6664 #ifdef GROWABLE_SEG_MAPPING_TABLE
6665 size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address);
6666 #else //GROWABLE_SEG_MAPPING_TABLE
6668 #endif //GROWABLE_SEG_MAPPING_TABLE
6670 // it is impossible for alloc_size to overflow due bounds on each of
6672 size_t alloc_size = sizeof (BYTE)*(bs + cs + cb + ms + st + sizeof (card_table_info));
6673 size_t alloc_size_aligned = Align (alloc_size, g_SystemInfo.dwAllocationGranularity-1);
6675 DWORD* ct = (DWORD*)VirtualAlloc (0, alloc_size_aligned,
6676 mem_flags, PAGE_READWRITE);
6681 dprintf (2, ("init - table alloc for %Id bytes: [%Ix, %Ix[",
6682 alloc_size, (size_t)ct, (size_t)((BYTE*)ct+alloc_size)));
6684 // mark array will be committed separately (per segment).
6685 size_t commit_size = alloc_size - ms;
6687 if (!VirtualAlloc ((BYTE*)ct, commit_size, MEM_COMMIT, PAGE_READWRITE))
6689 dprintf (2, ("Table commit failed: %d", GetLastError()));
6690 VirtualFree ((BYTE*)ct, 0, MEM_RELEASE);
6694 // initialize the ref count
6695 ct = (DWORD*)((BYTE*)ct+sizeof (card_table_info));
6696 card_table_refcount (ct) = 0;
6697 card_table_lowest_address (ct) = start;
6698 card_table_highest_address (ct) = end;
6699 card_table_brick_table (ct) = (short*)((BYTE*)ct + cs);
6700 card_table_next (ct) = 0;
6703 card_table_card_bundle_table (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs);
6704 #endif //CARD_BUNDLE
6706 #ifdef GROWABLE_SEG_MAPPING_TABLE
6707 seg_mapping_table = (seg_mapping*)((BYTE*)card_table_brick_table (ct) + bs + cb);
6708 seg_mapping_table = (seg_mapping*)((BYTE*)seg_mapping_table -
6709 size_seg_mapping_table_of (0, (align_lower_segment (g_lowest_address))));
6710 #endif //GROWABLE_SEG_MAPPING_TABLE
6713 if (gc_can_use_concurrent)
6714 card_table_mark_array (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs + cb + st);
6716 card_table_mark_array (ct) = NULL;
6719 return translate_card_table(ct);
6722 void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p)
6724 #ifdef MULTIPLE_HEAPS
6725 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
6727 gc_heap* hp = gc_heap::g_heaps [hn];
6728 hp->fgm_result.set_fgm (f, s, loh_p);
6730 #else //MULTIPLE_HEAPS
6731 fgm_result.set_fgm (f, s, loh_p);
6732 #endif //MULTIPLE_HEAPS
6735 //returns 0 for success, -1 otherwise
6736 // We are doing all the decommitting here because we want to make sure we have
6737 // enough memory to do so - if we do this during copy_brick_card_table and
6738 // and fail to decommit it would make the failure case very complicated to
6739 // handle. This way we can waste some decommit if we call this multiple
6740 // times before the next FGC but it's easier to handle the failure case.
6741 int gc_heap::grow_brick_card_tables (BYTE* start,
6744 heap_segment* new_seg,
6748 BYTE* la = g_lowest_address;
6749 BYTE* ha = g_highest_address;
6750 BYTE* saved_g_lowest_address = min (start, g_lowest_address);
6751 BYTE* saved_g_highest_address = max (end, g_highest_address);
6752 #ifdef BACKGROUND_GC
6753 // This value is only for logging purpose - it's not necessarily exactly what we
6754 // would commit for mark array but close enough for diagnostics purpose.
6755 size_t logging_ma_commit_size = size_mark_array_of (0, (BYTE*)size);
6756 #endif //BACKGROUND_GC
6758 // See if the address is already covered
6759 if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
6762 //modify the higest address so the span covered
6763 //is twice the previous one.
6765 GetProcessMemoryLoad (&st);
6766 BYTE* top = (BYTE*)0 + Align ((size_t)(st.ullTotalVirtual));
6768 BYTE* highest = max ((saved_g_lowest_address + 2*ps), saved_g_highest_address);
6769 //BYTE* highest = saved_g_highest_address;
6774 if (highest > saved_g_highest_address)
6776 saved_g_highest_address = highest;
6779 dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[",
6780 (size_t)saved_g_lowest_address,
6781 (size_t)saved_g_highest_address));
6783 DWORD mem_flags = MEM_RESERVE;
6784 DWORD* saved_g_card_table = g_card_table;
6788 size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address);
6789 size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address);
6792 size_t ms = (gc_heap::gc_can_use_concurrent ?
6793 size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) :
6802 if (can_use_write_watch())
6804 mem_flags |= MEM_WRITE_WATCH;
6805 cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address);
6807 #endif //CARD_BUNDLE
6809 #ifdef GROWABLE_SEG_MAPPING_TABLE
6810 size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address);
6811 #else //GROWABLE_SEG_MAPPING_TABLE
6813 #endif //GROWABLE_SEG_MAPPING_TABLE
6815 // it is impossible for alloc_size to overflow due bounds on each of
6817 size_t alloc_size = sizeof (BYTE)*(bs + cs + cb + ms +st + sizeof (card_table_info));
6818 size_t alloc_size_aligned = Align (alloc_size, g_SystemInfo.dwAllocationGranularity-1);
6819 dprintf (GC_TABLE_LOG, ("brick table: %Id; card table: %Id; mark array: %Id, card bundle: %Id, seg table: %Id",
6820 bs, cs, ms, cb, st));
6822 BYTE* mem = (BYTE*)VirtualAlloc (0, alloc_size_aligned, mem_flags, PAGE_READWRITE);
6826 set_fgm_result (fgm_grow_table, alloc_size, loh_p);
6830 dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[",
6831 alloc_size, (size_t)mem, (size_t)((BYTE*)mem+alloc_size)));
6834 // mark array will be committed separately (per segment).
6835 size_t commit_size = alloc_size - ms;
6837 if (!VirtualAlloc (mem, commit_size, MEM_COMMIT, PAGE_READWRITE))
6839 dprintf (GC_TABLE_LOG, ("Table commit failed"));
6840 set_fgm_result (fgm_commit_table, commit_size, loh_p);
6845 ct = (DWORD*)(mem + sizeof (card_table_info));
6846 card_table_refcount (ct) = 0;
6847 card_table_lowest_address (ct) = saved_g_lowest_address;
6848 card_table_highest_address (ct) = saved_g_highest_address;
6849 card_table_next (ct) = &g_card_table[card_word (gcard_of (la))];
6851 //clear the card table
6854 (((saved_g_highest_address - saved_g_lowest_address)*sizeof (DWORD) /
6855 (card_size * card_word_width))
6859 bt = (short*)((BYTE*)ct + cs);
6861 // No initialization needed, will be done in copy_brick_card
6863 card_table_brick_table (ct) = bt;
6866 card_table_card_bundle_table (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs);
6867 //set all bundle to look at all of the cards
6868 memset(card_table_card_bundle_table (ct), 0xFF, cb);
6869 #endif //CARD_BUNDLE
6871 #ifdef GROWABLE_SEG_MAPPING_TABLE
6873 seg_mapping* new_seg_mapping_table = (seg_mapping*)((BYTE*)card_table_brick_table (ct) + bs + cb);
6874 new_seg_mapping_table = (seg_mapping*)((BYTE*)new_seg_mapping_table -
6875 size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
6876 memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_lowest_address)],
6877 &seg_mapping_table[seg_mapping_word_of(g_lowest_address)],
6878 size_seg_mapping_table_of(g_lowest_address, g_highest_address));
6880 seg_mapping_table = new_seg_mapping_table;
6882 #endif //GROWABLE_SEG_MAPPING_TABLE
6885 if(gc_can_use_concurrent)
6886 card_table_mark_array (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs + cb + st);
6888 card_table_mark_array (ct) = NULL;
6891 g_card_table = translate_card_table (ct);
6893 dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix",
6894 (size_t)ct, (size_t)g_card_table, (size_t)seg_mapping_table, (size_t)card_table_mark_array (ct)));
6896 #ifdef BACKGROUND_GC
6897 if (hp->should_commit_mark_array())
6899 dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)",
6900 saved_g_lowest_address, saved_g_highest_address,
6901 card_table_mark_array (ct),
6902 translate_mark_array (card_table_mark_array (ct))));
6903 DWORD* new_mark_array = (DWORD*)((BYTE*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address));
6904 if (!commit_new_mark_array_global (new_mark_array))
6906 dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments"));
6907 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
6911 if (!commit_mark_array_new_seg (hp, new_seg, saved_g_lowest_address))
6913 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg"));
6914 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
6920 clear_commit_flag_global();
6922 #endif //BACKGROUND_GC
6924 // This passes a bool telling whether we need to switch to the post
6925 // grow version of the write barrier. This test tells us if the new
6926 // segment was allocated at a lower address than the old, requiring
6927 // that we start doing an upper bounds check in the write barrier.
6928 StompWriteBarrierResize(la != saved_g_lowest_address);
6930 // We need to make sure that other threads executing checked write barriers
6931 // will see the g_card_table update before g_lowest/highest_address updates.
6932 // Otherwise, the checked write barrier may AV accessing the old card table
6933 // with address that it does not cover. Write barriers access card table
6934 // without memory barriers for performance reasons, so we need to flush
6935 // the store buffers here.
6936 FlushProcessWriteBuffers();
6938 g_lowest_address = saved_g_lowest_address;
6939 VolatileStore(&g_highest_address, saved_g_highest_address);
6944 //cleanup mess and return -1;
6948 if (g_card_table != saved_g_card_table)
6950 g_card_table = saved_g_card_table;
6953 //delete (DWORD*)((BYTE*)ct - sizeof(card_table_info));
6954 if (!VirtualFree (mem, 0, MEM_RELEASE))
6956 dprintf (GC_TABLE_LOG, ("VirtualFree failed: %d", GetLastError()));
6957 assert (!"release failed");
6965 #ifdef BACKGROUND_GC
6966 if (hp->should_commit_mark_array())
6968 dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix", new_seg, hp->mark_array));
6969 if (!commit_mark_array_new_seg (hp, new_seg))
6971 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range"));
6972 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
6976 #endif //BACKGROUND_GC
6982 //copy all of the arrays managed by the card table for a page aligned range
6983 void gc_heap::copy_brick_card_range (BYTE* la, DWORD* old_card_table,
6984 short* old_brick_table,
6986 BYTE* start, BYTE* end, BOOL heap_expand)
6988 ptrdiff_t brick_offset = brick_of (start) - brick_of (la);
6991 dprintf (2, ("copying tables for range [%Ix %Ix[", (size_t)start, (size_t)end));
6994 short* brick_start = &brick_table [brick_of (start)];
6995 if (old_brick_table)
6997 // segments are always on page boundaries
6998 memcpy (brick_start, &old_brick_table[brick_offset],
6999 size_brick_of (start, end));
7004 // This is a large heap, just clear the brick table
7007 DWORD* old_ct = &old_card_table[card_word (card_of (la))];
7009 #ifdef BACKGROUND_GC
7010 if (recursive_gc_sync::background_running_p())
7012 DWORD* old_mark_array = card_table_mark_array (old_ct);
7014 // We don't need to go through all the card tables here because
7015 // we only need to copy from the GC version of the mark array - when we
7016 // mark (even in allocate_large_object) we always use that mark array.
7017 if ((card_table_highest_address (old_ct) >= start) &&
7018 (card_table_lowest_address (old_ct) <= end))
7020 if ((background_saved_highest_address >= start) &&
7021 (background_saved_lowest_address <= end))
7023 //copy the mark bits
7024 // segments are always on page boundaries
7025 BYTE* m_start = max (background_saved_lowest_address, start);
7026 BYTE* m_end = min (background_saved_highest_address, end);
7027 memcpy (&mark_array[mark_word_of (m_start)],
7028 &old_mark_array[mark_word_of (m_start) - mark_word_of (la)],
7029 size_mark_array_of (m_start, m_end));
7034 //only large segments can be out of range
7035 assert (old_brick_table == 0);
7038 #else //BACKGROUND_GC
7040 clear_mark_array (start, heap_segment_committed(seg));
7041 #endif //BACKGROUND_GC
7044 // n way merge with all of the card table ever used in between
7045 DWORD* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]);
7048 while (card_table_next (old_ct) != ct)
7050 //copy if old card table contained [start, end[
7051 if ((card_table_highest_address (ct) >= end) &&
7052 (card_table_lowest_address (ct) <= start))
7054 // or the card_tables
7055 DWORD* dest = &card_table [card_word (card_of (start))];
7056 DWORD* src = &((translate_card_table (ct)) [card_word (card_of (start))]);
7057 ptrdiff_t count = count_card_of (start, end);
7058 for (int x = 0; x < count; x++)
7065 ct = card_table_next (ct);
7070 //initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range
7071 void gc_heap::init_brick_card_range (heap_segment* seg)
7073 dprintf (2, ("initialising tables for range [%Ix %Ix[",
7074 (size_t)heap_segment_mem (seg),
7075 (size_t)heap_segment_allocated (seg)));
7077 // initialize the brick table
7078 for (size_t b = brick_of (heap_segment_mem (seg));
7079 b < brick_of (align_on_brick (heap_segment_allocated (seg)));
7086 if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed))
7089 clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg));
7093 clear_card_for_addresses (heap_segment_mem (seg),
7094 heap_segment_allocated (seg));
7097 void gc_heap::copy_brick_card_table(BOOL heap_expand)
7099 BYTE* la = lowest_address;
7100 BYTE* ha = highest_address;
7101 MAYBE_UNUSED_VAR(ha);
7102 DWORD* old_card_table = card_table;
7103 short* old_brick_table = brick_table;
7105 assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))]));
7106 assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))]));
7108 /* todo: Need a global lock for this */
7109 DWORD* ct = &g_card_table[card_word (gcard_of (g_lowest_address))];
7110 own_card_table (ct);
7111 card_table = translate_card_table (ct);
7112 /* End of global lock */
7113 highest_address = card_table_highest_address (ct);
7114 lowest_address = card_table_lowest_address (ct);
7116 brick_table = card_table_brick_table (ct);
7119 if (gc_can_use_concurrent)
7121 mark_array = translate_mark_array (card_table_mark_array (ct));
7122 assert (mark_word_of (g_highest_address) ==
7123 mark_word_of (align_on_mark_word (g_highest_address)));
7130 #if defined(MARK_ARRAY) && defined(_DEBUG)
7131 #ifdef GROWABLE_SEG_MAPPING_TABLE
7132 size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address);
7133 #else //GROWABLE_SEG_MAPPING_TABLE
7135 #endif //GROWABLE_SEG_MAPPING_TABLE
7136 assert (!gc_can_use_concurrent ||
7137 (((BYTE*)card_table_card_bundle_table (ct) + size_card_bundle_of (g_lowest_address, g_highest_address) + st) == (BYTE*)card_table_mark_array (ct)));
7138 #endif //MARK_ARRAY && _DEBUG
7139 card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct));
7140 assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] ==
7141 card_table_card_bundle_table (ct));
7143 //set the card table if we are in a heap growth scenario
7144 if (card_bundles_enabled())
7146 card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
7147 cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
7149 //check if we need to turn on card_bundles.
7150 #ifdef MULTIPLE_HEAPS
7151 // use __int64 arithmetic here because of possible overflow on 32p
7152 unsigned __int64 th = (unsigned __int64)MH_TH_CARD_BUNDLE*gc_heap::n_heaps;
7154 // use __int64 arithmetic here because of possible overflow on 32p
7155 unsigned __int64 th = (unsigned __int64)SH_TH_CARD_BUNDLE;
7156 #endif //MULTIPLE_HEAPS
7157 if (reserved_memory >= th)
7159 enable_card_bundles();
7162 #endif //CARD_BUNDLE
7164 // for each of the segments and heaps, copy the brick table and
7165 // or the card table
7166 heap_segment* seg = generation_start_segment (generation_of (max_generation));
7169 if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7171 //check if it became in range
7172 if ((heap_segment_reserved (seg) > lowest_address) &&
7173 (heap_segment_mem (seg) < highest_address))
7175 set_ro_segment_in_range (seg);
7181 BYTE* end = align_on_page (heap_segment_allocated (seg));
7182 copy_brick_card_range (la, old_card_table,
7185 align_lower_page (heap_segment_mem (seg)),
7189 seg = heap_segment_next (seg);
7192 seg = generation_start_segment (large_object_generation);
7195 if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7197 //check if it became in range
7198 if ((heap_segment_reserved (seg) > lowest_address) &&
7199 (heap_segment_mem (seg) < highest_address))
7201 set_ro_segment_in_range (seg);
7206 BYTE* end = align_on_page (heap_segment_allocated (seg));
7207 copy_brick_card_range (la, old_card_table,
7210 align_lower_page (heap_segment_mem (seg)),
7214 seg = heap_segment_next (seg);
7217 release_card_table (&old_card_table[card_word (card_of(la))]);
7220 #ifdef FEATURE_BASICFREEZE
7221 BOOL gc_heap::insert_ro_segment (heap_segment* seg)
7223 enter_spin_lock (&gc_heap::gc_lock);
7225 if (!gc_heap::seg_table->insure_space_for_insert () ||
7226 (!(should_commit_mark_array() && commit_mark_array_new_seg (__this, seg))))
7228 leave_spin_lock (&gc_heap::gc_lock);
7232 //insert at the head of the segment list
7233 generation* gen2 = generation_of (max_generation);
7234 heap_segment* oldhead = generation_start_segment (gen2);
7235 heap_segment_next (seg) = oldhead;
7236 generation_start_segment (gen2) = seg;
7238 ptrdiff_t sdelta = 0;
7239 seg_table->insert ((BYTE*)seg, sdelta);
7241 #ifdef SEG_MAPPING_TABLE
7242 seg_mapping_table_add_ro_segment (seg);
7243 #endif //SEG_MAPPING_TABLE
7246 if ((heap_segment_reserved (seg) > lowest_address) &&
7247 (heap_segment_mem (seg) < highest_address))
7249 set_ro_segment_in_range (seg);
7252 FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(seg), (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), ETW::GCLog::ETW_GC_INFO::READ_ONLY_HEAP, GetClrInstanceId());
7254 leave_spin_lock (&gc_heap::gc_lock);
7258 // No one is calling this function right now. If this is getting called we need
7259 // to take care of decommitting the mark array for it - we will need to remember
7260 // which portion of the mark array was committed and only decommit that.
7261 void gc_heap::remove_ro_segment (heap_segment* seg)
7263 //clear the mark bits so a new segment allocated in its place will have a clear mark bits
7265 if (gc_can_use_concurrent)
7267 clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)),
7268 align_on_card_word (min (heap_segment_allocated (seg), highest_address)),
7269 false); // read_only segments need the mark clear
7273 enter_spin_lock (&gc_heap::gc_lock);
7275 seg_table->remove ((BYTE*)seg);
7277 #ifdef SEG_MAPPING_TABLE
7278 seg_mapping_table_remove_ro_segment (seg);
7279 #endif //SEG_MAPPING_TABLE
7281 // Locate segment (and previous segment) in the list.
7282 generation* gen2 = generation_of (max_generation);
7283 heap_segment* curr_seg = generation_start_segment (gen2);
7284 heap_segment* prev_seg = NULL;
7286 while (curr_seg && curr_seg != seg)
7288 prev_seg = curr_seg;
7289 curr_seg = heap_segment_next (curr_seg);
7291 assert (curr_seg == seg);
7293 // Patch previous segment (or list head if there is none) to skip the removed segment.
7295 heap_segment_next (prev_seg) = heap_segment_next (curr_seg);
7297 generation_start_segment (gen2) = heap_segment_next (curr_seg);
7299 leave_spin_lock (&gc_heap::gc_lock);
7301 #endif //FEATURE_BASICFREEZE
7303 BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg)
7306 seg->flags |= heap_segment_flags_inrange;
7307 // init_brick_card_range (seg);
7308 ro_segments_in_range = TRUE;
7309 //right now, segments aren't protected
7310 //unprotect_segment (seg);
7316 BYTE** make_mark_list (size_t size)
7318 BYTE** mark_list = new (nothrow) BYTE* [size];
7322 #define swap(a,b){BYTE* t; t = a; a = b; b = t;}
7324 void verify_qsort_array (BYTE* *low, BYTE* *high)
7328 for (i = low+1; i <= high; i++)
7337 #ifndef USE_INTROSORT
7338 void qsort1( BYTE* *low, BYTE* *high, unsigned int depth)
7340 if (((low + 16) >= high) || (depth > 100))
7344 for (i = low+1; i <= high; i++)
7347 for (j=i;j >low && val<*(j-1);j--)
7356 BYTE *pivot, **left, **right;
7358 //sort low middle and high
7359 if (*(low+((high-low)/2)) < *low)
7360 swap (*(low+((high-low)/2)), *low);
7363 if (*high < *(low+((high-low)/2)))
7364 swap (*(low+((high-low)/2)), *high);
7366 swap (*(low+((high-low)/2)), *(high-1));
7368 left = low; right = high-1;
7370 while (*(--right) > pivot);
7371 while (*(++left) < pivot);
7374 swap(*left, *right);
7379 swap (*left, *(high-1));
7380 qsort1(low, left-1, depth+1);
7381 qsort1(left+1, high, depth+1);
7384 #endif //USE_INTROSORT
7385 void rqsort1( BYTE* *low, BYTE* *high)
7387 if ((low + 16) >= high)
7391 for (i = low+1; i <= high; i++)
7394 for (j=i;j >low && val>*(j-1);j--)
7403 BYTE *pivot, **left, **right;
7405 //sort low middle and high
7406 if (*(low+((high-low)/2)) > *low)
7407 swap (*(low+((high-low)/2)), *low);
7410 if (*high > *(low+((high-low)/2)))
7411 swap (*(low+((high-low)/2)), *high);
7413 swap (*(low+((high-low)/2)), *(high-1));
7415 left = low; right = high-1;
7417 while (*(--right) < pivot);
7418 while (*(++left) > pivot);
7421 swap(*left, *right);
7426 swap (*left, *(high-1));
7427 rqsort1(low, left-1);
7428 rqsort1(left+1, high);
7432 #ifdef USE_INTROSORT
7437 static const int size_threshold = 64;
7438 static const int max_depth = 100;
7441 inline static void swap_elements(BYTE** i,BYTE** j)
7449 static void sort (BYTE** begin, BYTE** end, int ignored)
7452 introsort_loop (begin, end, max_depth);
7453 insertionsort (begin, end);
7458 static void introsort_loop (BYTE** lo, BYTE** hi, int depth_limit)
7460 while (hi-lo >= size_threshold)
7462 if (depth_limit == 0)
7467 BYTE** p=median_partition (lo, hi);
7468 depth_limit=depth_limit-1;
7469 introsort_loop (p, hi, depth_limit);
7474 static BYTE** median_partition (BYTE** low, BYTE** high)
7476 BYTE *pivot, **left, **right;
7478 //sort low middle and high
7479 if (*(low+((high-low)/2)) < *low)
7480 swap_elements ((low+((high-low)/2)), low);
7482 swap_elements (low, high);
7483 if (*high < *(low+((high-low)/2)))
7484 swap_elements ((low+((high-low)/2)), high);
7486 swap_elements ((low+((high-low)/2)), (high-1));
7488 left = low; right = high-1;
7490 while (*(--right) > pivot);
7491 while (*(++left) < pivot);
7494 swap_elements(left, right);
7499 swap_elements (left, (high-1));
7504 static void insertionsort (BYTE** lo, BYTE** hi)
7506 for (BYTE** i=lo+1; i <= hi; i++)
7510 while((j > lo) && (t <*(j-1)))
7519 static void heapsort (BYTE** lo, BYTE** hi)
7521 size_t n = hi - lo + 1;
7522 for (size_t i=n / 2; i >= 1; i--)
7526 for (size_t i = n; i > 1; i--)
7528 swap_elements (lo, lo + i - 1);
7529 downheap(1, i - 1, lo);
7533 static void downheap (size_t i, size_t n, BYTE** lo)
7535 BYTE* d = *(lo + i - 1);
7540 if (child < n && *(lo + child - 1)<(*(lo + child)))
7544 if (!(d<*(lo + child - 1)))
7548 *(lo + i - 1) = *(lo + child - 1);
7556 #endif //USE_INTROSORT
7558 #ifdef MULTIPLE_HEAPS
7559 #ifdef PARALLEL_MARK_LIST_SORT
7560 void gc_heap::sort_mark_list()
7562 // if this heap had a mark list overflow, we don't do anything
7563 if (mark_list_index > mark_list_end)
7565 // printf("sort_mark_list: overflow on heap %d\n", heap_number);
7569 // if any other heap had a mark list overflow, we fake one too,
7570 // so we don't use an incomplete mark list by mistake
7571 for (int i = 0; i < n_heaps; i++)
7573 if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end)
7575 mark_list_index = mark_list_end + 1;
7576 // printf("sort_mark_list: overflow on heap %d\n", i);
7581 // unsigned long start = GetCycleCount32();
7583 dprintf (3, ("Sorting mark lists"));
7584 if (mark_list_index > mark_list)
7585 _sort (mark_list, mark_list_index - 1, 0);
7587 // printf("first phase of sort_mark_list for heap %d took %u cycles to sort %u entries\n", this->heap_number, GetCycleCount32() - start, mark_list_index - mark_list);
7588 // start = GetCycleCount32();
7590 // first set the pieces for all heaps to empty
7592 for (heap_num = 0; heap_num < n_heaps; heap_num++)
7594 mark_list_piece_start[heap_num] = NULL;
7595 mark_list_piece_end[heap_num] = NULL;
7598 BYTE** x = mark_list;
7600 // predicate means: x is still within the mark list, and within the bounds of this heap
7601 #define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high))
7604 while (x < mark_list_index)
7607 // find the heap x points into - searching cyclically from the last heap,
7608 // because in many cases the right heap is the next one or comes soon after
7609 int last_heap_num = heap_num;
7610 MAYBE_UNUSED_VAR(last_heap_num);
7614 if (heap_num >= n_heaps)
7616 assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not!
7617 heap = g_heaps[heap_num];
7619 while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high));
7621 // x is the start of the mark list piece for this heap
7622 mark_list_piece_start[heap_num] = x;
7624 // to find the end of the mark list piece for this heap, find the first x
7625 // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list
7628 // let's see if we get lucky and the whole rest belongs to this piece
7629 if (predicate(mark_list_index-1))
7631 x = mark_list_index;
7632 mark_list_piece_end[heap_num] = x;
7636 // we play a variant of binary search to find the point sooner.
7637 // the first loop advances by increasing steps until the predicate turns false.
7638 // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true.
7650 while (predicate(x));
7651 // we know that only the last step was wrong, so we undo it
7655 // loop invariant - predicate holds at x, but not x + inc
7656 assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc)));
7658 if (((x + inc) > x) && predicate(x + inc))
7664 // the termination condition and the loop invariant together imply this:
7665 assert(predicate(x) && !predicate(x + inc) && (inc == 1));
7666 // so the spot we're looking for is one further
7669 mark_list_piece_end[heap_num] = x;
7674 // printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start);
7677 void gc_heap::append_to_mark_list(BYTE **start, BYTE **end)
7679 size_t slots_needed = end - start;
7680 size_t slots_available = mark_list_end + 1 - mark_list_index;
7681 size_t slots_to_copy = min(slots_needed, slots_available);
7682 memcpy(mark_list_index, start, slots_to_copy*sizeof(*start));
7683 mark_list_index += slots_to_copy;
7684 // printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy);
7687 void gc_heap::merge_mark_lists()
7689 BYTE** source[MAX_SUPPORTED_CPUS];
7690 BYTE** source_end[MAX_SUPPORTED_CPUS];
7691 int source_heap[MAX_SUPPORTED_CPUS];
7692 int source_count = 0;
7694 // in case of mark list overflow, don't bother
7695 if (mark_list_index > mark_list_end)
7697 // printf("merge_mark_lists: overflow\n");
7701 dprintf(3, ("merge_mark_lists: heap_number = %d starts out with %Id entries", heap_number, mark_list_index - mark_list));
7702 // unsigned long start = GetCycleCount32();
7703 for (int i = 0; i < n_heaps; i++)
7705 gc_heap* heap = g_heaps[i];
7706 if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number])
7708 source[source_count] = heap->mark_list_piece_start[heap_number];
7709 source_end[source_count] = heap->mark_list_piece_end[heap_number];
7710 source_heap[source_count] = i;
7711 if (source_count < MAX_SUPPORTED_CPUS)
7715 // printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start);
7717 dprintf(3, ("heap_number = %d has %d sources\n", heap_number, source_count));
7718 #if defined(_DEBUG) || defined(TRACE_GC)
7719 for (int j = 0; j < source_count; j++)
7721 dprintf(3, ("heap_number = %d ", heap_number));
7722 dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)",
7723 (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j])));
7724 // the sources should all be sorted
7725 for (BYTE **x = source[j]; x < source_end[j] - 1; x++)
7729 dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n", j, heap_number));
7734 #endif //_DEBUG || TRACE_GC
7736 // start = GetCycleCount32();
7738 mark_list = &g_mark_list_copy [heap_number*mark_list_size];
7739 mark_list_index = mark_list;
7740 mark_list_end = &mark_list [mark_list_size-1];
7741 int piece_count = 0;
7742 if (source_count == 0)
7746 else if (source_count == 1)
7748 mark_list = source[0];
7749 mark_list_index = source_end[0];
7750 mark_list_end = mark_list_index;
7755 while (source_count > 1)
7757 // find the lowest and second lowest value in the sources we're merging from
7758 int lowest_source = 0;
7759 BYTE *lowest = *source[0];
7760 BYTE *second_lowest = *source[1];
7761 for (int i = 1; i < source_count; i++)
7763 if (lowest > *source[i])
7765 second_lowest = lowest;
7766 lowest = *source[i];
7769 else if (second_lowest > *source[i])
7771 second_lowest = *source[i];
7775 // find the point in the lowest source where it either runs out or is not <= second_lowest anymore
7777 // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common
7779 if (source_end[lowest_source][-1] <= second_lowest)
7780 x = source_end[lowest_source];
7783 // use linear search to find the end -- could also use binary search as in sort_mark_list,
7784 // but saw no improvement doing that
7785 for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++)
7789 // blast this piece to the mark list
7790 append_to_mark_list(source[lowest_source], x);
7793 source[lowest_source] = x;
7795 // check whether this source is now exhausted
7796 if (x >= source_end[lowest_source])
7798 // if it's not the source with the highest index, copy the source with the highest index
7799 // over it so the non-empty sources are always at the beginning
7800 if (lowest_source < source_count-1)
7802 source[lowest_source] = source[source_count-1];
7803 source_end[lowest_source] = source_end[source_count-1];
7808 // we're left with just one source that we copy
7809 append_to_mark_list(source[0], source_end[0]);
7813 // printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count);
7815 #if defined(_DEBUG) || defined(TRACE_GC)
7816 // the final mark list must be sorted
7817 for (BYTE **x = mark_list; x < mark_list_index - 1; x++)
7821 dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists", heap_number));
7825 #endif //defined(_DEBUG) || defined(TRACE_GC)
7827 #else //PARALLEL_MARK_LIST_SORT
7828 void gc_heap::combine_mark_lists()
7830 dprintf (3, ("Combining mark lists"));
7831 //verify if a heap has overflowed its mark list
7832 BOOL use_mark_list = TRUE;
7833 for (int i = 0; i < n_heaps; i++)
7835 if (g_heaps [i]->mark_list_index > g_heaps [i]->mark_list_end)
7837 use_mark_list = FALSE;
7844 dprintf (3, ("Using mark list"));
7845 //compact the gaps out of the mark list
7847 BYTE** current_gap = g_heaps [gn]->mark_list_index;
7848 BYTE** current_gap_end = g_heaps[gn]->mark_list_end + 1;
7849 BYTE** dst_last = current_gap-1;
7851 int srcn = n_heaps-1;
7852 gc_heap* srch = g_heaps [srcn];
7853 BYTE** src = srch->mark_list_index - 1;
7854 BYTE** src_beg = srch->mark_list;
7856 while (current_gap <= src)
7858 while ((gn < n_heaps-1) && (current_gap >= current_gap_end))
7860 //go to the next gap
7862 dprintf (3, ("Going to the next gap %d", gn));
7863 assert (gn < n_heaps);
7864 current_gap = g_heaps [gn]->mark_list_index;
7865 current_gap_end = g_heaps[gn]->mark_list_end + 1;
7866 assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list));
7868 while ((srcn > 0) && (src < src_beg))
7870 //go to the previous source
7872 dprintf (3, ("going to the previous source %d", srcn));
7874 gc_heap* srch = g_heaps [srcn];
7875 src = srch->mark_list_index - 1;
7876 src_beg = srch->mark_list;
7878 if (current_gap < src)
7880 dst_last = current_gap;
7881 *current_gap++ = *src--;
7884 dprintf (3, ("src: %Ix dst_last: %Ix", (size_t)src, (size_t)dst_last));
7886 BYTE** end_of_list = max (src, dst_last);
7888 //sort the resulting compacted list
7889 assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
7890 if (end_of_list > &g_mark_list[0])
7891 _sort (&g_mark_list[0], end_of_list, 0);
7892 //adjust the mark_list to the begining of the resulting mark list.
7893 for (int i = 0; i < n_heaps; i++)
7895 g_heaps [i]->mark_list = g_mark_list;
7896 g_heaps [i]->mark_list_index = end_of_list + 1;
7897 g_heaps [i]->mark_list_end = end_of_list + 1;
7902 BYTE** end_of_list = g_mark_list;
7903 //adjust the mark_list to the begining of the resulting mark list.
7904 //put the index beyond the end to turn off mark list processing
7905 for (int i = 0; i < n_heaps; i++)
7907 g_heaps [i]->mark_list = g_mark_list;
7908 g_heaps [i]->mark_list_index = end_of_list + 1;
7909 g_heaps [i]->mark_list_end = end_of_list;
7913 #endif // PARALLEL_MARK_LIST_SORT
7914 #endif //MULTIPLE_HEAPS
7918 #define TOTAL_TIMES_TO_SHIFT 6
7920 #define TOTAL_TIMES_TO_SHIFT 5
7923 size_t round_up_power2 (size_t size)
7925 unsigned short shift = 1;
7929 for (unsigned short i = 0; i < TOTAL_TIMES_TO_SHIFT; i++)
7931 shifted = size | (size >> shift);
7932 if (shifted == size)
7946 size_t round_down_power2 (size_t size)
7948 size_t power2 = round_up_power2 (size);
7958 // the index starts from 0.
7959 int index_of_set_bit (size_t power2)
7962 int high = sizeof (size_t) * 8 - 1;
7966 mid = ((low + high)/2);
7967 size_t temp = 1 << mid;
7972 else if (power2 < temp)
7986 int relative_index_power2_plug (size_t power2)
7988 int index = index_of_set_bit (power2);
7989 assert (index <= MAX_INDEX_POWER2);
7991 return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
7995 int relative_index_power2_free_space (size_t power2)
7997 int index = index_of_set_bit (power2);
7998 assert (index <= MAX_INDEX_POWER2);
8000 return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
8003 class seg_free_spaces
8005 struct seg_free_space
8011 struct free_space_bucket
8013 seg_free_space* free_space;
8014 SSIZE_T count_add; // Assigned when we first contruct the array.
8015 SSIZE_T count_fit; // How many items left when we are fitting plugs.
8018 void move_bucket (int old_power2, int new_power2)
8020 // PREFAST warning 22015: old_power2 could be negative
8021 assert (old_power2 >= 0);
8022 assert (old_power2 >= new_power2);
8024 if (old_power2 == new_power2)
8029 seg_free_space* src_index = free_space_buckets[old_power2].free_space;
8030 for (int i = old_power2; i > new_power2; i--)
8032 seg_free_space** dest = &(free_space_buckets[i].free_space);
8035 seg_free_space* dest_index = free_space_buckets[i - 1].free_space;
8036 if (i > (new_power2 + 1))
8038 seg_free_space temp = *src_index;
8039 *src_index = *dest_index;
8042 src_index = dest_index;
8045 free_space_buckets[old_power2].count_fit--;
8046 free_space_buckets[new_power2].count_fit++;
8051 void dump_free_space (seg_free_space* item)
8058 mark* m = (mark*)(item->start);
8059 len = pinned_len (m);
8060 addr = pinned_plug (m) - len;
8064 heap_segment* seg = (heap_segment*)(item->start);
8065 addr = heap_segment_plan_allocated (seg);
8066 len = heap_segment_committed (seg) - addr;
8069 dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id", heap_num, addr, len));
8074 seg_free_space* item = NULL;
8077 dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:", heap_num));
8078 for (i = 0; i < (free_space_bucket_count - 1); i++)
8080 dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8081 dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8082 item = free_space_buckets[i].free_space;
8083 while (item < free_space_buckets[i + 1].free_space)
8085 dump_free_space (item);
8088 dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8091 dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8092 dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8093 item = free_space_buckets[i].free_space;
8095 while (item <= &seg_free_space_array[free_space_item_count - 1])
8097 dump_free_space (item);
8100 dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8105 free_space_bucket* free_space_buckets;
8106 seg_free_space* seg_free_space_array;
8107 SSIZE_T free_space_bucket_count;
8108 SSIZE_T free_space_item_count;
8112 BOOL has_end_of_seg;
8117 seg_free_spaces (int h_number)
8119 heap_num = h_number;
8124 size_t total_prealloc_size =
8125 MAX_NUM_BUCKETS * sizeof (free_space_bucket) +
8126 MAX_NUM_FREE_SPACES * sizeof (seg_free_space);
8128 free_space_buckets = (free_space_bucket*) new (nothrow) (BYTE[total_prealloc_size]);
8130 return (!!free_space_buckets);
8133 // We take the ordered free space array we got from the 1st pass,
8134 // and feed the portion that we decided to use to this method, ie,
8135 // the largest item_count free spaces.
8136 void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count)
8138 assert (free_space_buckets);
8139 assert (item_count <= (size_t)MAX_PTR);
8141 free_space_bucket_count = bucket_count;
8142 free_space_item_count = item_count;
8145 has_end_of_seg = FALSE;
8148 SSIZE_T total_item_count = 0;
8151 seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count);
8153 for (i = 0; i < (SSIZE_T)item_count; i++)
8155 seg_free_space_array[i].start = 0;
8156 seg_free_space_array[i].is_plug = FALSE;
8159 for (i = 0; i < bucket_count; i++)
8161 free_space_buckets[i].count_add = ordered_free_spaces[i];
8162 free_space_buckets[i].count_fit = ordered_free_spaces[i];
8163 free_space_buckets[i].free_space = &seg_free_space_array[total_item_count];
8164 total_item_count += free_space_buckets[i].count_add;
8167 assert (total_item_count == (SSIZE_T)item_count);
8170 // If we are adding a free space before a plug we pass the
8171 // mark stack position so we can update the length; we could
8172 // also be adding the free space after the last plug in which
8173 // case start is the segment which we'll need to update the
8174 // heap_segment_plan_allocated.
8175 void add (void* start, BOOL plug_p, BOOL first_p)
8177 size_t size = (plug_p ?
8178 pinned_len ((mark*)start) :
8179 (heap_segment_committed ((heap_segment*)start) -
8180 heap_segment_plan_allocated ((heap_segment*)start)));
8184 dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id", heap_num, size));
8188 dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id", heap_num, size));
8190 has_end_of_seg = TRUE;
8196 size_t eph_gen_starts = gc_heap::eph_gen_starts_size;
8197 size -= eph_gen_starts;
8200 mark* m = (mark*)(start);
8201 pinned_len (m) -= eph_gen_starts;
8205 heap_segment* seg = (heap_segment*)start;
8206 heap_segment_plan_allocated (seg) += eph_gen_starts;
8210 int bucket_power2 = index_of_set_bit (round_down_power2 (size));
8211 if (bucket_power2 < base_power2)
8216 free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2];
8218 seg_free_space* bucket_free_space = bucket->free_space;
8219 assert (plug_p || (!plug_p && bucket->count_add));
8221 if (bucket->count_add == 0)
8223 dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d", heap_num, bucket_power2));
8227 SSIZE_T index = bucket->count_add - 1;
8229 dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)",
8232 (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) :
8233 heap_segment_plan_allocated ((heap_segment*)start)),
8239 bucket_free_space[index].is_plug = TRUE;
8242 bucket_free_space[index].start = start;
8243 bucket->count_add--;
8248 // Do a consistency check after all free spaces are added.
8252 int end_of_seg_count = 0;
8254 for (i = 0; i < free_space_item_count; i++)
8256 assert (seg_free_space_array[i].start);
8257 if (!(seg_free_space_array[i].is_plug))
8265 assert (end_of_seg_count == 1);
8269 assert (end_of_seg_count == 0);
8272 for (i = 0; i < free_space_bucket_count; i++)
8274 assert (free_space_buckets[i].count_add == 0);
8280 BYTE* fit (BYTE* old_loc,
8282 BOOL set_padding_on_saved_p,
8283 mark* pinned_plug_entry,
8284 #endif //SHORT_PLUGS
8286 REQD_ALIGN_AND_OFFSET_DCL)
8288 #ifdef FEATURE_STRUCTALIGN
8289 // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
8290 _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
8291 #endif // FEATURE_STRUCTALIGN
8292 // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the
8295 size_t plug_size_to_fit = plug_size;
8297 int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
8300 plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0);
8301 #endif //SHORT_PLUGS
8303 int plug_power2 = index_of_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size)));
8305 BYTE* new_address = 0;
8307 if (plug_power2 < base_power2)
8309 plug_power2 = base_power2;
8312 int chosen_power2 = plug_power2 - base_power2;
8314 for (i = chosen_power2; i < free_space_bucket_count; i++)
8316 if (free_space_buckets[i].count_fit != 0)
8323 dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space",
8327 (chosen_power2 + base_power2)));
8329 assert (i < free_space_bucket_count);
8331 seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space;
8332 SSIZE_T free_space_count = free_space_buckets[chosen_power2].count_fit;
8333 size_t new_free_space_size = 0;
8334 BOOL can_fit = FALSE;
8337 for (i = 0; i < free_space_count; i++)
8339 size_t free_space_size = 0;
8341 if (bucket_free_space[i].is_plug)
8343 mark* m = (mark*)(bucket_free_space[i].start);
8344 BYTE* plug_free_space_start = pinned_plug (m) - pinned_len (m);
8347 if ((pad_in_front & USE_PADDING_FRONT) &&
8348 (((plug_free_space_start - pin_allocation_context_start_region (m))==0) ||
8349 ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH)))
8351 pad = Align (min_obj_size);
8352 set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
8354 #endif //SHORT_PLUGS
8356 if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad)))
8358 pad += switch_alignment_size (FALSE);
8359 set_node_realigned (old_loc);
8364 free_space_size = pinned_len (m);
8365 new_address = pinned_plug (m) - pinned_len (m);
8367 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8368 free_space_size == plug_size)
8370 new_free_space_size = free_space_size - plug_size;
8371 pinned_len (m) = new_free_space_size;
8372 #ifdef SIMPLE_DPRINTF
8373 dprintf (SEG_REUSE_LOG_0, ("[%d]free space before pin: [0x%Ix, [0x%Ix (2^%d) -> [0x%Ix, [0x%Ix (2^%d)",
8375 new_address, pinned_plug (m),
8376 index_of_set_bit (round_down_power2 (free_space_size)),
8377 (pinned_plug (m) - pinned_len (m)), pinned_plug (m),
8378 index_of_set_bit (round_down_power2 (new_free_space_size))));
8379 #endif //SIMPLE_DPRINTF
8383 pin_allocation_context_start_region (m) = plug_free_space_start;
8391 heap_segment* seg = (heap_segment*)(bucket_free_space[i].start);
8392 free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
8394 if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg))))
8396 pad = switch_alignment_size (FALSE);
8397 set_node_realigned (old_loc);
8402 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8403 free_space_size == plug_size)
8405 new_address = heap_segment_plan_allocated (seg);
8406 new_free_space_size = free_space_size - plug_size;
8407 heap_segment_plan_allocated (seg) = new_address + plug_size;
8408 #ifdef SIMPLE_DPRINTF
8409 dprintf (SEG_REUSE_LOG_0, ("[%d]free space at the end of seg 0x%Ix (2^%d) -> 0x%Ix (2^%d)",
8412 index_of_set_bit (round_down_power2 (free_space_size)),
8413 heap_segment_plan_allocated (seg),
8414 index_of_set_bit (round_down_power2 (new_free_space_size))));
8415 #endif //SIMPLE_DPRINTF
8428 assert (chosen_power2 == 0);
8438 assert ((chosen_power2 && (i == 0)) ||
8439 (!chosen_power2) && (i < free_space_count));
8442 int new_bucket_power2 = index_of_set_bit (round_down_power2 (new_free_space_size));
8444 if (new_bucket_power2 < base_power2)
8446 new_bucket_power2 = base_power2;
8449 move_bucket (chosen_power2, new_bucket_power2 - base_power2);
8458 if (free_space_buckets)
8460 delete [] free_space_buckets;
8462 if (seg_free_space_array)
8464 delete [] seg_free_space_array;
8470 #define marked(i) header(i)->IsMarked()
8471 #define set_marked(i) header(i)->SetMarked()
8472 #define clear_marked(i) header(i)->ClearMarked()
8473 #define pinned(i) header(i)->IsPinned()
8474 #define set_pinned(i) header(i)->SetPinned()
8475 #define clear_pinned(i) header(i)->GetHeader()->ClrGCBit();
8477 inline size_t my_get_size (Object* ob)
8479 MethodTable* mT = header(ob)->GetMethodTable();
8480 return (mT->GetBaseSize() +
8481 (mT->HasComponentSize() ?
8482 ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0));
8485 //#define size(i) header(i)->GetSize()
8486 #define size(i) my_get_size (header(i))
8488 #define contain_pointers(i) header(i)->ContainsPointers()
8489 #ifdef COLLECTIBLE_CLASS
8490 #define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible()
8492 #define get_class_object(i) method_table(i)->GetLoaderAllocatorObjectForGC()
8493 #define is_collectible(i) method_table(i)->Collectible()
8494 #else //COLLECTIBLE_CLASS
8495 #define contain_pointers_or_collectible(i) header(i)->ContainsPointers()
8496 #endif //COLLECTIBLE_CLASS
8498 #if defined (MARK_ARRAY) && defined (BACKGROUND_GC)
8500 void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg)
8502 BYTE* range_beg = 0;
8503 BYTE* range_end = 0;
8504 if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end))
8506 clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE);
8510 void gc_heap::clear_batch_mark_array_bits (BYTE* start, BYTE* end)
8512 if ((start < background_saved_highest_address) &&
8513 (end > background_saved_lowest_address))
8515 start = max (start, background_saved_lowest_address);
8516 end = min (end, background_saved_highest_address);
8518 size_t start_mark_bit = mark_bit_of (start);
8519 size_t end_mark_bit = mark_bit_of (end);
8520 unsigned int startbit = mark_bit_bit (start_mark_bit);
8521 unsigned int endbit = mark_bit_bit (end_mark_bit);
8522 size_t startwrd = mark_bit_word (start_mark_bit);
8523 size_t endwrd = mark_bit_word (end_mark_bit);
8525 dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
8526 (size_t)start, (size_t)start_mark_bit,
8527 (size_t)end, (size_t)end_mark_bit));
8529 unsigned int firstwrd = lowbits (~0, startbit);
8530 unsigned int lastwrd = highbits (~0, endbit);
8532 if (startwrd == endwrd)
8534 unsigned int wrd = firstwrd | lastwrd;
8535 mark_array[startwrd] &= wrd;
8539 // clear the first mark word.
8542 mark_array[startwrd] &= firstwrd;
8546 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
8548 mark_array[wrdtmp] = 0;
8551 // clear the last mark word.
8554 mark_array[endwrd] &= lastwrd;
8559 void gc_heap::bgc_clear_batch_mark_array_bits (BYTE* start, BYTE* end)
8561 if ((start < background_saved_highest_address) &&
8562 (end > background_saved_lowest_address))
8564 start = max (start, background_saved_lowest_address);
8565 end = min (end, background_saved_highest_address);
8567 clear_batch_mark_array_bits (start, end);
8571 void gc_heap::clear_mark_array_by_objects (BYTE* from, BYTE* end, BOOL loh_p)
8573 dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix",
8575 int align_const = get_alignment_constant (!loh_p);
8581 BYTE* next_o = o + Align (size (o), align_const);
8583 if (background_object_marked (o, TRUE))
8585 dprintf (3, ("%Ix was marked by bgc, is now cleared", o));
8591 #endif //MARK_ARRAY && BACKGROUND_GC
8594 BOOL gc_heap::is_mark_set (BYTE* o)
8599 #if defined (_MSC_VER) && defined (_TARGET_X86_)
8600 #pragma optimize("y", on) // Small critical routines, don't put in EBP frame
8601 #endif //_MSC_VER && _TARGET_X86_
8603 // return the generation number of an object.
8604 // It is assumed that the object is valid.
8605 //Note that this will return max_generation for a LOH object
8606 int gc_heap::object_gennum (BYTE* o)
8608 if (in_range_for_segment (o, ephemeral_heap_segment) &&
8609 (o >= generation_allocation_start (generation_of (max_generation-1))))
8611 // in an ephemeral generation.
8612 for ( int i = 0; i < max_generation-1; i++)
8614 if ((o >= generation_allocation_start (generation_of (i))))
8617 return max_generation-1;
8621 return max_generation;
8625 int gc_heap::object_gennum_plan (BYTE* o)
8627 if (in_range_for_segment (o, ephemeral_heap_segment))
8629 for (int i = 0; i <= max_generation-1; i++)
8631 BYTE* plan_start = generation_plan_allocation_start (generation_of (i));
8632 if (plan_start && (o >= plan_start))
8638 return max_generation;
8641 #if defined(_MSC_VER) && defined(_TARGET_X86_)
8642 #pragma optimize("", on) // Go back to command line default optimizations
8643 #endif //_MSC_VER && _TARGET_X86_
8645 heap_segment* gc_heap::make_heap_segment (BYTE* new_pages, size_t size, int h_number)
8648 size_t initial_commit = SEGMENT_INITIAL_COMMIT;
8650 //Commit the first page
8651 if ((res = virtual_alloc_commit_for_heap (new_pages, initial_commit,
8652 MEM_COMMIT, PAGE_READWRITE, h_number)) == 0)
8657 //overlay the heap_segment
8658 heap_segment* new_segment = (heap_segment*)new_pages;
8661 #ifdef BACKGROUND_GC
8662 //leave the first page to contain only segment info
8663 //because otherwise we could need to revisit the first page frequently in
8665 start = new_pages + OS_PAGE_SIZE;
8668 Align (sizeof (heap_segment), get_alignment_constant (FALSE));
8669 #endif //BACKGROUND_GC
8670 heap_segment_mem (new_segment) = start;
8671 heap_segment_used (new_segment) = start;
8672 heap_segment_reserved (new_segment) = new_pages + size;
8673 heap_segment_committed (new_segment) = new_pages + initial_commit;
8674 init_heap_segment (new_segment);
8675 dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
8679 void gc_heap::init_heap_segment (heap_segment* seg)
8682 heap_segment_next (seg) = 0;
8683 heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
8684 heap_segment_allocated (seg) = heap_segment_mem (seg);
8685 #ifdef BACKGROUND_GC
8686 heap_segment_background_allocated (seg) = 0;
8687 heap_segment_saved_bg_allocated (seg) = 0;
8688 #endif //BACKGROUND_GC
8691 //Releases the segment to the OS.
8692 // this is always called on one thread only so calling seg_table->remove is fine.
8693 void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
8695 if (!heap_segment_loh_p (seg))
8697 //cleanup the brick table back to the empty value
8698 clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg));
8701 if (consider_hoarding)
8703 assert ((heap_segment_mem (seg) - (BYTE*)seg) <= 2*OS_PAGE_SIZE);
8704 size_t ss = (size_t) (heap_segment_reserved (seg) - (BYTE*)seg);
8705 //Don't keep the big ones.
8706 if (ss <= INITIAL_ALLOC)
8708 dprintf (2, ("Hoarding segment %Ix", (size_t)seg));
8709 #ifdef BACKGROUND_GC
8710 // We don't need to clear the decommitted flag because when this segment is used
8711 // for a new segment the flags will be cleared.
8712 if (!heap_segment_decommitted_p (seg))
8713 #endif //BACKGROUND_GC
8715 decommit_heap_segment (seg);
8718 #ifdef SEG_MAPPING_TABLE
8719 seg_mapping_table_remove_segment (seg);
8720 #endif //SEG_MAPPING_TABLE
8722 heap_segment_next (seg) = segment_standby_list;
8723 segment_standby_list = seg;
8730 dprintf (2, ("h%d: del seg: [%Ix, %Ix[",
8731 heap_number, (size_t)seg,
8732 (size_t)(heap_segment_reserved (seg))));
8734 #ifdef BACKGROUND_GC
8735 ::record_changed_seg ((BYTE*)seg, heap_segment_reserved (seg),
8736 settings.gc_index, current_bgc_state,
8738 decommit_mark_array_by_seg (seg);
8739 #endif //BACKGROUND_GC
8741 #ifdef SEG_MAPPING_TABLE
8742 seg_mapping_table_remove_segment (seg);
8743 #else //SEG_MAPPING_TABLE
8744 seg_table->remove ((BYTE*)seg);
8745 #endif //SEG_MAPPING_TABLE
8747 release_segment (seg);
8751 //resets the pages beyond alloctes size so they won't be swapped out and back in
8753 void gc_heap::reset_heap_segment_pages (heap_segment* seg)
8755 #ifndef FEATURE_PAL // No MEM_RESET support in PAL VirtualAlloc
8756 size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg));
8757 size_t size = (size_t)heap_segment_committed (seg) - page_start;
8759 VirtualAlloc ((char*)page_start, size, MEM_RESET, PAGE_READWRITE);
8760 #endif //!FEATURE_PAL
8763 void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
8766 BYTE* page_start = align_on_page (heap_segment_allocated(seg));
8767 size_t size = heap_segment_committed (seg) - page_start;
8768 extra_space = align_on_page (extra_space);
8769 if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE))
8771 page_start += max(extra_space, 32*OS_PAGE_SIZE);
8772 size -= max (extra_space, 32*OS_PAGE_SIZE);
8774 VirtualFree (page_start, size, MEM_DECOMMIT);
8775 dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)",
8777 (size_t)(page_start + size),
8779 heap_segment_committed (seg) = page_start;
8780 if (heap_segment_used (seg) > heap_segment_committed (seg))
8782 heap_segment_used (seg) = heap_segment_committed (seg);
8787 //decommit all pages except one or 2
8788 void gc_heap::decommit_heap_segment (heap_segment* seg)
8790 BYTE* page_start = align_on_page (heap_segment_mem (seg));
8792 dprintf (3, ("Decommitting heap segment %Ix", (size_t)seg));
8794 #ifdef BACKGROUND_GC
8795 page_start += OS_PAGE_SIZE;
8796 #endif //BACKGROUND_GC
8798 size_t size = heap_segment_committed (seg) - page_start;
8799 VirtualFree (page_start, size, MEM_DECOMMIT);
8801 //re-init the segment object
8802 heap_segment_committed (seg) = page_start;
8803 if (heap_segment_used (seg) > heap_segment_committed (seg))
8805 heap_segment_used (seg) = heap_segment_committed (seg);
8809 void gc_heap::clear_gen0_bricks()
8811 if (!gen0_bricks_cleared)
8813 gen0_bricks_cleared = TRUE;
8814 //initialize brick table for gen 0
8815 for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
8816 b < brick_of (align_on_brick
8817 (heap_segment_allocated (ephemeral_heap_segment)));
8825 #ifdef BACKGROUND_GC
8826 void gc_heap::rearrange_small_heap_segments()
8828 heap_segment* seg = freeable_small_heap_segment;
8831 heap_segment* next_seg = heap_segment_next (seg);
8832 // TODO: we need to consider hoarding here.
8833 delete_heap_segment (seg, FALSE);
8836 freeable_small_heap_segment = 0;
8838 #endif //BACKGROUND_GC
8840 void gc_heap::rearrange_large_heap_segments()
8842 dprintf (2, ("deleting empty large segments"));
8843 heap_segment* seg = freeable_large_heap_segment;
8846 heap_segment* next_seg = heap_segment_next (seg);
8847 delete_heap_segment (seg, (g_pConfig->GetGCRetainVM() != 0));
8850 freeable_large_heap_segment = 0;
8853 void gc_heap::rearrange_heap_segments(BOOL compacting)
8856 generation_start_segment (generation_of (max_generation));
8858 heap_segment* prev_seg = 0;
8859 heap_segment* next_seg = 0;
8862 next_seg = heap_segment_next (seg);
8864 //link ephemeral segment when expanding
8865 if ((next_seg == 0) && (seg != ephemeral_heap_segment))
8867 seg->next = ephemeral_heap_segment;
8868 next_seg = heap_segment_next (seg);
8871 //re-used expanded heap segment
8872 if ((seg == ephemeral_heap_segment) && next_seg)
8874 heap_segment_next (prev_seg) = next_seg;
8875 heap_segment_next (seg) = 0;
8879 BYTE* end_segment = (compacting ?
8880 heap_segment_plan_allocated (seg) :
8881 heap_segment_allocated (seg));
8882 // check if the segment was reached by allocation
8883 if ((end_segment == heap_segment_mem (seg))&&
8884 !heap_segment_read_only_p (seg))
8886 //if not, unthread and delete
8888 assert (seg != ephemeral_heap_segment);
8889 heap_segment_next (prev_seg) = next_seg;
8890 delete_heap_segment (seg, (g_pConfig->GetGCRetainVM() != 0));
8892 dprintf (2, ("Deleting heap segment %Ix", (size_t)seg));
8896 if (!heap_segment_read_only_p (seg))
8900 heap_segment_allocated (seg) =
8901 heap_segment_plan_allocated (seg);
8904 // reset the pages between allocated and committed.
8905 if (seg != ephemeral_heap_segment)
8907 decommit_heap_segment_pages (seg, 0);
8921 BYTE* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch
8923 #ifdef TIME_WRITE_WATCH
8924 static unsigned int tot_cycles = 0;
8925 #endif //TIME_WRITE_WATCH
8929 void gc_heap::update_card_table_bundle()
8931 if (card_bundles_enabled())
8933 BYTE* base_address = (BYTE*)(&card_table[card_word (card_of (lowest_address))]);
8934 BYTE* saved_base_address = base_address;
8935 ULONG_PTR bcount = array_size;
8936 ULONG granularity = 0;
8937 BYTE* high_address = (BYTE*)(&card_table[card_word (card_of (highest_address))]);
8938 size_t saved_region_size = align_on_page (high_address) - saved_base_address;
8942 size_t region_size = align_on_page (high_address) - base_address;
8943 dprintf (3,("Probing card table pages [%Ix, %Ix[", (size_t)base_address, (size_t)base_address+region_size));
8944 UINT status = GetWriteWatch (0, base_address, region_size,
8945 (PVOID*)g_addresses,
8946 &bcount, &granularity);
8947 assert (status == 0);
8948 assert (granularity == OS_PAGE_SIZE);
8949 dprintf (3,("Found %d pages written", bcount));
8950 for (unsigned i = 0; i < bcount; i++)
8952 size_t bcardw = (DWORD*)(max(g_addresses[i],base_address)) - &card_table[0];
8953 size_t ecardw = (DWORD*)(min(g_addresses[i]+granularity, high_address)) - &card_table[0];
8954 assert (bcardw >= card_word (card_of (g_lowest_address)));
8956 card_bundles_set (cardw_card_bundle (bcardw),
8957 cardw_card_bundle (align_cardw_on_bundle (ecardw)));
8959 dprintf (3,("Set Card bundle [%Ix, %Ix[",
8960 cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))));
8963 for (size_t x = cardw_card_bundle (bcardw); x < cardw_card_bundle (ecardw); x++)
8965 if (!card_bundle_set_p (x))
8967 assert (!"Card bundle not set");
8968 dprintf (3, ("Card bundle %Ix not set", x));
8974 if (bcount >= array_size){
8975 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
8976 bcount = array_size;
8978 } while ((bcount >= array_size) && (base_address < high_address));
8980 ResetWriteWatch (saved_base_address, saved_region_size);
8984 size_t lowest_card = card_word (card_of (lowest_address));
8985 size_t highest_card = card_word (card_of (highest_address));
8986 size_t cardb = cardw_card_bundle (lowest_card);
8987 size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card));
8989 //find a non null bundle
8990 while (cardb < end_cardb)
8992 if (card_bundle_set_p (cardb)==0)
8994 //verify that the cards are indeed empty
8995 DWORD* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)];
8996 DWORD* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)];
8997 while (card_word < card_word_end)
8999 if ((*card_word) != 0)
9001 dprintf (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear",
9002 dd_collection_count (dynamic_data_of (0)),
9003 (size_t)(card_word-&card_table[0]),
9004 (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb));
9006 assert((*card_word)==0);
9010 //end of verification
9016 #endif //CARD_BUNDLE
9018 const size_t ww_reset_quantum = 128*1024*1024;
9021 void gc_heap::switch_one_quantum()
9023 Thread* current_thread = GetThread();
9024 enable_preemptive (current_thread);
9025 __SwitchToThread (1, CALLER_LIMITS_SPINNING);
9026 disable_preemptive (current_thread, TRUE);
9029 void gc_heap::reset_ww_by_chunk (BYTE* start_address, size_t total_reset_size)
9031 size_t reset_size = 0;
9032 size_t remaining_reset_size = 0;
9033 size_t next_reset_size = 0;
9035 while (reset_size != total_reset_size)
9037 remaining_reset_size = total_reset_size - reset_size;
9038 next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size);
9039 if (next_reset_size)
9041 ResetWriteWatch (start_address, next_reset_size);
9042 reset_size += next_reset_size;
9044 switch_one_quantum();
9048 assert (reset_size == total_reset_size);
9051 // This does a __SwitchToThread for every reset ww_reset_quantum bytes of reset
9052 // we do concurrently.
9053 void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size)
9057 *current_total_reset_size += last_reset_size;
9059 dprintf (2, ("reset %Id bytes so far", *current_total_reset_size));
9061 if (*current_total_reset_size > ww_reset_quantum)
9063 switch_one_quantum();
9065 *current_total_reset_size = 0;
9070 void gc_heap::reset_write_watch (BOOL concurrent_p)
9072 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
9074 PREFIX_ASSUME(seg != NULL);
9076 size_t reset_size = 0;
9077 size_t region_size = 0;
9079 dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix", background_saved_lowest_address, background_saved_highest_address));
9083 BYTE* base_address = align_lower_page (heap_segment_mem (seg));
9087 base_address = max (base_address, background_saved_lowest_address);
9090 BYTE* high_address = 0;
9093 high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
9094 high_address = min (high_address, background_saved_highest_address);
9098 high_address = heap_segment_allocated (seg);
9101 if (base_address < high_address)
9103 region_size = high_address - base_address;
9105 #ifdef TIME_WRITE_WATCH
9106 unsigned int time_start = GetCycleCount32();
9107 #endif //TIME_WRITE_WATCH
9108 dprintf (3, ("h%d: soh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9109 //reset_ww_by_chunk (base_address, region_size);
9110 ResetWriteWatch (base_address, region_size);
9112 #ifdef TIME_WRITE_WATCH
9113 unsigned int time_stop = GetCycleCount32();
9114 tot_cycles += time_stop - time_start;
9115 printf ("ResetWriteWatch Duration: %d, total: %d\n",
9116 time_stop - time_start, tot_cycles);
9117 #endif //TIME_WRITE_WATCH
9119 switch_on_reset (concurrent_p, &reset_size, region_size);
9122 seg = heap_segment_next_rw (seg);
9124 concurrent_print_time_delta ("CRWW soh");
9127 //concurrent_print_time_delta ("CRW soh");
9129 seg = heap_segment_rw (generation_start_segment (large_object_generation));
9131 PREFIX_ASSUME(seg != NULL);
9135 BYTE* base_address = align_lower_page (heap_segment_mem (seg));
9136 BYTE* high_address = heap_segment_allocated (seg);
9140 base_address = max (base_address, background_saved_lowest_address);
9141 high_address = min (high_address, background_saved_highest_address);
9144 if (base_address < high_address)
9146 region_size = high_address - base_address;
9148 #ifdef TIME_WRITE_WATCH
9149 unsigned int time_start = GetCycleCount32();
9150 #endif //TIME_WRITE_WATCH
9151 dprintf (3, ("h%d: loh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9152 //reset_ww_by_chunk (base_address, region_size);
9153 ResetWriteWatch (base_address, region_size);
9155 #ifdef TIME_WRITE_WATCH
9156 unsigned int time_stop = GetCycleCount32();
9157 tot_cycles += time_stop - time_start;
9158 printf ("ResetWriteWatch Duration: %d, total: %d\n",
9159 time_stop - time_start, tot_cycles);
9160 #endif //TIME_WRITE_WATCH
9162 switch_on_reset (concurrent_p, &reset_size, region_size);
9165 seg = heap_segment_next_rw (seg);
9167 concurrent_print_time_delta ("CRWW loh");
9170 #ifdef DEBUG_WRITE_WATCH
9171 debug_write_watch = (BYTE**)~0;
9172 #endif //DEBUG_WRITE_WATCH
9175 #endif //WRITE_WATCH
9177 #ifdef BACKGROUND_GC
9178 void gc_heap::restart_vm()
9180 //assert (generation_allocation_pointer (youngest_generation) == 0);
9181 dprintf (3, ("Restarting EE"));
9182 STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n");
9183 ee_proceed_event.Set();
9187 void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p)
9189 if (awr != awr_ignored)
9193 FireEtwBGCAllocWaitBegin (awr, GetClrInstanceId());
9197 FireEtwBGCAllocWaitEnd (awr, GetClrInstanceId());
9203 void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr)
9205 fire_alloc_wait_event (awr, TRUE);
9209 void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr)
9211 fire_alloc_wait_event (awr, FALSE);
9213 #endif //BACKGROUND_GC
9214 void gc_heap::make_generation (generation& gen, heap_segment* seg, BYTE* start, BYTE* pointer)
9216 gen.allocation_start = start;
9217 gen.allocation_context.alloc_ptr = pointer;
9218 gen.allocation_context.alloc_limit = pointer;
9219 gen.allocation_context.alloc_bytes = 0;
9220 gen.allocation_context.alloc_bytes_loh = 0;
9221 gen.allocation_context_start_region = pointer;
9222 gen.start_segment = seg;
9223 gen.allocation_segment = seg;
9224 gen.plan_allocation_start = 0;
9225 gen.free_list_space = 0;
9226 gen.pinned_allocated = 0;
9227 gen.free_list_allocated = 0;
9228 gen.end_seg_allocated = 0;
9229 gen.condemned_allocated = 0;
9230 gen.free_obj_space = 0;
9231 gen.allocation_size = 0;
9232 gen.pinned_allocation_sweep_size = 0;
9233 gen.pinned_allocation_compact_size = 0;
9234 gen.allocate_end_seg_p = FALSE;
9235 gen.free_list_allocator.clear();
9237 #ifdef FREE_USAGE_STATS
9238 memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces));
9239 memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces));
9240 memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs));
9241 #endif //FREE_USAGE_STATS
9244 void gc_heap::adjust_ephemeral_limits ()
9246 ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
9247 ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
9249 dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
9250 (size_t)ephemeral_low, (size_t)ephemeral_high))
9252 // This updates the write barrier helpers with the new info.
9253 StompWriteBarrierEphemeral();
9256 HRESULT gc_heap::initialize_gc (size_t segment_size,
9258 #ifdef MULTIPLE_HEAPS
9259 ,unsigned number_of_heaps
9260 #endif //MULTIPLE_HEAPS
9264 int log_last_gcs = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogEnabled);
9267 LPWSTR temp_logfile_name = NULL;
9268 CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogFile, &temp_logfile_name);
9270 #ifdef FEATURE_REDHAWK
9271 gc_log = PalCreateFileW(
9277 FILE_ATTRIBUTE_NORMAL,
9279 #else // FEATURE_REDHAWK
9280 char logfile_name[MAX_PATH+1];
9281 if (temp_logfile_name != 0)
9284 ret = WszWideCharToMultiByte(CP_ACP, 0, temp_logfile_name, -1, logfile_name, sizeof(logfile_name)-1, NULL, NULL);
9286 delete temp_logfile_name;
9290 sprintf_s(szPid, _countof(szPid), ".%d", GetCurrentProcessId());
9291 strcat_s(logfile_name, _countof(logfile_name), szPid);
9292 strcat_s(logfile_name, _countof(logfile_name), ".log");
9294 gc_log = CreateFileA(
9300 FILE_ATTRIBUTE_NORMAL,
9302 #endif // FEATURE_REDHAWK
9304 if (gc_log == INVALID_HANDLE_VALUE)
9309 // GCLogFileSize in MBs.
9310 gc_log_file_size = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogFileSize);
9312 if (gc_log_file_size < 0 || gc_log_file_size > 500)
9314 CloseHandle (gc_log);
9318 gc_log_lock = ClrCreateMutex(NULL, FALSE, NULL);
9319 gc_log_buffer = new (nothrow) BYTE [gc_log_buffer_size];
9324 memset (gc_log_buffer, '*', gc_log_buffer_size);
9326 max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size;
9327 //max_gc_buffers = gc_log_file_size * 1024 * 5/ gc_log_buffer_size;
9333 GCStatistics::logFileName = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCMixLog);
9334 if (GCStatistics::logFileName != NULL)
9336 GCStatistics::logFile = _wfopen((LPCWSTR)GCStatistics::logFileName, W("a"));
9341 HRESULT hres = S_OK;
9344 write_watch_api_supported();
9345 #ifdef BACKGROUND_GC
9346 if (can_use_write_watch () && g_pConfig->GetGCconcurrent()!=0)
9348 gc_can_use_concurrent = TRUE;
9349 mem_reserve = MEM_WRITE_WATCH | MEM_RESERVE;
9353 gc_can_use_concurrent = FALSE;
9355 #endif //BACKGROUND_GC
9356 #endif //WRITE_WATCH
9358 reserved_memory = 0;
9359 unsigned block_count;
9360 #ifdef MULTIPLE_HEAPS
9361 reserved_memory_limit = (segment_size + heap_size) * number_of_heaps;
9362 block_count = number_of_heaps;
9363 #else //MULTIPLE_HEAPS
9364 reserved_memory_limit = segment_size + heap_size;
9366 #endif //MULTIPLE_HEAPS
9368 if (!reserve_initial_memory(segment_size,heap_size,block_count))
9369 return E_OUTOFMEMORY;
9372 //check if we need to turn on card_bundles.
9373 #ifdef MULTIPLE_HEAPS
9374 // use __int64 arithmetic here because of possible overflow on 32p
9375 unsigned __int64 th = (unsigned __int64)MH_TH_CARD_BUNDLE*number_of_heaps;
9377 // use __int64 arithmetic here because of possible overflow on 32p
9378 unsigned __int64 th = (unsigned __int64)SH_TH_CARD_BUNDLE;
9379 #endif //MULTIPLE_HEAPS
9381 if ((can_use_write_watch() && reserved_memory >= th))
9383 settings.card_bundles = TRUE;
9386 settings.card_bundles = FALSE;
9388 #endif //CARD_BUNDLE
9390 //Init the gc_mechanisms
9391 settings.first_init();
9393 //g_highest_address = (BYTE*)0x7ffe0000;
9394 g_card_table = make_card_table (g_lowest_address, g_highest_address);
9397 return E_OUTOFMEMORY;
9401 #ifdef MULTIPLE_HEAPS
9402 n_heaps = number_of_heaps;
9404 g_heaps = new (nothrow) gc_heap* [number_of_heaps];
9406 return E_OUTOFMEMORY;
9409 #pragma warning(push)
9410 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9412 g_promoted = new (nothrow) size_t [number_of_heaps*16];
9413 g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
9415 g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
9418 #pragma warning(pop)
9420 if (!g_promoted || !g_bpromoted)
9421 return E_OUTOFMEMORY;
9424 if (!g_mark_stack_busy)
9425 return E_OUTOFMEMORY;
9428 g_gc_threads = new (nothrow) HANDLE [number_of_heaps];
9430 return E_OUTOFMEMORY;
9432 if (!create_thread_support (number_of_heaps))
9433 return E_OUTOFMEMORY;
9435 if (!heap_select::init (number_of_heaps))
9436 return E_OUTOFMEMORY;
9438 #endif //MULTIPLE_HEAPS
9441 print_level = g_pConfig->GetGCprnLvl();
9442 gc_trace_fac = g_pConfig->GetGCtraceFac();
9445 if (!init_semi_shared())
9453 //Initializes PER_HEAP_ISOLATED data members.
9455 gc_heap::init_semi_shared()
9459 // This is used for heap expansion - it's to fix exactly the start for gen 0
9460 // through (max_generation-1). When we expand the heap we allocate all these
9461 // gen starts at the beginning of the new ephemeral seg.
9462 eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
9465 size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
9466 MAYBE_UNUSED_VAR(gen0size);
9468 #ifdef MULTIPLE_HEAPS
9470 mark_list_size = min (150*1024, max (8192, get_valid_segment_size()/(2*10*32)));
9471 g_mark_list = make_mark_list (mark_list_size*n_heaps);
9473 #ifdef PARALLEL_MARK_LIST_SORT
9474 g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
9475 if (!g_mark_list_copy)
9479 #endif //PARALLEL_MARK_LIST_SORT
9481 #else //MULTIPLE_HEAPS
9483 mark_list_size = max (8192, get_valid_segment_size()/(64*32));
9484 g_mark_list = make_mark_list (mark_list_size);
9486 #endif //MULTIPLE_HEAPS
9488 dprintf (3, ("gen0 size: %d, mark_list_size: %d",
9489 gen0size, mark_list_size));
9497 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
9498 if (!seg_mapping_table_init())
9500 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
9502 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
9503 seg_table = sorted_table::make_sorted_table();
9507 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
9509 segment_standby_list = 0;
9511 full_gc_approach_event.CreateManualEvent(FALSE);
9512 if (!full_gc_approach_event.IsValid())
9516 full_gc_end_event.CreateManualEvent(FALSE);
9517 if (!full_gc_end_event.IsValid())
9522 fgn_maxgen_percent = 0;
9523 fgn_loh_percent = 0;
9524 full_gc_approach_event_set = false;
9526 memset (full_gc_counts, 0, sizeof (full_gc_counts));
9529 should_expand_in_full_gc = FALSE;
9531 #ifdef FEATURE_LOH_COMPACTION
9532 loh_compaction_always_p = (g_pConfig->GetGCLOHCompactionMode() != 0);
9533 loh_compaction_mode = loh_compaction_default;
9534 #endif //FEATURE_LOH_COMPACTION
9536 #ifdef BACKGROUND_GC
9537 memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
9538 bgc_alloc_spin_count = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BGCSpinCount);
9539 bgc_alloc_spin = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BGCSpin);
9542 int number_bgc_threads = 1;
9543 #ifdef MULTIPLE_HEAPS
9544 number_bgc_threads = n_heaps;
9545 #endif //MULTIPLE_HEAPS
9546 if (!create_bgc_threads_support (number_bgc_threads))
9550 #endif //BACKGROUND_GC
9559 if (full_gc_approach_event.IsValid())
9561 full_gc_approach_event.CloseEvent();
9563 if (full_gc_end_event.IsValid())
9565 full_gc_end_event.CloseEvent();
9572 gc_heap* gc_heap::make_gc_heap (
9573 #ifdef MULTIPLE_HEAPS
9576 #endif //MULTIPLE_HEAPS
9581 #ifdef MULTIPLE_HEAPS
9582 res = new (nothrow) gc_heap;
9586 res->vm_heap = vm_hp;
9587 res->alloc_context_count = 0;
9590 #ifdef PARALLEL_MARK_LIST_SORT
9591 res->mark_list_piece_start = new (nothrow) BYTE**[n_heaps];
9592 if (!res->mark_list_piece_start)
9596 #pragma warning(push)
9597 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9599 res->mark_list_piece_end = new (nothrow) BYTE**[n_heaps + 32]; // +32 is padding to reduce false sharing
9601 #pragma warning(pop)
9604 if (!res->mark_list_piece_end)
9606 #endif //PARALLEL_MARK_LIST_SORT
9610 #endif //MULTIPLE_HEAPS
9612 if (res->init_gc_heap (
9613 #ifdef MULTIPLE_HEAPS
9615 #else //MULTIPLE_HEAPS
9617 #endif //MULTIPLE_HEAPS
9623 #ifdef MULTIPLE_HEAPS
9627 #endif //MULTIPLE_HEAPS
9631 gc_heap::wait_for_gc_done(INT32 timeOut)
9633 Thread* current_thread = GetThread();
9634 BOOL cooperative_mode = enable_preemptive (current_thread);
9636 DWORD dwWaitResult = NOERROR;
9638 gc_heap* wait_heap = NULL;
9639 while (gc_heap::gc_started)
9641 #ifdef MULTIPLE_HEAPS
9642 wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
9643 dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
9644 #endif // MULTIPLE_HEAPS
9647 PREFIX_ASSUME(wait_heap != NULL);
9650 dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE);
9652 disable_preemptive (current_thread, cooperative_mode);
9654 return dwWaitResult;
9658 gc_heap::set_gc_done()
9660 enter_gc_done_event_lock();
9661 if (!gc_done_event_set)
9663 gc_done_event_set = true;
9664 dprintf (2, ("heap %d: setting gc_done_event", heap_number));
9665 gc_done_event.Set();
9667 exit_gc_done_event_lock();
9671 gc_heap::reset_gc_done()
9673 enter_gc_done_event_lock();
9674 if (gc_done_event_set)
9676 gc_done_event_set = false;
9677 dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
9678 gc_done_event.Reset();
9680 exit_gc_done_event_lock();
9684 gc_heap::enter_gc_done_event_lock()
9686 DWORD dwSwitchCount = 0;
9689 if (FastInterlockExchange (&gc_done_event_lock, 0) >= 0)
9691 while (gc_done_event_lock >= 0)
9693 if (g_SystemInfo.dwNumberOfProcessors > 1)
9695 int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
9696 for (int j = 0; j < spin_count; j++)
9698 if (gc_done_event_lock < 0)
9700 YieldProcessor(); // indicate to the processor that we are spining
9702 if (gc_done_event_lock >= 0)
9703 __SwitchToThread(0, ++dwSwitchCount);
9706 __SwitchToThread(0, ++dwSwitchCount);
9713 gc_heap::exit_gc_done_event_lock()
9715 gc_done_event_lock = -1;
9718 #ifndef MULTIPLE_HEAPS
9720 #ifdef RECORD_LOH_STATE
9721 int gc_heap::loh_state_index = 0;
9722 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
9723 #endif //RECORD_LOH_STATE
9725 VOLATILE(LONG) gc_heap::gc_done_event_lock;
9726 VOLATILE(bool) gc_heap::gc_done_event_set;
9727 CLREvent gc_heap::gc_done_event;
9728 #endif //!MULTIPLE_HEAPS
9729 VOLATILE(bool) gc_heap::internal_gc_done;
9731 void gc_heap::add_saved_spinlock_info (
9732 msl_enter_state enter_state,
9733 msl_take_state take_state)
9736 #ifdef SPINLOCK_HISTORY
9737 spinlock_info* current = &last_spinlock_info[spinlock_info_index];
9739 current->enter_state = enter_state;
9740 current->take_state = take_state;
9741 current->thread_id = GetCurrentThreadId();
9743 spinlock_info_index++;
9745 assert (spinlock_info_index <= max_saved_spinlock_info);
9747 if (spinlock_info_index >= max_saved_spinlock_info)
9749 spinlock_info_index = 0;
9752 MAYBE_UNUSED_VAR(enter_state);
9753 MAYBE_UNUSED_VAR(take_state);
9754 #endif //SPINLOCK_HISTORY
9758 gc_heap::init_gc_heap (int h_number)
9760 #ifdef MULTIPLE_HEAPS
9764 #ifdef SPINLOCK_HISTORY
9765 spinlock_info_index = 0;
9766 memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
9767 #endif //SPINLOCK_HISTORY
9769 // initialize per heap members.
9770 ephemeral_low = (BYTE*)1;
9772 ephemeral_high = MAX_PTR;
9774 ephemeral_heap_segment = 0;
9776 freeable_large_heap_segment = 0;
9778 condemned_generation_num = 0;
9780 blocking_collection = FALSE;
9782 generation_skip_ratio = 100;
9788 mark_stack_array_length = 0;
9790 mark_stack_array = 0;
9792 verify_pinned_queue_p = FALSE;
9794 loh_pinned_queue_tos = 0;
9796 loh_pinned_queue_bos = 0;
9798 loh_pinned_queue_length = 0;
9800 loh_pinned_queue_decay = LOH_PIN_DECAY;
9802 loh_pinned_queue = 0;
9804 min_overflow_address = MAX_PTR;
9806 max_overflow_address = 0;
9808 gen0_bricks_cleared = FALSE;
9810 gen0_must_clear_bricks = 0;
9812 allocation_quantum = CLR_SIZE;
9814 more_space_lock = gc_lock;
9816 ro_segments_in_range = FALSE;
9818 loh_alloc_since_cg = 0;
9820 new_heap_segment = NULL;
9822 #ifdef RECORD_LOH_STATE
9823 loh_state_index = 0;
9824 #endif //RECORD_LOH_STATE
9825 #endif //MULTIPLE_HEAPS
9827 #ifdef MULTIPLE_HEAPS
9828 if (h_number > n_heaps)
9830 assert (!"Number of heaps exceeded");
9834 heap_number = h_number;
9835 #endif //MULTIPLE_HEAPS
9837 memset (&oom_info, 0, sizeof (oom_info));
9838 memset (&fgm_result, 0, sizeof (fgm_result));
9839 gc_done_event.CreateManualEvent(FALSE);
9840 if (!gc_done_event.IsValid())
9844 gc_done_event_lock = -1;
9845 gc_done_event_set = false;
9847 #ifndef SEG_MAPPING_TABLE
9848 if (!gc_heap::seg_table->insure_space_for_insert ())
9852 #endif //!SEG_MAPPING_TABLE
9854 heap_segment* seg = get_initial_segment (get_valid_segment_size(), h_number);
9858 FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(seg),
9859 (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
9860 ETW::GCLog::ETW_GC_INFO::SMALL_OBJECT_HEAP,
9861 GetClrInstanceId());
9863 #ifdef SEG_MAPPING_TABLE
9864 seg_mapping_table_add_segment (seg, __this);
9865 #else //SEG_MAPPING_TABLE
9866 seg_table->insert ((BYTE*)seg, sdelta);
9867 #endif //SEG_MAPPING_TABLE
9869 #ifdef MULTIPLE_HEAPS
9870 heap_segment_heap (seg) = this;
9871 #endif //MULTIPLE_HEAPS
9873 /* todo: Need a global lock for this */
9874 DWORD* ct = &g_card_table [card_word (card_of (g_lowest_address))];
9875 own_card_table (ct);
9876 card_table = translate_card_table (ct);
9877 /* End of global lock */
9879 brick_table = card_table_brick_table (ct);
9880 highest_address = card_table_highest_address (ct);
9881 lowest_address = card_table_lowest_address (ct);
9884 card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct));
9885 assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] ==
9886 card_table_card_bundle_table (ct));
9887 #endif //CARD_BUNDLE
9890 if (gc_can_use_concurrent)
9891 mark_array = translate_mark_array (card_table_mark_array (&g_card_table[card_word (card_of (g_lowest_address))]));
9896 BYTE* start = heap_segment_mem (seg);
9898 for (int i = 0; i < 1 + max_generation; i++)
9900 make_generation (generation_table [ (max_generation - i) ],
9902 generation_table [(max_generation - i)].gen_num = max_generation - i;
9903 start += Align (min_obj_size);
9906 heap_segment_allocated (seg) = start;
9907 alloc_allocated = start;
9908 heap_segment_used (seg) = start - plug_skew;
9910 ephemeral_heap_segment = seg;
9912 #ifndef SEG_MAPPING_TABLE
9913 if (!gc_heap::seg_table->insure_space_for_insert ())
9917 #endif //!SEG_MAPPING_TABLE
9918 //Create the large segment generation
9919 heap_segment* lseg = get_initial_segment(get_valid_segment_size(TRUE), h_number);
9922 lseg->flags |= heap_segment_flags_loh;
9924 FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(lseg),
9925 (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
9926 ETW::GCLog::ETW_GC_INFO::LARGE_OBJECT_HEAP,
9927 GetClrInstanceId());
9928 #ifdef SEG_MAPPING_TABLE
9929 seg_mapping_table_add_segment (lseg, __this);
9930 #else //SEG_MAPPING_TABLE
9931 seg_table->insert ((BYTE*)lseg, sdelta);
9932 #endif //SEG_MAPPING_TABLE
9934 generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
9935 //assign the alloc_list for the large generation
9936 generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
9937 generation_table [max_generation+1].gen_num = max_generation+1;
9938 make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
9939 heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
9940 heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
9942 for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
9944 generation* gen = generation_of (gen_num);
9945 make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
9948 #ifdef MULTIPLE_HEAPS
9949 heap_segment_heap (lseg) = this;
9951 //initialize the alloc context heap
9952 generation_alloc_context (generation_of (0))->alloc_heap = vm_heap;
9954 //initialize the alloc context heap
9955 generation_alloc_context (generation_of (max_generation+1))->alloc_heap = vm_heap;
9957 #endif //MULTIPLE_HEAPS
9960 #ifdef MULTIPLE_HEAPS
9962 #endif //MULTIPLE_HEAPS
9964 #ifndef INTERIOR_POINTERS
9965 //set the brick_table for large objects
9966 //but default value is clearded
9967 //clear_brick_table ((BYTE*)heap_segment_mem (lseg),
9968 // (BYTE*)heap_segment_reserved (lseg));
9970 #else //INTERIOR_POINTERS
9972 //Because of the interior pointer business, we have to clear
9973 //the whole brick table
9974 //but the default value is cleared
9975 // clear_brick_table (lowest_address, highest_address);
9976 #endif //INTERIOR_POINTERS
9979 if (!init_dynamic_data())
9984 etw_allocation_running_amount[0] = 0;
9985 etw_allocation_running_amount[1] = 0;
9987 //needs to be done after the dynamic data has been initialized
9988 #ifndef MULTIPLE_HEAPS
9989 allocation_running_amount = dd_min_gc_size (dynamic_data_of (0));
9990 #endif //!MULTIPLE_HEAPS
9992 fgn_last_alloc = dd_min_gc_size (dynamic_data_of (0));
9994 mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
9998 make_mark_stack(arr);
10000 #ifdef BACKGROUND_GC
10001 freeable_small_heap_segment = 0;
10002 gchist_index_per_heap = 0;
10003 BYTE** b_arr = new (nothrow) (BYTE* [MARK_STACK_INITIAL_LENGTH]);
10007 make_background_mark_stack (b_arr);
10008 #endif //BACKGROUND_GC
10010 adjust_ephemeral_limits();
10013 // why would we clear the mark array for this page? it should be cleared..
10014 // clear the first committed page
10015 //if(gc_can_use_concurrent)
10017 // clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10019 #endif //MARK_ARRAY
10021 #ifdef MULTIPLE_HEAPS
10022 //register the heap in the heaps array
10024 g_gc_threads [heap_number] = create_gc_thread ();
10025 if (!g_gc_threads [heap_number])
10028 g_heaps [heap_number] = this;
10030 #endif //MULTIPLE_HEAPS
10032 #ifdef FEATURE_PREMORTEM_FINALIZATION
10033 HRESULT hr = AllocateCFinalize(&finalize_queue);
10036 #endif // FEATURE_PREMORTEM_FINALIZATION
10038 max_free_space_items = MAX_NUM_FREE_SPACES;
10040 bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10047 if (!bestfit_seg->alloc())
10052 last_gc_before_oom = FALSE;
10054 #ifdef MULTIPLE_HEAPS
10056 #ifdef HEAP_ANALYZE
10058 heap_analyze_success = TRUE;
10060 internal_root_array = 0;
10062 internal_root_array_index = 0;
10064 internal_root_array_length = initial_internal_roots;
10068 current_obj_size = 0;
10070 #endif //HEAP_ANALYZE
10072 #endif // MULTIPLE_HEAPS
10074 #ifdef BACKGROUND_GC
10077 if (!create_bgc_thread_support())
10082 bgc_alloc_lock = new (nothrow) exclusive_sync;
10083 if (!bgc_alloc_lock)
10088 bgc_alloc_lock->init();
10092 if (!recursive_gc_sync::init())
10096 bgc_thread_running = 0;
10098 InitializeCriticalSection (&bgc_threads_timeout_cs);
10099 expanded_in_fgc = 0;
10100 current_bgc_state = bgc_not_in_process;
10101 background_soh_alloc_count = 0;
10102 background_loh_alloc_count = 0;
10103 bgc_overflow_count = 0;
10104 end_loh_size = dd_min_gc_size (dynamic_data_of (max_generation + 1));
10105 #endif //BACKGROUND_GC
10110 gc_heap::destroy_semi_shared()
10112 //TODO: will need to move this to per heap
10113 //#ifdef BACKGROUND_GC
10114 // if (c_mark_list)
10115 // delete c_mark_list;
10116 //#endif //BACKGROUND_GC
10120 delete g_mark_list;
10123 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10124 if (seg_mapping_table)
10125 delete seg_mapping_table;
10126 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10128 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10129 //destroy the segment map
10130 seg_table->delete_sorted_table();
10131 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10135 gc_heap::self_destroy()
10137 #ifdef BACKGROUND_GC
10139 #endif //BACKGROUND_GC
10141 if (gc_done_event.IsValid())
10143 gc_done_event.CloseEvent();
10146 // destroy every segment.
10147 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10149 PREFIX_ASSUME(seg != NULL);
10151 heap_segment* next_seg;
10154 next_seg = heap_segment_next_rw (seg);
10155 delete_heap_segment (seg);
10159 seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10161 PREFIX_ASSUME(seg != NULL);
10165 next_seg = heap_segment_next_rw (seg);
10166 delete_heap_segment (seg);
10170 // get rid of the card table
10171 release_card_table (card_table);
10173 // destroy the mark stack
10174 delete mark_stack_array;
10176 #ifdef FEATURE_PREMORTEM_FINALIZATION
10177 if (finalize_queue)
10178 delete finalize_queue;
10179 #endif // FEATURE_PREMORTEM_FINALIZATION
10183 gc_heap::destroy_gc_heap(gc_heap* heap)
10185 heap->self_destroy();
10189 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10190 // the finalizer queue has been drained.
10191 void gc_heap::shutdown_gc()
10193 destroy_semi_shared();
10195 #ifdef MULTIPLE_HEAPS
10196 //delete the heaps array
10198 for (int i = 0; i < n_heaps; i++)
10200 CloseHandle (g_gc_threads [i]);
10202 delete g_gc_threads;
10203 destroy_thread_support();
10205 #endif //MULTIPLE_HEAPS
10206 //destroy seg_manager
10208 destroy_initial_memory();
10212 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, BYTE* alloc_pointer, BYTE* alloc_limit,
10213 BYTE* old_loc, int use_padding)
10215 BOOL already_padded = FALSE;
10217 if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
10219 alloc_pointer = alloc_pointer + Align (min_obj_size);
10220 already_padded = TRUE;
10222 #endif //SHORT_PLUGS
10224 // TODO: this is incorrect - if we don't pad, we would have a different alignment so
10225 // calculating the alignment requirement here is incorrect.
10226 if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
10227 size = size + switch_alignment_size (already_padded);
10229 #ifdef FEATURE_STRUCTALIGN
10230 alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
10231 #endif // FEATURE_STRUCTALIGN
10233 // in allocate_in_condemned_generation we can have this when we
10234 // set the alloc_limit to plan_allocated which could be less than
10236 if (alloc_limit < alloc_pointer)
10243 return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0)))
10245 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
10246 #else //SHORT_PLUGS
10247 ||((alloc_pointer + size) == alloc_limit)
10248 #endif //SHORT_PLUGS
10253 assert (size == Align (min_obj_size));
10254 return ((size_t)(alloc_limit - alloc_pointer) >= size);
10259 BOOL gc_heap::a_size_fit_p (size_t size, BYTE* alloc_pointer, BYTE* alloc_limit,
10262 // We could have run into cases where this is true when alloc_allocated is the
10263 // the same as the seg committed.
10264 if (alloc_limit < alloc_pointer)
10269 return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
10272 // Grow by committing more pages
10273 BOOL gc_heap::grow_heap_segment (heap_segment* seg, BYTE* high_address)
10275 assert (high_address <= heap_segment_reserved (seg));
10277 //return 0 if we are at the end of the segment.
10278 if (align_on_page (high_address) > heap_segment_reserved (seg))
10281 if (high_address <= heap_segment_committed (seg))
10284 size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
10285 c_size = max (c_size, 16*OS_PAGE_SIZE);
10286 c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
10291 STRESS_LOG2(LF_GC, LL_INFO10000,
10292 "Growing heap_segment: %Ix high address: %Ix\n",
10293 (size_t)seg, (size_t)high_address);
10295 dprintf(3, ("Growing segment allocation %Ix %Ix", (size_t)heap_segment_committed(seg),c_size));
10297 if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size,
10298 MEM_COMMIT, PAGE_READWRITE, heap_number))
10300 dprintf(3, ("Cannot grow heap segment"));
10304 #ifndef BACKGROUND_GC
10305 clear_mark_array (heap_segment_committed (seg),
10306 heap_segment_committed (seg)+c_size, TRUE);
10307 #endif //BACKGROUND_GC
10308 #endif //MARK_ARRAY
10309 heap_segment_committed (seg) += c_size;
10310 STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
10311 (size_t)heap_segment_committed (seg));
10313 assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
10315 assert (high_address <= heap_segment_committed (seg));
10321 int gc_heap::grow_heap_segment (heap_segment* seg, BYTE* allocated, BYTE* old_loc, size_t size, BOOL pad_front_p REQD_ALIGN_AND_OFFSET_DCL)
10324 if ((old_loc != 0) && pad_front_p)
10326 allocated = allocated + Align (min_obj_size);
10328 #endif //SHORT_PLUGS
10330 if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
10331 size = size + switch_alignment_size (FALSE);
10332 #ifdef FEATURE_STRUCTALIGN
10333 size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
10334 return grow_heap_segment (seg, allocated + pad + size);
10335 #else // FEATURE_STRUCTALIGN
10336 return grow_heap_segment (seg, allocated + size);
10337 #endif // FEATURE_STRUCTALIGN
10340 //used only in older generation allocation (i.e during gc).
10341 void gc_heap::adjust_limit (BYTE* start, size_t limit_size, generation* gen,
10344 dprintf (3, ("gc Expanding segment allocation"));
10345 heap_segment* seg = generation_allocation_segment (gen);
10346 if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
10348 if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
10350 assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
10351 assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
10352 heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
10356 BYTE* hole = generation_allocation_pointer (gen);
10357 size_t size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
10361 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
10362 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
10363 if (size >= Align (min_free_list))
10365 if (allocated_size < min_free_list)
10367 if (size >= (Align (min_free_list) + Align (min_obj_size)))
10369 //split hole into min obj + threadable free item
10370 make_unused_array (hole, min_obj_size);
10371 generation_free_obj_space (gen) += Align (min_obj_size);
10372 make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
10373 generation_free_list_space (gen) += size - Align (min_obj_size);
10374 generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size),
10375 size - Align (min_obj_size));
10376 add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
10380 dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
10381 make_unused_array (hole, size);
10382 generation_free_obj_space (gen) += size;
10387 dprintf (3, ("threading hole in front of free list"));
10388 make_unused_array (hole, size);
10389 generation_free_list_space (gen) += size;
10390 generation_allocator(gen)->thread_item_front (hole, size);
10391 add_gen_free (gen->gen_num, size);
10396 make_unused_array (hole, size);
10397 generation_free_obj_space (gen) += size;
10401 generation_allocation_pointer (gen) = start;
10402 generation_allocation_context_start_region (gen) = start;
10404 generation_allocation_limit (gen) = (start + limit_size);
10407 void verify_mem_cleared (BYTE* start, size_t size)
10409 if (!Aligned (size))
10414 PTR_PTR curr_ptr = (PTR_PTR) start;
10415 for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
10417 if (*(curr_ptr++) != 0)
10424 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
10425 void gc_heap::set_batch_mark_array_bits (BYTE* start, BYTE* end)
10427 size_t start_mark_bit = mark_bit_of (start);
10428 size_t end_mark_bit = mark_bit_of (end);
10429 unsigned int startbit = mark_bit_bit (start_mark_bit);
10430 unsigned int endbit = mark_bit_bit (end_mark_bit);
10431 size_t startwrd = mark_bit_word (start_mark_bit);
10432 size_t endwrd = mark_bit_word (end_mark_bit);
10434 dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
10435 (size_t)start, (size_t)start_mark_bit,
10436 (size_t)end, (size_t)end_mark_bit));
10438 unsigned int firstwrd = ~(lowbits (~0, startbit));
10439 unsigned int lastwrd = ~(highbits (~0, endbit));
10441 if (startwrd == endwrd)
10443 unsigned int wrd = firstwrd & lastwrd;
10444 mark_array[startwrd] |= wrd;
10448 // set the first mark word.
10451 mark_array[startwrd] |= firstwrd;
10455 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
10457 mark_array[wrdtmp] = ~(unsigned int)0;
10460 // set the last mark word.
10463 mark_array[endwrd] |= lastwrd;
10467 // makes sure that the mark array bits between start and end are 0.
10468 void gc_heap::check_batch_mark_array_bits (BYTE* start, BYTE* end)
10470 size_t start_mark_bit = mark_bit_of (start);
10471 size_t end_mark_bit = mark_bit_of (end);
10472 unsigned int startbit = mark_bit_bit (start_mark_bit);
10473 unsigned int endbit = mark_bit_bit (end_mark_bit);
10474 size_t startwrd = mark_bit_word (start_mark_bit);
10475 size_t endwrd = mark_bit_word (end_mark_bit);
10477 //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
10478 // (size_t)start, (size_t)start_mark_bit,
10479 // (size_t)end, (size_t)end_mark_bit));
10481 unsigned int firstwrd = ~(lowbits (~0, startbit));
10482 unsigned int lastwrd = ~(highbits (~0, endbit));
10484 if (startwrd == endwrd)
10486 unsigned int wrd = firstwrd & lastwrd;
10487 if (mark_array[startwrd] & wrd)
10489 dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
10491 mark_array [startwrd], mark_word_address (startwrd)));
10497 // set the first mark word.
10500 if (mark_array[startwrd] & firstwrd)
10502 dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
10503 firstwrd, startwrd,
10504 mark_array [startwrd], mark_word_address (startwrd)));
10511 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
10513 if (mark_array[wrdtmp])
10515 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
10517 mark_array [wrdtmp], mark_word_address (wrdtmp)));
10522 // set the last mark word.
10525 if (mark_array[endwrd] & lastwrd)
10527 dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
10529 mark_array [lastwrd], mark_word_address (lastwrd)));
10534 #endif //VERIFY_HEAP && BACKGROUND_GC
10536 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
10538 assert (num_b < MAX_BUCKET_COUNT);
10539 num_buckets = num_b;
10540 frst_bucket_size = fbs;
10544 alloc_list& allocator::alloc_list_of (unsigned int bn)
10546 assert (bn < num_buckets);
10548 return first_bucket;
10550 return buckets [bn-1];
10553 void allocator::unlink_item (unsigned int bn, BYTE* item, BYTE* prev_item, BOOL use_undo_p)
10555 //unlink the free_item
10556 alloc_list* al = &alloc_list_of (bn);
10559 if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
10561 free_list_undo (prev_item) = item;
10563 free_list_slot (prev_item) = free_list_slot(item);
10567 al->alloc_list_head() = (BYTE*)free_list_slot(item);
10569 if (al->alloc_list_tail() == item)
10571 al->alloc_list_tail() = prev_item;
10575 void allocator::clear()
10577 for (unsigned int i = 0; i < num_buckets; i++)
10579 alloc_list_head_of (i) = 0;
10580 alloc_list_tail_of (i) = 0;
10584 //always thread to the end.
10585 void allocator::thread_free_item (BYTE* item, BYTE*& head, BYTE*& tail)
10587 free_list_slot (item) = 0;
10588 free_list_undo (item) = UNDO_EMPTY;
10589 assert (item != head);
10595 //TODO: This shouldn't happen anymore - verify that's the case.
10596 //the following is necessary because the last free element
10597 //may have been truncated, and tail isn't updated.
10598 else if (free_list_slot (head) == 0)
10600 free_list_slot (head) = item;
10604 assert (item != tail);
10605 assert (free_list_slot(tail) == 0);
10606 free_list_slot (tail) = item;
10611 void allocator::thread_item (BYTE* item, size_t size)
10613 size_t sz = frst_bucket_size;
10614 unsigned int a_l_number = 0;
10616 for (; a_l_number < (num_buckets-1); a_l_number++)
10624 alloc_list* al = &alloc_list_of (a_l_number);
10625 thread_free_item (item,
10626 al->alloc_list_head(),
10627 al->alloc_list_tail());
10630 void allocator::thread_item_front (BYTE* item, size_t size)
10632 //find right free list
10633 size_t sz = frst_bucket_size;
10634 unsigned int a_l_number = 0;
10635 for (; a_l_number < (num_buckets-1); a_l_number++)
10643 alloc_list* al = &alloc_list_of (a_l_number);
10644 free_list_slot (item) = al->alloc_list_head();
10645 free_list_undo (item) = UNDO_EMPTY;
10646 if (al->alloc_list_tail() == 0)
10648 al->alloc_list_tail() = al->alloc_list_head();
10650 al->alloc_list_head() = item;
10651 if (al->alloc_list_tail() == 0)
10653 al->alloc_list_tail() = item;
10657 void allocator::copy_to_alloc_list (alloc_list* toalist)
10659 for (unsigned int i = 0; i < num_buckets; i++)
10661 toalist [i] = alloc_list_of (i);
10665 void allocator::copy_from_alloc_list (alloc_list* fromalist)
10667 BOOL repair_list = !discard_if_no_fit_p ();
10668 for (unsigned int i = 0; i < num_buckets; i++)
10670 alloc_list_of (i) = fromalist [i];
10673 //repair the the list
10674 //new items may have been added during the plan phase
10675 //items may have been unlinked.
10676 BYTE* free_item = alloc_list_head_of (i);
10679 assert (((CObjectHeader*)free_item)->IsFree());
10680 if ((free_list_undo (free_item) != UNDO_EMPTY))
10682 free_list_slot (free_item) = free_list_undo (free_item);
10683 free_list_undo (free_item) = UNDO_EMPTY;
10686 free_item = free_list_slot (free_item);
10690 BYTE* tail_item = alloc_list_tail_of (i);
10691 assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
10696 void allocator::commit_alloc_list_changes()
10698 BOOL repair_list = !discard_if_no_fit_p ();
10701 for (unsigned int i = 0; i < num_buckets; i++)
10703 //remove the undo info from list.
10704 BYTE* free_item = alloc_list_head_of (i);
10707 assert (((CObjectHeader*)free_item)->IsFree());
10708 free_list_undo (free_item) = UNDO_EMPTY;
10709 free_item = free_list_slot (free_item);
10715 void gc_heap::adjust_limit_clr (BYTE* start, size_t limit_size,
10716 alloc_context* acontext, heap_segment* seg,
10719 //probably should pass seg==0 for free lists.
10722 assert (heap_segment_used (seg) <= heap_segment_committed (seg));
10725 dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
10726 (size_t)start + limit_size - Align (min_obj_size, align_const)));
10728 if ((acontext->alloc_limit != start) &&
10729 (acontext->alloc_limit + Align (min_obj_size, align_const))!= start)
10731 BYTE* hole = acontext->alloc_ptr;
10734 size_t size = (acontext->alloc_limit - acontext->alloc_ptr);
10735 dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
10736 // when we are finishing an allocation from a free list
10737 // we know that the free area was Align(min_obj_size) larger
10738 make_unused_array (hole, size + Align (min_obj_size, align_const));
10740 acontext->alloc_ptr = start;
10742 acontext->alloc_limit = (start + limit_size - Align (min_obj_size, align_const));
10743 acontext->alloc_bytes += limit_size;
10745 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
10748 AppDomain* alloc_appdomain = GetAppDomain();
10749 alloc_appdomain->RecordAllocBytes (limit_size, heap_number);
10751 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
10753 BYTE* saved_used = 0;
10757 saved_used = heap_segment_used (seg);
10760 if (seg == ephemeral_heap_segment)
10762 //Sometimes the allocated size is advanced without clearing the
10763 //memory. Let's catch up here
10764 if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
10767 #ifndef BACKGROUND_GC
10768 clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
10769 #endif //BACKGROUND_GC
10770 #endif //MARK_ARRAY
10771 heap_segment_used (seg) = alloc_allocated - plug_skew;
10774 #ifdef BACKGROUND_GC
10777 BYTE* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
10778 #ifdef FEATURE_LOH_COMPACTION
10779 old_allocated -= Align (loh_padding_obj_size, align_const);
10780 #endif //FEATURE_LOH_COMPACTION
10782 assert (heap_segment_used (seg) >= old_allocated);
10784 #endif //BACKGROUND_GC
10786 (start - plug_skew + limit_size) <= heap_segment_used (seg))
10788 dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory(1)", heap_number));
10789 add_saved_spinlock_info (me_release, mt_clr_mem);
10790 leave_spin_lock (&more_space_lock);
10791 dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
10792 memclr (start - plug_skew, limit_size);
10796 BYTE* used = heap_segment_used (seg);
10797 heap_segment_used (seg) = start + limit_size - plug_skew;
10799 dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory", heap_number));
10800 add_saved_spinlock_info (me_release, mt_clr_mem);
10801 leave_spin_lock (&more_space_lock);
10802 if ((start - plug_skew) < used)
10804 if (used != saved_used)
10809 dprintf (2, ("clearing memory before used at %Ix for %Id bytes",
10810 (start - plug_skew), (plug_skew + used - start)));
10811 memclr (start - plug_skew, used - (start - plug_skew));
10815 //this portion can be done after we release the lock
10816 if (seg == ephemeral_heap_segment)
10818 #ifdef FFIND_OBJECT
10819 if (gen0_must_clear_bricks > 0)
10821 //set the brick table to speed up find_object
10822 size_t b = brick_of (acontext->alloc_ptr);
10823 set_brick (b, acontext->alloc_ptr - brick_address (b));
10825 dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
10826 b, brick_of (align_on_brick (start + limit_size))));
10827 short* x = &brick_table [b];
10828 short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
10830 for (;x < end_x;x++)
10834 #endif //FFIND_OBJECT
10836 gen0_bricks_cleared = FALSE;
10840 // verifying the memory is completely cleared.
10841 //verify_mem_cleared (start - plug_skew, limit_size);
10844 /* in order to make the allocator faster, allocate returns a
10845 * 0 filled object. Care must be taken to set the allocation limit to the
10846 * allocation pointer after gc
10849 size_t gc_heap::limit_from_size (size_t size, size_t room, int gen_number,
10852 size_t new_limit = new_allocation_limit ((size + Align (min_obj_size, align_const)),
10853 min (room,max (size + Align (min_obj_size, align_const),
10854 ((gen_number < max_generation+1) ?
10855 allocation_quantum :
10858 assert (new_limit >= (size + Align (min_obj_size, align_const)));
10859 dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
10863 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size,
10864 BYTE* allocated, BYTE* reserved)
10866 if (reason == oom_budget)
10868 alloc_size = dd_min_gc_size (dynamic_data_of (0)) / 2;
10871 if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
10873 // This means during the last GC we needed to reserve and/or commit more memory
10874 // but we couldn't. We proceeded with the GC and ended up not having enough
10875 // memory at the end. This is a legitimate OOM situtation. Otherwise we
10876 // probably made a mistake and didn't expand the heap when we should have.
10877 reason = oom_low_mem;
10880 oom_info.reason = reason;
10881 oom_info.allocated = allocated;
10882 oom_info.reserved = reserved;
10883 oom_info.alloc_size = alloc_size;
10884 oom_info.gc_index = settings.gc_index;
10885 oom_info.fgm = fgm_result.fgm;
10886 oom_info.size = fgm_result.size;
10887 oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
10888 oom_info.loh_p = fgm_result.loh_p;
10890 fgm_result.fgm = fgm_no_failure;
10892 // Break early - before the more_space_lock is release so no other threads
10893 // could have allocated on the same heap when OOM happened.
10894 if (g_pConfig->IsGCBreakOnOOMEnabled())
10900 #ifdef BACKGROUND_GC
10901 BOOL gc_heap::background_allowed_p()
10903 return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
10905 #endif //BACKGROUND_GC
10907 void gc_heap::check_for_full_gc (int gen_num, size_t size)
10909 BOOL should_notify = FALSE;
10910 // if we detect full gc because of the allocation budget specified this is TRUE;
10911 // it's FALSE if it's due to other factors.
10912 BOOL alloc_factor = TRUE;
10915 int n_initial = gen_num;
10916 BOOL local_blocking_collection = FALSE;
10917 BOOL local_elevation_requested = FALSE;
10918 int new_alloc_remain_percent = 0;
10920 if (full_gc_approach_event_set)
10925 if (gen_num != (max_generation + 1))
10927 gen_num = max_generation;
10930 dynamic_data* dd_full = dynamic_data_of (gen_num);
10931 SSIZE_T new_alloc_remain = 0;
10932 DWORD pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
10934 for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
10936 dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)",
10937 heap_number, gen_index,
10938 dd_new_allocation (dynamic_data_of (gen_index)),
10939 dd_desired_allocation (dynamic_data_of (gen_index))));
10942 // For small object allocations we only check every fgn_check_quantum bytes.
10943 if (n_initial == 0)
10945 dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
10946 dynamic_data* dd_0 = dynamic_data_of (n_initial);
10947 if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
10948 (dd_new_allocation (dd_0) >= 0))
10954 fgn_last_alloc = dd_new_allocation (dd_0);
10955 dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
10958 // We don't consider the size that came from soh 'cause it doesn't contribute to the
10963 for (i = n+1; i <= max_generation; i++)
10965 if (get_new_allocation (i) <= 0)
10967 n = min (i, max_generation);
10973 dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
10974 if (gen_num == max_generation)
10976 // If it's small object heap we should first see if we will even be looking at gen2 budget
10977 // in the next GC or not. If not we should go directly to checking other factors.
10978 if (n < (max_generation - 1))
10980 goto check_other_factors;
10984 new_alloc_remain = dd_new_allocation (dd_full) - size;
10986 new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
10988 dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%",
10989 gen_num, pct, new_alloc_remain_percent));
10991 if (new_alloc_remain_percent <= (int)pct)
10993 #ifdef BACKGROUND_GC
10994 // If background GC is enabled, we still want to check whether this will
10995 // be a blocking GC or not because we only want to notify when it's a
10996 // blocking full GC.
10997 if (background_allowed_p())
10999 goto check_other_factors;
11001 #endif //BACKGROUND_GC
11003 should_notify = TRUE;
11007 check_other_factors:
11009 dprintf (2, ("FGC: checking other factors"));
11010 n = generation_to_condemn (n,
11011 &local_blocking_collection,
11012 &local_elevation_requested,
11015 if (local_elevation_requested && (n == max_generation))
11017 if (settings.should_lock_elevation)
11019 int local_elevation_locked_count = settings.elevation_locked_count + 1;
11020 if (local_elevation_locked_count != 6)
11022 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1",
11023 local_elevation_locked_count));
11024 n = max_generation - 1;
11029 dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11031 #ifdef BACKGROUND_GC
11032 // When background GC is enabled it decreases the accurancy of our predictability -
11033 // by the time the GC happens, we may not be under BGC anymore. If we try to
11034 // predict often enough it should be ok.
11035 if ((n == max_generation) &&
11036 (recursive_gc_sync::background_running_p()))
11038 n = max_generation - 1;
11039 dprintf (2, ("FGN: bgc - 1 instead of 2"));
11042 if ((n == max_generation) && !local_blocking_collection)
11044 if (!background_allowed_p())
11046 local_blocking_collection = TRUE;
11049 #endif //BACKGROUND_GC
11051 dprintf (2, ("FGN: we estimate gen%d will be collected: %s",
11053 (local_blocking_collection ? "blocking" : "background")));
11055 if ((n == max_generation) && local_blocking_collection)
11057 alloc_factor = FALSE;
11058 should_notify = TRUE;
11066 dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)",
11068 (alloc_factor ? "alloc" : "other"),
11069 dd_collection_count (dynamic_data_of (0)),
11070 new_alloc_remain_percent,
11073 send_full_gc_notification (n_initial, alloc_factor);
11077 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11079 if (!full_gc_approach_event_set)
11081 assert (full_gc_approach_event.IsValid());
11082 FireEtwGCFullNotify_V1 (gen_num, due_to_alloc_p, GetClrInstanceId());
11084 full_gc_end_event.Reset();
11085 full_gc_approach_event.Set();
11086 full_gc_approach_event_set = true;
11090 wait_full_gc_status gc_heap::full_gc_wait (CLREvent *event, int time_out_ms)
11092 if (fgn_maxgen_percent == 0)
11094 return wait_full_gc_na;
11097 DWORD wait_result = user_thread_wait(event, FALSE, time_out_ms);
11099 if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11101 if (fgn_maxgen_percent == 0)
11103 return wait_full_gc_cancelled;
11106 if (wait_result == WAIT_OBJECT_0)
11108 #ifdef BACKGROUND_GC
11109 if (fgn_last_gc_was_concurrent)
11111 fgn_last_gc_was_concurrent = FALSE;
11112 return wait_full_gc_na;
11115 #endif //BACKGROUND_GC
11117 return wait_full_gc_success;
11122 return wait_full_gc_timeout;
11127 return wait_full_gc_failed;
11131 size_t gc_heap::get_full_compact_gc_count()
11133 return full_gc_counts[gc_type_compacting];
11136 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
11139 BOOL gc_heap::short_on_end_of_seg (int gen_number,
11143 BYTE* allocated = heap_segment_allocated(seg);
11145 return (!a_size_fit_p (end_space_after_gc(),
11147 heap_segment_reserved (seg),
11152 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
11156 BOOL gc_heap::a_fit_free_list_p (int gen_number,
11158 alloc_context* acontext,
11161 BOOL can_fit = FALSE;
11162 generation* gen = generation_of (gen_number);
11163 allocator* gen_allocator = generation_allocator (gen);
11164 size_t sz_list = gen_allocator->first_bucket_size();
11165 for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
11167 if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
11169 BYTE* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
11170 BYTE* prev_free_item = 0;
11172 while (free_list != 0)
11174 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11175 size_t free_list_size = unused_array_size (free_list);
11176 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
11178 dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
11179 (size_t)free_list, free_list_size));
11181 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11182 // We ask for more Align (min_obj_size)
11183 // to make sure that we can insert a free object
11184 // in adjust_limit will set the limit lower
11185 size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
11187 BYTE* remain = (free_list + limit);
11188 size_t remain_size = (free_list_size - limit);
11189 if (remain_size >= Align(min_free_list, align_const))
11191 make_unused_array (remain, remain_size);
11192 gen_allocator->thread_item_front (remain, remain_size);
11193 assert (remain_size >= Align (min_obj_size, align_const));
11197 //absorb the entire free list
11198 limit += remain_size;
11200 generation_free_list_space (gen) -= limit;
11202 adjust_limit_clr (free_list, limit, acontext, 0, align_const);
11207 else if (gen_allocator->discard_if_no_fit_p())
11209 assert (prev_free_item == 0);
11210 dprintf (3, ("couldn't use this free area, discarding"));
11211 generation_free_obj_space (gen) += free_list_size;
11213 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11214 generation_free_list_space (gen) -= free_list_size;
11218 prev_free_item = free_list;
11220 free_list = free_list_slot (free_list);
11223 sz_list = sz_list * 2;
11230 #ifdef BACKGROUND_GC
11231 void gc_heap::bgc_loh_alloc_clr (BYTE* alloc_start,
11233 alloc_context* acontext,
11239 make_unused_array (alloc_start, size);
11241 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11244 AppDomain* alloc_appdomain = GetAppDomain();
11245 alloc_appdomain->RecordAllocBytes (size, heap_number);
11247 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11249 size_t size_of_array_base = sizeof(ArrayBase);
11251 bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
11253 // clear memory while not holding the lock.
11254 size_t size_to_skip = size_of_array_base;
11255 size_t size_to_clear = size - size_to_skip - plug_skew;
11256 size_t saved_size_to_clear = size_to_clear;
11259 BYTE* end = alloc_start + size - plug_skew;
11260 BYTE* used = heap_segment_used (seg);
11263 if ((alloc_start + size_to_skip) < used)
11265 size_to_clear = used - (alloc_start + size_to_skip);
11271 dprintf (2, ("bgc loh: setting used to %Ix", end));
11272 heap_segment_used (seg) = end;
11275 dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
11276 used, alloc_start, end, size_to_clear));
11280 dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
11284 // since we filled in 0xcc for free object when we verify heap,
11285 // we need to make sure we clear those bytes.
11286 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
11288 if (size_to_clear < saved_size_to_clear)
11290 size_to_clear = saved_size_to_clear;
11293 #endif //VERIFY_HEAP
11295 dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
11296 add_saved_spinlock_info (me_release, mt_clr_large_mem);
11297 leave_spin_lock (&more_space_lock);
11298 memclr (alloc_start + size_to_skip, size_to_clear);
11300 bgc_alloc_lock->loh_alloc_set (alloc_start);
11302 acontext->alloc_ptr = alloc_start;
11303 acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
11305 // need to clear the rest of the object before we hand it out.
11306 clear_unused_array(alloc_start, size);
11308 #endif //BACKGROUND_GC
11310 BOOL gc_heap::a_fit_free_list_large_p (size_t size,
11311 alloc_context* acontext,
11314 #ifdef BACKGROUND_GC
11315 wait_for_background_planning (awr_loh_alloc_during_plan);
11316 #endif //BACKGROUND_GC
11318 BOOL can_fit = FALSE;
11319 int gen_number = max_generation + 1;
11320 generation* gen = generation_of (gen_number);
11321 allocator* loh_allocator = generation_allocator (gen);
11323 #ifdef FEATURE_LOH_COMPACTION
11324 size_t loh_pad = Align (loh_padding_obj_size, align_const);
11325 #endif //FEATURE_LOH_COMPACTION
11327 #ifdef BACKGROUND_GC
11329 #endif //BACKGROUND_GC
11330 size_t sz_list = loh_allocator->first_bucket_size();
11331 for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
11333 if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
11335 BYTE* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
11336 BYTE* prev_free_item = 0;
11337 while (free_list != 0)
11339 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11341 size_t free_list_size = unused_array_size(free_list);
11343 #ifdef FEATURE_LOH_COMPACTION
11344 if ((size + loh_pad) <= free_list_size)
11346 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
11347 (size == free_list_size))
11348 #endif //FEATURE_LOH_COMPACTION
11350 #ifdef BACKGROUND_GC
11351 cookie = bgc_alloc_lock->loh_alloc_set (free_list);
11352 #endif //BACKGROUND_GC
11354 //unlink the free_item
11355 loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11357 // Substract min obj size because limit_from_size adds it. Not needed for LOH
11358 size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size,
11359 gen_number, align_const);
11361 #ifdef FEATURE_LOH_COMPACTION
11362 make_unused_array (free_list, loh_pad);
11364 free_list += loh_pad;
11365 free_list_size -= loh_pad;
11366 #endif //FEATURE_LOH_COMPACTION
11368 BYTE* remain = (free_list + limit);
11369 size_t remain_size = (free_list_size - limit);
11370 if (remain_size != 0)
11372 assert (remain_size >= Align (min_obj_size, align_const));
11373 make_unused_array (remain, remain_size);
11375 if (remain_size >= Align(min_free_list, align_const))
11377 loh_thread_gap_front (remain, remain_size, gen);
11378 assert (remain_size >= Align (min_obj_size, align_const));
11382 generation_free_obj_space (gen) += remain_size;
11384 generation_free_list_space (gen) -= free_list_size;
11385 dprintf (3, ("found fit on loh at %Ix", free_list));
11386 #ifdef BACKGROUND_GC
11389 bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
11392 #endif //BACKGROUND_GC
11394 adjust_limit_clr (free_list, limit, acontext, 0, align_const);
11397 //fix the limit to compensate for adjust_limit_clr making it too short
11398 acontext->alloc_limit += Align (min_obj_size, align_const);
11402 prev_free_item = free_list;
11403 free_list = free_list_slot (free_list);
11406 sz_list = sz_list * 2;
11413 #pragma warning(default:4706)
11416 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
11419 alloc_context* acontext,
11421 BOOL* commit_failed_p)
11423 *commit_failed_p = FALSE;
11425 #ifdef BACKGROUND_GC
11427 #endif //BACKGROUND_GC
11429 BYTE*& allocated = ((gen_number == 0) ?
11431 heap_segment_allocated(seg));
11433 size_t pad = Align (min_obj_size, align_const);
11435 #ifdef FEATURE_LOH_COMPACTION
11436 if (gen_number == (max_generation + 1))
11438 pad += Align (loh_padding_obj_size, align_const);
11440 #endif //FEATURE_LOH_COMPACTION
11442 BYTE* end = heap_segment_committed (seg) - pad;
11444 if (a_size_fit_p (size, allocated, end, align_const))
11446 limit = limit_from_size (size,
11448 gen_number, align_const);
11452 end = heap_segment_reserved (seg) - pad;
11454 if (a_size_fit_p (size, allocated, end, align_const))
11456 limit = limit_from_size (size,
11458 gen_number, align_const);
11459 if (grow_heap_segment (seg, allocated + limit))
11465 dprintf (2, ("can't grow segment, doing a full gc"));
11466 *commit_failed_p = TRUE;
11473 #ifdef BACKGROUND_GC
11474 if (gen_number != 0)
11476 cookie = bgc_alloc_lock->loh_alloc_set (allocated);
11478 #endif //BACKGROUND_GC
11481 old_alloc = allocated;
11482 #ifdef FEATURE_LOH_COMPACTION
11483 if (gen_number == (max_generation + 1))
11485 size_t loh_pad = Align (loh_padding_obj_size, align_const);
11486 make_unused_array (old_alloc, loh_pad);
11487 old_alloc += loh_pad;
11488 allocated += loh_pad;
11491 #endif //FEATURE_LOH_COMPACTION
11493 #if defined (VERIFY_HEAP) && defined (_DEBUG)
11494 ((void**) allocated)[-1] = 0; //clear the sync block
11495 #endif //VERIFY_HEAP && _DEBUG
11496 allocated += limit;
11498 dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
11500 #ifdef BACKGROUND_GC
11503 bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
11506 #endif //BACKGROUND_GC
11508 adjust_limit_clr (old_alloc, limit, acontext, seg, align_const);
11518 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
11520 alloc_context* acontext,
11522 BOOL* commit_failed_p,
11525 *commit_failed_p = FALSE;
11526 heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
11527 BOOL can_allocate_p = FALSE;
11531 if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)),
11532 acontext, align_const, commit_failed_p))
11534 acontext->alloc_limit += Align (min_obj_size, align_const);
11535 can_allocate_p = TRUE;
11540 if (*commit_failed_p)
11542 *oom_r = oom_cant_commit;
11547 seg = heap_segment_next_rw (seg);
11552 return can_allocate_p;
11555 #ifdef BACKGROUND_GC
11557 void gc_heap::wait_for_background (alloc_wait_reason awr)
11559 dprintf (2, ("BGC is already in progress, waiting for it to finish"));
11560 dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc done", heap_number));
11561 add_saved_spinlock_info (me_release, mt_wait_bgc);
11562 leave_spin_lock (&more_space_lock);
11563 background_gc_wait (awr);
11564 enter_spin_lock (&more_space_lock);
11565 add_saved_spinlock_info (me_acquire, mt_wait_bgc);
11566 dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc done", heap_number));
11569 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr)
11571 if (recursive_gc_sync::background_running_p())
11574 memset (&ms, 0, sizeof(ms));
11575 GetProcessMemoryLoad(&ms);
11576 if (ms.dwMemoryLoad >= 95)
11578 dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
11579 wait_for_background (awr);
11584 #endif //BACKGROUND_GC
11586 // We request to trigger an ephemeral GC but we may get a full compacting GC.
11587 // return TRUE if that's the case.
11588 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
11590 #ifdef BACKGROUND_GC
11591 wait_for_bgc_high_memory (awr_loh_oos_bgc);
11592 #endif //BACKGROUND_GC
11594 BOOL did_full_compact_gc = FALSE;
11596 dprintf (2, ("triggering a gen1 GC"));
11597 size_t last_full_compact_gc_count = get_full_compact_gc_count();
11598 vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
11600 #ifdef MULTIPLE_HEAPS
11601 enter_spin_lock (&more_space_lock);
11602 add_saved_spinlock_info (me_acquire, mt_t_eph_gc);
11603 dprintf (SPINLOCK_LOG, ("[%d]Emsl after a GC", heap_number));
11604 #endif //MULTIPLE_HEAPS
11606 size_t current_full_compact_gc_count = get_full_compact_gc_count();
11608 if (current_full_compact_gc_count > last_full_compact_gc_count)
11610 dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
11611 did_full_compact_gc = TRUE;
11614 return did_full_compact_gc;
11617 BOOL gc_heap::soh_try_fit (int gen_number,
11619 alloc_context* acontext,
11621 BOOL* commit_failed_p,
11622 BOOL* short_seg_end_p)
11624 BOOL can_allocate = TRUE;
11625 if (short_seg_end_p)
11627 *short_seg_end_p = FALSE;
11630 can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
11633 if (short_seg_end_p)
11635 *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
11637 // If the caller doesn't care, we always try to fit at the end of seg;
11638 // otherwise we would only try if we are actually not short at end of seg.
11639 if (!short_seg_end_p || !(*short_seg_end_p))
11641 can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size,
11642 acontext, align_const, commit_failed_p);
11646 return can_allocate;
11649 BOOL gc_heap::allocate_small (int gen_number,
11651 alloc_context* acontext,
11654 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
11655 if (recursive_gc_sync::background_running_p())
11657 background_soh_alloc_count++;
11658 if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
11660 Thread* current_thread = GetThread();
11661 add_saved_spinlock_info (me_release, mt_alloc_small);
11662 dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl", heap_number));
11663 leave_spin_lock (&more_space_lock);
11664 BOOL cooperative_mode = enable_preemptive (current_thread);
11665 __SwitchToThread (bgc_alloc_spin, CALLER_LIMITS_SPINNING);
11666 disable_preemptive (current_thread, cooperative_mode);
11667 enter_spin_lock (&more_space_lock);
11668 add_saved_spinlock_info (me_acquire, mt_alloc_small);
11669 dprintf (SPINLOCK_LOG, ("[%d]spin Emsl", heap_number));
11673 //__SwitchToThread (0, CALLER_LIMITS_SPINNING);
11676 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
11678 gc_reason gr = reason_oos_soh;
11679 oom_reason oom_r = oom_no_failure;
11681 // No variable values should be "carried over" from one state to the other.
11682 // That's why there are local variable for each state
11684 allocation_state soh_alloc_state = a_state_start;
11686 // If we can get a new seg it means allocation will succeed.
11689 dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
11690 switch (soh_alloc_state)
11692 case a_state_can_allocate:
11693 case a_state_cant_allocate:
11697 case a_state_start:
11699 soh_alloc_state = a_state_try_fit;
11702 case a_state_try_fit:
11704 BOOL commit_failed_p = FALSE;
11705 BOOL can_use_existing_p = FALSE;
11707 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11708 align_const, &commit_failed_p,
11710 soh_alloc_state = (can_use_existing_p ?
11711 a_state_can_allocate :
11713 a_state_trigger_full_compact_gc :
11714 a_state_trigger_ephemeral_gc));
11717 case a_state_try_fit_after_bgc:
11719 BOOL commit_failed_p = FALSE;
11720 BOOL can_use_existing_p = FALSE;
11721 BOOL short_seg_end_p = FALSE;
11723 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11724 align_const, &commit_failed_p,
11726 soh_alloc_state = (can_use_existing_p ?
11727 a_state_can_allocate :
11729 a_state_trigger_2nd_ephemeral_gc :
11730 a_state_trigger_full_compact_gc));
11733 case a_state_try_fit_after_cg:
11735 BOOL commit_failed_p = FALSE;
11736 BOOL can_use_existing_p = FALSE;
11737 BOOL short_seg_end_p = FALSE;
11739 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11740 align_const, &commit_failed_p,
11742 if (short_seg_end_p)
11744 soh_alloc_state = a_state_cant_allocate;
11745 oom_r = oom_budget;
11749 if (can_use_existing_p)
11751 soh_alloc_state = a_state_can_allocate;
11755 #ifdef MULTIPLE_HEAPS
11756 if (!commit_failed_p)
11758 // some other threads already grabbed the more space lock and allocated
11759 // so we should attemp an ephemeral GC again.
11760 assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
11761 soh_alloc_state = a_state_trigger_ephemeral_gc;
11764 #endif //MULTIPLE_HEAPS
11766 assert (commit_failed_p);
11767 soh_alloc_state = a_state_cant_allocate;
11768 oom_r = oom_cant_commit;
11774 case a_state_check_and_wait_for_bgc:
11776 BOOL bgc_in_progress_p = FALSE;
11777 BOOL did_full_compacting_gc = FALSE;
11779 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc);
11780 soh_alloc_state = (did_full_compacting_gc ?
11781 a_state_try_fit_after_cg :
11782 a_state_try_fit_after_bgc);
11785 case a_state_trigger_ephemeral_gc:
11787 BOOL commit_failed_p = FALSE;
11788 BOOL can_use_existing_p = FALSE;
11789 BOOL short_seg_end_p = FALSE;
11790 BOOL bgc_in_progress_p = FALSE;
11791 BOOL did_full_compacting_gc = FALSE;
11793 did_full_compacting_gc = trigger_ephemeral_gc (gr);
11794 if (did_full_compacting_gc)
11796 soh_alloc_state = a_state_try_fit_after_cg;
11800 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11801 align_const, &commit_failed_p,
11803 #ifdef BACKGROUND_GC
11804 bgc_in_progress_p = recursive_gc_sync::background_running_p();
11805 #endif //BACKGROUND_GC
11807 if (short_seg_end_p)
11809 soh_alloc_state = (bgc_in_progress_p ?
11810 a_state_check_and_wait_for_bgc :
11811 a_state_trigger_full_compact_gc);
11813 if (fgn_maxgen_percent)
11815 dprintf (2, ("FGN: doing last GC before we throw OOM"));
11816 send_full_gc_notification (max_generation, FALSE);
11821 if (can_use_existing_p)
11823 soh_alloc_state = a_state_can_allocate;
11827 #ifdef MULTIPLE_HEAPS
11828 if (!commit_failed_p)
11830 // some other threads already grabbed the more space lock and allocated
11831 // so we should attemp an ephemeral GC again.
11832 assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
11833 soh_alloc_state = a_state_trigger_ephemeral_gc;
11836 #endif //MULTIPLE_HEAPS
11838 soh_alloc_state = a_state_trigger_full_compact_gc;
11839 if (fgn_maxgen_percent)
11841 dprintf (2, ("FGN: failed to commit, doing full compacting GC"));
11842 send_full_gc_notification (max_generation, FALSE);
11850 case a_state_trigger_2nd_ephemeral_gc:
11852 BOOL commit_failed_p = FALSE;
11853 BOOL can_use_existing_p = FALSE;
11854 BOOL short_seg_end_p = FALSE;
11855 BOOL did_full_compacting_gc = FALSE;
11858 did_full_compacting_gc = trigger_ephemeral_gc (gr);
11860 if (did_full_compacting_gc)
11862 soh_alloc_state = a_state_try_fit_after_cg;
11866 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11867 align_const, &commit_failed_p,
11869 if (short_seg_end_p || commit_failed_p)
11871 soh_alloc_state = a_state_trigger_full_compact_gc;
11875 assert (can_use_existing_p);
11876 soh_alloc_state = a_state_can_allocate;
11881 case a_state_trigger_full_compact_gc:
11883 BOOL got_full_compacting_gc = FALSE;
11885 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
11886 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
11891 assert (!"Invalid state!");
11898 if (soh_alloc_state == a_state_cant_allocate)
11900 assert (oom_r != oom_no_failure);
11901 handle_oom (heap_number,
11904 heap_segment_allocated (ephemeral_heap_segment),
11905 heap_segment_reserved (ephemeral_heap_segment));
11907 dprintf (SPINLOCK_LOG, ("[%d]Lmsl for oom", heap_number));
11908 add_saved_spinlock_info (me_release, mt_alloc_small_cant);
11909 leave_spin_lock (&more_space_lock);
11912 return (soh_alloc_state == a_state_can_allocate);
11915 #ifdef BACKGROUND_GC
11917 void gc_heap::wait_for_background_planning (alloc_wait_reason awr)
11919 while (current_c_gc_state == c_gc_state_planning)
11921 dprintf (3, ("lh state planning, cannot allocate"));
11923 dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc plan", heap_number));
11924 add_saved_spinlock_info (me_release, mt_wait_bgc_plan);
11925 leave_spin_lock (&more_space_lock);
11926 background_gc_wait_lh (awr);
11927 enter_spin_lock (&more_space_lock);
11928 add_saved_spinlock_info (me_acquire, mt_wait_bgc_plan);
11929 dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc plan", heap_number));
11931 assert ((current_c_gc_state == c_gc_state_free) ||
11932 (current_c_gc_state == c_gc_state_marking));
11935 BOOL gc_heap::bgc_loh_should_allocate()
11937 size_t min_gc_size = dd_min_gc_size(dynamic_data_of (max_generation + 1));
11939 if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
11944 if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
11946 if ((bgc_begin_loh_size / end_loh_size) > 2)
11948 dprintf (3, ("alloc-ed too much before bgc started"));
11952 dprintf (3, ("alloc-ed too much after bgc started"));
11958 bgc_alloc_spin_loh = (DWORD)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
11962 #endif //BACKGROUND_GC
11964 size_t gc_heap::get_large_seg_size (size_t size)
11966 size_t default_seg_size = get_valid_segment_size(TRUE);
11967 #ifdef SEG_MAPPING_TABLE
11968 size_t align_size = default_seg_size;
11969 #else //SEG_MAPPING_TABLE
11970 size_t align_size = default_seg_size / 2;
11971 #endif //SEG_MAPPING_TABLE
11972 int align_const = get_alignment_constant (FALSE);
11973 size_t large_seg_size = align_on_page (
11974 max (default_seg_size,
11975 ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
11976 align_size) / align_size * align_size)));
11977 return large_seg_size;
11980 BOOL gc_heap::loh_get_new_seg (generation* gen,
11983 BOOL* did_full_compact_gc,
11986 *did_full_compact_gc = FALSE;
11988 size_t seg_size = get_large_seg_size (size);
11990 heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
11994 loh_alloc_since_cg += seg_size;
12001 return (new_seg != 0);
12004 BOOL gc_heap::retry_full_compact_gc (size_t size)
12006 size_t seg_size = get_large_seg_size (size);
12008 if (loh_alloc_since_cg >= (2 * (unsigned __int64)seg_size))
12013 #ifdef MULTIPLE_HEAPS
12014 unsigned __int64 total_alloc_size = 0;
12015 for (int i = 0; i < n_heaps; i++)
12017 total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12020 if (total_alloc_size >= (2 * (unsigned __int64)seg_size))
12024 #endif //MULTIPLE_HEAPS
12029 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12030 BOOL* did_full_compact_gc)
12032 BOOL bgc_in_progress = FALSE;
12033 *did_full_compact_gc = FALSE;
12034 #ifdef BACKGROUND_GC
12035 if (recursive_gc_sync::background_running_p())
12037 bgc_in_progress = TRUE;
12038 size_t last_full_compact_gc_count = get_full_compact_gc_count();
12039 wait_for_background (awr_loh_oos_bgc);
12040 size_t current_full_compact_gc_count = get_full_compact_gc_count();
12041 if (current_full_compact_gc_count > last_full_compact_gc_count)
12043 *did_full_compact_gc = TRUE;
12046 #endif //BACKGROUND_GC
12048 return bgc_in_progress;
12051 BOOL gc_heap::loh_try_fit (int gen_number,
12053 alloc_context* acontext,
12055 BOOL* commit_failed_p,
12058 BOOL can_allocate = TRUE;
12060 if (!a_fit_free_list_large_p (size, acontext, align_const))
12062 can_allocate = loh_a_fit_segment_end_p (gen_number, size,
12063 acontext, align_const,
12064 commit_failed_p, oom_r);
12066 #ifdef BACKGROUND_GC
12067 if (can_allocate && recursive_gc_sync::background_running_p())
12069 bgc_loh_size_increased += size;
12071 #endif //BACKGROUND_GC
12073 #ifdef BACKGROUND_GC
12076 if (recursive_gc_sync::background_running_p())
12078 bgc_loh_allocated_in_free += size;
12081 #endif //BACKGROUND_GC
12083 return can_allocate;
12086 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr,
12089 BOOL did_full_compact_gc = FALSE;
12091 size_t last_full_compact_gc_count = get_full_compact_gc_count();
12093 // Set this so the next GC will be a full compacting GC.
12094 if (!last_gc_before_oom)
12096 last_gc_before_oom = TRUE;
12099 #ifdef BACKGROUND_GC
12100 if (recursive_gc_sync::background_running_p())
12102 wait_for_background (awr_loh_oos_bgc);
12103 dprintf (2, ("waited for BGC - done"));
12105 #endif //BACKGROUND_GC
12107 size_t current_full_compact_gc_count = get_full_compact_gc_count();
12108 if (current_full_compact_gc_count > last_full_compact_gc_count)
12110 dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
12111 assert (current_full_compact_gc_count > last_full_compact_gc_count);
12112 did_full_compact_gc = TRUE;
12116 dprintf (3, ("h%d full GC", heap_number));
12117 vm_heap->GarbageCollectGeneration(max_generation, gr);
12119 #ifdef MULTIPLE_HEAPS
12120 enter_spin_lock (&more_space_lock);
12121 dprintf (SPINLOCK_LOG, ("[%d]Emsl after full gc", heap_number));
12122 add_saved_spinlock_info (me_acquire, mt_t_full_gc);
12123 #endif //MULTIPLE_HEAPS
12125 current_full_compact_gc_count = get_full_compact_gc_count();
12127 if (current_full_compact_gc_count == last_full_compact_gc_count)
12129 dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
12130 // We requested a full GC but didn't get because of the elevation logic
12131 // which means we should fail.
12132 *oom_r = oom_unproductive_full_gc;
12136 dprintf (3, ("h%d: T full compacting GC (%d->%d)",
12138 last_full_compact_gc_count,
12139 current_full_compact_gc_count));
12141 assert (current_full_compact_gc_count > last_full_compact_gc_count);
12142 did_full_compact_gc = TRUE;
12146 return did_full_compact_gc;
12149 #ifdef RECORD_LOH_STATE
12150 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, DWORD thread_id)
12152 // When the state is can_allocate we already have released the more
12153 // space lock. So we are not logging states here since this code
12154 // is not thread safe.
12155 if (loh_state_to_save != a_state_can_allocate)
12157 last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
12158 last_loh_states[loh_state_index].thread_id = thread_id;
12161 if (loh_state_index == max_saved_loh_states)
12163 loh_state_index = 0;
12166 assert (loh_state_index < max_saved_loh_states);
12169 #endif //RECORD_LOH_STATE
12171 BOOL gc_heap::allocate_large (int gen_number,
12173 alloc_context* acontext,
12176 #ifdef BACKGROUND_GC
12177 if (recursive_gc_sync::background_running_p() && (current_c_gc_state != c_gc_state_planning))
12179 background_loh_alloc_count++;
12180 //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
12182 if (bgc_loh_should_allocate())
12184 if (!bgc_alloc_spin_loh)
12186 Thread* current_thread = GetThread();
12187 add_saved_spinlock_info (me_release, mt_alloc_large);
12188 dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl loh", heap_number));
12189 leave_spin_lock (&more_space_lock);
12190 BOOL cooperative_mode = enable_preemptive (current_thread);
12191 __SwitchToThread (bgc_alloc_spin_loh, CALLER_LIMITS_SPINNING);
12192 disable_preemptive (current_thread, cooperative_mode);
12193 enter_spin_lock (&more_space_lock);
12194 add_saved_spinlock_info (me_acquire, mt_alloc_large);
12195 dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
12200 wait_for_background (awr_loh_alloc_during_bgc);
12204 #endif //BACKGROUND_GC
12206 gc_reason gr = reason_oos_loh;
12207 generation* gen = generation_of (gen_number);
12208 oom_reason oom_r = oom_no_failure;
12209 size_t current_full_compact_gc_count = 0;
12211 // No variable values should be "carried over" from one state to the other.
12212 // That's why there are local variable for each state
12213 allocation_state loh_alloc_state = a_state_start;
12214 #ifdef RECORD_LOH_STATE
12215 DWORD current_thread_id = GetCurrentThreadId();
12216 #endif //RECORD_LOH_STATE
12218 // If we can get a new seg it means allocation will succeed.
12221 dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
12223 #ifdef RECORD_LOH_STATE
12224 add_saved_loh_state (loh_alloc_state, current_thread_id);
12225 #endif //RECORD_LOH_STATE
12226 switch (loh_alloc_state)
12228 case a_state_can_allocate:
12229 case a_state_cant_allocate:
12233 case a_state_start:
12235 loh_alloc_state = a_state_try_fit;
12238 case a_state_try_fit:
12240 BOOL commit_failed_p = FALSE;
12241 BOOL can_use_existing_p = FALSE;
12243 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12244 align_const, &commit_failed_p, &oom_r);
12245 loh_alloc_state = (can_use_existing_p ?
12246 a_state_can_allocate :
12248 a_state_trigger_full_compact_gc :
12249 a_state_acquire_seg));
12250 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12253 case a_state_try_fit_new_seg:
12255 BOOL commit_failed_p = FALSE;
12256 BOOL can_use_existing_p = FALSE;
12258 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12259 align_const, &commit_failed_p, &oom_r);
12260 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12261 // another LOH allocating thread could have beat us to acquire the msl so
12262 // we need to try again.
12263 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
12264 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12267 case a_state_try_fit_new_seg_after_cg:
12269 BOOL commit_failed_p = FALSE;
12270 BOOL can_use_existing_p = FALSE;
12272 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12273 align_const, &commit_failed_p, &oom_r);
12274 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12275 // another LOH allocating thread could have beat us to acquire the msl so
12276 // we need to try again. However, if we failed to commit, which means we
12277 // did have space on the seg, we bail right away 'cause we already did a
12278 // full compacting GC.
12279 loh_alloc_state = (can_use_existing_p ?
12280 a_state_can_allocate :
12282 a_state_cant_allocate :
12283 a_state_acquire_seg_after_cg));
12284 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12287 case a_state_try_fit_no_seg:
12289 BOOL commit_failed_p = FALSE;
12290 BOOL can_use_existing_p = FALSE;
12292 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12293 align_const, &commit_failed_p, &oom_r);
12294 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate);
12295 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12296 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12299 case a_state_try_fit_after_cg:
12301 BOOL commit_failed_p = FALSE;
12302 BOOL can_use_existing_p = FALSE;
12304 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12305 align_const, &commit_failed_p, &oom_r);
12306 loh_alloc_state = (can_use_existing_p ?
12307 a_state_can_allocate :
12309 a_state_cant_allocate :
12310 a_state_acquire_seg_after_cg));
12311 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12314 case a_state_try_fit_after_bgc:
12316 BOOL commit_failed_p = FALSE;
12317 BOOL can_use_existing_p = FALSE;
12319 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12320 align_const, &commit_failed_p, &oom_r);
12321 loh_alloc_state = (can_use_existing_p ?
12322 a_state_can_allocate :
12324 a_state_trigger_full_compact_gc :
12325 a_state_acquire_seg_after_bgc));
12326 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12329 case a_state_acquire_seg:
12331 BOOL can_get_new_seg_p = FALSE;
12332 BOOL did_full_compacting_gc = FALSE;
12334 current_full_compact_gc_count = get_full_compact_gc_count();
12336 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12337 loh_alloc_state = (can_get_new_seg_p ?
12338 a_state_try_fit_new_seg :
12339 (did_full_compacting_gc ?
12340 a_state_check_retry_seg :
12341 a_state_check_and_wait_for_bgc));
12344 case a_state_acquire_seg_after_cg:
12346 BOOL can_get_new_seg_p = FALSE;
12347 BOOL did_full_compacting_gc = FALSE;
12349 current_full_compact_gc_count = get_full_compact_gc_count();
12351 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12352 // Since we release the msl before we try to allocate a seg, other
12353 // threads could have allocated a bunch of segments before us so
12354 // we might need to retry.
12355 loh_alloc_state = (can_get_new_seg_p ?
12356 a_state_try_fit_new_seg_after_cg :
12357 a_state_check_retry_seg);
12360 case a_state_acquire_seg_after_bgc:
12362 BOOL can_get_new_seg_p = FALSE;
12363 BOOL did_full_compacting_gc = FALSE;
12365 current_full_compact_gc_count = get_full_compact_gc_count();
12367 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12368 loh_alloc_state = (can_get_new_seg_p ?
12369 a_state_try_fit_new_seg :
12370 (did_full_compacting_gc ?
12371 a_state_check_retry_seg :
12372 a_state_trigger_full_compact_gc));
12373 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12376 case a_state_check_and_wait_for_bgc:
12378 BOOL bgc_in_progress_p = FALSE;
12379 BOOL did_full_compacting_gc = FALSE;
12381 if (fgn_maxgen_percent)
12383 dprintf (2, ("FGN: failed to acquire seg, may need to do a full blocking GC"));
12384 send_full_gc_notification (max_generation, FALSE);
12387 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc);
12388 loh_alloc_state = (!bgc_in_progress_p ?
12389 a_state_trigger_full_compact_gc :
12390 (did_full_compacting_gc ?
12391 a_state_try_fit_after_cg :
12392 a_state_try_fit_after_bgc));
12395 case a_state_trigger_full_compact_gc:
12397 BOOL got_full_compacting_gc = FALSE;
12399 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
12400 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12401 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12404 case a_state_check_retry_seg:
12406 BOOL should_retry_gc = retry_full_compact_gc (size);
12407 BOOL should_retry_get_seg = FALSE;
12408 if (!should_retry_gc)
12410 size_t last_full_compact_gc_count = current_full_compact_gc_count;
12411 current_full_compact_gc_count = get_full_compact_gc_count();
12413 if (current_full_compact_gc_count > (last_full_compact_gc_count + 1))
12415 should_retry_get_seg = TRUE;
12419 loh_alloc_state = (should_retry_gc ?
12420 a_state_trigger_full_compact_gc :
12421 (should_retry_get_seg ?
12422 a_state_acquire_seg_after_cg :
12423 a_state_cant_allocate));
12424 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12429 assert (!"Invalid state!");
12436 if (loh_alloc_state == a_state_cant_allocate)
12438 assert (oom_r != oom_no_failure);
12439 handle_oom (heap_number,
12445 add_saved_spinlock_info (me_release, mt_alloc_large_cant);
12446 dprintf (SPINLOCK_LOG, ("[%d]Lmsl for loh oom", heap_number));
12447 leave_spin_lock (&more_space_lock);
12450 return (loh_alloc_state == a_state_can_allocate);
12453 int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
12456 if (gc_heap::gc_started)
12458 wait_for_gc_done();
12462 #ifdef SYNCHRONIZATION_STATS
12463 unsigned int msl_acquire_start = GetCycleCount32();
12464 #endif //SYNCHRONIZATION_STATS
12465 enter_spin_lock (&more_space_lock);
12466 add_saved_spinlock_info (me_acquire, mt_try_alloc);
12467 dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
12468 #ifdef SYNCHRONIZATION_STATS
12469 unsigned int msl_acquire = GetCycleCount32() - msl_acquire_start;
12470 total_msl_acquire += msl_acquire;
12471 num_msl_acquired++;
12472 if (msl_acquire > 200)
12474 num_high_msl_acquire++;
12478 num_low_msl_acquire++;
12480 #endif //SYNCHRONIZATION_STATS
12483 // We are commenting this out 'cause we don't see the point - we already
12484 // have checked gc_started when we were acquiring the msl - no need to check
12485 // again. This complicates the logic in bgc_suspend_EE 'cause that one would
12486 // need to release msl which causes all sorts of trouble.
12487 if (gc_heap::gc_started)
12489 #ifdef SYNCHRONIZATION_STATS
12491 #endif //SYNCHRONIZATION_STATS
12492 BOOL fStress = (g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_TRANSITION) != 0;
12495 //Rendez vous early (MP scaling issue)
12496 //dprintf (1, ("[%d]waiting for gc", heap_number));
12497 wait_for_gc_done();
12498 #ifdef MULTIPLE_HEAPS
12500 #endif //MULTIPLE_HEAPS
12505 dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
12507 int align_const = get_alignment_constant (gen_number != (max_generation+1));
12509 if (fgn_maxgen_percent)
12511 check_for_full_gc (gen_number, size);
12514 if (!(new_allocation_allowed (gen_number)))
12516 if (fgn_maxgen_percent && (gen_number == 0))
12518 // We only check gen0 every so often, so take this opportunity to check again.
12519 check_for_full_gc (gen_number, size);
12522 #ifdef BACKGROUND_GC
12523 wait_for_bgc_high_memory (awr_gen0_alloc);
12524 #endif //BACKGROUND_GC
12526 #ifdef SYNCHRONIZATION_STATS
12528 #endif //SYNCHRONIZATION_STATS
12529 dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
12531 if (!settings.concurrent || (gen_number == 0))
12533 vm_heap->GarbageCollectGeneration (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh));
12534 #ifdef MULTIPLE_HEAPS
12535 enter_spin_lock (&more_space_lock);
12536 add_saved_spinlock_info (me_acquire, mt_try_budget);
12537 dprintf (SPINLOCK_LOG, ("[%d]Emsl out budget", heap_number));
12538 #endif //MULTIPLE_HEAPS
12542 BOOL can_allocate = ((gen_number == 0) ?
12543 allocate_small (gen_number, size, acontext, align_const) :
12544 allocate_large (gen_number, size, acontext, align_const));
12548 //ETW trace for allocation tick
12549 size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
12550 int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
12552 etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
12554 if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
12556 #ifdef FEATURE_REDHAWK
12557 FireEtwGCAllocationTick_V1((ULONG)etw_allocation_running_amount[etw_allocation_index],
12558 ((gen_number == 0) ? ETW::GCLog::ETW_GC_INFO::AllocationSmall : ETW::GCLog::ETW_GC_INFO::AllocationLarge),
12559 GetClrInstanceId());
12561 // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
12562 // The ones that do are much less efficient.
12563 #if defined(FEATURE_EVENT_TRACE)
12564 if (EventEnabledGCAllocationTick_V2())
12566 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
12568 #endif //FEATURE_EVENT_TRACE
12569 #endif //FEATURE_REDHAWK
12570 etw_allocation_running_amount[etw_allocation_index] = 0;
12574 return (int)can_allocate;
12577 #ifdef MULTIPLE_HEAPS
12578 void gc_heap::balance_heaps (alloc_context* acontext)
12581 if (acontext->alloc_count < 4)
12583 if (acontext->alloc_count == 0)
12585 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, 0) );
12586 gc_heap* hp = acontext->home_heap->pGenGCHeap;
12587 dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
12588 acontext->alloc_heap = acontext->home_heap;
12589 hp->alloc_context_count++;
12594 BOOL set_home_heap = FALSE;
12597 if (heap_select::can_find_heap_fast())
12599 if (acontext->home_heap != NULL)
12600 hint = acontext->home_heap->pGenGCHeap->heap_number;
12601 if (acontext->home_heap != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
12603 set_home_heap = TRUE;
12609 if ((acontext->alloc_count & 3) == 0)
12610 set_home_heap = TRUE;
12616 // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
12617 if (n_heaps > MAX_SUPPORTED_CPUS)
12619 // on machines with many processors cache affinity is really king, so don't even try
12620 // to balance on these.
12621 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
12622 acontext->alloc_heap = acontext->home_heap;
12627 gc_heap* org_hp = acontext->alloc_heap->pGenGCHeap;
12629 dynamic_data* dd = org_hp->dynamic_data_of (0);
12630 ptrdiff_t org_size = dd_new_allocation (dd);
12631 int org_alloc_context_count;
12632 int max_alloc_context_count;
12634 ptrdiff_t max_size;
12635 size_t delta = dd_min_size (dd)/4;
12637 int start, end, finish;
12638 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
12639 finish = start + n_heaps;
12645 max_size = org_size + delta;
12646 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
12648 if (org_hp == acontext->home_heap->pGenGCHeap)
12649 max_size = max_size + delta;
12651 org_alloc_context_count = org_hp->alloc_context_count;
12652 max_alloc_context_count = org_alloc_context_count;
12653 if (max_alloc_context_count > 1)
12654 max_size /= max_alloc_context_count;
12656 for (int i = start; i < end; i++)
12658 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
12659 dd = hp->dynamic_data_of (0);
12660 ptrdiff_t size = dd_new_allocation (dd);
12661 if (hp == acontext->home_heap->pGenGCHeap)
12662 size = size + delta;
12663 int hp_alloc_context_count = hp->alloc_context_count;
12664 if (hp_alloc_context_count > 0)
12665 size /= (hp_alloc_context_count + 1);
12666 if (size > max_size)
12670 max_alloc_context_count = hp_alloc_context_count;
12674 while (org_alloc_context_count != org_hp->alloc_context_count ||
12675 max_alloc_context_count != max_hp->alloc_context_count);
12677 if ((max_hp == org_hp) && (end < finish))
12679 start = end; end = finish;
12680 delta = dd_min_size(dd)/4; //Use the same threshold as tier 1 for now. Tune it later
12684 if (max_hp != org_hp)
12686 org_hp->alloc_context_count--;
12687 max_hp->alloc_context_count++;
12688 acontext->alloc_heap = GCHeap::GetHeap(max_hp->heap_number);
12689 #if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
12690 if (CPUGroupInfo::CanEnableGCCPUGroups())
12691 { //only set ideal processor when max_hp and org_hp are in the same cpu
12692 //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
12693 BYTE org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
12694 BYTE max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
12695 if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
12697 BYTE group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
12699 #if !defined(FEATURE_CORESYSTEM)
12700 SetThreadIdealProcessor(GetCurrentThread(), (DWORD)group_proc_no);
12702 PROCESSOR_NUMBER proc;
12703 proc.Group = org_gn;
12704 proc.Number = group_proc_no;
12707 if(!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL))
12709 dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
12710 org_hp->heap_number));
12717 BYTE proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
12719 #if !defined(FEATURE_CORESYSTEM)
12720 SetThreadIdealProcessor(GetCurrentThread(), (DWORD)proc_no);
12722 PROCESSOR_NUMBER proc;
12723 if(GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
12725 proc.Number = proc_no;
12727 if(!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL))
12729 dprintf (3, ("Failed to set the ideal processor for heap %d.",
12730 org_hp->heap_number));
12735 #endif // !FEATURE_REDHAWK && !FEATURE_PAL
12736 dprintf (3, ("Switching context %p (home heap %d) ",
12738 acontext->home_heap->pGenGCHeap->heap_number));
12739 dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ",
12740 org_hp->heap_number,
12742 org_alloc_context_count));
12743 dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n",
12744 max_hp->heap_number,
12745 dd_new_allocation(max_hp->dynamic_data_of(0)),
12746 max_alloc_context_count));
12751 acontext->alloc_count++;
12754 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t size)
12756 gc_heap* org_hp = acontext->alloc_heap->pGenGCHeap;
12757 //dprintf (1, ("LA: %Id", size));
12759 //if (size > 128*1024)
12762 dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
12764 ptrdiff_t org_size = dd_new_allocation (dd);
12766 ptrdiff_t max_size;
12767 size_t delta = dd_min_size (dd) * 4;
12769 int start, end, finish;
12770 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
12771 finish = start + n_heaps;
12776 max_size = org_size + delta;
12777 dprintf (3, ("orig hp: %d, max size: %d",
12778 org_hp->heap_number,
12781 for (int i = start; i < end; i++)
12783 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
12784 dd = hp->dynamic_data_of (max_generation + 1);
12785 ptrdiff_t size = dd_new_allocation (dd);
12786 dprintf (3, ("hp: %d, size: %d",
12789 if (size > max_size)
12793 dprintf (3, ("max hp: %d, max size: %d",
12794 max_hp->heap_number,
12800 if ((max_hp == org_hp) && (end < finish))
12802 start = end; end = finish;
12803 delta = dd_min_size(dd) * 4; // Need to tuning delta
12807 if (max_hp != org_hp)
12809 dprintf (3, ("loh: %d(%Id)->%d(%Id)",
12810 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
12811 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
12821 #endif //MULTIPLE_HEAPS
12823 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
12824 int alloc_generation_number)
12829 #ifdef MULTIPLE_HEAPS
12830 if (alloc_generation_number == 0)
12832 balance_heaps (acontext);
12833 status = acontext->alloc_heap->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
12837 gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
12838 status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
12841 status = try_allocate_more_space (acontext, size, alloc_generation_number);
12842 #endif //MULTIPLE_HEAPS
12844 while (status == -1);
12846 return (status != 0);
12850 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
12852 size_t size = Align (jsize);
12853 assert (size >= Align (min_obj_size));
12856 BYTE* result = acontext->alloc_ptr;
12857 acontext->alloc_ptr+=size;
12858 if (acontext->alloc_ptr <= acontext->alloc_limit)
12860 CObjectHeader* obj = (CObjectHeader*)result;
12866 acontext->alloc_ptr -= size;
12869 #pragma inline_depth(0)
12872 if (! allocate_more_space (acontext, size, 0))
12876 #pragma inline_depth(20)
12885 CObjectHeader* gc_heap::try_fast_alloc (size_t jsize)
12887 size_t size = Align (jsize);
12888 assert (size >= Align (min_obj_size));
12889 generation* gen = generation_of (0);
12890 BYTE* result = generation_allocation_pointer (gen);
12891 generation_allocation_pointer (gen) += size;
12892 if (generation_allocation_pointer (gen) <=
12893 generation_allocation_limit (gen))
12895 return (CObjectHeader*)result;
12899 generation_allocation_pointer (gen) -= size;
12903 void gc_heap::leave_allocation_segment (generation* gen)
12905 adjust_limit (0, 0, gen, max_generation);
12908 void gc_heap::init_free_and_plug()
12910 #ifdef FREE_USAGE_STATS
12911 for (int i = 0; i <= settings.condemned_generation; i++)
12913 generation* gen = generation_of (i);
12914 memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
12915 memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
12916 memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
12919 if (settings.condemned_generation != max_generation)
12921 for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
12923 generation* gen = generation_of (i);
12924 memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
12927 #endif //FREE_USAGE_STATS
12930 void gc_heap::print_free_and_plug (const char* msg)
12932 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
12933 int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
12934 for (int i = 0; i <= older_gen; i++)
12936 generation* gen = generation_of (i);
12937 for (int j = 0; j < NUM_GEN_POWER2; j++)
12939 if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
12941 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id",
12944 (settings.concurrent ? "BGC" : "GC"),
12947 (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
12951 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
12954 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
12956 #ifdef FREE_USAGE_STATS
12957 dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
12958 generation* gen = generation_of (gen_number);
12959 size_t sz = BASE_GEN_SIZE;
12962 for (; i < NUM_GEN_POWER2; i++)
12964 if (plug_size < sz)
12971 (gen->gen_plugs[i])++;
12972 #endif //FREE_USAGE_STATS
12975 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
12977 #ifdef FREE_USAGE_STATS
12978 generation* gen = generation_of (gen_number);
12979 size_t sz = BASE_GEN_SIZE;
12982 for (; i < NUM_GEN_POWER2; i++)
12984 if (free_size < sz)
12991 (gen->gen_current_pinned_free_spaces[i])++;
12992 generation_pinned_free_obj_space (gen) += free_size;
12993 dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)",
12994 free_size, (i + 10), gen_number,
12995 generation_pinned_free_obj_space (gen),
12996 gen->gen_current_pinned_free_spaces[i]));
12997 #endif //FREE_USAGE_STATS
13000 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13002 #ifdef FREE_USAGE_STATS
13003 dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13004 generation* gen = generation_of (gen_number);
13005 size_t sz = BASE_GEN_SIZE;
13008 for (; i < NUM_GEN_POWER2; i++)
13010 if (free_size < sz)
13017 (gen->gen_free_spaces[i])++;
13018 #endif //FREE_USAGE_STATS
13021 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13023 #ifdef FREE_USAGE_STATS
13024 dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13025 generation* gen = generation_of (gen_number);
13026 size_t sz = BASE_GEN_SIZE;
13029 for (; i < NUM_GEN_POWER2; i++)
13031 if (free_size < sz)
13038 (gen->gen_free_spaces[i])--;
13039 #endif //FREE_USAGE_STATS
13042 BYTE* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13043 int from_gen_number,
13044 BYTE* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13046 size = Align (size);
13047 assert (size >= Align (min_obj_size));
13048 assert (from_gen_number < max_generation);
13049 assert (from_gen_number >= 0);
13050 assert (generation_of (from_gen_number + 1) == gen);
13052 allocator* gen_allocator = generation_allocator (gen);
13053 BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13054 int pad_in_front = (old_loc != 0)? USE_PADDING_FRONT : 0;
13055 if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13056 generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13058 size_t sz_list = gen_allocator->first_bucket_size();
13059 for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13061 if ((size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13063 BYTE* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13064 BYTE* prev_free_item = 0;
13065 while (free_list != 0)
13067 dprintf (3, ("considering free list %Ix", (size_t)free_list));
13069 size_t free_list_size = unused_array_size (free_list);
13070 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13071 old_loc, USE_PADDING_TAIL | pad_in_front))
13073 dprintf (4, ("F:%Ix-%Id",
13074 (size_t)free_list, free_list_size));
13075 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
13076 generation_free_list_space (gen) -= free_list_size;
13077 remove_gen_free (gen->gen_num, free_list_size);
13079 adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
13082 else if (discard_p)
13084 dprintf (3, ("couldn't use this free area, discarding"));
13085 generation_free_obj_space (gen) += free_list_size;
13087 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
13088 generation_free_list_space (gen) -= free_list_size;
13089 remove_gen_free (gen->gen_num, free_list_size);
13093 prev_free_item = free_list;
13095 free_list = free_list_slot (free_list);
13098 sz_list = sz_list * 2;
13100 //go back to the beginning of the segment list
13101 generation_allocate_end_seg_p (gen) = TRUE;
13102 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
13103 if (seg != generation_allocation_segment (gen))
13105 leave_allocation_segment (gen);
13106 generation_allocation_segment (gen) = seg;
13108 while (seg != ephemeral_heap_segment)
13110 if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13111 heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
13113 dprintf (3, ("using what's left in committed"));
13114 adjust_limit (heap_segment_plan_allocated (seg),
13115 heap_segment_committed (seg) -
13116 heap_segment_plan_allocated (seg),
13117 gen, from_gen_number+1);
13118 // dformat (t, 3, "Expanding segment allocation");
13119 heap_segment_plan_allocated (seg) =
13120 heap_segment_committed (seg);
13125 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13126 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13127 grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
13129 dprintf (3, ("using what's left in reserved"));
13130 adjust_limit (heap_segment_plan_allocated (seg),
13131 heap_segment_committed (seg) -
13132 heap_segment_plan_allocated (seg),
13133 gen, from_gen_number+1);
13134 heap_segment_plan_allocated (seg) =
13135 heap_segment_committed (seg);
13141 leave_allocation_segment (gen);
13142 heap_segment* next_seg = heap_segment_next_rw (seg);
13145 dprintf (3, ("getting next segment"));
13146 generation_allocation_segment (gen) = next_seg;
13147 generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13148 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13157 seg = generation_allocation_segment (gen);
13159 //No need to fix the last region. Will be done later
13170 BYTE* result = generation_allocation_pointer (gen);
13174 if ((pad_in_front & USE_PADDING_FRONT) &&
13175 (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13176 ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13178 pad = Align (min_obj_size);
13179 set_plug_padded (old_loc);
13181 #endif //SHORT_PLUGS
13183 #ifdef FEATURE_STRUCTALIGN
13184 _ASSERTE(!old_loc || alignmentOffset != 0);
13185 _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13188 size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13189 set_node_aligninfo (old_loc, requiredAlignment, pad1);
13192 #else // FEATURE_STRUCTALIGN
13193 if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13195 pad += switch_alignment_size (is_plug_padded (old_loc));
13196 set_node_realigned (old_loc);
13197 dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13198 (size_t)old_loc, (size_t)(result+pad)));
13199 assert (same_large_alignment_p (result + pad, old_loc));
13201 #endif // FEATURE_STRUCTALIGN
13202 dprintf (3, ("Allocate %Id bytes", size));
13204 if ((old_loc == 0) || (pad != 0))
13206 //allocating a non plug or a gap, so reset the start region
13207 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13210 generation_allocation_pointer (gen) += size + pad;
13211 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13212 if (generation_allocate_end_seg_p (gen))
13214 generation_end_seg_allocated (gen) += size;
13218 generation_free_list_allocated (gen) += size;
13220 generation_allocation_size (gen) += size;
13222 dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix",
13223 generation_allocation_pointer (gen), generation_allocation_limit (gen),
13224 generation_allocation_context_start_region (gen)));
13226 return result + pad;;
13230 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
13232 //make sure that every generation has a planned allocation start
13233 int gen_number = max_generation - 1;
13234 while (gen_number>= 0)
13236 generation* gen = generation_of (gen_number);
13237 if (0 == generation_plan_allocation_start (gen))
13239 realloc_plan_generation_start (gen, consing_gen);
13241 assert (generation_plan_allocation_start (gen));
13246 // now we know the planned allocation size
13247 size_t size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
13248 heap_segment* seg = generation_allocation_segment (consing_gen);
13249 if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
13253 heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13258 assert (settings.condemned_generation == max_generation);
13259 BYTE* first_address = generation_allocation_limit (consing_gen);
13260 //look through the pinned plugs for relevant ones.
13261 //Look for the right pinned plug to start from.
13264 while (mi != mark_stack_tos)
13266 m = pinned_plug_of (mi);
13267 if ((pinned_plug (m) == first_address))
13272 assert (mi != mark_stack_tos);
13273 pinned_len (m) = size;
13277 //tododefrag optimize for new segment (plan_allocated == mem)
13278 BYTE* gc_heap::allocate_in_expanded_heap (generation* gen,
13283 BOOL set_padding_on_saved_p,
13284 mark* pinned_plug_entry,
13285 #endif //SHORT_PLUGS
13286 BOOL consider_bestfit,
13287 int active_new_gen_number
13288 REQD_ALIGN_AND_OFFSET_DCL)
13290 dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
13292 size = Align (size);
13293 assert (size >= Align (min_obj_size));
13294 int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13296 if (consider_bestfit && use_bestfit)
13298 assert (bestfit_seg);
13299 dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id",
13301 return bestfit_seg->fit (old_loc,
13303 set_padding_on_saved_p,
13305 #endif //SHORT_PLUGS
13306 size REQD_ALIGN_AND_OFFSET_ARG);
13309 heap_segment* seg = generation_allocation_segment (gen);
13311 if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13312 generation_allocation_limit (gen), old_loc,
13313 ((generation_allocation_limit (gen) !=
13314 heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
13316 dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
13317 generation_allocation_limit (gen)));
13320 BYTE* first_address = (generation_allocation_limit (gen) ?
13321 generation_allocation_limit (gen) :
13322 heap_segment_mem (seg));
13323 assert (in_range_for_segment (first_address, seg));
13325 BYTE* end_address = heap_segment_reserved (seg);
13327 dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
13328 first_address, generation_allocation_limit (gen), end_address));
13333 if (heap_segment_allocated (seg) != heap_segment_mem (seg))
13335 assert (settings.condemned_generation == max_generation);
13336 //look through the pinned plugs for relevant ones.
13337 //Look for the right pinned plug to start from.
13338 while (mi != mark_stack_tos)
13340 m = pinned_plug_of (mi);
13341 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
13343 dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
13349 if (mi != mark_stack_tos)
13351 //fix old free list.
13352 size_t hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
13354 dprintf(3,("gc filling up hole"));
13355 ptrdiff_t mi1 = (ptrdiff_t)mi;
13356 while ((mi1 >= 0) &&
13357 (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
13359 dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
13364 size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
13365 pinned_len (pinned_plug_of(mi1)) = hsize;
13366 dprintf (3, ("changing %Ix len %Ix->%Ix",
13367 pinned_plug (pinned_plug_of(mi1)),
13368 saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
13375 assert (generation_allocation_limit (gen) ==
13376 generation_allocation_pointer (gen));
13377 mi = mark_stack_tos;
13380 while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
13382 size_t len = pinned_len (m);
13383 BYTE* free_list = (pinned_plug (m) - len);
13384 dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)",
13385 free_list, (free_list + len), len));
13386 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
13388 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
13389 (size_t)free_list, len));
13391 generation_allocation_pointer (gen) = free_list;
13392 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13393 generation_allocation_limit (gen) = (free_list + len);
13395 goto allocate_in_free;
13398 m = pinned_plug_of (mi);
13401 //switch to the end of the segment.
13402 generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
13403 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13404 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
13405 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13406 dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)",
13407 generation_allocation_pointer (gen), generation_allocation_limit (gen),
13408 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
13410 if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13411 generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
13413 dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
13414 generation_allocation_limit (gen)));
13415 assert (!"Can't allocate if no free space");
13426 BYTE* result = generation_allocation_pointer (gen);
13430 if ((pad_in_front & USE_PADDING_FRONT) &&
13431 (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13432 ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13435 pad = Align (min_obj_size);
13436 set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
13438 #endif //SHORT_PLUGS
13440 #ifdef FEATURE_STRUCTALIGN
13441 _ASSERTE(!old_loc || alignmentOffset != 0);
13442 _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13445 size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13446 set_node_aligninfo (old_loc, requiredAlignment, pad1);
13450 #else // FEATURE_STRUCTALIGN
13451 if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13453 pad += switch_alignment_size (is_plug_padded (old_loc));
13454 set_node_realigned (old_loc);
13455 dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13456 (size_t)old_loc, (size_t)(result+pad)));
13457 assert (same_large_alignment_p (result + pad, old_loc));
13460 #endif // FEATURE_STRUCTALIGN
13462 if ((old_loc == 0) || (pad != 0))
13464 //allocating a non plug or a gap, so reset the start region
13465 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13468 generation_allocation_pointer (gen) += size + pad;
13469 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13470 dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
13472 dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix",
13473 generation_allocation_pointer (gen), generation_allocation_limit (gen),
13474 generation_allocation_context_start_region (gen)));
13476 return result + pad;
13480 generation* gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
13482 heap_segment* seg = generation_allocation_segment (consing_gen);
13483 if (seg != ephemeral_heap_segment)
13485 assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
13486 assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
13488 //fix the allocated size of the segment.
13489 heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13491 generation* new_consing_gen = generation_of (max_generation - 1);
13492 generation_allocation_pointer (new_consing_gen) =
13493 heap_segment_mem (ephemeral_heap_segment);
13494 generation_allocation_limit (new_consing_gen) =
13495 generation_allocation_pointer (new_consing_gen);
13496 generation_allocation_context_start_region (new_consing_gen) =
13497 generation_allocation_pointer (new_consing_gen);
13498 generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
13500 return new_consing_gen;
13503 return consing_gen;
13506 BYTE* gc_heap::allocate_in_condemned_generations (generation* gen,
13508 int from_gen_number,
13510 BYTE* next_pinned_plug,
13511 heap_segment* current_seg,
13512 #endif //SHORT_PLUGS
13514 REQD_ALIGN_AND_OFFSET_DCL)
13516 // Make sure that the youngest generation gap hasn't been allocated
13517 if (settings.promotion)
13519 assert (generation_plan_allocation_start (youngest_generation) == 0);
13522 size = Align (size);
13523 assert (size >= Align (min_obj_size));
13524 int to_gen_number = from_gen_number;
13525 if (from_gen_number != (int)max_generation)
13527 to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
13530 dprintf (3, ("aic gen%d: s: %Id", gen->gen_num, size));
13532 int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13534 if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
13536 generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
13537 generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
13541 heap_segment* seg = generation_allocation_segment (gen);
13542 if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13543 generation_allocation_limit (gen), old_loc,
13544 ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
13546 if ((! (pinned_plug_que_empty_p()) &&
13547 (generation_allocation_limit (gen) ==
13548 pinned_plug (oldest_pin()))))
13550 size_t entry = deque_pinned_plug();
13551 mark* pinned_plug_entry = pinned_plug_of (entry);
13552 size_t len = pinned_len (pinned_plug_entry);
13553 BYTE* plug = pinned_plug (pinned_plug_entry);
13554 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
13556 #ifdef FREE_USAGE_STATS
13557 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
13558 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id",
13559 generation_allocated_since_last_pin (gen),
13561 generation_allocated_in_pinned_free (gen)));
13562 generation_allocated_since_last_pin (gen) = 0;
13564 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
13565 #endif //FREE_USAGE_STATS
13567 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix",
13568 mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
13570 assert(mark_stack_array[entry].len == 0 ||
13571 mark_stack_array[entry].len >= Align(min_obj_size));
13572 generation_allocation_pointer (gen) = plug + len;
13573 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13574 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13575 set_allocator_next_pin (gen);
13577 //Add the size of the pinned plug to the right pinned allocations
13578 //find out which gen this pinned plug came from
13579 int frgn = object_gennum (plug);
13580 if ((frgn != (int)max_generation) && settings.promotion)
13582 generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
13583 int togn = object_gennum_plan (plug);
13586 generation_pinned_allocation_compact_size (generation_of (togn)) += len;
13592 if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
13594 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13595 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
13599 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
13601 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
13602 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13603 dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
13607 #ifndef RESPECT_LARGE_ALIGNMENT
13608 assert (gen != youngest_generation);
13609 #endif //RESPECT_LARGE_ALIGNMENT
13611 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13612 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13613 (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
13614 size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
13616 dprintf (3, ("Expanded segment allocation by committing more memory"));
13617 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
13618 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13622 heap_segment* next_seg = heap_segment_next (seg);
13623 assert (generation_allocation_pointer (gen)>=
13624 heap_segment_mem (seg));
13625 // Verify that all pinned plugs for this segment are consumed
13626 if (!pinned_plug_que_empty_p() &&
13627 ((pinned_plug (oldest_pin()) <
13628 heap_segment_allocated (seg)) &&
13629 (pinned_plug (oldest_pin()) >=
13630 generation_allocation_pointer (gen))))
13632 LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
13633 pinned_plug (oldest_pin())));
13636 assert (generation_allocation_pointer (gen)>=
13637 heap_segment_mem (seg));
13638 assert (generation_allocation_pointer (gen)<=
13639 heap_segment_committed (seg));
13640 heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
13644 generation_allocation_segment (gen) = next_seg;
13645 generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13646 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13647 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13651 return 0; //should only happen during allocation of generation 0 gap
13652 // in that case we are going to grow the heap anyway
13657 set_allocator_next_pin (gen);
13664 assert (generation_allocation_pointer (gen)>=
13665 heap_segment_mem (generation_allocation_segment (gen)));
13666 BYTE* result = generation_allocation_pointer (gen);
13669 if ((pad_in_front & USE_PADDING_FRONT) &&
13670 (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13671 ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13673 SSIZE_T dist = old_loc - result;
13676 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
13681 if ((dist > 0) && (dist < (SSIZE_T)Align (min_obj_size)))
13683 dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
13687 pad = Align (min_obj_size);
13688 set_plug_padded (old_loc);
13691 #endif //SHORT_PLUGS
13692 #ifdef FEATURE_STRUCTALIGN
13693 _ASSERTE(!old_loc || alignmentOffset != 0);
13694 _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13695 if ((old_loc != 0))
13697 size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13698 set_node_aligninfo (old_loc, requiredAlignment, pad1);
13701 #else // FEATURE_STRUCTALIGN
13702 if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13704 pad += switch_alignment_size (is_plug_padded (old_loc));
13705 set_node_realigned(old_loc);
13706 dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13707 (size_t)old_loc, (size_t)(result+pad)));
13708 assert (same_large_alignment_p (result + pad, old_loc));
13710 #endif // FEATURE_STRUCTALIGN
13713 if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
13715 assert (old_loc != 0);
13716 ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
13717 assert (dist_to_next_pin >= 0);
13719 if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
13721 clear_plug_padded (old_loc);
13725 #endif //SHORT_PLUGS
13727 if ((old_loc == 0) || (pad != 0))
13729 //allocating a non plug or a gap, so reset the start region
13730 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13733 generation_allocation_pointer (gen) += size + pad;
13734 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13736 #ifdef FREE_USAGE_STATS
13737 generation_allocated_since_last_pin (gen) += size;
13738 #endif //FREE_USAGE_STATS
13740 dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix",
13741 generation_allocation_pointer (gen), generation_allocation_limit (gen),
13742 generation_allocation_context_start_region (gen)));
13744 assert (result + pad);
13745 return result + pad;
13749 inline int power (int x, int y)
13752 for (int i = 0; i < y; i++)
13760 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
13762 BOOL* blocking_collection_p
13763 STRESS_HEAP_ARG(int n_original))
13766 #ifdef MULTIPLE_HEAPS
13767 BOOL blocking_p = *blocking_collection_p;
13770 for (int i = 0; i < n_heaps; i++)
13772 if (g_heaps[i]->last_gc_before_oom)
13774 dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
13775 *blocking_collection_p = TRUE;
13780 #endif //MULTIPLE_HEAPS
13782 if (should_evaluate_elevation && (n == max_generation))
13784 dprintf (GTC_LOG, ("lock: %d(%d)",
13785 (settings.should_lock_elevation ? 1 : 0),
13786 settings.elevation_locked_count));
13788 if (settings.should_lock_elevation)
13790 settings.elevation_locked_count++;
13791 if (settings.elevation_locked_count == 6)
13793 settings.elevation_locked_count = 0;
13797 n = max_generation - 1;
13802 settings.elevation_locked_count = 0;
13807 settings.should_lock_elevation = FALSE;
13808 settings.elevation_locked_count = 0;
13812 // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
13813 // generations to be collected,
13815 if (n_original != max_generation &&
13816 g_pConfig->GetGCStressLevel() && g_pConfig->GetGCconcurrent())
13818 #ifndef FEATURE_REDHAWK
13819 // for the GC stress mix mode throttle down gen2 collections
13820 if (g_pConfig->IsGCStressMix())
13822 size_t current_gc_count = 0;
13824 #ifdef MULTIPLE_HEAPS
13825 current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
13827 current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
13828 #endif //MULTIPLE_HEAPS
13829 // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
13830 if ((current_gc_count % 10) == 0)
13832 n = max_generation;
13835 // for traditional GC stress
13837 #endif // !FEATURE_REDHAWK
13838 if (*blocking_collection_p)
13840 // We call StressHeap() a lot for Concurrent GC Stress. However,
13841 // if we can not do a concurrent collection, no need to stress anymore.
13842 // @TODO: Enable stress when the memory pressure goes down again
13843 GCStressPolicy::GlobalDisable();
13847 n = max_generation;
13850 #endif //STRESS_HEAP
13856 size_t get_survived_size (gc_history_per_heap* hist)
13858 size_t surv_size = 0;
13859 gc_generation_data* gen_data;
13861 for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
13863 gen_data = &(hist->gen_data[gen_number]);
13864 surv_size += (gen_data->size_after -
13865 gen_data->free_list_space_after -
13866 gen_data->free_obj_space_after);
13872 size_t gc_heap::get_total_survived_size()
13874 size_t total_surv_size = 0;
13875 #ifdef MULTIPLE_HEAPS
13876 for (int i = 0; i < gc_heap::n_heaps; i++)
13878 gc_heap* hp = gc_heap::g_heaps[i];
13879 gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
13880 total_surv_size += get_survived_size (current_gc_data_per_heap);
13883 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
13884 total_surv_size = get_survived_size (current_gc_data_per_heap);
13885 #endif //MULTIPLE_HEAPS
13886 return total_surv_size;
13889 // Gets what's allocated on both SOH and LOH that hasn't been collected.
13890 size_t gc_heap::get_current_allocated()
13892 dynamic_data* dd = dynamic_data_of (0);
13893 size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
13894 dd = dynamic_data_of (max_generation + 1);
13895 current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
13897 return current_alloc;
13900 size_t gc_heap::get_total_allocated()
13902 size_t total_current_allocated = 0;
13903 #ifdef MULTIPLE_HEAPS
13904 for (int i = 0; i < gc_heap::n_heaps; i++)
13906 gc_heap* hp = gc_heap::g_heaps[i];
13907 total_current_allocated += hp->get_current_allocated();
13910 total_current_allocated = get_current_allocated();
13911 #endif //MULTIPLE_HEAPS
13912 return total_current_allocated;
13915 size_t gc_heap::current_generation_size (int gen_number)
13917 dynamic_data* dd = dynamic_data_of (gen_number);
13918 size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
13919 - dd_new_allocation (dd));
13925 #pragma warning(push)
13926 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
13930 This is called by when we are actually doing a GC, or when we are just checking whether
13931 we would do a full blocking GC, in which case check_only_p is TRUE.
13933 The difference between calling this with check_only_p TRUE and FALSE is that when it's
13935 settings.reason is ignored
13936 budgets are not checked (since they are checked before this is called)
13937 it doesn't change anything non local like generation_skip_ratio
13939 int gc_heap::generation_to_condemn (int n_initial,
13940 BOOL* blocking_collection_p,
13941 BOOL* elevation_requested_p,
13944 gc_mechanisms temp_settings = settings;
13945 gen_to_condemn_tuning temp_condemn_reasons;
13946 gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
13947 gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
13950 if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
13952 assert (n_initial >= 1);
13955 assert (settings.reason != reason_empty);
13958 local_condemn_reasons->init();
13962 if (heap_number == 0)
13964 dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
13969 memset (&ms, 0, sizeof(ms));
13970 BOOL low_memory_detected = g_low_memory_status;
13971 BOOL check_memory = FALSE;
13972 BOOL high_fragmentation = FALSE;
13973 BOOL v_high_memory_load = FALSE;
13974 BOOL high_memory_load = FALSE;
13975 BOOL low_ephemeral_space = FALSE;
13976 BOOL evaluate_elevation = TRUE;
13977 *elevation_requested_p = FALSE;
13978 *blocking_collection_p = FALSE;
13980 BOOL check_max_gen_alloc = TRUE;
13984 #endif //STRESS_HEAP
13988 dd_fragmentation (dynamic_data_of (0)) =
13989 generation_free_list_space (youngest_generation) +
13990 generation_free_obj_space (youngest_generation);
13992 dd_fragmentation (dynamic_data_of (max_generation + 1)) =
13993 generation_free_list_space (large_object_generation) +
13994 generation_free_obj_space (large_object_generation);
13996 //save new_allocation
13997 for (i = 0; i <= max_generation+1; i++)
13999 dynamic_data* dd = dynamic_data_of (i);
14000 dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)",
14002 dd_new_allocation (dd),
14003 dd_desired_allocation (dd)));
14004 dd_gc_new_allocation (dd) = dd_new_allocation (dd);
14007 local_condemn_reasons->set_gen (gen_initial, n);
14010 #ifdef BACKGROUND_GC
14011 if (recursive_gc_sync::background_running_p())
14013 dprintf (GTC_LOG, ("bgc in prog, 1"));
14014 check_max_gen_alloc = FALSE;
14016 #endif //BACKGROUND_GC
14018 if (check_max_gen_alloc)
14020 //figure out if large objects need to be collected.
14021 if (get_new_allocation (max_generation+1) <= 0)
14023 n = max_generation;
14024 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14028 //figure out which generation ran out of allocation
14029 for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
14031 if (get_new_allocation (i) <= 0)
14042 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14045 dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
14049 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
14050 //time based tuning
14051 // if enough time has elapsed since the last gc
14052 // and the number of gc is too low (1/10 of lower gen) then collect
14053 // This should also be enabled if we have memory concerns
14054 int n_time_max = max_generation;
14058 if (recursive_gc_sync::background_running_p())
14060 n_time_max = max_generation - 1;
14064 if ((local_settings->pause_mode == pause_interactive) ||
14065 (local_settings->pause_mode == pause_sustained_low_latency))
14067 dynamic_data* dd0 = dynamic_data_of (0);
14069 if (!QueryPerformanceCounter(&ts))
14072 size_t now = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
14074 for (i = (temp_gen+1); i <= n_time_max; i++)
14076 dynamic_data* dd = dynamic_data_of (i);
14077 if ((now > dd_time_clock(dd) + power (10, i)*1000) &&
14078 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + (power (10, i)))) &&
14079 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
14081 n = min (i, n_time_max);
14082 dprintf (GTC_LOG, ("time %d", n));
14087 local_condemn_reasons->set_gen (gen_time_tuning, n);
14093 dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
14095 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
14097 if (n < (max_generation - 1))
14099 if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
14101 n = max (n, max_generation - 1);
14102 local_settings->promotion = TRUE;
14103 dprintf (GTC_LOG, ("h%d: skip %d, c %d",
14104 heap_number, generation_skip_ratio, n));
14105 local_condemn_reasons->set_condition (gen_low_card_p);
14111 generation_skip_ratio = 100;
14114 if (dt_low_ephemeral_space_p (check_only_p ?
14115 tuning_deciding_full_gc :
14116 tuning_deciding_condemned_gen))
14118 low_ephemeral_space = TRUE;
14120 n = max (n, max_generation - 1);
14121 local_condemn_reasons->set_condition (gen_low_ephemeral_p);
14122 dprintf (GTC_LOG, ("h%d: low eph", heap_number));
14124 #ifdef BACKGROUND_GC
14125 if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
14126 #endif //BACKGROUND_GC
14128 //It is better to defragment first if we are running out of space for
14129 //the ephemeral generation but we have enough fragmentation to make up for it
14130 //in the non ephemeral generation. Essentially we are trading a gen2 for
14131 // having to expand heap in ephemeral collections.
14132 if (dt_high_frag_p (tuning_deciding_condemned_gen,
14133 max_generation - 1,
14136 high_fragmentation = TRUE;
14137 local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
14138 dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
14143 //figure out which ephemeral generation is too fragramented
14145 for (i = n+1; i < max_generation; i++)
14147 if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
14149 dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
14156 if (low_ephemeral_space)
14159 local_settings->promotion = TRUE;
14164 local_condemn_reasons->set_condition (gen_eph_high_frag_p);
14169 if (settings.pause_mode == pause_low_latency)
14171 if (!is_induced (settings.reason))
14173 n = min (n, max_generation - 1);
14174 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
14175 evaluate_elevation = FALSE;
14181 // It's hard to catch when we get to the point that the memory load is so high
14182 // we get an induced GC from the finalizer thread so we are checking the memory load
14183 // for every gen0 GC.
14184 check_memory = (check_only_p ?
14186 ((n >= 1) || low_memory_detected));
14190 //find out if we are short on memory
14191 GetProcessMemoryLoad(&ms);
14192 if (heap_number == 0)
14194 dprintf (GTC_LOG, ("ml: %d", ms.dwMemoryLoad));
14198 if (heap_number == 0)
14200 available_physical_mem = ms.ullAvailPhys;
14201 local_settings->entry_memory_load = ms.dwMemoryLoad;
14205 // @TODO: Force compaction more often under GCSTRESS
14206 if (ms.dwMemoryLoad >= 90 || low_memory_detected)
14208 #ifdef SIMPLE_DPRINTF
14209 // stress log can't handle any parameter that's bigger than a void*.
14210 if (heap_number == 0)
14212 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d, tp: %I64d, ap: %I64d",
14213 ms.ullTotalPhys, ms.ullAvailPhys, ms.ullTotalPageFile, ms.ullAvailPageFile));
14215 #endif //SIMPLE_DPRINTF
14217 high_memory_load = TRUE;
14219 if (ms.dwMemoryLoad >= 97 || low_memory_detected)
14221 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
14222 // gen1/gen0 may take a lot more memory than gen2.
14223 if (!high_fragmentation)
14225 high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation, ms.ullTotalPhys);
14227 v_high_memory_load = TRUE;
14231 if (!high_fragmentation)
14233 high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, ms.ullAvailPhys);
14237 if (high_fragmentation)
14239 if (high_memory_load)
14241 local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
14243 else if (v_high_memory_load)
14245 local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
14251 dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
14252 heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
14253 high_fragmentation));
14255 if (should_expand_in_full_gc)
14257 dprintf (GTC_LOG, ("h%d: expand_in_full", heap_number));
14258 *blocking_collection_p = TRUE;
14261 should_expand_in_full_gc = FALSE;
14263 evaluate_elevation = FALSE;
14264 n = max_generation;
14265 local_condemn_reasons->set_condition (gen_expand_fullgc_p);
14268 if (last_gc_before_oom)
14270 dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
14271 n = max_generation;
14272 *blocking_collection_p = TRUE;
14274 local_condemn_reasons->set_condition (gen_before_oom);
14279 if (is_induced_blocking (settings.reason) &&
14280 n_initial == max_generation
14281 IN_STRESS_HEAP( && !settings.stress_induced ))
14283 if (heap_number == 0)
14285 dprintf (GTC_LOG, ("induced - BLOCK"));
14288 *blocking_collection_p = TRUE;
14289 local_condemn_reasons->set_condition (gen_induced_fullgc_p);
14290 evaluate_elevation = FALSE;
14293 if (settings.reason == reason_induced_noforce)
14295 local_condemn_reasons->set_condition (gen_induced_noforce_p);
14296 evaluate_elevation = FALSE;
14300 if (evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
14302 *elevation_requested_p = TRUE;
14304 // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
14305 if (high_memory_load || v_high_memory_load)
14307 dynamic_data* dd_max = dynamic_data_of (max_generation);
14308 if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
14310 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)",
14311 dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
14312 n = max_generation;
14316 if (n <= max_generation)
14319 if (high_fragmentation)
14321 //elevate to max_generation
14322 n = max_generation;
14323 dprintf (GTC_LOG, ("h%d: f full", heap_number));
14325 #ifdef BACKGROUND_GC
14326 if (high_memory_load || v_high_memory_load)
14328 // For background GC we want to do blocking collections more eagerly because we don't
14329 // want to get into the situation where the memory load becomes high while we are in
14330 // a background GC and we'd have to wait for the background GC to finish to start
14331 // a blocking collection (right now the implemenation doesn't handle converting
14332 // a background GC to a blocking collection midway.
14333 dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
14334 *blocking_collection_p = TRUE;
14337 if (v_high_memory_load)
14339 dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
14340 *blocking_collection_p = TRUE;
14342 #endif //BACKGROUND_GC
14346 n = max (n, max_generation - 1);
14347 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
14354 if ((n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
14356 dprintf (GTC_LOG, ("h%d: budget %d, check 2",
14357 heap_number, n_alloc));
14358 if (get_new_allocation (max_generation) <= 0)
14360 dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
14361 n = max_generation;
14362 local_condemn_reasons->set_condition (gen_max_gen1);
14366 //figure out if max_generation is too fragmented -> blocking collection
14367 if (n == max_generation)
14369 if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
14371 dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
14372 local_condemn_reasons->set_condition (gen_max_high_frag_p);
14373 if (local_settings->pause_mode != pause_sustained_low_latency)
14375 *blocking_collection_p = TRUE;
14380 #ifdef BACKGROUND_GC
14381 if (n == max_generation)
14383 if (heap_number == 0)
14385 BOOL bgc_heap_too_small = TRUE;
14386 size_t gen2size = 0;
14387 size_t gen3size = 0;
14388 #ifdef MULTIPLE_HEAPS
14389 for (int i = 0; i < n_heaps; i++)
14391 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) ||
14392 ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
14394 bgc_heap_too_small = FALSE;
14398 #else //MULTIPLE_HEAPS
14399 if ((current_generation_size (max_generation) > bgc_min_per_heap) ||
14400 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
14402 bgc_heap_too_small = FALSE;
14404 #endif //MULTIPLE_HEAPS
14406 if (bgc_heap_too_small)
14408 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
14411 // do not turn stress-induced collections into blocking GCs
14412 if (!settings.stress_induced)
14413 #endif //STRESS_HEAP
14415 *blocking_collection_p = TRUE;
14418 local_condemn_reasons->set_condition (gen_gen2_too_small);
14422 #endif //BACKGROUND_GC
14428 // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14429 // generations to be collected,
14431 if (orig_gen != max_generation &&
14432 g_pConfig->GetGCStressLevel() && g_pConfig->GetGCconcurrent())
14434 *elevation_requested_p = FALSE;
14436 #endif //STRESS_HEAP
14440 gc_data_per_heap.mem_pressure = ms.dwMemoryLoad;
14441 fgm_result.available_pagefile_mb = (size_t)(ms.ullAvailPageFile / (1024 * 1024));
14444 local_condemn_reasons->set_gen (gen_final_per_heap, n);
14445 gc_data_per_heap.gen_to_condemn_reasons.init (local_condemn_reasons);
14448 local_condemn_reasons->print (heap_number);
14451 if ((local_settings->reason == reason_oos_soh) ||
14452 (local_settings->reason == reason_oos_loh))
14458 #ifndef FEATURE_REDHAWK
14459 if (n == max_generation)
14461 if (SystemDomain::System()->RequireAppDomainCleanup())
14463 #ifdef BACKGROUND_GC
14464 // do not turn stress-induced collections into blocking GCs, unless there
14465 // have already been more full BGCs than full NGCs
14467 // This exposes DevDiv 94129, so we'll leave this out for now
14468 if (!settings.stress_induced ||
14469 full_gc_counts[gc_type_blocking] <= full_gc_counts[gc_type_background])
14471 #endif // BACKGROUND_GC
14473 *blocking_collection_p = TRUE;
14477 #endif //!FEATURE_REDHAWK
14483 #pragma warning(pop)
14487 size_t gc_heap::min_reclaim_fragmentation_threshold(ULONGLONG total_mem, DWORD num_heaps)
14489 return min ((size_t)((float)total_mem * 0.03), (100*1024*1024)) / num_heaps;
14493 ULONGLONG gc_heap::min_high_fragmentation_threshold(ULONGLONG available_mem, DWORD num_heaps)
14495 return min (available_mem, (256*1024*1024)) / num_heaps;
14499 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
14503 #ifdef BACKGROUND_GC
14504 void gc_heap::init_background_gc ()
14506 //reset the allocation so foreground gc can allocate into older (max_generation) generation
14507 generation* gen = generation_of (max_generation);
14508 generation_allocation_pointer (gen)= 0;
14509 generation_allocation_limit (gen) = 0;
14510 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
14512 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
14514 //reset the plan allocation for each segment
14515 for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
14516 seg = heap_segment_next_rw (seg))
14518 heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
14521 if (heap_number == 0)
14523 dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix",
14525 background_saved_lowest_address,
14526 background_saved_highest_address));
14529 gc_lh_block_event.Reset();
14532 #endif //BACKGROUND_GC
14534 #define fire_bgc_event(x) { FireEtw##x(GetClrInstanceId()); }
14537 void fire_drain_mark_list_event (size_t mark_list_objects)
14539 FireEtwBGCDrainMark (mark_list_objects, GetClrInstanceId());
14543 void fire_revisit_event (size_t dirtied_pages,
14544 size_t marked_objects,
14545 BOOL large_objects_p)
14547 FireEtwBGCRevisit (dirtied_pages, marked_objects, large_objects_p, GetClrInstanceId());
14551 void fire_overflow_event (BYTE* overflow_min,
14552 BYTE* overflow_max,
14553 size_t marked_objects,
14554 int large_objects_p)
14556 FireEtwBGCOverflow ((UINT64)overflow_min, (UINT64)overflow_max,
14557 marked_objects, large_objects_p,
14558 GetClrInstanceId());
14561 void gc_heap::concurrent_print_time_delta (const char* msg)
14565 QueryPerformanceCounter (&ts);
14567 size_t current_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
14568 size_t elapsed_time = current_time - time_bgc_last;
14569 time_bgc_last = current_time;
14571 dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
14575 void gc_heap::free_list_info (int gen_num, const char* msg)
14577 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
14578 dprintf (3, ("h%d: %s", heap_number, msg));
14579 for (int i = 0; i <= (max_generation + 1); i++)
14581 generation* gen = generation_of (i);
14582 if ((generation_allocation_size (gen) == 0) &&
14583 (generation_free_list_space (gen) == 0) &&
14584 (generation_free_obj_space (gen) == 0))
14586 // don't print if everything is 0.
14590 dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
14592 generation_allocation_size (gen),
14593 generation_free_list_space (gen),
14594 generation_free_obj_space (gen)));
14597 #endif // BACKGROUND_GC && TRACE_GC
14600 //internal part of gc used by the serial and concurrent version
14601 void gc_heap::gc1()
14603 #ifdef BACKGROUND_GC
14604 assert (settings.concurrent == (DWORD)(GetCurrentThreadId() == bgc_thread_id));
14605 #endif //BACKGROUND_GC
14608 mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
14611 verify_soh_segment_list();
14613 int n = settings.condemned_generation;
14615 update_collection_counts ();
14617 #ifdef BACKGROUND_GC
14618 bgc_alloc_lock->check();
14619 #endif //BACKGROUND_GC
14621 free_list_info (max_generation, "beginning");
14623 vm_heap->GcCondemnedGeneration = settings.condemned_generation;
14625 assert (g_card_table == card_table);
14628 if (n == max_generation)
14630 gc_low = lowest_address;
14631 gc_high = highest_address;
14635 gc_low = generation_allocation_start (generation_of (n));
14636 gc_high = heap_segment_reserved (ephemeral_heap_segment);
14638 #ifdef BACKGROUND_GC
14639 if (settings.concurrent)
14643 QueryPerformanceCounter (&ts);
14645 time_bgc_last = (size_t)(ts.QuadPart/(qpf.QuadPart/1000));
14648 fire_bgc_event (BGCBegin);
14650 concurrent_print_time_delta ("BGC");
14652 //#ifdef WRITE_WATCH
14653 //reset_write_watch (FALSE);
14654 //#endif //WRITE_WATCH
14656 concurrent_print_time_delta ("RW");
14657 background_mark_phase();
14658 free_list_info (max_generation, "after mark phase");
14660 background_sweep();
14661 free_list_info (max_generation, "after sweep phase");
14664 #endif //BACKGROUND_GC
14666 mark_phase (n, FALSE);
14668 CNameSpace::GcRuntimeStructuresValid (FALSE);
14670 CNameSpace::GcRuntimeStructuresValid (TRUE);
14675 if (!QueryPerformanceCounter(&ts))
14678 size_t end_gc_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
14679 // printf ("generation: %d, elapsed time: %Id\n", n, end_gc_time - dd_time_clock (dynamic_data_of (0)));
14681 //adjust the allocation size from the pinned quantities.
14682 for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
14684 generation* gn = generation_of (gen_number);
14685 if (settings.compaction)
14687 generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
14688 generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
14692 generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
14693 generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
14695 generation_pinned_allocation_sweep_size (gn) = 0;
14696 generation_pinned_allocation_compact_size (gn) = 0;
14699 #ifdef BACKGROUND_GC
14700 if (settings.concurrent)
14702 dynamic_data* dd = dynamic_data_of (n);
14703 dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
14705 free_list_info (max_generation, "after computing new dynamic data");
14707 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14709 for (int gen_number = 0; gen_number < max_generation; gen_number++)
14711 dprintf (2, ("end of BGC: gen%d new_alloc: %Id",
14712 gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
14713 current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
14714 current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
14715 current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
14719 #endif //BACKGROUND_GC
14721 free_list_info (max_generation, "end");
14722 for (int gen_number = 0; gen_number <= n; gen_number++)
14724 dynamic_data* dd = dynamic_data_of (gen_number);
14725 dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
14726 compute_new_dynamic_data (gen_number);
14729 if (n != max_generation)
14731 for (int gen_number = (n+1); gen_number <= (max_generation+1); gen_number++)
14733 gc_data_per_heap.gen_data[gen_number].size_after = generation_size (gen_number);
14734 gc_data_per_heap.gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
14735 gc_data_per_heap.gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
14739 free_list_info (max_generation, "after computing new dynamic data");
14741 if (heap_number == 0)
14743 dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms",
14744 dd_collection_count (dynamic_data_of (0)),
14745 settings.condemned_generation,
14746 dd_gc_elapsed_time (dynamic_data_of (0))));
14749 for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14751 dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id",
14752 gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
14756 if (n < max_generation)
14758 compute_promoted_allocation (1 + n);
14759 dynamic_data* dd = dynamic_data_of (1 + n);
14760 size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) +
14761 generation_free_obj_space (generation_of (1 + n));
14763 #ifdef BACKGROUND_GC
14764 if (current_c_gc_state != c_gc_state_planning)
14765 #endif //BACKGROUND_GC
14767 if (settings.promotion)
14769 dd_fragmentation (dd) = new_fragmentation;
14773 //assert (dd_fragmentation (dd) == new_fragmentation);
14778 #ifdef BACKGROUND_GC
14779 if (!settings.concurrent)
14780 #endif //BACKGROUND_GC
14782 adjust_ephemeral_limits();
14785 #ifdef BACKGROUND_GC
14786 assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
14787 assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
14788 #endif //BACKGROUND_GC
14790 int bottom_gen = 0;
14791 #ifdef BACKGROUND_GC
14792 if (settings.concurrent)
14794 bottom_gen = max_generation;
14796 #endif //BACKGROUND_GC
14798 for (int gen_number = bottom_gen; gen_number <= max_generation+1; gen_number++)
14800 dynamic_data* dd = dynamic_data_of (gen_number);
14801 dd_new_allocation(dd) = dd_gc_new_allocation (dd);
14805 if (fgn_maxgen_percent)
14807 if (settings.condemned_generation == (max_generation - 1))
14809 check_for_full_gc (max_generation - 1, 0);
14811 else if (settings.condemned_generation == max_generation)
14813 if (full_gc_approach_event_set
14814 #ifdef MULTIPLE_HEAPS
14815 && (heap_number == 0)
14816 #endif //MULTIPLE_HEAPS
14819 dprintf (2, ("FGN-GC: setting gen2 end event"));
14821 full_gc_approach_event.Reset();
14822 #ifdef BACKGROUND_GC
14823 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
14824 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
14825 #endif //BACKGROUND_GC
14826 full_gc_end_event.Set();
14827 full_gc_approach_event_set = false;
14832 #ifdef BACKGROUND_GC
14833 if (!settings.concurrent)
14834 #endif //BACKGROUND_GC
14836 //decide on the next allocation quantum
14837 if (alloc_contexts_used >= 1)
14839 allocation_quantum = Align (min ((size_t)CLR_SIZE,
14840 (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
14841 get_alignment_constant(FALSE));
14842 dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
14845 #ifdef NO_WRITE_BARRIER
14846 reset_write_watch(FALSE);
14847 #endif //NO_WRITE_BARRIER
14849 descr_generations (FALSE);
14850 descr_card_table();
14852 verify_soh_segment_list();
14854 #ifdef BACKGROUND_GC
14855 add_to_history_per_heap();
14856 if (heap_number == 0)
14860 #endif // BACKGROUND_GC
14863 if (GCStatistics::Enabled() && heap_number == 0)
14864 g_GCStatistics.AddGCStats(settings,
14865 dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
14869 fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
14870 n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
14873 #ifdef BACKGROUND_GC
14874 assert (settings.concurrent == (DWORD)(GetCurrentThreadId() == bgc_thread_id));
14875 #endif //BACKGROUND_GC
14877 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
14880 || (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
14882 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
14883 || (ETW::GCLog::ShouldTrackMovementForEtw() && settings.concurrent)
14887 #ifdef BACKGROUND_GC
14888 Thread* current_thread = GetThread();
14889 BOOL cooperative_mode = TRUE;
14891 if (settings.concurrent)
14893 cooperative_mode = enable_preemptive (current_thread);
14895 #ifdef MULTIPLE_HEAPS
14896 bgc_t_join.join(this, gc_join_suspend_ee_verify);
14897 if (bgc_t_join.joined())
14899 bgc_threads_sync_event.Reset();
14901 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
14902 bgc_t_join.restart();
14904 if (heap_number == 0)
14907 bgc_threads_sync_event.Set();
14911 bgc_threads_sync_event.Wait(INFINITE, FALSE);
14912 dprintf (2, ("bgc_threads_sync_event is signalled"));
14916 #endif //MULTIPLE_HEAPS
14918 //fix the allocation area so verify_heap can proceed.
14919 fix_allocation_contexts (FALSE);
14921 #endif //BACKGROUND_GC
14923 #ifdef BACKGROUND_GC
14924 assert (settings.concurrent == (DWORD)(GetCurrentThreadId() == bgc_thread_id));
14925 #ifdef FEATURE_EVENT_TRACE
14926 if (ETW::GCLog::ShouldTrackMovementForEtw() && settings.concurrent)
14928 make_free_lists_for_profiler_for_bgc();
14930 #endif // FEATURE_EVENT_TRACE
14931 #endif //BACKGROUND_GC
14934 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
14935 verify_heap (FALSE);
14936 #endif // VERIFY_HEAP
14938 #ifdef BACKGROUND_GC
14939 if (settings.concurrent)
14941 repair_allocation_contexts (TRUE);
14943 #ifdef MULTIPLE_HEAPS
14944 bgc_t_join.join(this, gc_join_restart_ee_verify);
14945 if (bgc_t_join.joined())
14947 bgc_threads_sync_event.Reset();
14949 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
14950 bgc_t_join.restart();
14952 if (heap_number == 0)
14955 bgc_threads_sync_event.Set();
14959 bgc_threads_sync_event.Wait(INFINITE, FALSE);
14960 dprintf (2, ("bgc_threads_sync_event is signalled"));
14964 #endif //MULTIPLE_HEAPS
14966 disable_preemptive (current_thread, cooperative_mode);
14968 #endif //BACKGROUND_GC
14970 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
14972 #ifdef MULTIPLE_HEAPS
14973 if (!settings.concurrent)
14975 gc_t_join.join(this, gc_join_done);
14976 if (gc_t_join.joined ())
14978 gc_heap::internal_gc_done = false;
14980 //equalize the new desired size of the generations
14981 int limit = settings.condemned_generation;
14982 if (limit == max_generation)
14984 limit = max_generation+1;
14986 for (int gen = 0; gen <= limit; gen++)
14988 size_t total_desired = 0;
14990 for (int i = 0; i < gc_heap::n_heaps; i++)
14992 gc_heap* hp = gc_heap::g_heaps[i];
14993 dynamic_data* dd = hp->dynamic_data_of (gen);
14994 size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
14995 if (temp_total_desired < total_desired)
14998 total_desired = (size_t)MAX_PTR;
15001 total_desired = temp_total_desired;
15004 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
15005 get_alignment_constant ((gen != (max_generation+1))));
15009 #if 1 //subsumed by the linear allocation model
15010 // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15011 // apply some smoothing.
15012 static size_t smoothed_desired_per_heap = 0;
15013 size_t smoothing = 3; // exponential smoothing factor
15014 if (smoothing > VolatileLoad(&settings.gc_index))
15015 smoothing = VolatileLoad(&settings.gc_index);
15016 smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
15017 dprintf (1, ("sn = %Id n = %Id", smoothed_desired_per_heap, desired_per_heap));
15018 desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
15021 // if desired_per_heap is close to min_gc_size, trim it
15022 // down to min_gc_size to stay in the cache
15023 gc_heap* hp = gc_heap::g_heaps[0];
15024 dynamic_data* dd = hp->dynamic_data_of (gen);
15025 size_t min_gc_size = dd_min_gc_size(dd);
15026 // if min GC size larger than true on die cache, then don't bother
15027 // limiting the desired size
15028 if ((min_gc_size <= GetLargestOnDieCacheSize(TRUE) / GetLogicalCpuCount()) &&
15029 desired_per_heap <= 2*min_gc_size)
15031 desired_per_heap = min_gc_size;
15034 desired_per_heap = joined_youngest_desired (desired_per_heap);
15035 dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
15038 gc_data_global.final_youngest_desired = desired_per_heap;
15040 #if 1 //subsumed by the linear allocation model
15041 if (gen == (max_generation + 1))
15043 // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15044 // apply some smoothing.
15045 static size_t smoothed_desired_per_heap_loh = 0;
15046 size_t smoothing = 3; // exponential smoothing factor
15047 size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
15048 if (smoothing > loh_count)
15049 smoothing = loh_count;
15050 smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
15051 dprintf( 2, ("smoothed_desired_per_heap_loh = %Id desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
15052 desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
15055 for (int i = 0; i < gc_heap::n_heaps; i++)
15057 gc_heap* hp = gc_heap::g_heaps[i];
15058 dynamic_data* dd = hp->dynamic_data_of (gen);
15059 dd_desired_allocation (dd) = desired_per_heap;
15060 dd_gc_new_allocation (dd) = desired_per_heap;
15061 dd_new_allocation (dd) = desired_per_heap;
15065 hp->fgn_last_alloc = desired_per_heap;
15070 #ifdef FEATURE_LOH_COMPACTION
15071 BOOL all_heaps_compacted_p = TRUE;
15072 #endif //FEATURE_LOH_COMPACTION
15073 for (int i = 0; i < gc_heap::n_heaps; i++)
15075 gc_heap* hp = gc_heap::g_heaps[i];
15076 hp->decommit_ephemeral_segment_pages();
15077 hp->rearrange_large_heap_segments();
15078 #ifdef FEATURE_LOH_COMPACTION
15079 all_heaps_compacted_p &= hp->loh_compacted_p;
15080 #endif //FEATURE_LOH_COMPACTION
15083 #ifdef FEATURE_LOH_COMPACTION
15084 check_loh_compact_mode (all_heaps_compacted_p);
15085 #endif //FEATURE_LOH_COMPACTION
15089 gc_t_join.restart();
15091 alloc_context_count = 0;
15092 heap_select::mark_heap (heap_number);
15096 gc_data_global.final_youngest_desired =
15097 dd_desired_allocation (dynamic_data_of (0));
15099 check_loh_compact_mode (loh_compacted_p);
15101 decommit_ephemeral_segment_pages();
15104 if (!(settings.concurrent))
15106 rearrange_large_heap_segments();
15110 #ifdef BACKGROUND_GC
15111 recover_bgc_settings();
15112 #endif //BACKGROUND_GC
15113 #endif //MULTIPLE_HEAPS
15117 void gc_heap::update_collection_counts ()
15119 dynamic_data* dd0 = dynamic_data_of (0);
15120 dd_gc_clock (dd0) += 1;
15123 if (!QueryPerformanceCounter (&ts))
15126 size_t now = (size_t)(ts.QuadPart/(qpf.QuadPart/1000));
15128 for (int i = 0; i <= settings.condemned_generation;i++)
15130 dynamic_data* dd = dynamic_data_of (i);
15131 dd_collection_count (dd)++;
15132 //this is needed by the linear allocation model
15133 if (i == max_generation)
15134 dd_collection_count (dynamic_data_of (max_generation+1))++;
15135 dd_gc_clock (dd) = dd_gc_clock (dd0);
15136 dd_time_clock (dd) = now;
15140 #ifdef HEAP_ANALYZE
15142 BOOL AnalyzeSurvivorsRequested(int condemnedGeneration)
15144 // Is the list active?
15145 GcNotifications gn(g_pGcNotificationTable);
15148 GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
15149 if (gn.GetNotification(gea) != 0)
15157 void DACNotifyGcMarkEnd(int condemnedGeneration)
15159 // Is the list active?
15160 GcNotifications gn(g_pGcNotificationTable);
15163 GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
15164 if (gn.GetNotification(gea) != 0)
15166 DACNotify::DoGCNotification(gea);
15170 #endif // HEAP_ANALYZE
15172 int gc_heap::garbage_collect (int n)
15174 //TODO BACKGROUND_GC remove these when ready
15175 #ifndef NO_CATCH_HANDLERS
15177 #endif //NO_CATCH_HANDLERS
15179 //reset the number of alloc contexts
15180 alloc_contexts_used = 0;
15182 fix_allocation_contexts (TRUE);
15183 #ifdef MULTIPLE_HEAPS
15184 clear_gen0_bricks();
15185 #endif //MULTIPLE_HEAPS
15187 #ifdef BACKGROUND_GC
15188 if (recursive_gc_sync::background_running_p())
15190 save_bgc_data_per_heap();
15192 #endif //BACKGROUND_GC
15194 memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
15195 gc_data_per_heap.heap_index = heap_number;
15196 memset (&fgm_result, 0, sizeof (fgm_result));
15197 settings.reason = gc_trigger_reason;
15198 verify_pinned_queue_p = FALSE;
15201 if (settings.reason == reason_gcstress)
15203 settings.reason = reason_induced;
15204 settings.stress_induced = TRUE;
15206 #endif // STRESS_HEAP
15208 #ifdef MULTIPLE_HEAPS
15209 //align all heaps on the max generation to condemn
15210 dprintf (3, ("Joining for max generation to condemn"));
15211 condemned_generation_num = generation_to_condemn (n,
15212 &blocking_collection,
15213 &elevation_requested,
15215 gc_t_join.join(this, gc_join_generation_determined);
15216 if (gc_t_join.joined())
15217 #endif //MULTIPLE_HEAPS
15220 int gc_count = (int)dd_collection_count (dynamic_data_of (0));
15221 if (gc_count >= g_pConfig->GetGCtraceStart())
15223 if (gc_count >= g_pConfig->GetGCtraceEnd())
15227 #ifdef MULTIPLE_HEAPS
15228 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
15229 //delete old slots from the segment table
15230 seg_table->delete_old_slots();
15231 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
15232 for (int i = 0; i < n_heaps; i++)
15234 //copy the card and brick tables
15235 if (g_card_table != g_heaps[i]->card_table)
15237 g_heaps[i]->copy_brick_card_table (FALSE);
15240 g_heaps[i]->rearrange_large_heap_segments();
15241 if (!recursive_gc_sync::background_running_p())
15243 g_heaps[i]->rearrange_small_heap_segments();
15246 #else //MULTIPLE_HEAPS
15247 #ifdef BACKGROUND_GC
15248 //delete old slots from the segment table
15249 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
15250 seg_table->delete_old_slots();
15251 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
15252 rearrange_large_heap_segments();
15253 if (!recursive_gc_sync::background_running_p())
15255 rearrange_small_heap_segments();
15257 #endif //BACKGROUND_GC
15258 // check for card table growth
15259 if (g_card_table != card_table)
15260 copy_brick_card_table (FALSE);
15262 #endif //MULTIPLE_HEAPS
15264 BOOL should_evaluate_elevation = FALSE;
15265 BOOL should_do_blocking_collection = FALSE;
15267 #ifdef MULTIPLE_HEAPS
15268 int gen_max = condemned_generation_num;
15269 for (int i = 0; i < n_heaps; i++)
15271 if (gen_max < g_heaps[i]->condemned_generation_num)
15272 gen_max = g_heaps[i]->condemned_generation_num;
15273 if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
15274 should_evaluate_elevation = TRUE;
15275 if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
15276 should_do_blocking_collection = TRUE;
15279 settings.condemned_generation = gen_max;
15280 //logically continues after GC_PROFILING.
15281 #else //MULTIPLE_HEAPS
15282 settings.condemned_generation = generation_to_condemn (n,
15283 &blocking_collection,
15284 &elevation_requested,
15286 should_evaluate_elevation = elevation_requested;
15287 should_do_blocking_collection = blocking_collection;
15288 #endif //MULTIPLE_HEAPS
15290 settings.condemned_generation = joined_generation_to_condemn (
15291 should_evaluate_elevation,
15292 settings.condemned_generation,
15293 &should_do_blocking_collection
15297 STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10,
15298 "condemned generation num: %d\n", settings.condemned_generation);
15300 if (settings.condemned_generation > 1)
15301 settings.promotion = TRUE;
15303 #ifdef HEAP_ANALYZE
15304 // At this point we've decided what generation is condemned
15305 // See if we've been requested to analyze survivors after the mark phase
15306 if (AnalyzeSurvivorsRequested(settings.condemned_generation))
15308 heap_analyze_enabled = TRUE;
15310 #endif // HEAP_ANALYZE
15312 #ifdef GC_PROFILING
15314 // If we're tracking GCs, then we need to walk the first generation
15315 // before collection to track how many items of each class has been
15317 UpdateGenerationBounds();
15318 GarbageCollectionStartedCallback(settings.condemned_generation, settings.reason == reason_induced);
15320 BEGIN_PIN_PROFILER(CORProfilerTrackGC());
15321 size_t profiling_context = 0;
15323 #ifdef MULTIPLE_HEAPS
15325 for (hn = 0; hn < gc_heap::n_heaps; hn++)
15327 gc_heap* hp = gc_heap::g_heaps [hn];
15329 // When we're walking objects allocated by class, then we don't want to walk the large
15330 // object heap because then it would count things that may have been around for a while.
15331 hp->walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE);
15334 // When we're walking objects allocated by class, then we don't want to walk the large
15335 // object heap because then it would count things that may have been around for a while.
15336 gc_heap::walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE);
15337 #endif //MULTIPLE_HEAPS
15339 // Notify that we've reached the end of the Gen 0 scan
15340 g_profControlBlock.pProfInterface->EndAllocByClass(&profiling_context);
15341 END_PIN_PROFILER();
15344 #endif // GC_PROFILING
15346 #ifdef BACKGROUND_GC
15347 if ((settings.condemned_generation == max_generation) &&
15348 (recursive_gc_sync::background_running_p()))
15350 //TODO BACKGROUND_GC If we just wait for the end of gc, it won't woork
15351 // because we have to collect 0 and 1 properly
15352 // in particular, the allocation contexts are gone.
15353 // For now, it is simpler to collect max_generation-1
15354 settings.condemned_generation = max_generation - 1;
15355 dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
15358 if ((settings.condemned_generation == max_generation) &&
15359 (should_do_blocking_collection == FALSE) &&
15360 gc_can_use_concurrent &&
15361 !temp_disable_concurrent_p &&
15362 ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
15364 keep_bgc_threads_p = TRUE;
15365 c_write (settings.concurrent, TRUE);
15367 #endif //BACKGROUND_GC
15369 settings.gc_index = (DWORD)dd_collection_count (dynamic_data_of (0)) + 1;
15371 // Call the EE for start of GC work
15372 // just one thread for MP GC
15373 GCToEEInterface::GcStartWork (settings.condemned_generation,
15376 // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
15377 // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
15378 // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
15382 #ifdef MULTIPLE_HEAPS
15383 gc_start_event.Reset();
15384 //start all threads on the roots.
15385 dprintf(3, ("Starting all gc threads for gc"));
15386 gc_t_join.restart();
15387 #endif //MULTIPLE_HEAPS
15390 for (int i = 0; i <= (max_generation+1); i++)
15392 gc_data_per_heap.gen_data[i].size_before = generation_size (i);
15393 generation* gen = generation_of (i);
15394 gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
15395 gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
15398 descr_generations (TRUE);
15399 // descr_card_table();
15401 #ifdef NO_WRITE_BARRIER
15403 #endif //NO_WRITE_BARRIER
15406 if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC) &&
15407 !(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_POST_GC_ONLY))
15409 verify_heap (TRUE);
15411 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
15412 checkGCWriteBarrier();
15414 #endif // VERIFY_HEAP
15416 #ifdef BACKGROUND_GC
15417 if (settings.concurrent)
15419 // We need to save the settings because we'll need to restore it after each FGC.
15420 assert (settings.condemned_generation == max_generation);
15421 saved_bgc_settings = settings;
15422 bgc_data_saved_p = FALSE;
15424 #ifdef MULTIPLE_HEAPS
15425 if (heap_number == 0)
15427 for (int i = 0; i < n_heaps; i++)
15429 prepare_bgc_thread (g_heaps[i]);
15431 dprintf (2, ("setting bgc_threads_sync_event"));
15432 bgc_threads_sync_event.Set();
15436 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15437 dprintf (2, ("bgc_threads_sync_event is signalled"));
15440 prepare_bgc_thread(0);
15441 #endif //MULTIPLE_HEAPS
15443 #ifdef MULTIPLE_HEAPS
15444 gc_t_join.join(this, gc_join_start_bgc);
15445 if (gc_t_join.joined())
15446 #endif //MULTIPLE_HEAPS
15448 do_concurrent_p = TRUE;
15449 do_ephemeral_gc_p = FALSE;
15450 #ifdef MULTIPLE_HEAPS
15451 dprintf(2, ("Joined to perform a background GC"));
15453 for (int i = 0; i < n_heaps; i++)
15455 gc_heap* hp = g_heaps[i];
15456 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
15458 do_concurrent_p = FALSE;
15463 hp->background_saved_lowest_address = hp->lowest_address;
15464 hp->background_saved_highest_address = hp->highest_address;
15468 do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
15469 if (do_concurrent_p)
15471 background_saved_lowest_address = lowest_address;
15472 background_saved_highest_address = highest_address;
15474 #endif //MULTIPLE_HEAPS
15476 if (do_concurrent_p)
15478 #ifdef MULTIPLE_HEAPS
15479 for (int i = 0; i < n_heaps; i++)
15480 g_heaps[i]->current_bgc_state = bgc_initialized;
15482 current_bgc_state = bgc_initialized;
15483 #endif //MULTIPLE_HEAPS
15485 int gen = check_for_ephemeral_alloc();
15486 // always do a gen1 GC before we start BGC.
15487 // This is temporary for testing purpose.
15488 //int gen = max_generation - 1;
15489 dont_restart_ee_p = TRUE;
15492 // If we decide to not do a GC before the BGC we need to
15493 // restore the gen0 alloc context.
15494 #ifdef MULTIPLE_HEAPS
15495 for (int i = 0; i < n_heaps; i++)
15497 generation_allocation_pointer (g_heaps[i]->generation_of (0)) = 0;
15498 generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
15501 generation_allocation_pointer (youngest_generation) = 0;
15502 generation_allocation_limit (youngest_generation) = 0;
15503 #endif //MULTIPLE_HEAPS
15507 do_ephemeral_gc_p = TRUE;
15509 settings.init_mechanisms();
15510 settings.condemned_generation = gen;
15511 settings.gc_index = (SIZE_T)dd_collection_count (dynamic_data_of (0)) + 2;
15514 // TODO BACKGROUND_GC need to add the profiling stuff here.
15515 dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
15518 //clear the cards so they don't bleed in gen 1 during collection
15519 // shouldn't this always be done at the beginning of any GC?
15520 //clear_card_for_addresses (
15521 // generation_allocation_start (generation_of (0)),
15522 // heap_segment_allocated (ephemeral_heap_segment));
15524 if (!do_ephemeral_gc_p)
15526 do_background_gc();
15531 c_write (settings.concurrent, FALSE);
15534 #ifdef MULTIPLE_HEAPS
15535 gc_t_join.restart();
15536 #endif //MULTIPLE_HEAPS
15539 if (do_concurrent_p)
15541 if (do_ephemeral_gc_p)
15543 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
15544 save_bgc_data_per_heap();
15546 gen_to_condemn_reasons.init();
15547 gen_to_condemn_reasons.set_condition (gen_before_bgc);
15548 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
15550 #ifdef MULTIPLE_HEAPS
15551 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
15552 if (gc_t_join.joined())
15553 #endif //MULTIPLE_HEAPS
15555 #ifdef MULTIPLE_HEAPS
15557 #endif //MULTIPLE_HEAPS
15558 settings = saved_bgc_settings;
15559 assert (settings.concurrent);
15561 do_background_gc();
15563 #ifdef MULTIPLE_HEAPS
15564 gc_t_join.restart();
15565 #endif //MULTIPLE_HEAPS
15571 dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
15576 #endif //BACKGROUND_GC
15580 #ifndef MULTIPLE_HEAPS
15581 allocation_running_time = (size_t)GetTickCount();
15582 allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
15583 fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
15584 #endif //MULTIPLE_HEAPS
15587 //TODO BACKGROUND_GC remove these when ready
15588 #ifndef NO_CATCH_HANDLERS
15590 PAL_EXCEPT_FILTER(CheckException, NULL)
15592 _ASSERTE(!"Exception during garbage_collect()");
15593 EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
15596 #endif //NO_CATCH_HANDLERS
15598 int gn = settings.condemned_generation;
15602 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
15605 size_t& gc_heap::promoted_bytes(int thread)
15607 #ifdef MULTIPLE_HEAPS
15608 return g_promoted [thread*16];
15609 #else //MULTIPLE_HEAPS
15612 #endif //MULTIPLE_HEAPS
15615 #ifdef INTERIOR_POINTERS
15616 heap_segment* gc_heap::find_segment (BYTE* interior, BOOL small_segment_only_p)
15618 #ifdef SEG_MAPPING_TABLE
15619 heap_segment* seg = seg_mapping_table_segment_of (interior);
15622 if (small_segment_only_p && heap_segment_loh_p (seg))
15626 #else //SEG_MAPPING_TABLE
15627 #ifdef MULTIPLE_HEAPS
15628 for (int i = 0; i < gc_heap::n_heaps; i++)
15630 gc_heap* h = gc_heap::g_heaps [i];
15631 hs = h->find_segment_per_heap (o, small_segment_only_p);
15639 gc_heap* h = pGenGCHeap;
15640 hs = h->find_segment_per_heap (o, small_segment_only_p);
15642 #endif //MULTIPLE_HEAPS
15643 #endif //SEG_MAPPING_TABLE
15646 heap_segment* gc_heap::find_segment_per_heap (BYTE* interior, BOOL small_segment_only_p)
15648 #ifdef SEG_MAPPING_TABLE
15649 return find_segment (interior, small_segment_only_p);
15650 #else //SEG_MAPPING_TABLE
15651 if (in_range_for_segment (interior, ephemeral_heap_segment))
15653 return ephemeral_heap_segment;
15657 heap_segment* found_seg = 0;
15660 heap_segment* seg = generation_start_segment (generation_of (max_generation));
15663 if (in_range_for_segment (interior, seg))
15666 goto end_find_segment;
15669 } while ((seg = heap_segment_next (seg)) != 0);
15671 if (!small_segment_only_p)
15673 #ifdef BACKGROUND_GC
15675 ptrdiff_t delta = 0;
15676 heap_segment* seg = segment_of (interior, delta);
15677 if (seg && in_range_for_segment (interior, seg))
15681 goto end_find_segment;
15683 #else //BACKGROUND_GC
15684 heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
15687 if (in_range_for_segment(interior, seg))
15690 goto end_find_segment;
15693 } while ((seg = heap_segment_next (seg)) != 0);
15694 #endif //BACKGROUND_GC
15700 #endif //SEG_MAPPING_TABLE
15702 #endif //INTERIOR_POINTERS
15704 #if !defined(_DEBUG) && !defined(__GNUC__)
15705 inline // This causes link errors if global optimization is off
15706 #endif //!_DEBUG && !__GNUC__
15707 gc_heap* gc_heap::heap_of (BYTE* o)
15709 #ifdef MULTIPLE_HEAPS
15711 return g_heaps [0];
15712 #ifdef SEG_MAPPING_TABLE
15713 gc_heap* hp = seg_mapping_table_heap_of (o);
15714 return (hp ? hp : g_heaps[0]);
15715 #else //SEG_MAPPING_TABLE
15716 ptrdiff_t delta = 0;
15717 heap_segment* seg = segment_of (o, delta);
15718 return (seg ? heap_segment_heap (seg) : g_heaps [0]);
15719 #endif //SEG_MAPPING_TABLE
15720 #else //MULTIPLE_HEAPS
15722 #endif //MULTIPLE_HEAPS
15726 gc_heap* gc_heap::heap_of_gc (BYTE* o)
15728 #ifdef MULTIPLE_HEAPS
15730 return g_heaps [0];
15731 #ifdef SEG_MAPPING_TABLE
15732 gc_heap* hp = seg_mapping_table_heap_of_gc (o);
15733 return (hp ? hp : g_heaps[0]);
15734 #else //SEG_MAPPING_TABLE
15735 ptrdiff_t delta = 0;
15736 heap_segment* seg = segment_of (o, delta);
15737 return (seg ? heap_segment_heap (seg) : g_heaps [0]);
15738 #endif //SEG_MAPPING_TABLE
15739 #else //MULTIPLE_HEAPS
15741 #endif //MULTIPLE_HEAPS
15744 #ifdef INTERIOR_POINTERS
15745 // will find all heap objects (large and small)
15746 BYTE* gc_heap::find_object (BYTE* interior, BYTE* low)
15748 if (!gen0_bricks_cleared)
15750 #ifdef MULTIPLE_HEAPS
15751 assert (!"Should have already been done in server GC");
15752 #endif //MULTIPLE_HEAPS
15753 gen0_bricks_cleared = TRUE;
15754 //initialize brick table for gen 0
15755 for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
15756 b < brick_of (align_on_brick
15757 (heap_segment_allocated (ephemeral_heap_segment)));
15763 #ifdef FFIND_OBJECT
15764 //indicate that in the future this needs to be done during allocation
15765 #ifdef MULTIPLE_HEAPS
15766 gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
15768 gen0_must_clear_bricks = FFIND_DECAY;
15769 #endif //MULTIPLE_HEAPS
15770 #endif //FFIND_OBJECT
15772 int brick_entry = brick_table [brick_of (interior)];
15773 if (brick_entry == 0)
15775 // this is a pointer to a large object
15776 heap_segment* seg = find_segment_per_heap (interior, FALSE);
15778 #ifdef FEATURE_CONSERVATIVE_GC
15779 && (!g_pConfig->GetGCConservative() || interior <= heap_segment_allocated(seg))
15783 // If interior falls within the first free object at the beginning of a generation,
15784 // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
15785 int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
15786 #ifdef FEATURE_CONSERVATIVE_GC
15787 || (g_pConfig->GetGCConservative() && !heap_segment_loh_p (seg))
15790 //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
15791 assert (interior < heap_segment_allocated (seg));
15793 BYTE* o = heap_segment_mem (seg);
15794 while (o < heap_segment_allocated (seg))
15796 BYTE* next_o = o + Align (size (o), align_const);
15797 assert (next_o > o);
15798 if ((o <= interior) && (interior < next_o))
15809 else if (interior >= low)
15811 heap_segment* seg = find_segment_per_heap (interior, TRUE);
15814 #ifdef FEATURE_CONSERVATIVE_GC
15815 if (interior >= heap_segment_allocated (seg))
15818 assert (interior < heap_segment_allocated (seg));
15820 BYTE* o = find_first_object (interior, heap_segment_mem (seg));
15831 gc_heap::find_object_for_relocation (BYTE* interior, BYTE* low, BYTE* high)
15833 BYTE* old_address = interior;
15834 if (!((old_address >= low) && (old_address < high)))
15837 size_t brick = brick_of (old_address);
15838 int brick_entry = brick_table [ brick ];
15839 if (brick_entry != 0)
15843 while (brick_entry < 0)
15845 brick = (brick + brick_entry);
15846 brick_entry = brick_table [ brick ];
15848 BYTE* old_loc = old_address;
15849 BYTE* node = tree_search ((brick_address (brick) + brick_entry-1),
15851 if (node <= old_loc)
15856 brick_entry = brick_table [ brick ];
15862 //find the object by going along the plug
15864 while (o <= interior)
15866 BYTE* next_o = o + Align (size (o));
15867 assert (next_o > o);
15868 if (next_o > interior)
15874 assert ((o <= interior) && ((o + Align (size (o))) > interior));
15879 // this is a pointer to a large object
15880 heap_segment* seg = find_segment_per_heap (interior, FALSE);
15883 assert (interior < heap_segment_allocated (seg));
15885 BYTE* o = heap_segment_mem (seg);
15886 while (o < heap_segment_allocated (seg))
15888 BYTE* next_o = o + Align (size (o));
15889 assert (next_o > o);
15890 if ((o < interior) && (interior < next_o))
15902 #else //INTERIOR_POINTERS
15904 BYTE* gc_heap::find_object (BYTE* o, BYTE* low)
15908 #endif //INTERIOR_POINTERS
15911 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;}if (slow > o) slow = o; if (shigh < o) shigh = o;}
15913 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
15916 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
15918 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
15921 BOOL gc_heap::gc_mark1 (BYTE* o)
15923 BOOL marked = !marked (o);
15925 dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
15930 BOOL gc_heap::gc_mark (BYTE* o, BYTE* low, BYTE* high)
15932 BOOL marked = FALSE;
15933 if ((o >= low) && (o < high))
15934 marked = gc_mark1 (o);
15935 #ifdef MULTIPLE_HEAPS
15939 gc_heap* hp = heap_of_gc (o);
15941 if ((o >= hp->gc_low) && (o < hp->gc_high))
15942 marked = gc_mark1 (o);
15945 snoop_stat.objects_checked_count++;
15949 snoop_stat.objects_marked_count++;
15953 snoop_stat.zero_ref_count++;
15956 #endif //SNOOP_STATS
15957 #endif //MULTIPLE_HEAPS
15961 #ifdef BACKGROUND_GC
15964 BOOL gc_heap::background_marked (BYTE* o)
15966 return mark_array_marked (o);
15969 BOOL gc_heap::background_mark1 (BYTE* o)
15971 BOOL to_mark = !mark_array_marked (o);
15973 dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
15976 mark_array_set_marked (o);
15977 dprintf (4, ("n*%Ix*n", (size_t)o));
15984 // TODO: we could consider filtering out NULL's here instead of going to
15985 // look for it on other heaps
15987 BOOL gc_heap::background_mark (BYTE* o, BYTE* low, BYTE* high)
15989 BOOL marked = FALSE;
15990 if ((o >= low) && (o < high))
15991 marked = background_mark1 (o);
15992 #ifdef MULTIPLE_HEAPS
15996 gc_heap* hp = heap_of (o);
15998 if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
15999 marked = background_mark1 (o);
16001 #endif //MULTIPLE_HEAPS
16005 #endif //BACKGROUND_GC
16008 BYTE* gc_heap::next_end (heap_segment* seg, BYTE* f)
16010 if (seg == ephemeral_heap_segment)
16013 return heap_segment_allocated (seg);
16016 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
16017 #define ignore_start 0
16018 #define use_start 1
16020 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp) \
16022 CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt)); \
16023 CGCDescSeries* cur = map->GetHighestSeries(); \
16024 SSIZE_T cnt = (SSIZE_T) map->GetNumSeries(); \
16028 CGCDescSeries* last = map->GetLowestSeries(); \
16032 assert (parm <= (BYTE**)((o) + cur->GetSeriesOffset())); \
16033 parm = (BYTE**)((o) + cur->GetSeriesOffset()); \
16035 (BYTE**)((BYTE*)parm + cur->GetSeriesSize() + (size)); \
16036 if (!start_useful || (BYTE*)ppstop > (start)) \
16038 if (start_useful && (BYTE*)parm < (start)) parm = (BYTE**)(start);\
16039 while (parm < ppstop) \
16047 } while (cur >= last); \
16051 /* Handle the repeating case - array of valuetypes */ \
16052 BYTE** parm = (BYTE**)((o) + cur->startoffset); \
16053 if (start_useful && start > (BYTE*)parm) \
16055 SSIZE_T cs = mt->RawGetComponentSize(); \
16056 parm = (BYTE**)((BYTE*)parm + (((start) - (BYTE*)parm)/cs)*cs); \
16058 while ((BYTE*)parm < ((o)+(size)-plug_skew)) \
16060 for (SSIZE_T __i = 0; __i > cnt; __i--) \
16062 HALF_SIZE_T skip = cur->val_serie[__i].skip; \
16063 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs; \
16064 BYTE** ppstop = parm + nptrs; \
16065 if (!start_useful || (BYTE*)ppstop > (start)) \
16067 if (start_useful && (BYTE*)parm < (start)) parm = (BYTE**)(start); \
16072 } while (parm < ppstop); \
16074 parm = (BYTE**)((BYTE*)ppstop + skip); \
16080 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
16082 // 1 thing to note about this macro:
16083 // 1) you can use *parm safely but in general you don't want to use parm
16084 // because for the collectible types it's not an address on the managed heap.
16085 #ifndef COLLECTIBLE_CLASS
16086 #define go_through_object_cl(mt,o,size,parm,exp) \
16088 if (header(o)->ContainsPointers()) \
16090 go_through_object_nostart(mt,o,size,parm,exp); \
16093 #else //COLLECTIBLE_CLASS
16094 #define go_through_object_cl(mt,o,size,parm,exp) \
16096 if (header(o)->Collectible()) \
16098 BYTE* class_obj = get_class_object (o); \
16099 BYTE** parm = &class_obj; \
16100 do {exp} while (false); \
16102 if (header(o)->ContainsPointers()) \
16104 go_through_object_nostart(mt,o,size,parm,exp); \
16107 #endif //COLLECTIBLE_CLASS
16109 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
16110 void gc_heap::enque_pinned_plug (BYTE* plug,
16111 BOOL save_pre_plug_info_p,
16112 BYTE* last_object_in_last_plug)
16114 if (mark_stack_array_length <= mark_stack_tos)
16116 if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
16118 // we don't want to continue here due to security
16119 // risks. This happens very rarely and fixing it in the
16120 // way so that we can continue is a bit involved and will
16121 // not be done in Dev10.
16122 EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
16126 dprintf (3, ("enquing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d",
16127 mark_stack_tos, &mark_stack_array[mark_stack_tos], plug, mark_stack_bos, last_object_in_last_plug, (save_pre_plug_info_p ? 1 : 0)));
16128 mark& m = mark_stack_array[mark_stack_tos];
16130 // Must be set now because if we have a short object we'll need the value of saved_pre_p.
16131 m.saved_pre_p = save_pre_plug_info_p;
16133 if (save_pre_plug_info_p)
16135 memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
16136 memcpy (&(m.saved_pre_plug_reloc), &(m.saved_pre_plug), sizeof (gap_reloc_pair));
16138 // If the last object in the last plug is too short, it requires special handling.
16139 size_t last_obj_size = plug - last_object_in_last_plug;
16140 if (last_obj_size < min_pre_pin_obj_size)
16142 dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!",
16143 last_object_in_last_plug, plug));
16144 // Need to set the short bit regardless of having refs or not because we need to
16145 // indicate that this object is not walkable.
16148 #ifdef COLLECTIBLE_CLASS
16149 if (is_collectible (last_object_in_last_plug))
16151 m.set_pre_short_collectible();
16153 #endif //COLLECTIBLE_CLASS
16155 if (contain_pointers (last_object_in_last_plug))
16157 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
16159 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
16161 size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (BYTE*);
16162 dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (BYTE*)pval, *pval, gap_offset));
16163 m.set_pre_short_bit (gap_offset);
16170 m.saved_post_p = FALSE;
16173 void gc_heap::save_post_plug_info (BYTE* last_pinned_plug, BYTE* last_object_in_last_plug, BYTE* post_plug)
16175 mark& m = mark_stack_array[mark_stack_tos - 1];
16176 assert (last_pinned_plug == m.first);
16177 m.saved_post_plug_info_start = (BYTE*)&(((plug_and_gap*)post_plug)[-1]);
16178 memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
16179 memcpy (&(m.saved_post_plug_reloc), &(m.saved_post_plug), sizeof (gap_reloc_pair));
16181 // This is important - we need to clear all bits here except the last one.
16182 m.saved_post_p = TRUE;
16185 m.saved_post_plug_debug.gap = 1;
16188 dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
16190 size_t last_obj_size = post_plug - last_object_in_last_plug;
16191 if (last_obj_size < min_pre_pin_obj_size)
16193 dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
16194 m.set_post_short();
16195 verify_pinned_queue_p = TRUE;
16197 #ifdef COLLECTIBLE_CLASS
16198 if (is_collectible (last_object_in_last_plug))
16200 m.set_post_short_collectible();
16202 #endif //COLLECTIBLE_CLASS
16204 if (contain_pointers (last_object_in_last_plug))
16206 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
16208 // TODO: since we won't be able to walk this object in relocation, we still need to
16209 // take care of collectible assemblies here.
16210 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
16212 size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (BYTE*);
16213 dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (BYTE*)pval, *pval, gap_offset));
16214 m.set_post_short_bit (gap_offset);
16223 __declspec(naked) void __fastcall Prefetch(void* addr)
16231 inline void Prefetch (void* addr)
16238 VOLATILE(BYTE*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
16240 return ((VOLATILE(BYTE*)*)(hp->mark_stack_array))[index];
16243 #endif //MH_SC_MARK
16247 #define partial_object 3
16249 BYTE* ref_from_slot (BYTE* r)
16251 return (BYTE*)((size_t)r & ~(stolen | partial));
16254 BOOL stolen_p (BYTE* r)
16256 return (((size_t)r&2) && !((size_t)r&1));
16259 BOOL ready_p (BYTE* r)
16261 return ((size_t)r != 1);
16264 BOOL partial_p (BYTE* r)
16266 return (((size_t)r&1) && !((size_t)r&2));
16269 BOOL straight_ref_p (BYTE* r)
16271 return (!stolen_p (r) && !partial_p (r));
16274 BOOL partial_object_p (BYTE* r)
16276 return (((size_t)r & partial_object) == partial_object);
16279 BOOL ref_p (BYTE* r)
16281 return (straight_ref_p (r) || partial_object_p (r));
16284 void gc_heap::mark_object_simple1 (BYTE* oo, BYTE* start THREAD_NUMBER_DCL)
16286 SERVER_SC_MARK_VOLATILE(BYTE*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(BYTE*)*)mark_stack_array;
16287 SERVER_SC_MARK_VOLATILE(BYTE*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(BYTE*)*)&mark_stack_array[mark_stack_array_length];
16288 SERVER_SC_MARK_VOLATILE(BYTE*)* mark_stack_base = mark_stack_tos;
16289 #ifdef SORT_MARK_STACK
16290 SERVER_SC_MARK_VOLATILE(BYTE*)* sorted_tos = mark_stack_base;
16291 #endif //SORT_MARK_STACK
16293 // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't
16294 // update mark list.
16295 BOOL full_p = (settings.condemned_generation == max_generation);
16297 assert ((start >= oo) && (start < oo+size(oo)));
16300 *mark_stack_tos = oo;
16301 #endif //!MH_SC_MARK
16305 #ifdef MULTIPLE_HEAPS
16306 #else //MULTIPLE_HEAPS
16307 const int thread = 0;
16308 #endif //MULTIPLE_HEAPS
16310 if (oo && ((size_t)oo != 4))
16318 else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (BYTE*))))
16320 BOOL overflow_p = FALSE;
16322 if (mark_stack_tos + (s) /sizeof (BYTE*) >= (mark_stack_limit - 1))
16324 size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
16325 if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
16331 if (overflow_p == FALSE)
16333 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
16335 go_through_object_cl (method_table(oo), oo, s, ppslot,
16339 if (gc_mark (o, gc_low, gc_high))
16343 m_boundary_fullgc (o);
16349 size_t obj_size = size (o);
16350 promoted_bytes (thread) += obj_size;
16351 if (contain_pointers_or_collectible (o))
16353 *(mark_stack_tos++) = o;
16361 dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
16362 min_overflow_address = min (min_overflow_address, oo);
16363 max_overflow_address = max (max_overflow_address, oo);
16368 if (partial_p (oo))
16370 start = ref_from_slot (oo);
16371 oo = ref_from_slot (*(--mark_stack_tos));
16372 dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
16373 assert ((oo < start) && (start < (oo + size (oo))));
16375 #ifdef COLLECTIBLE_CLASS
16378 // If there's a class object, push it now. We are guaranteed to have the slot since
16379 // we just popped one object off.
16380 if (is_collectible (oo))
16382 BYTE* class_obj = get_class_object (oo);
16383 if (gc_mark (class_obj, gc_low, gc_high))
16387 m_boundary_fullgc (class_obj);
16391 m_boundary (class_obj);
16394 size_t obj_size = size (class_obj);
16395 promoted_bytes (thread) += obj_size;
16396 *(mark_stack_tos++) = class_obj;
16400 #endif //COLLECTIBLE_CLASS
16404 BOOL overflow_p = FALSE;
16406 if (mark_stack_tos + (num_partial_refs + 2) >= mark_stack_limit)
16410 if (overflow_p == FALSE)
16412 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
16414 //push the object and its current
16415 SERVER_SC_MARK_VOLATILE(BYTE*)* place = ++mark_stack_tos;
16419 *(place) = (BYTE*)partial;
16420 #endif //MH_SC_MARK
16421 int i = num_partial_refs;
16422 BYTE* ref_to_continue = 0;
16424 go_through_object (method_table(oo), oo, s, ppslot,
16425 start, use_start, (oo + s),
16429 if (gc_mark (o, gc_low, gc_high))
16433 m_boundary_fullgc (o);
16439 size_t obj_size = size (o);
16440 promoted_bytes (thread) += obj_size;
16441 if (contain_pointers_or_collectible (o))
16443 *(mark_stack_tos++) = o;
16446 ref_to_continue = (BYTE*)((size_t)(ppslot+1) | partial);
16455 //we are finished with this object
16456 assert (ref_to_continue == 0);
16458 assert ((*(place-1)) == (BYTE*)0);
16461 #endif //MH_SC_MARK
16463 // shouldn't we decrease tos by 2 here??
16466 if (ref_to_continue)
16470 assert ((*(place-1)) == (BYTE*)0);
16471 *(place-1) = (BYTE*)((size_t)oo | partial_object);
16472 assert (((*place) == (BYTE*)1) || ((*place) == (BYTE*)2));
16473 #endif //MH_SC_MARK
16474 *place = ref_to_continue;
16479 dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
16480 min_overflow_address = min (min_overflow_address, oo);
16481 max_overflow_address = max (max_overflow_address, oo);
16484 #ifdef SORT_MARK_STACK
16485 if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
16487 rqsort1 (sorted_tos, mark_stack_tos-1);
16488 sorted_tos = mark_stack_tos-1;
16490 #endif //SORT_MARK_STACK
16493 if (!(mark_stack_empty_p()))
16495 oo = *(--mark_stack_tos);
16498 #ifdef SORT_MARK_STACK
16499 sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
16500 #endif //SORT_MARK_STACK
16508 BOOL same_numa_node_p (int hn1, int hn2)
16510 return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
16513 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
16515 int hn = (current_buddy+1)%n_heaps;
16516 while (hn != current_buddy)
16518 if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
16520 hn = (hn+1)%n_heaps;
16522 return current_buddy;
16526 gc_heap::mark_steal()
16528 mark_stack_busy() = 0;
16529 //clear the mark stack in the snooping range
16530 for (int i = 0; i < max_snoop_level; i++)
16532 ((VOLATILE(BYTE*)*)(mark_stack_array))[i] = 0;
16535 //pick the next heap as our buddy
16536 int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
16539 dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
16540 DWORD begin_tick = GetTickCount();
16541 #endif //SNOOP_STATS
16543 int idle_loop_count = 0;
16544 int first_not_ready_level = 0;
16548 gc_heap* hp = g_heaps [thpn];
16549 int level = first_not_ready_level;
16550 first_not_ready_level = 0;
16552 while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
16554 idle_loop_count = 0;
16556 snoop_stat.busy_count++;
16557 dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix",
16558 heap_number, level, (int)((BYTE**)(hp->mark_stack_array))[level]));
16559 #endif //SNOOP_STATS
16561 BYTE* o = ref_mark_stack (hp, level);
16566 mark_stack_busy() = 1;
16568 BOOL success = TRUE;
16569 BYTE* next = (ref_mark_stack (hp, level+1));
16572 if (((size_t)o > 4) && !partial_object_p (o))
16574 //this is a normal object, not a partial mark tuple
16575 //success = (FastInterlockCompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
16576 success = (FastInterlockCompareExchangePointer (&ref_mark_stack (hp, level), 4, o)==o);
16578 snoop_stat.interlocked_count++;
16580 snoop_stat.normal_count++;
16581 #endif //SNOOP_STATS
16585 //it is a stolen entry, or beginning/ending of a partial mark
16588 snoop_stat.stolen_or_pm_count++;
16589 #endif //SNOOP_STATS
16593 else if (stolen_p (next))
16595 //ignore the stolen guy and go to the next level
16599 snoop_stat.stolen_entry_count++;
16600 #endif //SNOOP_STATS
16604 assert (partial_p (next));
16605 start = ref_from_slot (next);
16606 //re-read the object
16607 o = ref_from_slot (ref_mark_stack (hp, level));
16611 success = (FastInterlockCompareExchangePointer (&ref_mark_stack (hp, level+1), stolen, next)==next);
16613 snoop_stat.interlocked_count++;
16616 snoop_stat.partial_mark_parent_count++;
16618 #endif //SNOOP_STATS
16622 // stack is not ready, or o is completely different from the last time we read from this stack level.
16623 // go up 2 levels to steal children or totally unrelated objects.
16625 if (first_not_ready_level == 0)
16627 first_not_ready_level = level;
16631 snoop_stat.pm_not_ready_count++;
16632 #endif //SNOOP_STATS
16639 dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
16640 heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
16641 (GetTickCount()-begin_tick)));
16642 DWORD start_tick = GetTickCount();
16643 #endif //SNOOP_STATS
16645 mark_object_simple1 (o, start, heap_number);
16648 dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
16649 heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
16650 (GetTickCount()-start_tick),(GetTickCount()-begin_tick)));
16651 #endif //SNOOP_STATS
16653 mark_stack_busy() = 0;
16655 //clear the mark stack in snooping range
16656 for (int i = 0; i < max_snoop_level; i++)
16658 if (((BYTE**)mark_stack_array)[i] != 0)
16660 ((VOLATILE(BYTE*)*)(mark_stack_array))[i] = 0;
16662 snoop_stat.stack_bottom_clear_count++;
16663 #endif //SNOOP_STATS
16669 mark_stack_busy() = 0;
16673 //slot is either partial or stolen
16677 if ((first_not_ready_level != 0) && hp->mark_stack_busy())
16681 if (!hp->mark_stack_busy())
16683 first_not_ready_level = 0;
16686 if ((idle_loop_count % (6) )==1)
16689 snoop_stat.switch_to_thread_count++;
16690 #endif //SNOOP_STATS
16691 __SwitchToThread(1,0);
16693 int free_count = 1;
16695 snoop_stat.stack_idle_count++;
16696 //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
16697 #endif //SNOOP_STATS
16698 for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
16700 if (!((g_heaps [hpn])->mark_stack_busy()))
16704 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
16705 #endif //SNOOP_STATS
16707 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
16712 hpn = (hpn+1)%n_heaps;
16715 if (free_count == n_heaps)
16724 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
16727 snoop_stat.check_level_count++;
16728 #endif //SNOOP_STATS
16729 return (next_heap->mark_stack_busy()>=1);
16731 #endif //MH_SC_MARK
16734 void gc_heap::print_snoop_stat()
16736 dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s",
16737 "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
16738 dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
16739 snoop_stat.heap_index,
16740 snoop_stat.objects_checked_count,
16741 snoop_stat.zero_ref_count,
16742 snoop_stat.objects_marked_count,
16743 snoop_stat.stolen_stack_count,
16744 snoop_stat.partial_stack_count,
16745 snoop_stat.normal_stack_count,
16746 snoop_stat.non_stack_count));
16747 dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s",
16748 "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
16749 dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
16750 snoop_stat.heap_index,
16751 snoop_stat.check_level_count,
16752 snoop_stat.busy_count,
16753 snoop_stat.interlocked_count,
16754 snoop_stat.partial_mark_parent_count,
16755 snoop_stat.stolen_or_pm_count,
16756 snoop_stat.stolen_entry_count,
16757 snoop_stat.pm_not_ready_count,
16758 snoop_stat.normal_count,
16759 snoop_stat.stack_bottom_clear_count));
16761 printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n",
16762 "heap", "check", "zero", "mark", "idle", "switch");
16763 printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
16764 snoop_stat.heap_index,
16765 snoop_stat.objects_checked_count,
16766 snoop_stat.zero_ref_count,
16767 snoop_stat.objects_marked_count,
16768 snoop_stat.stack_idle_count,
16769 snoop_stat.switch_to_thread_count);
16770 printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n",
16771 "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
16772 printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
16773 snoop_stat.heap_index,
16774 snoop_stat.check_level_count,
16775 snoop_stat.busy_count,
16776 snoop_stat.interlocked_count,
16777 snoop_stat.partial_mark_parent_count,
16778 snoop_stat.stolen_or_pm_count,
16779 snoop_stat.stolen_entry_count,
16780 snoop_stat.pm_not_ready_count,
16781 snoop_stat.normal_count,
16782 snoop_stat.stack_bottom_clear_count);
16784 #endif //SNOOP_STATS
16786 #ifdef HEAP_ANALYZE
16788 gc_heap::ha_mark_object_simple (BYTE** po THREAD_NUMBER_DCL)
16790 if (!internal_root_array)
16792 internal_root_array = new (nothrow) (BYTE* [internal_root_array_length]);
16793 if (!internal_root_array)
16795 heap_analyze_success = FALSE;
16799 if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
16801 size_t new_size = 2*internal_root_array_length;
16803 MEMORYSTATUSEX statex;
16804 GetProcessMemoryLoad(&statex);
16805 if (new_size > (size_t)(statex.ullAvailPhys / 10))
16807 heap_analyze_success = FALSE;
16811 BYTE** tmp = new (nothrow) (BYTE* [new_size]);
16814 memcpy (tmp, internal_root_array,
16815 internal_root_array_length*sizeof (BYTE*));
16816 delete[] internal_root_array;
16817 internal_root_array = tmp;
16818 internal_root_array_length = new_size;
16822 heap_analyze_success = FALSE;
16827 if (heap_analyze_success)
16829 PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
16831 BYTE* ref = (BYTE*)po;
16832 if (!current_obj ||
16833 !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
16835 gc_heap* hp = gc_heap::heap_of (ref);
16836 current_obj = hp->find_object (ref, hp->lowest_address);
16837 current_obj_size = size (current_obj);
16839 internal_root_array[internal_root_array_index] = current_obj;
16840 internal_root_array_index++;
16844 mark_object_simple (po THREAD_NUMBER_ARG);
16846 #endif //HEAP_ANALYZE
16848 //this method assumes that *po is in the [low. high[ range
16850 gc_heap::mark_object_simple (BYTE** po THREAD_NUMBER_DCL)
16853 #ifdef MULTIPLE_HEAPS
16854 #else //MULTIPLE_HEAPS
16855 const int thread = 0;
16856 #endif //MULTIPLE_HEAPS
16859 snoop_stat.objects_checked_count++;
16860 #endif //SNOOP_STATS
16865 size_t s = size (o);
16866 promoted_bytes (thread) += s;
16868 go_through_object_cl (method_table(o), o, s, poo,
16871 if (gc_mark (oo, gc_low, gc_high))
16874 size_t obj_size = size (oo);
16875 promoted_bytes (thread) += obj_size;
16877 if (contain_pointers_or_collectible (oo))
16878 mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
16888 BYTE* gc_heap::mark_object (BYTE* o THREAD_NUMBER_DCL)
16890 if ((o >= gc_low) && (o < gc_high))
16891 mark_object_simple (&o THREAD_NUMBER_ARG);
16892 #ifdef MULTIPLE_HEAPS
16896 gc_heap* hp = heap_of (o);
16898 if ((o >= hp->gc_low) && (o < hp->gc_high))
16899 mark_object_simple (&o THREAD_NUMBER_ARG);
16901 #endif //MULTIPLE_HEAPS
16906 #ifdef BACKGROUND_GC
16908 void gc_heap::background_mark_simple1 (BYTE* oo THREAD_NUMBER_DCL)
16910 BYTE** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
16912 #ifdef SORT_MARK_STACK
16913 BYTE** sorted_tos = background_mark_stack_array;
16914 #endif //SORT_MARK_STACK
16916 background_mark_stack_tos = background_mark_stack_array;
16920 #ifdef MULTIPLE_HEAPS
16921 #else //MULTIPLE_HEAPS
16922 const int thread = 0;
16923 #endif //MULTIPLE_HEAPS
16927 if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (BYTE*))))
16929 BOOL overflow_p = FALSE;
16931 if (background_mark_stack_tos + (s) /sizeof (BYTE*) >= (mark_stack_limit - 1))
16933 size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
16934 size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
16935 if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
16937 dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs",
16939 (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
16943 bgc_overflow_count++;
16948 if (overflow_p == FALSE)
16950 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
16952 go_through_object_cl (method_table(oo), oo, s, ppslot,
16956 if (background_mark (o,
16957 background_saved_lowest_address,
16958 background_saved_highest_address))
16961 size_t obj_size = size (o);
16962 bpromoted_bytes (thread) += obj_size;
16963 if (contain_pointers_or_collectible (o))
16965 *(background_mark_stack_tos++) = o;
16974 dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
16975 background_min_overflow_address = min (background_min_overflow_address, oo);
16976 background_max_overflow_address = max (background_max_overflow_address, oo);
16982 if ((size_t)oo & 1)
16984 oo = (BYTE*)((size_t)oo & ~1);
16985 start = *(--background_mark_stack_tos);
16986 dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
16988 #ifdef COLLECTIBLE_CLASS
16991 // If there's a class object, push it now. We are guaranteed to have the slot since
16992 // we just popped one object off.
16993 if (is_collectible (oo))
16995 BYTE* class_obj = get_class_object (oo);
16996 if (background_mark (class_obj,
16997 background_saved_lowest_address,
16998 background_saved_highest_address))
17000 size_t obj_size = size (class_obj);
17001 bpromoted_bytes (thread) += obj_size;
17003 *(background_mark_stack_tos++) = class_obj;
17007 #endif //COLLECTIBLE_CLASS
17011 BOOL overflow_p = FALSE;
17013 if (background_mark_stack_tos + (num_partial_refs + 2) >= mark_stack_limit)
17015 size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
17016 size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
17018 dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id",
17020 (size_t)(mark_stack_limit - background_mark_stack_tos),
17026 bgc_overflow_count++;
17029 if (overflow_p == FALSE)
17031 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17033 //push the object and its current
17034 BYTE** place = background_mark_stack_tos++;
17036 *(background_mark_stack_tos++) = (BYTE*)((size_t)oo | 1);
17038 int i = num_partial_refs;
17040 go_through_object (method_table(oo), oo, s, ppslot,
17041 start, use_start, (oo + s),
17046 if (background_mark (o,
17047 background_saved_lowest_address,
17048 background_saved_highest_address))
17051 size_t obj_size = size (o);
17052 bpromoted_bytes (thread) += obj_size;
17053 if (contain_pointers_or_collectible (o))
17055 *(background_mark_stack_tos++) = o;
17059 *place = (BYTE*)(ppslot+1);
17068 //we are finished with this object
17076 dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
17077 background_min_overflow_address = min (background_min_overflow_address, oo);
17078 background_max_overflow_address = max (background_max_overflow_address, oo);
17082 #ifdef SORT_MARK_STACK
17083 if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
17085 rqsort1 (sorted_tos, background_mark_stack_tos-1);
17086 sorted_tos = background_mark_stack_tos-1;
17088 #endif //SORT_MARK_STACK
17092 if (!(background_mark_stack_tos == background_mark_stack_array))
17094 oo = *(--background_mark_stack_tos);
17096 #ifdef SORT_MARK_STACK
17097 sorted_tos = (BYTE**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
17098 #endif //SORT_MARK_STACK
17104 assert (background_mark_stack_tos == background_mark_stack_array);
17109 //this version is different than the foreground GC because
17110 //it can't keep pointers to the inside of an object
17111 //while calling background_mark_simple1. The object could be moved
17112 //by an intervening foreground gc.
17113 //this method assumes that *po is in the [low. high[ range
17115 gc_heap::background_mark_simple (BYTE* o THREAD_NUMBER_DCL)
17117 #ifdef MULTIPLE_HEAPS
17118 #else //MULTIPLE_HEAPS
17119 const int thread = 0;
17120 #endif //MULTIPLE_HEAPS
17122 dprintf (3, ("bmarking %Ix", o));
17124 if (background_mark1 (o))
17127 size_t s = size (o);
17128 bpromoted_bytes (thread) += s;
17130 if (contain_pointers_or_collectible (o))
17132 background_mark_simple1 (o THREAD_NUMBER_ARG);
17139 BYTE* gc_heap::background_mark_object (BYTE* o THREAD_NUMBER_DCL)
17141 if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
17143 background_mark_simple (o THREAD_NUMBER_ARG);
17149 dprintf (3, ("or-%Ix", o));
17155 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, DWORD flags)
17157 assert (settings.concurrent);
17158 BYTE* o = (BYTE*)object;
17160 gc_heap* hp = gc_heap::heap_of (o);
17161 #ifdef INTERIOR_POINTERS
17162 if (flags & GC_CALL_INTERIOR)
17164 o = hp->find_object (o, background_saved_lowest_address);
17166 #endif //INTERIOR_POINTERS
17168 if (!background_object_marked (o, FALSE))
17174 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, DWORD flags)
17177 //in order to save space on the array, mark the object,
17178 //knowing that it will be visited later
17179 assert (settings.concurrent);
17181 THREAD_NUMBER_FROM_CONTEXT;
17182 #ifndef MULTIPLE_HEAPS
17183 const int thread = 0;
17184 #endif //!MULTIPLE_HEAPS
17186 BYTE* o = (BYTE*)*ppObject;
17191 #ifdef DEBUG_DestroyedHandleValue
17192 // we can race with destroy handle during concurrent scan
17193 if (o == (BYTE*)DEBUG_DestroyedHandleValue)
17195 #endif //DEBUG_DestroyedHandleValue
17199 gc_heap* hp = gc_heap::heap_of (o);
17201 if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
17206 #ifdef INTERIOR_POINTERS
17207 if (flags & GC_CALL_INTERIOR)
17209 o = hp->find_object (o, hp->background_saved_lowest_address);
17213 #endif //INTERIOR_POINTERS
17215 #ifdef FEATURE_CONSERVATIVE_GC
17216 // For conservative GC, a value on stack may point to middle of a free object.
17217 // In this case, we don't need to promote the pointer.
17218 if (g_pConfig->GetGCConservative() && ((CObjectHeader*)o)->IsFree())
17222 #endif //FEATURE_CONSERVATIVE_GC
17225 ((CObjectHeader*)o)->Validate();
17228 dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
17230 //needs to be called before the marking because it is possible for a foreground
17231 //gc to take place during the mark and move the object
17232 STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, " GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetMethodTable() : NULL);
17234 hpt->background_mark_simple (o THREAD_NUMBER_ARG);
17237 //used by the ephemeral collection to scan the local background structures
17238 //containing references.
17240 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
17246 pSC->thread_number = hn;
17248 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
17249 pSC->pCurrentDomain = 0;
17252 BOOL relocate_p = (fn == &GCHeap::Relocate);
17254 dprintf (3, ("Scanning background mark list"));
17257 size_t mark_list_finger = 0;
17258 while (mark_list_finger < c_mark_list_index)
17260 BYTE** o = &c_mark_list [mark_list_finger];
17263 // We may not be able to calculate the size during relocate as POPO
17264 // may have written over the object.
17265 size_t s = size (*o);
17266 assert (Align (s) >= Align (min_obj_size));
17267 dprintf(3,("background root %Ix", (size_t)*o));
17269 (*fn) ((Object**)o, pSC, 0);
17270 mark_list_finger++;
17273 //scan the mark stack
17274 dprintf (3, ("Scanning background mark stack"));
17276 BYTE** finger = background_mark_stack_array;
17277 while (finger < background_mark_stack_tos)
17279 if ((finger + 1) < background_mark_stack_tos)
17281 // We need to check for the partial mark case here.
17282 BYTE* parent_obj = *(finger + 1);
17283 if ((size_t)parent_obj & 1)
17285 BYTE* place = *finger;
17286 size_t place_offset = 0;
17287 BYTE* real_parent_obj = (BYTE*)((size_t)parent_obj & ~1);
17291 *(finger + 1) = real_parent_obj;
17292 place_offset = place - real_parent_obj;
17293 dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
17294 (*fn) ((Object**)(finger + 1), pSC, 0);
17295 real_parent_obj = *(finger + 1);
17296 *finger = real_parent_obj + place_offset;
17297 *(finger + 1) = (BYTE*)((size_t)real_parent_obj | 1);
17298 dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
17302 BYTE** temp = &real_parent_obj;
17303 dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
17304 (*fn) ((Object**)temp, pSC, 0);
17311 dprintf(3,("background root %Ix", (size_t)*finger));
17312 (*fn) ((Object**)finger, pSC, 0);
17317 #endif //BACKGROUND_GC
17320 void gc_heap::fix_card_table ()
17323 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
17325 PREFIX_ASSUME(seg != NULL);
17328 #ifdef BACKGROUND_GC
17329 DWORD mode = settings.concurrent ? 1 : 0;
17330 #else //BACKGROUND_GC
17332 #endif //BACKGROUND_GC
17333 BOOL small_object_segments = TRUE;
17338 if (small_object_segments)
17340 small_object_segments = FALSE;
17341 seg = heap_segment_rw (generation_start_segment (large_object_generation));
17343 PREFIX_ASSUME(seg != NULL);
17352 BYTE* base_address = align_lower_page (heap_segment_mem (seg));
17353 BYTE* high_address = align_on_page (
17354 (seg != ephemeral_heap_segment) ?
17355 heap_segment_allocated (seg) :
17356 generation_allocation_start (generation_of (0))
17358 ULONG_PTR bcount = array_size;
17361 if(high_address <= base_address)
17364 size_t region_size = high_address - base_address;
17365 assert (region_size > 0);
17366 dprintf (3,("Probing pages [%Ix, %Ix[", (size_t)base_address, (size_t)high_address));
17368 #ifdef TIME_WRITE_WATCH
17369 unsigned int time_start = GetCycleCount32();
17370 #endif //TIME_WRITE_WATCH
17371 UINT status = GetWriteWatch (mode, base_address, region_size,
17372 (PVOID*)g_addresses,
17373 &bcount, &granularity);
17374 assert (status == 0);
17376 #ifdef TIME_WRITE_WATCH
17377 unsigned int time_stop = GetCycleCount32();
17378 tot_cycles += time_stop - time_start;
17379 printf ("GetWriteWatch Duration: %d, total: %d\n",
17380 time_stop - time_start, tot_cycles);
17381 #endif //TIME_WRITE_WATCH
17383 assert( ((card_size * card_word_width)&(OS_PAGE_SIZE-1))==0 );
17384 assert (granularity == OS_PAGE_SIZE);
17385 //printf ("%Ix written into\n", bcount);
17386 dprintf (3,("Found %Id pages written", bcount));
17387 for (unsigned i = 0; i < bcount; i++)
17389 for (unsigned j = 0; j< (card_size*card_word_width)/OS_PAGE_SIZE; j++)
17391 card_table [card_word (card_of (g_addresses [i]))+j] = ~0u;
17393 dprintf (2,("Set Cards [%Ix:%Ix, %Ix:%Ix[",
17394 card_of (g_addresses [i]), (size_t)g_addresses [i],
17395 card_of (g_addresses [i]+OS_PAGE_SIZE), (size_t)g_addresses [i]+OS_PAGE_SIZE));
17397 if (bcount >= array_size){
17398 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
17399 bcount = array_size;
17401 } while (bcount >= array_size);
17402 seg = heap_segment_next_rw (seg);
17404 #ifdef BACKGROUND_GC
17405 if (settings.concurrent)
17407 //reset the ephemeral page allocated by generation_of (0)
17408 BYTE* base_address =
17409 align_on_page (generation_allocation_start (generation_of (0)));
17410 size_t region_size =
17411 heap_segment_allocated (ephemeral_heap_segment) - base_address;
17412 ResetWriteWatch (base_address, region_size);
17414 #endif //BACKGROUND_GC
17415 #endif //WRITE_WATCH
17418 #ifdef BACKGROUND_GC
17420 void gc_heap::background_mark_through_object (BYTE* oo THREAD_NUMBER_DCL)
17422 if (contain_pointers (oo))
17424 size_t total_refs = 0;
17425 size_t s = size (oo);
17426 go_through_object_nostart (method_table(oo), oo, s, po,
17430 background_mark_object (o THREAD_NUMBER_ARG);
17434 dprintf (3,("Background marking through %Ix went through %Id refs",
17440 BYTE* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
17442 if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
17444 // for now we stop at where gen1 started when we started processing
17445 return background_min_soh_overflow_address;
17449 return heap_segment_allocated (seg);
17453 BYTE* gc_heap::background_first_overflow (BYTE* min_add,
17456 BOOL small_object_p)
17460 if (small_object_p)
17462 if (in_range_for_segment (min_add, seg))
17464 // min_add was the beginning of gen1 when we did the concurrent
17465 // overflow. Now we could be in a situation where min_add is
17466 // actually the same as allocated for that segment (because
17467 // we expanded heap), in which case we can not call
17468 // find first on this address or we will AV.
17469 if (min_add >= heap_segment_allocated (seg))
17475 if (concurrent_p &&
17476 ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
17478 return background_min_soh_overflow_address;
17482 o = find_first_object (min_add, heap_segment_mem (seg));
17489 o = max (heap_segment_mem (seg), min_add);
17493 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
17494 BYTE* min_add, BYTE* max_add,
17499 current_bgc_state = bgc_overflow_soh;
17502 size_t total_marked_objects = 0;
17504 #ifdef MULTIPLE_HEAPS
17505 int thread = heap_number;
17506 #endif //MULTIPLE_HEAPS
17508 exclusive_sync* loh_alloc_lock = 0;
17510 dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
17511 #ifdef MULTIPLE_HEAPS
17512 // We don't have each heap scan all heaps concurrently because we are worried about
17513 // multiple threads calling things like find_first_object.
17514 int h_start = (concurrent_p ? heap_number : 0);
17515 int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
17516 for (int hi = h_start; hi < h_end; hi++)
17518 gc_heap* hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
17524 #endif //MULTIPLE_HEAPS
17525 BOOL small_object_segments = TRUE;
17526 int align_const = get_alignment_constant (small_object_segments);
17527 generation* gen = hp->generation_of (condemned_gen_number);
17528 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
17529 PREFIX_ASSUME(seg != NULL);
17530 loh_alloc_lock = hp->bgc_alloc_lock;
17532 BYTE* o = hp->background_first_overflow (min_add,
17535 small_object_segments);
17539 while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
17541 dprintf (3, ("considering %Ix", (size_t)o));
17545 if (concurrent_p && !small_object_segments)
17547 loh_alloc_lock->bgc_mark_set (o);
17549 if (((CObjectHeader*)o)->IsFree())
17551 s = unused_array_size (o);
17563 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
17565 total_marked_objects++;
17566 go_through_object_cl (method_table(o), o, s, poo,
17568 background_mark_object (oo THREAD_NUMBER_ARG);
17572 if (concurrent_p && !small_object_segments)
17574 loh_alloc_lock->bgc_mark_done ();
17577 o = o + Align (s, align_const);
17585 dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)",
17586 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
17588 if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
17589 (seg = heap_segment_next_in_range (seg)) == 0)
17591 if (small_object_segments)
17595 current_bgc_state = bgc_overflow_loh;
17598 dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
17599 fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
17600 concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
17601 total_marked_objects = 0;
17602 small_object_segments = FALSE;
17603 align_const = get_alignment_constant (small_object_segments);
17604 seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
17606 PREFIX_ASSUME(seg != NULL);
17608 o = max (heap_segment_mem (seg), min_add);
17613 dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
17614 fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
17620 o = hp->background_first_overflow (min_add,
17623 small_object_segments);
17630 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
17632 BOOL grow_mark_array_p = TRUE;
17636 assert (!processed_soh_overflow_p);
17638 if ((background_max_overflow_address != 0) &&
17639 (background_min_overflow_address != MAX_PTR))
17641 // We have overflow to process but we know we can't process the ephemeral generations
17642 // now (we actually could process till the current gen1 start but since we are going to
17643 // make overflow per segment, for now I'll just stop at the saved gen1 start.
17644 saved_overflow_ephemeral_seg = ephemeral_heap_segment;
17645 background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
17646 background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
17651 assert ((saved_overflow_ephemeral_seg == 0) ||
17652 ((background_max_soh_overflow_address != 0) &&
17653 (background_min_soh_overflow_address != MAX_PTR)));
17655 if (!processed_soh_overflow_p)
17657 // if there was no more overflow we just need to process what we didn't process
17658 // on the saved ephemeral segment.
17659 if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
17661 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
17662 grow_mark_array_p = FALSE;
17665 background_min_overflow_address = min (background_min_overflow_address,
17666 background_min_soh_overflow_address);
17667 background_max_overflow_address = max (background_max_overflow_address,
17668 background_max_soh_overflow_address);
17669 processed_soh_overflow_p = TRUE;
17673 BOOL overflow_p = FALSE;
17675 if ((! ((background_max_overflow_address == 0)) ||
17676 ! ((background_min_overflow_address == MAX_PTR))))
17680 if (grow_mark_array_p)
17682 // Try to grow the array.
17683 size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
17685 if ((new_size * sizeof(mark)) > 100*1024)
17687 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
17689 new_size = min(new_max_size, new_size);
17692 if ((background_mark_stack_array_length < new_size) &&
17693 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
17695 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
17697 BYTE** tmp = new (nothrow) (BYTE* [new_size]);
17700 delete background_mark_stack_array;
17701 background_mark_stack_array = tmp;
17702 background_mark_stack_array_length = new_size;
17703 background_mark_stack_tos = background_mark_stack_array;
17709 grow_mark_array_p = TRUE;
17712 BYTE* min_add = background_min_overflow_address;
17713 BYTE* max_add = background_max_overflow_address;
17715 background_max_overflow_address = 0;
17716 background_min_overflow_address = MAX_PTR;
17718 background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
17728 #endif //BACKGROUND_GC
17731 void gc_heap::mark_through_object (BYTE* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
17733 #ifndef COLLECTIBLE_CLASS
17734 BOOL to_mark_class_object = FALSE;
17735 #else //COLLECTIBLE_CLASS
17736 BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
17737 #endif //COLLECTIBLE_CLASS
17738 if (contain_pointers (oo) || to_mark_class_object)
17740 dprintf(3,( "Marking through %Ix", (size_t)oo));
17741 size_t s = size (oo);
17743 #ifdef COLLECTIBLE_CLASS
17744 if (to_mark_class_object)
17746 BYTE* class_obj = get_class_object (oo);
17747 mark_object (class_obj THREAD_NUMBER_ARG);
17749 #endif //COLLECTIBLE_CLASS
17751 if (contain_pointers (oo))
17753 go_through_object_nostart (method_table(oo), oo, s, po,
17755 mark_object (o THREAD_NUMBER_ARG);
17761 size_t gc_heap::get_total_heap_size()
17763 size_t total_heap_size = 0;
17765 #ifdef MULTIPLE_HEAPS
17768 for (hn = 0; hn < gc_heap::n_heaps; hn++)
17770 gc_heap* hp2 = gc_heap::g_heaps [hn];
17771 total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
17774 total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
17775 #endif //MULTIPLE_HEAPS
17777 return total_heap_size;
17780 //returns TRUE is an overflow happened.
17781 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
17783 BOOL overflow_p = FALSE;
17785 if ((! (max_overflow_address == 0) ||
17786 ! (min_overflow_address == MAX_PTR)))
17789 // Try to grow the array.
17791 max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
17793 if ((new_size * sizeof(mark)) > 100*1024)
17795 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
17797 new_size = min(new_max_size, new_size);
17800 if ((mark_stack_array_length < new_size) &&
17801 ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
17803 mark* tmp = new (nothrow) (mark [new_size]);
17806 delete mark_stack_array;
17807 mark_stack_array = tmp;
17808 mark_stack_array_length = new_size;
17812 BYTE* min_add = min_overflow_address;
17813 BYTE* max_add = max_overflow_address;
17814 max_overflow_address = 0;
17815 min_overflow_address = MAX_PTR;
17816 process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
17823 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
17824 BYTE* min_add, BYTE* max_add)
17826 #ifdef MULTIPLE_HEAPS
17827 int thread = heap_number;
17828 #endif //MULTIPLE_HEAPS
17829 BOOL full_p = (condemned_gen_number == max_generation);
17831 dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
17832 #ifdef MULTIPLE_HEAPS
17833 for (int hi = 0; hi < n_heaps; hi++)
17835 gc_heap* hp = g_heaps [(heap_number + hi) % n_heaps];
17841 #endif //MULTIPLE_HEAPS
17842 BOOL small_object_segments = TRUE;
17843 int align_const = get_alignment_constant (small_object_segments);
17844 generation* gen = hp->generation_of (condemned_gen_number);
17845 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
17847 PREFIX_ASSUME(seg != NULL);
17848 BYTE* o = max (heap_segment_mem (seg), min_add);
17851 BYTE* end = heap_segment_allocated (seg);
17853 while ((o < end) && (o <= max_add))
17855 assert ((min_add <= o) && (max_add >= o));
17856 dprintf (3, ("considering %Ix", (size_t)o));
17859 mark_through_object (o, TRUE THREAD_NUMBER_ARG);
17862 o = o + Align (size (o), align_const);
17865 if (( seg = heap_segment_next_in_range (seg)) == 0)
17867 if (small_object_segments && full_p)
17869 small_object_segments = FALSE;
17870 align_const = get_alignment_constant (small_object_segments);
17871 seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
17873 PREFIX_ASSUME(seg != NULL);
17875 o = max (heap_segment_mem (seg), min_add);
17885 o = max (heap_segment_mem (seg), min_add);
17893 void fire_mark_event (int heap_num, int mark_num)
17897 case ETW_TYPE_GC_MARK_1:
17898 FireEtwGCMarkStackRoots(heap_num, GetClrInstanceId());
17899 FireEtwPrvGCMarkStackRoots_V1(heap_num, GetClrInstanceId());
17902 case ETW_TYPE_GC_MARK_2:
17903 FireEtwGCMarkFinalizeQueueRoots(heap_num, GetClrInstanceId());
17904 FireEtwPrvGCMarkFinalizeQueueRoots_V1(heap_num, GetClrInstanceId());
17907 case ETW_TYPE_GC_MARK_3:
17908 FireEtwGCMarkHandles(heap_num, GetClrInstanceId());
17909 FireEtwPrvGCMarkHandles_V1(heap_num, GetClrInstanceId());
17912 case ETW_TYPE_GC_MARK_4:
17913 FireEtwGCMarkOlderGenerationRoots(heap_num, GetClrInstanceId());
17914 FireEtwPrvGCMarkCards_V1(heap_num, GetClrInstanceId());
17918 _ASSERTE(mark_num==ETW_TYPE_GC_MARK_1 || mark_num==ETW_TYPE_GC_MARK_2 || mark_num==ETW_TYPE_GC_MARK_3 || mark_num==ETW_TYPE_GC_MARK_4);
17923 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
17924 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
17925 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
17926 // promotion scan multiple times.
17927 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
17928 // also has the effect of processing any mark stack overflow.
17930 #ifdef MULTIPLE_HEAPS
17931 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
17932 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
17933 // implementations based on whether MULTIPLE_HEAPS is defined or not.
17935 // Define some static variables used for synchronization in the method below. These should really be defined
17936 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
17938 // A note about the synchronization used within this method. Communication between the worker threads is
17939 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
17940 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
17941 // protection of a join.
17942 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
17943 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
17944 static VOLATILE(BOOL) s_fScanRequired;
17945 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
17947 // Whenever we call this method there may have been preceding object promotions. So set
17948 // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
17949 // based on the how the scanning proceeded).
17950 s_fUnscannedPromotions = TRUE;
17952 // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
17953 // the state of this thread's portion of the dependent handle table. That's because promotions on other
17954 // threads could cause handle promotions to become necessary here. Even if there are definitely no more
17955 // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
17956 // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
17957 // as all the others or they'll get out of step).
17960 // The various worker threads are all currently racing in this code. We need to work out if at least
17961 // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
17962 // dependent handle table when both of the following conditions apply:
17963 // 1) At least one (arbitrary) object might have been promoted since the last scan (because if this
17964 // object happens to correspond to a primary in one of our handles we might potentially have to
17965 // promote the associated secondary).
17966 // 2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
17968 // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
17969 // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
17970 // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
17971 // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
17972 // follows below. Note that we can't read this outside of the join since on any iteration apart from
17973 // the first threads will be racing between reading this value and completing their previous
17974 // iteration's table scan.
17976 // The second condition is tracked by the dependent handle code itself on a per worker thread basis
17977 // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
17978 // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
17979 // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
17980 // we're safely joined.
17981 if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
17982 s_fUnpromotedHandles = TRUE;
17984 // Synchronize all the threads so we can read our state variables safely. The shared variable
17985 // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
17986 // a single thread inside the join.
17987 gc_t_join.join(this, gc_join_scan_dependent_handles);
17988 if (gc_t_join.joined())
17990 // We're synchronized so it's safe to read our shared state variables. We update another shared
17991 // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
17992 // the loop. We scan if there has been at least one object promotion since last time and at least
17993 // one thread has a dependent handle table with a potential handle promotion possible.
17994 s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
17996 // Reset our shared state variables (ready to be set again on this scan or with a good initial
17997 // value for the next call if we're terminating the loop).
17998 s_fUnscannedPromotions = FALSE;
17999 s_fUnpromotedHandles = FALSE;
18001 if (!s_fScanRequired)
18003 // We're terminating the loop. Perform any last operations that require single threaded access.
18004 if (!initial_scan_p)
18006 // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
18007 // load balance if some of the heaps have an abnormally large workload.
18008 BYTE* all_heaps_max = 0;
18009 BYTE* all_heaps_min = MAX_PTR;
18011 for (i = 0; i < n_heaps; i++)
18013 if (all_heaps_max < g_heaps[i]->max_overflow_address)
18014 all_heaps_max = g_heaps[i]->max_overflow_address;
18015 if (all_heaps_min > g_heaps[i]->min_overflow_address)
18016 all_heaps_min = g_heaps[i]->min_overflow_address;
18018 for (i = 0; i < n_heaps; i++)
18020 g_heaps[i]->max_overflow_address = all_heaps_max;
18021 g_heaps[i]->min_overflow_address = all_heaps_min;
18026 // Restart all the workers.
18027 dprintf(3, ("Starting all gc thread mark stack overflow processing"));
18028 gc_t_join.restart();
18031 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
18032 // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
18033 // global flag indicating that at least one object promotion may have occurred (the usual comment
18034 // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
18035 // exit the method since we unconditionally set this variable on method entry anyway).
18036 if (process_mark_overflow(condemned_gen_number))
18037 s_fUnscannedPromotions = TRUE;
18039 // If we decided that no scan was required we can terminate the loop now.
18040 if (!s_fScanRequired)
18043 // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
18044 // processed before we start scanning dependent handle tables (if overflows remain while we scan we
18045 // could miss noting the promotion of some primary objects).
18046 gc_t_join.join(this, gc_join_rescan_dependent_handles);
18047 if (gc_t_join.joined())
18049 // Restart all the workers.
18050 dprintf(3, ("Starting all gc thread for dependent handle promotion"));
18051 gc_t_join.restart();
18054 // If the portion of the dependent handle table managed by this worker has handles that could still be
18055 // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
18056 // could require a rescan of handles on this or other workers.
18057 if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
18058 if (CNameSpace::GcDhReScan(sc))
18059 s_fUnscannedPromotions = TRUE;
18062 #else //MULTIPLE_HEAPS
18063 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
18064 // threads synchronized.
18065 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
18067 // Whenever we call this method there may have been preceding object promotions. So set
18068 // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
18069 // based on the how the scanning proceeded).
18070 bool fUnscannedPromotions = true;
18072 // Loop until there are either no more dependent handles that can have their secondary promoted or we've
18073 // managed to perform a scan without promoting anything new.
18074 while (CNameSpace::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
18076 // On each iteration of the loop start with the assumption that no further objects have been promoted.
18077 fUnscannedPromotions = false;
18079 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
18080 // being visible. If there was an overflow (process_mark_overflow returned true) then additional
18081 // objects now appear to be promoted and we should set the flag.
18082 if (process_mark_overflow(condemned_gen_number))
18083 fUnscannedPromotions = true;
18085 // Perform the scan and set the flag if any promotions resulted.
18086 if (CNameSpace::GcDhReScan(sc))
18087 fUnscannedPromotions = true;
18090 // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
18091 // scan any handles at all this is the processing of overflows that may have occured prior to this method
18093 process_mark_overflow(condemned_gen_number);
18095 #endif //MULTIPLE_HEAPS
18097 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
18099 assert (settings.concurrent == FALSE);
18102 sc.thread_number = heap_number;
18103 sc.promotion = TRUE;
18104 sc.concurrent = FALSE;
18106 dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
18107 BOOL full_p = (condemned_gen_number == max_generation);
18112 start = GetCycleCount32();
18115 int gen_to_init = condemned_gen_number;
18116 if (condemned_gen_number == max_generation)
18118 gen_to_init = max_generation + 1;
18120 for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
18122 dynamic_data* dd = dynamic_data_of (gen_idx);
18123 dd_begin_data_size (dd) = generation_size (gen_idx) -
18124 dd_fragmentation (dd) -
18125 Align (size (generation_allocation_start (generation_of (gen_idx))));
18126 dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
18127 dd_survived_size (dd) = 0;
18128 dd_pinned_survived_size (dd) = 0;
18129 dd_artificial_pinned_survived_size (dd) = 0;
18130 dd_added_pinned_size (dd) = 0;
18132 dd_padding_size (dd) = 0;
18133 #endif //SHORT_PLUGS
18134 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
18135 dd_num_npinned_plugs (dd) = 0;
18136 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
18139 #ifdef FFIND_OBJECT
18140 if (gen0_must_clear_bricks > 0)
18141 gen0_must_clear_bricks--;
18142 #endif //FFIND_OBJECT
18144 promoted_bytes (heap_number) = 0;
18145 reset_mark_stack();
18148 memset (&snoop_stat, 0, sizeof(snoop_stat));
18149 snoop_stat.heap_index = heap_number;
18150 #endif //SNOOP_STATS
18155 //initialize the mark stack
18156 for (int i = 0; i < max_snoop_level; i++)
18158 ((BYTE**)(mark_stack_array))[i] = 0;
18161 mark_stack_busy() = 1;
18163 #endif //MH_SC_MARK
18165 static DWORD num_sizedrefs = 0;
18168 static BOOL do_mark_steal_p = FALSE;
18169 #endif //MH_SC_MARK
18171 #ifdef MULTIPLE_HEAPS
18172 gc_t_join.join(this, gc_join_begin_mark_phase);
18173 if (gc_t_join.joined())
18175 #endif //MULTIPLE_HEAPS
18177 num_sizedrefs = SystemDomain::System()->GetTotalNumSizedRefHandles();
18179 #ifdef MULTIPLE_HEAPS
18184 size_t total_heap_size = get_total_heap_size();
18186 if (total_heap_size > (100 * 1024 * 1024))
18188 do_mark_steal_p = TRUE;
18192 do_mark_steal_p = FALSE;
18197 do_mark_steal_p = FALSE;
18199 #endif //MH_SC_MARK
18201 gc_t_join.restart();
18203 #endif //MULTIPLE_HEAPS
18208 //set up the mark lists from g_mark_list
18209 assert (g_mark_list);
18210 #ifdef MULTIPLE_HEAPS
18211 mark_list = &g_mark_list [heap_number*mark_list_size];
18213 mark_list = g_mark_list;
18214 #endif //MULTIPLE_HEAPS
18215 //dont use the mark list for full gc
18216 //because multiple segments are more complex to handle and the list
18217 //is likely to overflow
18218 if (condemned_gen_number != max_generation)
18219 mark_list_end = &mark_list [mark_list_size-1];
18221 mark_list_end = &mark_list [0];
18222 mark_list_index = &mark_list [0];
18228 //%type% category = quote (mark);
18230 if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
18232 CNameSpace::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
18234 #ifdef MULTIPLE_HEAPS
18235 gc_t_join.join(this, gc_join_scan_sizedref_done);
18236 if (gc_t_join.joined())
18238 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
18239 gc_t_join.restart();
18241 #endif //MULTIPLE_HEAPS
18244 dprintf(3,("Marking Roots"));
18246 CNameSpace::GcScanRoots(GCHeap::Promote,
18247 condemned_gen_number, max_generation,
18250 fire_mark_event (heap_number, ETW_TYPE_GC_MARK_1);
18252 #ifdef BACKGROUND_GC
18253 if (recursive_gc_sync::background_running_p())
18255 scan_background_roots (GCHeap::Promote, heap_number, &sc);
18257 #endif //BACKGROUND_GC
18259 #ifdef FEATURE_PREMORTEM_FINALIZATION
18260 dprintf(3, ("Marking finalization data"));
18261 finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
18262 #endif // FEATURE_PREMORTEM_FINALIZATION
18264 fire_mark_event (heap_number, ETW_TYPE_GC_MARK_2);
18269 dprintf(3,("Marking handle table"));
18270 CNameSpace::GcScanHandles(GCHeap::Promote,
18271 condemned_gen_number, max_generation,
18273 fire_mark_event (heap_number, ETW_TYPE_GC_MARK_3);
18277 size_t promoted_before_cards = promoted_bytes (heap_number);
18280 dprintf (3, ("before cards: %Id", promoted_before_cards));
18284 #ifdef MULTIPLE_HEAPS
18285 if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
18287 #endif //MULTIPLE_HEAPS
18289 update_card_table_bundle ();
18291 #ifdef MULTIPLE_HEAPS
18292 gc_t_join.r_restart();
18294 #endif //MULTIPLE_HEAPS
18295 #endif //CARD_BUNDLE
18297 card_fn mark_object_fn = &gc_heap::mark_object_simple;
18298 #ifdef HEAP_ANALYZE
18299 heap_analyze_success = TRUE;
18300 if (heap_analyze_enabled)
18302 internal_root_array_index = 0;
18304 current_obj_size = 0;
18305 mark_object_fn = &gc_heap::ha_mark_object_simple;
18307 #endif //HEAP_ANALYZE
18309 dprintf(3,("Marking cross generation pointers"));
18310 mark_through_cards_for_segments (mark_object_fn, FALSE);
18312 dprintf(3,("Marking cross generation pointers for large objects"));
18313 mark_through_cards_for_large_objects (mark_object_fn, FALSE);
18315 dprintf (3, ("marked by cards: %Id",
18316 (promoted_bytes (heap_number) - promoted_before_cards)));
18317 fire_mark_event (heap_number, ETW_TYPE_GC_MARK_4);
18322 if (do_mark_steal_p)
18326 #endif //MH_SC_MARK
18328 // Dependent handles need to be scanned with a special algorithm (see the header comment on
18329 // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
18330 // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
18331 // but in a common case (where there are no dependent handles that are due to be collected) it allows us
18332 // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
18333 // iterations if required and will also perform processing of any mark stack overflow once the dependent
18334 // handle table has been fully promoted.
18335 CNameSpace::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
18336 scan_dependent_handles(condemned_gen_number, &sc, true);
18338 #ifdef MULTIPLE_HEAPS
18339 dprintf(3, ("Joining for short weak handle scan"));
18340 gc_t_join.join(this, gc_join_null_dead_short_weak);
18341 if (gc_t_join.joined())
18342 #endif //MULTIPLE_HEAPS
18344 #ifdef HEAP_ANALYZE
18345 heap_analyze_enabled = FALSE;
18346 DACNotifyGcMarkEnd(condemned_gen_number);
18347 #endif // HEAP_ANALYZE
18348 GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
18350 #ifdef MULTIPLE_HEAPS
18353 // we used r_join and need to reinitialize states for it here.
18354 gc_t_join.r_init();
18357 //start all threads on the roots.
18358 dprintf(3, ("Starting all gc thread for short weak handle scan"));
18359 gc_t_join.restart();
18360 #endif //MULTIPLE_HEAPS
18364 // null out the target of short weakref that were not promoted.
18365 CNameSpace::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
18367 // MTHTS: keep by single thread
18368 #ifdef MULTIPLE_HEAPS
18369 dprintf(3, ("Joining for finalization"));
18370 gc_t_join.join(this, gc_join_scan_finalization);
18371 if (gc_t_join.joined())
18372 #endif //MULTIPLE_HEAPS
18375 #ifdef MULTIPLE_HEAPS
18376 //start all threads on the roots.
18377 dprintf(3, ("Starting all gc thread for Finalization"));
18378 gc_t_join.restart();
18379 #endif //MULTIPLE_HEAPS
18382 //Handle finalization.
18383 size_t promoted_bytes_live = promoted_bytes (heap_number);
18385 #ifdef FEATURE_PREMORTEM_FINALIZATION
18386 dprintf (3, ("Finalize marking"));
18387 finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
18389 #ifdef GC_PROFILING
18390 if (CORProfilerTrackGC())
18392 finalize_queue->WalkFReachableObjects (__this);
18394 #endif //GC_PROFILING
18395 #endif // FEATURE_PREMORTEM_FINALIZATION
18397 // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
18398 // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
18399 scan_dependent_handles(condemned_gen_number, &sc, false);
18401 #ifdef MULTIPLE_HEAPS
18402 dprintf(3, ("Joining for weak pointer deletion"));
18403 gc_t_join.join(this, gc_join_null_dead_long_weak);
18404 if (gc_t_join.joined())
18406 //start all threads on the roots.
18407 dprintf(3, ("Starting all gc thread for weak pointer deletion"));
18408 gc_t_join.restart();
18410 #endif //MULTIPLE_HEAPS
18412 // null out the target of long weakref that were not promoted.
18413 CNameSpace::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
18415 // MTHTS: keep by single thread
18416 #ifdef MULTIPLE_HEAPS
18418 #ifdef PARALLEL_MARK_LIST_SORT
18419 // unsigned long start = GetCycleCount32();
18421 // printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
18422 #endif //PARALLEL_MARK_LIST_SORT
18425 dprintf (3, ("Joining for sync block cache entry scanning"));
18426 gc_t_join.join(this, gc_join_null_dead_syncblk);
18427 if (gc_t_join.joined())
18428 #endif //MULTIPLE_HEAPS
18430 // scan for deleted entries in the syncblk cache
18431 CNameSpace::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
18433 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
18436 size_t promoted_all_heaps = 0;
18437 #ifdef MULTIPLE_HEAPS
18438 for (int i = 0; i < n_heaps; i++)
18440 promoted_all_heaps += promoted_bytes (i);
18443 promoted_all_heaps = promoted_bytes (heap_number);
18444 #endif //MULTIPLE_HEAPS
18445 SystemDomain::RecordTotalSurvivedBytes (promoted_all_heaps);
18447 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
18449 #ifdef MULTIPLE_HEAPS
18452 #ifndef PARALLEL_MARK_LIST_SORT
18453 //compact g_mark_list and sort it.
18454 combine_mark_lists();
18455 #endif //PARALLEL_MARK_LIST_SORT
18458 //decide on promotion
18459 if (settings.promotion != TRUE)
18462 for (int n = 0; n <= condemned_gen_number;n++)
18464 m += (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.1);
18467 for (int i = 0; i < n_heaps; i++)
18469 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
18471 size_t older_gen_size = (dd_current_size (dd) +
18472 (dd_desired_allocation (dd) -
18473 dd_new_allocation (dd)));
18475 if ((m > (older_gen_size)) ||
18476 (promoted_bytes (i) > m))
18478 settings.promotion = TRUE;
18484 if (do_mark_steal_p)
18486 size_t objects_checked_count = 0;
18487 size_t zero_ref_count = 0;
18488 size_t objects_marked_count = 0;
18489 size_t check_level_count = 0;
18490 size_t busy_count = 0;
18491 size_t interlocked_count = 0;
18492 size_t partial_mark_parent_count = 0;
18493 size_t stolen_or_pm_count = 0;
18494 size_t stolen_entry_count = 0;
18495 size_t pm_not_ready_count = 0;
18496 size_t normal_count = 0;
18497 size_t stack_bottom_clear_count = 0;
18499 for (int i = 0; i < n_heaps; i++)
18501 gc_heap* hp = g_heaps[i];
18502 hp->print_snoop_stat();
18503 objects_checked_count += hp->snoop_stat.objects_checked_count;
18504 zero_ref_count += hp->snoop_stat.zero_ref_count;
18505 objects_marked_count += hp->snoop_stat.objects_marked_count;
18506 check_level_count += hp->snoop_stat.check_level_count;
18507 busy_count += hp->snoop_stat.busy_count;
18508 interlocked_count += hp->snoop_stat.interlocked_count;
18509 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
18510 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
18511 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
18512 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
18513 normal_count += hp->snoop_stat.normal_count;
18514 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
18519 printf ("-------total stats-------\n");
18520 printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n",
18521 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18522 printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18523 objects_checked_count,
18525 objects_marked_count,
18529 partial_mark_parent_count,
18530 stolen_or_pm_count,
18531 stolen_entry_count,
18532 pm_not_ready_count,
18534 stack_bottom_clear_count);
18536 #endif //SNOOP_STATS
18538 //start all threads.
18539 dprintf(3, ("Starting all threads for end of mark phase"));
18540 gc_t_join.restart();
18541 #else //MULTIPLE_HEAPS
18543 //decide on promotion
18544 if (!settings.promotion)
18547 for (int n = 0; n <= condemned_gen_number;n++)
18549 m += (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.06);
18551 dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
18553 size_t older_gen_size = (dd_current_size (dd) +
18554 (dd_desired_allocation (dd) -
18555 dd_new_allocation (dd)));
18557 dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
18558 m, promoted_bytes (heap_number), older_gen_size));
18560 if ((m > older_gen_size) ||
18561 (promoted_bytes (heap_number) > m))
18563 settings.promotion = TRUE;
18567 #endif //MULTIPLE_HEAPS
18570 #ifdef MULTIPLE_HEAPS
18572 #ifdef PARALLEL_MARK_LIST_SORT
18573 // start = GetCycleCount32();
18574 merge_mark_lists();
18575 // printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
18576 #endif //PARALLEL_MARK_LIST_SORT
18578 #endif //MULTIPLE_HEAPS
18580 #ifdef BACKGROUND_GC
18581 total_promoted_bytes = promoted_bytes (heap_number);
18582 #endif //BACKGROUND_GC
18584 promoted_bytes (heap_number) -= promoted_bytes_live;
18587 finish = GetCycleCount32();
18588 mark_time = finish - start;
18591 dprintf(2,("---- End of mark phase ----"));
18595 void gc_heap::pin_object (BYTE* o, BYTE** ppObject, BYTE* low, BYTE* high)
18597 dprintf (3, ("Pinning %Ix", (size_t)o));
18598 if ((o >= low) && (o < high))
18600 dprintf(3,("^%Ix^", (size_t)o));
18603 #ifdef FEATURE_EVENT_TRACE
18604 if(EventEnabledPinObjectAtGCTime())
18606 fire_etw_pin_object_event(o, ppObject);
18608 #endif // FEATURE_EVENT_TRACE
18609 COUNTER_ONLY(GetPerfCounters().m_GC.cPinnedObj ++);
18613 void gc_heap::reset_mark_stack ()
18615 reset_pinned_queue();
18616 max_overflow_address = 0;
18617 min_overflow_address = MAX_PTR;
18620 #ifdef FEATURE_STRUCTALIGN
18622 // The word with left child, right child, and align info is laid out as follows:
18624 // | upper short word | lower short word |
18625 // |<------------> <----->|<------------> <----->|
18626 // | left child info hi| right child info lo|
18627 // x86: | 10 bits 6 bits| 10 bits 6 bits|
18629 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
18631 // The "align info" encodes two numbers: the required alignment (a power of two)
18632 // and the misalignment (the number of machine words the destination address needs
18633 // to be adjusted by to provide alignment - so this number is always smaller than
18634 // the required alignment). Thus, the two can be represented as the "logical or"
18635 // of the two numbers. Note that the actual pad is computed from the misalignment
18636 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
18639 // The number of bits in a brick.
18640 #if defined (_TARGET_AMD64_)
18641 #define brick_bits (12)
18643 #define brick_bits (11)
18644 #endif //_TARGET_AMD64_
18645 C_ASSERT(brick_size == (1 << brick_bits));
18647 // The number of bits needed to represent the offset to a child node.
18648 // "brick_bits + 1" allows us to represent a signed offset within a brick.
18649 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
18651 // The number of bits in each of the pad hi, pad lo fields.
18652 #define pad_bits (sizeof(short) * 8 - child_bits)
18654 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
18655 #define pad_mask ((1 << pad_bits) - 1)
18656 #define pad_from_short(w) ((size_t)(w) & pad_mask)
18657 #else // FEATURE_STRUCTALIGN
18658 #define child_from_short(w) (w)
18659 #endif // FEATURE_STRUCTALIGN
18662 short node_left_child(BYTE* node)
18664 return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
18668 void set_node_left_child(BYTE* node, ptrdiff_t val)
18670 assert (val > -(ptrdiff_t)brick_size);
18671 assert (val < (ptrdiff_t)brick_size);
18672 assert (Aligned (val));
18673 #ifdef FEATURE_STRUCTALIGN
18674 size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
18675 ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
18676 #else // FEATURE_STRUCTALIGN
18677 ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
18678 #endif // FEATURE_STRUCTALIGN
18679 assert (node_left_child (node) == val);
18683 short node_right_child(BYTE* node)
18685 return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
18689 void set_node_right_child(BYTE* node, ptrdiff_t val)
18691 assert (val > -(ptrdiff_t)brick_size);
18692 assert (val < (ptrdiff_t)brick_size);
18693 assert (Aligned (val));
18694 #ifdef FEATURE_STRUCTALIGN
18695 size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
18696 ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
18697 #else // FEATURE_STRUCTALIGN
18698 ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
18699 #endif // FEATURE_STRUCTALIGN
18700 assert (node_right_child (node) == val);
18703 #ifdef FEATURE_STRUCTALIGN
18704 void node_aligninfo (BYTE* node, int& requiredAlignment, ptrdiff_t& pad)
18706 // Extract the single-number aligninfo from the fields.
18707 short left = ((plug_and_pair*)node)[-1].m_pair.left;
18708 short right = ((plug_and_pair*)node)[-1].m_pair.right;
18709 ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
18710 ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
18712 // Replicate the topmost bit into all lower bits.
18713 ptrdiff_t x = aligninfo;
18719 // Clear all bits but the highest.
18720 requiredAlignment = (int)(x ^ (x >> 1));
18721 pad = aligninfo - requiredAlignment;
18722 pad += AdjustmentForMinPadSize(pad, requiredAlignment);
18726 ptrdiff_t node_alignpad (BYTE* node)
18728 int requiredAlignment;
18729 ptrdiff_t alignpad;
18730 node_aligninfo (node, requiredAlignment, alignpad);
18734 void clear_node_aligninfo (BYTE* node)
18736 ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
18737 ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
18740 void set_node_aligninfo (BYTE* node, int requiredAlignment, ptrdiff_t pad)
18742 // Encode the alignment requirement and alignment offset as a single number
18743 // as described above.
18744 ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
18745 assert (Aligned (aligninfo));
18746 ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
18747 assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
18749 ptrdiff_t hi = aligninfo_shifted >> pad_bits;
18750 assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
18751 ((plug_and_pair*)node)[-1].m_pair.left |= hi;
18753 ptrdiff_t lo = aligninfo_shifted & pad_mask;
18754 assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
18755 ((plug_and_pair*)node)[-1].m_pair.right |= lo;
18758 int requiredAlignment2;
18760 node_aligninfo (node, requiredAlignment2, pad2);
18761 assert (requiredAlignment == requiredAlignment2);
18762 assert (pad == pad2);
18765 #endif // FEATURE_STRUCTALIGN
18768 void loh_set_node_relocation_distance(BYTE* node, ptrdiff_t val)
18770 ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
18775 ptrdiff_t loh_node_relocation_distance(BYTE* node)
18777 return (((loh_obj_and_pad*)node)[-1].reloc);
18781 ptrdiff_t node_relocation_distance (BYTE* node)
18783 return (((plug_and_reloc*)(node))[-1].reloc & ~3);
18787 void set_node_relocation_distance(BYTE* node, ptrdiff_t val)
18789 assert (val == (val & ~3));
18790 ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
18791 //clear the left bit and the relocation field
18797 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
18799 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
18801 #ifndef FEATURE_STRUCTALIGN
18802 #define node_realigned(node) (((plug_and_reloc*)(node))[-1].reloc & 1)
18804 void set_node_realigned(BYTE* node)
18806 ((plug_and_reloc*)(node))[-1].reloc |= 1;
18809 void clear_node_realigned(BYTE* node)
18811 #ifdef RESPECT_LARGE_ALIGNMENT
18812 ((plug_and_reloc*)(node))[-1].reloc &= ~1;
18813 #endif //RESPECT_LARGE_ALIGNMENT
18815 #endif // FEATURE_STRUCTALIGN
18818 size_t node_gap_size (BYTE* node)
18820 return ((plug_and_gap *)node)[-1].gap;
18823 void set_gap_size (BYTE* node, size_t size)
18825 assert (Aligned (size));
18827 // clear the 2 DWORD used by the node.
18828 ((plug_and_gap *)node)[-1].reloc = 0;
18829 ((plug_and_gap *)node)[-1].lr =0;
18830 ((plug_and_gap *)node)[-1].gap = size;
18832 assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
18836 BYTE* gc_heap::insert_node (BYTE* new_node, size_t sequence_number,
18837 BYTE* tree, BYTE* last_node)
18839 dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
18840 (size_t)new_node, brick_of(new_node),
18841 (size_t)tree, brick_of(tree),
18842 (size_t)last_node, brick_of(last_node),
18844 if (power_of_two_p (sequence_number))
18846 set_node_left_child (new_node, (tree - new_node));
18847 dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
18852 if (oddp (sequence_number))
18854 set_node_right_child (last_node, (new_node - last_node));
18855 dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
18859 BYTE* earlier_node = tree;
18860 size_t imax = logcount(sequence_number) - 2;
18861 for (size_t i = 0; i != imax; i++)
18863 earlier_node = earlier_node + node_right_child (earlier_node);
18865 int tmp_offset = node_right_child (earlier_node);
18866 assert (tmp_offset); // should never be empty
18867 set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
18868 set_node_right_child (earlier_node, (new_node - earlier_node));
18870 dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix",
18871 new_node, ((earlier_node + tmp_offset ) - new_node),
18872 earlier_node, (new_node - earlier_node)));
18878 size_t gc_heap::update_brick_table (BYTE* tree, size_t current_brick,
18879 BYTE* x, BYTE* plug_end)
18881 dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
18882 tree, current_brick, x, plug_end));
18886 dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix",
18887 current_brick, (size_t)(tree - brick_address (current_brick)), tree));
18888 set_brick (current_brick, (tree - brick_address (current_brick)));
18892 dprintf (3, ("b- %Ix->-1", current_brick));
18893 set_brick (current_brick, -1);
18895 size_t b = 1 + current_brick;
18896 ptrdiff_t offset = 0;
18897 size_t last_br = brick_of (plug_end-1);
18898 current_brick = brick_of (x-1);
18899 dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
18900 while (b <= current_brick)
18904 set_brick (b, --offset);
18912 return brick_of (x);
18915 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, BYTE* next_plug_to_allocate)
18918 // We should never demote big plugs to ephemeral generations.
18919 if (gen == youngest_generation)
18921 heap_segment* seg = ephemeral_heap_segment;
18922 size_t mark_stack_large_bos = mark_stack_bos;
18923 size_t large_plug_pos = 0;
18924 while (mark_stack_large_bos < mark_stack_tos)
18926 if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
18928 while (mark_stack_bos <= mark_stack_large_bos)
18930 size_t entry = deque_pinned_plug();
18931 size_t len = pinned_len (pinned_plug_of (entry));
18932 BYTE* plug = pinned_plug (pinned_plug_of(entry));
18933 if (len > demotion_plug_len_th)
18935 dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
18937 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
18938 assert(mark_stack_array[entry].len == 0 ||
18939 mark_stack_array[entry].len >= Align(min_obj_size));
18940 generation_allocation_pointer (consing_gen) = plug + len;
18941 generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
18942 set_allocator_next_pin (consing_gen);
18946 mark_stack_large_bos++;
18951 generation_plan_allocation_start (gen) =
18952 allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
18953 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
18954 size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
18955 #ifdef RESPECT_LARGE_ALIGNMENT
18956 if (next_plug_to_allocate)
18958 size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
18959 if (allocation_left > dist_to_next_plug)
18961 allocation_left = dist_to_next_plug;
18964 #endif //RESPECT_LARGE_ALIGNMENT
18965 if (allocation_left < Align (min_obj_size))
18967 generation_plan_allocation_start_size (gen) += allocation_left;
18968 generation_allocation_pointer (consing_gen) += allocation_left;
18971 dprintf (1, ("plan alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num,
18972 generation_plan_allocation_start (gen),
18973 generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen)));
18976 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
18978 BOOL adjacentp = FALSE;
18980 generation_plan_allocation_start (gen) =
18981 allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0,
18984 #endif //SHORT_PLUGS
18985 FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
18987 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
18988 size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
18989 if ((allocation_left < Align (min_obj_size)) &&
18990 (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
18992 generation_plan_allocation_start_size (gen) += allocation_left;
18993 generation_allocation_pointer (consing_gen) += allocation_left;
18996 dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num,
18997 generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen)));
19000 void gc_heap::plan_generation_starts (generation*& consing_gen)
19002 //make sure that every generation has a planned allocation start
19003 int gen_number = settings.condemned_generation;
19004 while (gen_number >= 0)
19006 if (gen_number < max_generation)
19008 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
19010 generation* gen = generation_of (gen_number);
19011 if (0 == generation_plan_allocation_start (gen))
19013 plan_generation_start (gen, consing_gen, 0);
19014 assert (generation_plan_allocation_start (gen));
19018 // now we know the planned allocation size
19019 heap_segment_plan_allocated (ephemeral_heap_segment) =
19020 generation_allocation_pointer (consing_gen);
19023 void gc_heap::advance_pins_for_demotion (generation* gen)
19025 BYTE* original_youngest_start = generation_allocation_start (youngest_generation);
19026 heap_segment* seg = ephemeral_heap_segment;
19028 if ((!(pinned_plug_que_empty_p())))
19030 size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
19031 size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
19032 size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
19033 float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
19034 float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
19035 if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
19037 while (!pinned_plug_que_empty_p() &&
19038 (pinned_plug (oldest_pin()) < original_youngest_start))
19040 size_t entry = deque_pinned_plug();
19041 size_t len = pinned_len (pinned_plug_of (entry));
19042 BYTE* plug = pinned_plug (pinned_plug_of(entry));
19043 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
19044 assert(mark_stack_array[entry].len == 0 ||
19045 mark_stack_array[entry].len >= Align(min_obj_size));
19046 generation_allocation_pointer (gen) = plug + len;
19047 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19048 set_allocator_next_pin (gen);
19050 //Add the size of the pinned plug to the right pinned allocations
19051 //find out which gen this pinned plug came from
19052 int frgn = object_gennum (plug);
19053 if ((frgn != (int)max_generation) && settings.promotion)
19055 int togn = object_gennum_plan (plug);
19056 generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
19059 generation_pinned_allocation_compact_size (generation_of (togn)) += len;
19063 dprintf (2, ("skipping gap %d, pin %Ix (%Id)",
19064 pinned_len (pinned_plug_of (entry)), plug, len));
19067 dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d",
19068 gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
19072 void gc_heap::process_ephemeral_boundaries (BYTE* x,
19073 int& active_new_gen_number,
19074 int& active_old_gen_number,
19075 generation*& consing_gen,
19076 BOOL& allocate_in_condemned)
19079 if ((active_old_gen_number > 0) &&
19080 (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
19082 dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
19084 if (!pinned_plug_que_empty_p())
19086 dprintf (1, ("oldest pin: %Ix(%Id)",
19087 pinned_plug (oldest_pin()),
19088 (x - pinned_plug (oldest_pin()))));
19091 if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
19093 active_new_gen_number--;
19096 active_old_gen_number--;
19097 assert ((!settings.promotion) || (active_new_gen_number>0));
19099 if (active_new_gen_number == (max_generation - 1))
19101 #ifdef FREE_USAGE_STATS
19102 if (settings.condemned_generation == max_generation)
19104 // We need to do this before we skip the rest of the pinned plugs.
19105 generation* gen_2 = generation_of (max_generation);
19106 generation* gen_1 = generation_of (max_generation - 1);
19108 size_t total_num_pinned_free_spaces_left = 0;
19110 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
19111 for (int j = 0; j < NUM_GEN_POWER2; j++)
19113 dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)",
19117 gen_2->gen_current_pinned_free_spaces[j],
19118 gen_2->gen_plugs[j], gen_1->gen_plugs[j],
19119 (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
19121 total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
19124 float pinned_free_list_efficiency = 0;
19125 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
19126 if (total_pinned_free_space != 0)
19128 pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
19131 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
19133 generation_allocated_in_pinned_free (gen_2),
19134 total_pinned_free_space,
19135 (int)(pinned_free_list_efficiency * 100),
19136 generation_pinned_free_obj_space (gen_2),
19137 total_num_pinned_free_spaces_left));
19139 #endif //FREE_USAGE_STATS
19141 //Go past all of the pinned plugs for this generation.
19142 while (!pinned_plug_que_empty_p() &&
19143 (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
19145 size_t entry = deque_pinned_plug();
19146 mark* m = pinned_plug_of (entry);
19147 BYTE* plug = pinned_plug (m);
19148 size_t len = pinned_len (m);
19149 // detect pinned block in different segment (later) than
19150 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
19151 // adjust the allocation segment along the way (at the end it will
19152 // be the ephemeral segment.
19153 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
19155 PREFIX_ASSUME(nseg != NULL);
19157 while (!((plug >= generation_allocation_pointer (consing_gen))&&
19158 (plug < heap_segment_allocated (nseg))))
19160 //adjust the end of the segment to be the end of the plug
19161 assert (generation_allocation_pointer (consing_gen)>=
19162 heap_segment_mem (nseg));
19163 assert (generation_allocation_pointer (consing_gen)<=
19164 heap_segment_committed (nseg));
19166 heap_segment_plan_allocated (nseg) =
19167 generation_allocation_pointer (consing_gen);
19168 //switch allocation segment
19169 nseg = heap_segment_next_rw (nseg);
19170 generation_allocation_segment (consing_gen) = nseg;
19171 //reset the allocation pointer and limits
19172 generation_allocation_pointer (consing_gen) =
19173 heap_segment_mem (nseg);
19175 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
19176 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
19177 generation_allocation_pointer (consing_gen) = plug + len;
19178 generation_allocation_limit (consing_gen) =
19179 generation_allocation_pointer (consing_gen);
19181 allocate_in_condemned = TRUE;
19182 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
19185 if (active_new_gen_number != max_generation)
19187 if ((active_new_gen_number == (max_generation - 1)) && !demote_gen1_p)
19189 advance_pins_for_demotion (consing_gen);
19192 plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
19194 dprintf (1, ("process eph: allocated gen%d start at %Ix",
19195 active_new_gen_number,
19196 generation_plan_allocation_start (generation_of (active_new_gen_number))));
19198 if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
19200 BYTE* pplug = pinned_plug (oldest_pin());
19201 if (object_gennum (pplug) > 0)
19203 demotion_low = pplug;
19204 dprintf (3, ("process eph: dlow->%Ix", demotion_low));
19208 assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
19216 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
19218 BYTE* o = heap_segment_mem (seg);
19219 while (o < heap_segment_allocated (seg))
19225 o = o + Align (size (o));
19229 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
19233 //go through all of the segment in range and reset the mark bit
19234 //TODO works only on small object segments
19236 heap_segment* seg = start_seg;
19240 if (heap_segment_read_only_p (seg) &&
19241 heap_segment_in_range_p (seg))
19243 #ifdef BACKGROUND_GC
19244 if (settings.concurrent)
19246 seg_clear_mark_array_bits_soh (seg);
19250 seg_clear_mark_bits (seg);
19252 #else //BACKGROUND_GC
19255 if(gc_can_use_concurrent)
19257 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
19258 min (heap_segment_allocated (seg), highest_address),
19259 false); // read_only segments need the mark clear
19262 seg_clear_mark_bits (seg);
19263 #endif //MARK_ARRAY
19265 #endif //BACKGROUND_GC
19267 seg = heap_segment_next (seg);
19272 #ifdef FEATURE_LOH_COMPACTION
19274 BOOL gc_heap::loh_pinned_plug_que_empty_p()
19276 return (loh_pinned_queue_bos == loh_pinned_queue_tos);
19279 void gc_heap::loh_set_allocator_next_pin()
19281 if (!(loh_pinned_plug_que_empty_p()))
19283 mark* oldest_entry = loh_oldest_pin();
19284 BYTE* plug = pinned_plug (oldest_entry);
19285 generation* gen = large_object_generation;
19286 if ((plug >= generation_allocation_pointer (gen)) &&
19287 (plug < generation_allocation_limit (gen)))
19289 generation_allocation_limit (gen) = pinned_plug (oldest_entry);
19292 assert (!((plug < generation_allocation_pointer (gen)) &&
19293 (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
19297 size_t gc_heap::loh_deque_pinned_plug ()
19299 size_t m = loh_pinned_queue_bos;
19300 loh_pinned_queue_bos++;
19305 mark* gc_heap::loh_pinned_plug_of (size_t bos)
19307 return &loh_pinned_queue[bos];
19311 mark* gc_heap::loh_oldest_pin()
19313 return loh_pinned_plug_of (loh_pinned_queue_bos);
19316 // If we can't grow the queue, then don't compact.
19317 BOOL gc_heap::loh_enque_pinned_plug (BYTE* plug, size_t len)
19319 assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
19321 if (loh_pinned_queue_length <= loh_pinned_queue_tos)
19323 if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
19328 dprintf (3, (" P: %Ix(%Id)", plug, len));
19329 mark& m = loh_pinned_queue[loh_pinned_queue_tos];
19332 loh_pinned_queue_tos++;
19333 loh_set_allocator_next_pin();
19338 BOOL gc_heap::loh_size_fit_p (size_t size, BYTE* alloc_pointer, BYTE* alloc_limit)
19340 dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)",
19342 (2* AlignQword (loh_padding_obj_size) + size),
19345 (alloc_limit - alloc_pointer)));
19347 return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) + size) <= alloc_limit);
19350 BYTE* gc_heap::loh_allocate_in_condemned (BYTE* old_loc, size_t size)
19352 generation* gen = large_object_generation;
19353 dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id",
19354 generation_allocation_pointer (gen),
19355 generation_allocation_limit (gen),
19360 heap_segment* seg = generation_allocation_segment (gen);
19361 if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
19363 if ((!(loh_pinned_plug_que_empty_p()) &&
19364 (generation_allocation_limit (gen) ==
19365 pinned_plug (loh_oldest_pin()))))
19367 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
19368 size_t len = pinned_len (m);
19369 BYTE* plug = pinned_plug (m);
19370 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
19371 pinned_len (m) = plug - generation_allocation_pointer (gen);
19372 generation_allocation_pointer (gen) = plug + len;
19374 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19375 loh_set_allocator_next_pin();
19376 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)",
19377 generation_allocation_pointer (gen),
19378 generation_allocation_limit (gen),
19379 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19384 if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
19386 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19387 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
19391 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
19393 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
19394 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19395 dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
19399 if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
19400 (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
19402 dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
19403 (generation_allocation_pointer (gen) + size)));
19405 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
19406 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19408 dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)",
19409 generation_allocation_pointer (gen),
19410 generation_allocation_limit (gen),
19411 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19415 heap_segment* next_seg = heap_segment_next (seg);
19416 assert (generation_allocation_pointer (gen)>=
19417 heap_segment_mem (seg));
19418 // Verify that all pinned plugs for this segment are consumed
19419 if (!loh_pinned_plug_que_empty_p() &&
19420 ((pinned_plug (loh_oldest_pin()) <
19421 heap_segment_allocated (seg)) &&
19422 (pinned_plug (loh_oldest_pin()) >=
19423 generation_allocation_pointer (gen))))
19425 LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
19426 pinned_plug (loh_oldest_pin())));
19427 dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
19430 assert (generation_allocation_pointer (gen)>=
19431 heap_segment_mem (seg));
19432 assert (generation_allocation_pointer (gen)<=
19433 heap_segment_committed (seg));
19434 heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
19438 // for LOH do we want to try starting from the first LOH every time though?
19439 generation_allocation_segment (gen) = next_seg;
19440 generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
19441 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
19443 dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)",
19444 generation_allocation_pointer (gen),
19445 generation_allocation_limit (gen),
19446 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19450 dprintf (1, ("We ran out of space compacting, shouldn't happen"));
19456 loh_set_allocator_next_pin();
19458 dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)",
19459 generation_allocation_pointer (gen),
19460 generation_allocation_limit (gen),
19461 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19468 assert (generation_allocation_pointer (gen)>=
19469 heap_segment_mem (generation_allocation_segment (gen)));
19470 BYTE* result = generation_allocation_pointer (gen);
19471 size_t loh_pad = AlignQword (loh_padding_obj_size);
19473 generation_allocation_pointer (gen) += size + loh_pad;
19474 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
19476 dprintf (1235, ("p: %Ix, l: %Ix (%Id)",
19477 generation_allocation_pointer (gen),
19478 generation_allocation_limit (gen),
19479 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19481 assert (result + loh_pad);
19482 return result + loh_pad;
19486 BOOL gc_heap::should_compact_loh()
19488 return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
19492 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
19494 if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
19496 if (all_heaps_compacted_p)
19498 // If the compaction mode says to compact once and we are going to compact LOH,
19499 // we need to revert it back to no compaction.
19500 loh_compaction_mode = loh_compaction_default;
19505 BOOL gc_heap::plan_loh()
19507 if (!loh_pinned_queue)
19509 loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
19510 if (!loh_pinned_queue)
19512 dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction",
19513 LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
19517 loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
19520 if (heap_number == 0)
19521 loh_pinned_queue_decay = LOH_PIN_DECAY;
19523 loh_pinned_queue_tos = 0;
19524 loh_pinned_queue_bos = 0;
19526 generation* gen = large_object_generation;
19527 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
19528 PREFIX_ASSUME(start_seg != NULL);
19529 heap_segment* seg = start_seg;
19530 BYTE* o = generation_allocation_start (gen);
19532 dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n",
19533 generation_size (max_generation + 1),
19534 generation_free_list_space (gen),
19535 generation_free_obj_space (gen)));
19539 heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
19540 seg = heap_segment_next (seg);
19545 //Skip the generation gap object
19546 o = o + AlignQword (size (o));
19547 // We don't need to ever realloc gen3 start so don't touch it.
19548 heap_segment_plan_allocated (seg) = o;
19549 generation_allocation_pointer (gen) = o;
19550 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
19551 generation_allocation_segment (gen) = start_seg;
19553 BYTE* free_space_start = o;
19554 BYTE* free_space_end = o;
19555 BYTE* new_address = 0;
19559 if (o >= heap_segment_allocated (seg))
19561 seg = heap_segment_next (seg);
19567 o = heap_segment_mem (seg);
19572 free_space_end = o;
19573 size_t size = AlignQword (size (o));
19574 dprintf (1235, ("%Ix(%Id) M", o, size));
19578 // We don't clear the pinned bit yet so we can check in
19579 // compact phase how big a free object we should allocate
19580 // in front of the pinned object. We use the reloc address
19581 // field to store this.
19582 if (!loh_enque_pinned_plug (o, size))
19590 new_address = loh_allocate_in_condemned (o, size);
19593 loh_set_node_relocation_distance (o, (new_address - o));
19594 dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
19597 free_space_start = o;
19598 if (o < heap_segment_allocated (seg))
19600 assert (!marked (o));
19605 while (o < heap_segment_allocated (seg) && !marked (o))
19607 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_pFreeObjectMethodTable) ? 1 : 0)));
19608 o = o + AlignQword (size (o));
19613 while (!loh_pinned_plug_que_empty_p())
19615 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
19616 size_t len = pinned_len (m);
19617 BYTE* plug = pinned_plug (m);
19619 // detect pinned block in different segment (later) than
19620 // allocation segment
19621 heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
19623 while ((plug < generation_allocation_pointer (gen)) ||
19624 (plug >= heap_segment_allocated (nseg)))
19626 assert ((plug < heap_segment_mem (nseg)) ||
19627 (plug > heap_segment_reserved (nseg)));
19628 //adjust the end of the segment to be the end of the plug
19629 assert (generation_allocation_pointer (gen)>=
19630 heap_segment_mem (nseg));
19631 assert (generation_allocation_pointer (gen)<=
19632 heap_segment_committed (nseg));
19634 heap_segment_plan_allocated (nseg) =
19635 generation_allocation_pointer (gen);
19636 //switch allocation segment
19637 nseg = heap_segment_next_rw (nseg);
19638 generation_allocation_segment (gen) = nseg;
19639 //reset the allocation pointer and limits
19640 generation_allocation_pointer (gen) =
19641 heap_segment_mem (nseg);
19644 dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
19645 pinned_len (m) = plug - generation_allocation_pointer (gen);
19646 generation_allocation_pointer (gen) = plug + len;
19649 heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
19650 generation_allocation_pointer (gen) = 0;
19651 generation_allocation_limit (gen) = 0;
19656 void gc_heap::compact_loh()
19658 assert (should_compact_loh());
19660 generation* gen = large_object_generation;
19661 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
19662 PREFIX_ASSUME(start_seg != NULL);
19663 heap_segment* seg = start_seg;
19664 heap_segment* prev_seg = 0;
19665 BYTE* o = generation_allocation_start (gen);
19667 //Skip the generation gap object
19668 o = o + AlignQword (size (o));
19669 // We don't need to ever realloc gen3 start so don't touch it.
19670 BYTE* free_space_start = o;
19671 BYTE* free_space_end = o;
19672 generation_allocator (gen)->clear();
19673 generation_free_list_space (gen) = 0;
19674 generation_free_obj_space (gen) = 0;
19676 loh_pinned_queue_bos = 0;
19680 if (o >= heap_segment_allocated (seg))
19682 heap_segment* next_seg = heap_segment_next (seg);
19684 if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
19685 (seg != start_seg) && !heap_segment_read_only_p (seg))
19687 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
19689 heap_segment_next (prev_seg) = next_seg;
19690 heap_segment_next (seg) = freeable_large_heap_segment;
19691 freeable_large_heap_segment = seg;
19695 if (!heap_segment_read_only_p (seg))
19697 // We grew the segment to accommondate allocations.
19698 if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
19700 if ((heap_segment_plan_allocated (seg) - plug_skew) > heap_segment_used (seg))
19702 heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
19706 heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
19707 dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
19708 decommit_heap_segment_pages (seg, 0);
19709 dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
19711 heap_segment_allocated (seg),
19712 heap_segment_used (seg),
19713 heap_segment_committed (seg)));
19714 //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
19715 dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
19725 o = heap_segment_mem (seg);
19731 free_space_end = o;
19732 size_t size = AlignQword (size (o));
19740 // We are relying on the fact the pinned objects are always looked at in the same order
19741 // in plan phase and in compact phase.
19742 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
19743 BYTE* plug = pinned_plug (m);
19744 assert (plug == o);
19746 loh_pad = pinned_len (m);
19751 loh_pad = AlignQword (loh_padding_obj_size);
19753 reloc += loh_node_relocation_distance (o);
19754 gcmemcopy (reloc, o, size, TRUE);
19757 thread_gap ((reloc - loh_pad), loh_pad, gen);
19760 free_space_start = o;
19761 if (o < heap_segment_allocated (seg))
19763 assert (!marked (o));
19768 while (o < heap_segment_allocated (seg) && !marked (o))
19770 o = o + AlignQword (size (o));
19775 assert (loh_pinned_plug_que_empty_p());
19777 dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n",
19778 generation_size (max_generation + 1),
19779 generation_free_list_space (gen),
19780 generation_free_obj_space (gen)));
19783 void gc_heap::relocate_in_loh_compact()
19785 generation* gen = large_object_generation;
19786 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19787 BYTE* o = generation_allocation_start (gen);
19789 //Skip the generation gap object
19790 o = o + AlignQword (size (o));
19792 relocate_args args;
19794 args.high = gc_high;
19795 args.last_plug = 0;
19799 if (o >= heap_segment_allocated (seg))
19801 seg = heap_segment_next (seg);
19807 o = heap_segment_mem (seg);
19812 size_t size = AlignQword (size (o));
19814 check_class_object_demotion (o);
19815 if (contain_pointers (o))
19817 go_through_object_nostart (method_table (o), o, size(o), pval,
19819 reloc_survivor_helper (pval);
19824 if (o < heap_segment_allocated (seg))
19826 assert (!marked (o));
19831 while (o < heap_segment_allocated (seg) && !marked (o))
19833 o = o + AlignQword (size (o));
19838 dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n",
19839 generation_size (max_generation + 1),
19840 generation_free_list_space (gen),
19841 generation_free_obj_space (gen)));
19844 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
19845 void gc_heap::walk_relocation_loh (size_t profiling_context)
19847 generation* gen = large_object_generation;
19848 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19849 BYTE* o = generation_allocation_start (gen);
19851 //Skip the generation gap object
19852 o = o + AlignQword (size (o));
19856 if (o >= heap_segment_allocated (seg))
19858 seg = heap_segment_next (seg);
19864 o = heap_segment_mem (seg);
19869 size_t size = AlignQword (size (o));
19871 ptrdiff_t reloc = loh_node_relocation_distance (o);
19873 STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
19876 ETW::GCLog::MovedReference(
19881 settings.compaction);
19885 if (o < heap_segment_allocated (seg))
19887 assert (!marked (o));
19892 while (o < heap_segment_allocated (seg) && !marked (o))
19894 o = o + AlignQword (size (o));
19899 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
19901 BOOL gc_heap::loh_object_p (BYTE* o)
19903 #ifdef MULTIPLE_HEAPS
19904 gc_heap* hp = gc_heap::g_heaps [0];
19905 int brick_entry = hp->brick_table[hp->brick_of (o)];
19906 #else //MULTIPLE_HEAPS
19907 int brick_entry = brick_table[brick_of (o)];
19908 #endif //MULTIPLE_HEAPS
19910 return (brick_entry == 0);
19912 #endif //FEATURE_LOH_COMPACTION
19914 // Because we have the artifical pinning, we can't gaurantee that pinned and npinned
19915 // plugs are always interleaved.
19916 void gc_heap::store_plug_gap_info (BYTE* plug_start,
19918 BOOL& last_npinned_plug_p,
19919 BOOL& last_pinned_plug_p,
19920 BYTE*& last_pinned_plug,
19921 BOOL& pinned_plug_p,
19922 BYTE* last_object_in_last_plug,
19923 BOOL& merge_with_last_pin_p,
19924 // this is only for verification purpose
19925 size_t last_plug_len)
19927 if (!last_npinned_plug_p && !last_pinned_plug_p)
19929 //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
19930 dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
19931 assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
19932 set_gap_size (plug_start, plug_start - plug_end);
19935 if (pinned (plug_start))
19937 BOOL save_pre_plug_info_p = FALSE;
19939 if (last_npinned_plug_p || last_pinned_plug_p)
19941 //if (last_plug_len == Align (min_obj_size))
19943 // dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
19946 save_pre_plug_info_p = TRUE;
19949 pinned_plug_p = TRUE;
19950 last_npinned_plug_p = FALSE;
19952 if (last_pinned_plug_p)
19954 dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
19955 merge_with_last_pin_p = TRUE;
19959 last_pinned_plug_p = TRUE;
19960 last_pinned_plug = plug_start;
19962 enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
19964 if (save_pre_plug_info_p)
19966 set_gap_size (plug_start, sizeof (gap_reloc_pair));
19972 if (last_pinned_plug_p)
19974 //if (Align (last_plug_len) < min_pre_pin_obj_size)
19976 // dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
19980 save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
19981 set_gap_size (plug_start, sizeof (gap_reloc_pair));
19983 verify_pins_with_post_plug_info("after saving post plug info");
19985 last_npinned_plug_p = TRUE;
19986 last_pinned_plug_p = FALSE;
19991 #pragma warning(push)
19992 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
19994 void gc_heap::plan_phase (int condemned_gen_number)
19996 size_t old_gen2_allocated = 0;
19997 size_t old_gen2_size = 0;
19999 if (condemned_gen_number == (max_generation - 1))
20001 old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
20002 old_gen2_size = generation_size (max_generation);
20005 assert (settings.concurrent == FALSE);
20007 // %type% category = quote (plan);
20011 start = GetCycleCount32();
20014 dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
20015 condemned_gen_number, settings.promotion ? 1 : 0));
20017 generation* condemned_gen1 = generation_of (condemned_gen_number);
20020 BOOL use_mark_list = FALSE;
20021 BYTE** mark_list_next = &mark_list[0];
20022 dprintf (3, ("mark_list length: %Id",
20023 mark_list_index - &mark_list[0]));
20025 if ((condemned_gen_number < max_generation) &&
20026 (mark_list_index <= mark_list_end)
20027 #ifdef BACKGROUND_GC
20028 && (!recursive_gc_sync::background_running_p())
20029 #endif //BACKGROUND_GC
20032 #ifndef MULTIPLE_HEAPS
20033 _sort (&mark_list[0], mark_list_index-1, 0);
20034 //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
20035 //verify_qsort_array (&mark_list[0], mark_list_index-1);
20036 #endif //!MULTIPLE_HEAPS
20037 use_mark_list = TRUE;
20041 dprintf (3, ("mark_list not used"));
20046 if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
20047 ro_segments_in_range)
20049 sweep_ro_segments (generation_start_segment (condemned_gen1));
20052 #ifndef MULTIPLE_HEAPS
20053 if (shigh != (BYTE*)0)
20055 heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
20057 PREFIX_ASSUME(seg != NULL);
20059 heap_segment* fseg = seg;
20062 if (slow > heap_segment_mem (seg) &&
20063 slow < heap_segment_reserved (seg))
20067 BYTE* o = generation_allocation_start (condemned_gen1) +
20068 Align (size (generation_allocation_start (condemned_gen1)));
20071 assert ((slow - o) >= (int)Align (min_obj_size));
20072 #ifdef BACKGROUND_GC
20073 if (current_c_gc_state == c_gc_state_marking)
20075 bgc_clear_batch_mark_array_bits (o, slow);
20077 #endif //BACKGROUND_GC
20078 make_unused_array (o, slow - o);
20083 assert (condemned_gen_number == max_generation);
20084 make_unused_array (heap_segment_mem (seg),
20085 slow - heap_segment_mem (seg));
20088 if (in_range_for_segment (shigh, seg))
20090 #ifdef BACKGROUND_GC
20091 if (current_c_gc_state == c_gc_state_marking)
20093 bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
20095 #endif //BACKGROUND_GC
20096 heap_segment_allocated (seg) = shigh + Align (size (shigh));
20098 // test if the segment is in the range of [slow, shigh]
20099 if (!((heap_segment_reserved (seg) >= slow) &&
20100 (heap_segment_mem (seg) <= shigh)))
20102 // shorten it to minimum
20103 heap_segment_allocated (seg) = heap_segment_mem (seg);
20105 seg = heap_segment_next_rw (seg);
20110 heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
20112 PREFIX_ASSUME(seg != NULL);
20114 heap_segment* sseg = seg;
20117 // shorten it to minimum
20120 // no survivors make all generations look empty
20121 BYTE* o = generation_allocation_start (condemned_gen1) +
20122 Align (size (generation_allocation_start (condemned_gen1)));
20123 #ifdef BACKGROUND_GC
20124 if (current_c_gc_state == c_gc_state_marking)
20126 bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
20128 #endif //BACKGROUND_GC
20129 heap_segment_allocated (seg) = o;
20133 assert (condemned_gen_number == max_generation);
20134 #ifdef BACKGROUND_GC
20135 if (current_c_gc_state == c_gc_state_marking)
20137 bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
20139 #endif //BACKGROUND_GC
20140 heap_segment_allocated (seg) = heap_segment_mem (seg);
20142 seg = heap_segment_next_rw (seg);
20146 #endif //MULTIPLE_HEAPS
20148 heap_segment* seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
20150 PREFIX_ASSUME(seg1 != NULL);
20152 BYTE* end = heap_segment_allocated (seg1);
20153 BYTE* first_condemned_address = generation_allocation_start (condemned_gen1);
20154 BYTE* x = first_condemned_address;
20156 assert (!marked (x));
20157 BYTE* plug_end = x;
20159 size_t sequence_number = 0;
20160 BYTE* last_node = 0;
20161 size_t current_brick = brick_of (x);
20162 BOOL allocate_in_condemned = ((condemned_gen_number == max_generation)||
20163 (settings.promotion == FALSE));
20164 int active_old_gen_number = condemned_gen_number;
20165 int active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
20166 (1 + condemned_gen_number));
20167 generation* older_gen = 0;
20168 generation* consing_gen = condemned_gen1;
20169 alloc_list r_free_list [MAX_BUCKET_COUNT];
20171 size_t r_free_list_space = 0;
20172 size_t r_free_obj_space = 0;
20173 size_t r_older_gen_free_list_allocated = 0;
20174 size_t r_older_gen_condemned_allocated = 0;
20175 size_t r_older_gen_end_seg_allocated = 0;
20176 BYTE* r_allocation_pointer = 0;
20177 BYTE* r_allocation_limit = 0;
20178 BYTE* r_allocation_start_region = 0;
20179 heap_segment* r_allocation_segment = 0;
20180 #ifdef FREE_USAGE_STATS
20181 size_t r_older_gen_free_space[NUM_GEN_POWER2];
20182 #endif //FREE_USAGE_STATS
20184 if ((condemned_gen_number < max_generation))
20186 older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
20187 generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
20189 r_free_list_space = generation_free_list_space (older_gen);
20190 r_free_obj_space = generation_free_obj_space (older_gen);
20191 #ifdef FREE_USAGE_STATS
20192 memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
20193 #endif //FREE_USAGE_STATS
20194 generation_allocate_end_seg_p (older_gen) = FALSE;
20195 r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
20196 r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
20197 r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
20198 r_allocation_limit = generation_allocation_limit (older_gen);
20199 r_allocation_pointer = generation_allocation_pointer (older_gen);
20200 r_allocation_start_region = generation_allocation_context_start_region (older_gen);
20201 r_allocation_segment = generation_allocation_segment (older_gen);
20202 heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
20204 PREFIX_ASSUME(start_seg != NULL);
20206 if (start_seg != ephemeral_heap_segment)
20208 assert (condemned_gen_number == (max_generation - 1));
20209 while (start_seg && (start_seg != ephemeral_heap_segment))
20211 assert (heap_segment_allocated (start_seg) >=
20212 heap_segment_mem (start_seg));
20213 assert (heap_segment_allocated (start_seg) <=
20214 heap_segment_reserved (start_seg));
20215 heap_segment_plan_allocated (start_seg) =
20216 heap_segment_allocated (start_seg);
20217 start_seg = heap_segment_next_rw (start_seg);
20222 //reset all of the segment allocated sizes
20224 heap_segment* seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
20226 PREFIX_ASSUME(seg2 != NULL);
20230 heap_segment_plan_allocated (seg2) =
20231 heap_segment_mem (seg2);
20232 seg2 = heap_segment_next_rw (seg2);
20235 int condemned_gn = condemned_gen_number;
20237 int bottom_gen = 0;
20238 init_free_and_plug();
20240 while (condemned_gn >= bottom_gen)
20242 generation* condemned_gen2 = generation_of (condemned_gn);
20243 generation_allocator (condemned_gen2)->clear();
20244 generation_free_list_space (condemned_gen2) = 0;
20245 generation_free_obj_space (condemned_gen2) = 0;
20246 generation_allocation_size (condemned_gen2) = 0;
20247 generation_condemned_allocated (condemned_gen2) = 0;
20248 generation_pinned_allocated (condemned_gen2) = 0;
20249 generation_free_list_allocated(condemned_gen2) = 0;
20250 generation_end_seg_allocated (condemned_gen2) = 0;
20251 generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
20252 generation_pinned_allocation_compact_size (condemned_gen2) = 0;
20253 #ifdef FREE_USAGE_STATS
20254 generation_pinned_free_obj_space (condemned_gen2) = 0;
20255 generation_allocated_in_pinned_free (condemned_gen2) = 0;
20256 generation_allocated_since_last_pin (condemned_gen2) = 0;
20257 #endif //FREE_USAGE_STATS
20258 generation_plan_allocation_start (condemned_gen2) = 0;
20259 generation_allocation_segment (condemned_gen2) =
20260 heap_segment_rw (generation_start_segment (condemned_gen2));
20262 PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
20264 if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
20266 generation_allocation_pointer (condemned_gen2) =
20267 heap_segment_mem (generation_allocation_segment (condemned_gen2));
20271 generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
20274 generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
20275 generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
20280 BOOL allocate_first_generation_start = FALSE;
20282 if (allocate_in_condemned)
20284 allocate_first_generation_start = TRUE;
20287 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
20289 demotion_low = MAX_PTR;
20290 demotion_high = heap_segment_allocated (ephemeral_heap_segment);
20292 // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
20293 // from gen1. They should get promoted to gen2.
20294 demote_gen1_p = !(settings.promotion &&
20295 (settings.condemned_generation == (max_generation - 1)) &&
20296 gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
20298 total_ephemeral_size = 0;
20300 print_free_and_plug ("BP");
20302 for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
20304 generation* temp_gen = generation_of (gen_idx);
20306 dprintf (2, ("gen%d start %Ix, plan start %Ix",
20308 generation_allocation_start (temp_gen),
20309 generation_plan_allocation_start (temp_gen)));
20312 BOOL fire_pinned_plug_events_p = ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PinPlugAtGCTime);
20313 size_t last_plug_len = 0;
20320 assert (heap_segment_allocated (seg1) == end);
20321 heap_segment_allocated (seg1) = plug_end;
20323 current_brick = update_brick_table (tree, current_brick, x, plug_end);
20324 dprintf (3, ("end of seg: new tree, sequence# 0"));
20325 sequence_number = 0;
20328 if (heap_segment_next_rw (seg1))
20330 seg1 = heap_segment_next_rw (seg1);
20331 end = heap_segment_allocated (seg1);
20332 plug_end = x = heap_segment_mem (seg1);
20333 current_brick = brick_of (x);
20334 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
20343 BOOL last_npinned_plug_p = FALSE;
20344 BOOL last_pinned_plug_p = FALSE;
20346 // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
20347 // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
20348 // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
20349 BYTE* last_pinned_plug = 0;
20350 size_t num_pinned_plugs_in_plug = 0;
20352 BYTE* last_object_in_plug = 0;
20354 while ((x < end) && marked (x))
20356 BYTE* plug_start = x;
20357 BYTE* saved_plug_end = plug_end;
20358 BOOL pinned_plug_p = FALSE;
20359 BOOL npin_before_pin_p = FALSE;
20360 BOOL saved_last_npinned_plug_p = last_npinned_plug_p;
20361 BYTE* saved_last_object_in_plug = last_object_in_plug;
20362 BOOL merge_with_last_pin_p = FALSE;
20364 size_t added_pinning_size = 0;
20365 size_t artificial_pinned_size = 0;
20367 store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p,
20368 last_pinned_plug, pinned_plug_p, last_object_in_plug,
20369 merge_with_last_pin_p, last_plug_len);
20371 #ifdef FEATURE_STRUCTALIGN
20372 int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
20373 size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
20374 #endif // FEATURE_STRUCTALIGN
20378 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
20385 #ifdef FEATURE_STRUCTALIGN
20388 int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
20389 if (obj_requiredAlignment > requiredAlignment)
20391 requiredAlignment = obj_requiredAlignment;
20392 alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
20395 #endif // FEATURE_STRUCTALIGN
20399 dprintf(4, ("+%Ix+", (size_t)xl));
20400 assert ((size (xl) > 0));
20401 assert ((size (xl) <= LARGE_OBJECT_SIZE));
20403 last_object_in_plug = xl;
20405 xl = xl + Align (size (xl));
20409 BOOL next_object_marked_p = ((xl < end) && marked (xl));
20413 // If it is pinned we need to extend to the next marked object as we can't use part of
20414 // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
20415 // references but for now I am just using the next non pinned object for that).
20416 if (next_object_marked_p)
20419 last_object_in_plug = xl;
20420 size_t extra_size = Align (size (xl));
20421 xl = xl + extra_size;
20422 added_pinning_size = extra_size;
20427 if (next_object_marked_p)
20428 npin_before_pin_p = TRUE;
20431 assert (xl <= end);
20434 dprintf (3, ( "%Ix[", (size_t)x));
20436 size_t ps = plug_end - plug_start;
20437 last_plug_len = ps;
20438 dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
20439 BYTE* new_address = 0;
20441 if (!pinned_plug_p)
20443 if (allocate_in_condemned &&
20444 (settings.condemned_generation == max_generation) &&
20445 (ps > (OS_PAGE_SIZE)))
20447 ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
20448 //reloc should >=0 except when we relocate
20449 //across segments and the dest seg is higher then the src
20451 if ((ps > (8*OS_PAGE_SIZE)) &&
20453 ((size_t)reloc < (ps/16)))
20455 dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
20456 (size_t)plug_start, reloc));
20457 // The last plug couldn't have been a npinned plug or it would have
20458 // included this plug.
20459 assert (!saved_last_npinned_plug_p);
20461 if (last_pinned_plug)
20463 dprintf (3, ("artificially pinned plug merged with last pinned plug"));
20464 merge_with_last_pin_p = TRUE;
20468 enque_pinned_plug (plug_start, FALSE, 0);
20469 last_pinned_plug = plug_start;
20472 last_pinned_plug_p = TRUE;
20473 last_npinned_plug_p = FALSE;
20474 pinned_plug_p = TRUE;
20475 artificial_pinned_size = ps;
20482 if (fire_pinned_plug_events_p)
20483 FireEtwPinPlugAtGCTime(plug_start, plug_end,
20484 (merge_with_last_pin_p ? 0 : (BYTE*)node_gap_size (plug_start)),
20485 GetClrInstanceId());
20487 if (merge_with_last_pin_p)
20489 merge_with_last_pinned_plug (last_pinned_plug, ps);
20493 assert (last_pinned_plug == plug_start);
20494 set_pinned_info (plug_start, ps, consing_gen);
20497 new_address = plug_start;
20500 if (allocate_first_generation_start)
20502 allocate_first_generation_start = FALSE;
20503 plan_generation_start (condemned_gen1, consing_gen, plug_start);
20504 assert (generation_plan_allocation_start (condemned_gen1));
20507 if (seg1 == ephemeral_heap_segment)
20509 process_ephemeral_boundaries (plug_start, active_new_gen_number,
20510 active_old_gen_number,
20512 allocate_in_condemned);
20515 dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
20517 dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
20518 dd_survived_size (dd_active_old) += ps;
20520 if (!pinned_plug_p)
20522 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
20523 dd_num_npinned_plugs (dd_active_old)++;
20524 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
20526 add_gen_plug (active_old_gen_number, ps);
20528 if (allocate_in_condemned)
20530 verify_pins_with_post_plug_info("before aic");
20533 allocate_in_condemned_generations (consing_gen,
20535 active_old_gen_number,
20537 (npin_before_pin_p ? plug_end : 0),
20539 #endif //SHORT_PLUGS
20540 plug_start REQD_ALIGN_AND_OFFSET_ARG);
20541 verify_pins_with_post_plug_info("after aic");
20545 new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
20547 if (new_address != 0)
20549 if (settings.condemned_generation == (max_generation - 1))
20551 dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
20552 plug_start, plug_end,
20553 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
20554 (size_t)(plug_end - plug_start)));
20559 allocate_in_condemned = TRUE;
20560 new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number,
20562 (npin_before_pin_p ? plug_end : 0),
20564 #endif //SHORT_PLUGS
20565 plug_start REQD_ALIGN_AND_OFFSET_ARG);
20571 //verify that we are at then end of the ephemeral segment
20572 assert (generation_allocation_segment (consing_gen) ==
20573 ephemeral_heap_segment);
20574 //verify that we are near the end
20575 assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
20576 heap_segment_allocated (ephemeral_heap_segment));
20577 assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
20578 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
20582 #ifdef SIMPLE_DPRINTF
20583 dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix",
20584 (size_t)(node_gap_size (plug_start)),
20585 plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
20586 (size_t)new_address + ps, ps));
20587 #endif //SIMPLE_DPRINTF
20589 if (is_plug_padded (plug_start))
20591 dprintf (3, ("%Ix was padded", plug_start));
20592 dd_padding_size (dd_active_old) += Align (min_obj_size);
20594 #endif //SHORT_PLUGS
20599 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix]",
20600 (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
20601 (size_t)plug_end, ps));
20602 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
20603 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
20604 dd_added_pinned_size (dd_active_old) += added_pinning_size;
20605 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
20607 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
20609 last_gen1_pin_end = plug_end;
20614 // detect forward allocation in the same segment
20615 assert (!((new_address > plug_start) &&
20616 (new_address < heap_segment_reserved (seg1))));
20619 if (!merge_with_last_pin_p)
20621 if (current_brick != brick_of (plug_start))
20623 current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
20624 sequence_number = 0;
20628 set_node_relocation_distance (plug_start, (new_address - plug_start));
20629 if (last_node && (node_relocation_distance (last_node) ==
20630 (node_relocation_distance (plug_start) +
20631 (int)node_gap_size (plug_start))))
20633 //dprintf(3,( " Lb"));
20634 dprintf (3, ("%Ix Lb", plug_start));
20635 set_node_left (plug_start);
20637 if (0 == sequence_number)
20639 dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
20643 verify_pins_with_post_plug_info("before insert node");
20645 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
20646 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
20647 last_node = plug_start;
20650 // If we detect if the last plug is pinned plug right before us, we should save this gap info
20651 if (!pinned_plug_p)
20653 if (mark_stack_tos > 0)
20655 mark& m = mark_stack_array[mark_stack_tos - 1];
20656 if (m.has_post_plug_info())
20658 BYTE* post_plug_info_start = m.saved_post_plug_info_start;
20659 size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
20660 if ((BYTE*)current_plug_gap_start == post_plug_info_start)
20662 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
20663 *current_plug_gap_start, *(current_plug_gap_start + 1),
20664 *(current_plug_gap_start + 2)));
20665 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
20672 verify_pins_with_post_plug_info("after insert node");
20676 if (num_pinned_plugs_in_plug > 1)
20678 dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
20685 while ((mark_list_next < mark_list_index) &&
20686 (*mark_list_next <= x))
20690 if ((mark_list_next < mark_list_index)
20691 #ifdef MULTIPLE_HEAPS
20692 && (*mark_list_next < end) //for multiple segments
20693 #endif //MULTIPLE_HEAPS
20695 x = *mark_list_next;
20703 #ifdef BACKGROUND_GC
20704 if (current_c_gc_state == c_gc_state_marking)
20706 assert (recursive_gc_sync::background_running_p());
20707 while ((xl < end) && !marked (xl))
20709 dprintf (4, ("-%Ix-", (size_t)xl));
20710 assert ((size (xl) > 0));
20711 background_object_marked (xl, TRUE);
20712 xl = xl + Align (size (xl));
20717 #endif //BACKGROUND_GC
20719 while ((xl < end) && !marked (xl))
20721 dprintf (4, ("-%Ix-", (size_t)xl));
20722 assert ((size (xl) > 0));
20723 xl = xl + Align (size (xl));
20727 assert (xl <= end);
20733 while (!pinned_plug_que_empty_p())
20735 if (settings.promotion)
20737 BYTE* pplug = pinned_plug (oldest_pin());
20738 if (in_range_for_segment (pplug, ephemeral_heap_segment))
20740 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20741 //allocate all of the generation gaps
20742 while (active_new_gen_number > 0)
20744 active_new_gen_number--;
20746 if ((active_new_gen_number == (max_generation - 1)) && !demote_gen1_p)
20748 advance_pins_for_demotion (consing_gen);
20751 generation* gen = generation_of (active_new_gen_number);
20752 plan_generation_start (gen, consing_gen, 0);
20754 if ((demotion_low == MAX_PTR))
20756 demotion_low = pplug;
20757 dprintf (3, ("end plan: dlow->%Ix", demotion_low));
20760 dprintf (2, ("(%d)gen%d plan start: %Ix",
20761 heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
20762 assert (generation_plan_allocation_start (gen));
20767 if (pinned_plug_que_empty_p())
20770 size_t entry = deque_pinned_plug();
20771 mark* m = pinned_plug_of (entry);
20772 BYTE* plug = pinned_plug (m);
20773 size_t len = pinned_len (m);
20775 // detect pinned block in different segment (later) than
20776 // allocation segment
20777 heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
20779 while ((plug < generation_allocation_pointer (consing_gen)) ||
20780 (plug >= heap_segment_allocated (nseg)))
20782 assert ((plug < heap_segment_mem (nseg)) ||
20783 (plug > heap_segment_reserved (nseg)));
20784 //adjust the end of the segment to be the end of the plug
20785 assert (generation_allocation_pointer (consing_gen)>=
20786 heap_segment_mem (nseg));
20787 assert (generation_allocation_pointer (consing_gen)<=
20788 heap_segment_committed (nseg));
20790 heap_segment_plan_allocated (nseg) =
20791 generation_allocation_pointer (consing_gen);
20792 //switch allocation segment
20793 nseg = heap_segment_next_rw (nseg);
20794 generation_allocation_segment (consing_gen) = nseg;
20795 //reset the allocation pointer and limits
20796 generation_allocation_pointer (consing_gen) =
20797 heap_segment_mem (nseg);
20800 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
20801 dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
20802 (size_t)(brick_table[brick_of (plug)])));
20804 generation_allocation_pointer (consing_gen) = plug + len;
20805 generation_allocation_limit (consing_gen) =
20806 generation_allocation_pointer (consing_gen);
20807 //Add the size of the pinned plug to the right pinned allocations
20808 //find out which gen this pinned plug came from
20809 int frgn = object_gennum (plug);
20810 if ((frgn != (int)max_generation) && settings.promotion)
20812 generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20817 plan_generation_starts (consing_gen);
20818 print_free_and_plug ("AP");
20821 #ifdef SIMPLE_DPRINTF
20822 for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
20824 generation* temp_gen = generation_of (gen_idx);
20825 dynamic_data* temp_dd = dynamic_data_of (gen_idx);
20827 int added_pinning_ratio = 0;
20828 int artificial_pinned_ratio = 0;
20830 if (dd_pinned_survived_size (temp_dd) != 0)
20832 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
20833 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
20836 size_t padding_size =
20838 dd_padding_size (temp_dd);
20841 #endif //SHORT_PLUGS
20842 dprintf (1, ("gen%d: %Ix, %Ix(%Id), NON PIN alloc: %Id, pin com: %Id, sweep: %Id, surv: %Id, pinsurv: %Id(%d%% added, %d%% art), np surv: %Id, pad: %Id",
20844 generation_allocation_start (temp_gen),
20845 generation_plan_allocation_start (temp_gen),
20846 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
20847 generation_allocation_size (temp_gen),
20848 generation_pinned_allocation_compact_size (temp_gen),
20849 generation_pinned_allocation_sweep_size (temp_gen),
20850 dd_survived_size (temp_dd),
20851 dd_pinned_survived_size (temp_dd),
20852 added_pinning_ratio,
20853 artificial_pinned_ratio,
20854 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
20857 #endif //SIMPLE_DPRINTF
20860 #ifdef FREE_USAGE_STATS
20861 if (settings.condemned_generation == (max_generation - 1 ))
20863 size_t plan_gen2_size = generation_plan_size (max_generation);
20864 size_t growth = plan_gen2_size - old_gen2_size;
20866 dprintf (1, ("gen2's FL effi: %d", (int)(generation_allocator_efficiency (generation_of (max_generation)) * 100)));
20870 dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, gen1 c alloc: %Id",
20871 growth, generation_end_seg_allocated (generation_of (max_generation)),
20872 generation_condemned_allocated (generation_of (max_generation - 1))));
20876 dprintf (1, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id",
20877 (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)),
20878 generation_condemned_allocated (generation_of (max_generation - 1))));
20881 generation* older_gen = generation_of (settings.condemned_generation + 1);
20882 size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
20883 size_t free_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
20885 dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
20886 r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
20887 r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen),
20888 r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
20890 dprintf (1, ("this GC did %Id free list alloc(%Id bytes free space rejected), %Id seg alloc and %Id condemned alloc, gen1 condemned alloc is %Id",
20892 rejected_free_space,
20893 (generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated),
20894 (generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated),
20895 generation_condemned_allocated (generation_of (settings.condemned_generation))));
20897 float running_free_list_efficiency = 0;
20898 if ((free_allocated + rejected_free_space) != 0)
20900 running_free_list_efficiency = (float) (free_allocated) / (float)(free_allocated + rejected_free_space);
20903 float free_list_efficiency = 0;
20904 if ((generation_free_list_allocated (older_gen) + generation_free_obj_space (older_gen)) != 0)
20906 free_list_efficiency =
20907 (float) (generation_free_list_allocated (older_gen)) / (float)(generation_free_list_allocated (older_gen) + generation_free_obj_space (older_gen));
20910 dprintf (1, ("gen%d running free list alloc effi: %d%%(%d%%), current effi: %d%%",
20911 older_gen->gen_num,
20912 (int)(running_free_list_efficiency*100),
20913 (int)(free_list_efficiency*100),
20914 (int)(generation_allocator_efficiency(older_gen)*100)));
20916 dprintf (1, ("gen2 free list change"));
20917 for (int j = 0; j < NUM_GEN_POWER2; j++)
20919 dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id",
20922 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j],
20923 (SSIZE_T)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
20924 (generation_of(max_generation - 1))->gen_plugs[j]));
20927 #endif //FREE_USAGE_STATS
20929 size_t fragmentation =
20930 generation_fragmentation (generation_of (condemned_gen_number),
20932 heap_segment_allocated (ephemeral_heap_segment));
20934 dprintf (2,("Fragmentation: %Id", fragmentation));
20935 dprintf (2,("---- End of Plan phase ----"));
20938 finish = GetCycleCount32();
20939 plan_time = finish - start;
20942 // We may update write barrier code. We assume here EE has been suspended if we are on a GC thread.
20943 assert(GCHeap::IsGCInProgress());
20945 BOOL should_expand = FALSE;
20946 BOOL should_compact= FALSE;
20947 ephemeral_promotion = FALSE;
20950 if ((!settings.concurrent) &&
20951 ((condemned_gen_number < max_generation) &&
20952 ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
20954 dprintf (2, ("gen0 reduction count is %d, condemning %d, mem load %d",
20955 settings.gen0_reduction_count,
20956 condemned_gen_number,
20957 settings.entry_memory_load));
20958 should_compact = TRUE;
20960 if ((condemned_gen_number >= (max_generation - 1)) &&
20961 dt_low_ephemeral_space_p (tuning_deciding_expansion))
20963 dprintf(2,("Not enough space for all ephemeral generations with compaction"));
20964 should_expand = TRUE;
20970 should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
20975 #ifdef FEATURE_LOH_COMPACTION
20976 loh_compacted_p = FALSE;
20977 #endif //FEATURE_LOH_COMPACTION
20979 if (condemned_gen_number == max_generation)
20981 #ifdef FEATURE_LOH_COMPACTION
20982 if (settings.loh_compaction)
20986 should_compact = TRUE;
20987 gc_data_per_heap.set_mechanism (gc_compact, compact_loh_forced);
20988 loh_compacted_p = TRUE;
20993 if ((heap_number == 0) && (loh_pinned_queue))
20995 loh_pinned_queue_decay--;
20997 if (!loh_pinned_queue_decay)
20999 delete loh_pinned_queue;
21000 loh_pinned_queue = 0;
21005 if (!loh_compacted_p)
21006 #endif //FEATURE_LOH_COMPACTION
21008 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21009 if (ShouldTrackMovementForProfilerOrEtw())
21010 notify_profiler_of_surviving_large_objects();
21011 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21012 sweep_large_objects();
21017 settings.loh_compaction = FALSE;
21020 #ifdef MULTIPLE_HEAPS
21022 new_heap_segment = NULL;
21024 if (should_compact && should_expand)
21025 gc_policy = policy_expand;
21026 else if (should_compact)
21027 gc_policy = policy_compact;
21029 gc_policy = policy_sweep;
21031 //vote for result of should_compact
21032 dprintf (3, ("Joining for compaction decision"));
21033 gc_t_join.join(this, gc_join_decide_on_compaction);
21034 if (gc_t_join.joined())
21036 //safe place to delete large heap segments
21037 if (condemned_gen_number == max_generation)
21039 for (int i = 0; i < n_heaps; i++)
21041 g_heaps [i]->rearrange_large_heap_segments ();
21045 settings.demotion = FALSE;
21046 int pol_max = policy_sweep;
21048 for (i = 0; i < n_heaps; i++)
21050 if (pol_max < g_heaps[i]->gc_policy)
21051 pol_max = policy_compact;
21052 // set the demotion flag is any of the heap has demotion
21053 if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
21054 settings.demotion = TRUE;
21057 for (i = 0; i < n_heaps; i++)
21059 if (pol_max > g_heaps[i]->gc_policy)
21060 g_heaps[i]->gc_policy = pol_max;
21061 //get the segment while we are serialized
21062 if (g_heaps[i]->gc_policy == policy_expand)
21064 g_heaps[i]->new_heap_segment =
21065 g_heaps[i]->soh_get_segment_to_expand();
21066 if (!g_heaps[i]->new_heap_segment)
21068 set_expand_in_full_gc (condemned_gen_number);
21069 //we are out of memory, cancel the expansion
21070 g_heaps[i]->gc_policy = policy_compact;
21075 BOOL is_full_compacting_gc = FALSE;
21077 if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
21079 full_gc_counts[gc_type_compacting]++;
21080 is_full_compacting_gc = TRUE;
21083 for (i = 0; i < n_heaps; i++)
21085 //copy the card and brick tables
21086 if (g_card_table!= g_heaps[i]->card_table)
21088 g_heaps[i]->copy_brick_card_table (TRUE);
21091 if (is_full_compacting_gc)
21093 g_heaps[i]->loh_alloc_since_cg = 0;
21097 //start all threads on the roots.
21098 dprintf(3, ("Starting all gc threads after compaction decision"));
21099 gc_t_join.restart();
21102 //reset the local variable accordingly
21103 should_compact = (gc_policy >= policy_compact);
21104 should_expand = (gc_policy >= policy_expand);
21106 #else //MULTIPLE_HEAPS
21108 //safe place to delete large heap segments
21109 if (condemned_gen_number == max_generation)
21111 rearrange_large_heap_segments ();
21114 settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
21116 if (should_compact && (condemned_gen_number == max_generation))
21118 full_gc_counts[gc_type_compacting]++;
21119 loh_alloc_since_cg = 0;
21121 #endif //MULTIPLE_HEAPS
21123 if (should_compact)
21125 dprintf (2,( "**** Doing Compacting GC ****"));
21129 #ifndef MULTIPLE_HEAPS
21130 heap_segment* new_heap_segment = soh_get_segment_to_expand();
21131 #endif //!MULTIPLE_HEAPS
21132 if (new_heap_segment)
21134 consing_gen = expand_heap(condemned_gen_number,
21139 // If we couldn't get a new segment, or we were able to
21140 // reserve one but no space to commit, we couldn't
21142 if (ephemeral_heap_segment != new_heap_segment)
21144 set_expand_in_full_gc (condemned_gen_number);
21145 should_expand = FALSE;
21148 generation_allocation_limit (condemned_gen1) =
21149 generation_allocation_pointer (condemned_gen1);
21150 if ((condemned_gen_number < max_generation))
21152 generation_allocator (older_gen)->commit_alloc_list_changes();
21154 // Fix the allocation area of the older generation
21155 fix_older_allocation_area (older_gen);
21157 assert (generation_allocation_segment (consing_gen) ==
21158 ephemeral_heap_segment);
21160 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21161 if (ShouldTrackMovementForProfilerOrEtw())
21163 record_survived_for_profiler(condemned_gen_number, first_condemned_address);
21165 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21167 relocate_phase (condemned_gen_number, first_condemned_address);
21168 compact_phase (condemned_gen_number, first_condemned_address,
21169 (!settings.demotion && settings.promotion));
21170 fix_generation_bounds (condemned_gen_number, consing_gen);
21171 assert (generation_allocation_limit (youngest_generation) ==
21172 generation_allocation_pointer (youngest_generation));
21173 if (condemned_gen_number >= (max_generation -1))
21175 #ifdef MULTIPLE_HEAPS
21176 // this needs be serialized just because we have one
21177 // segment_standby_list/seg_table for all heaps. We should make it at least
21178 // so that when hoarding is not on we don't need this join because
21179 // decommitting memory can take a long time.
21180 //must serialize on deleting segments
21181 gc_t_join.join(this, gc_join_rearrange_segs_compaction);
21182 if (gc_t_join.joined())
21184 for (int i = 0; i < n_heaps; i++)
21186 g_heaps[i]->rearrange_heap_segments(TRUE);
21188 gc_t_join.restart();
21191 rearrange_heap_segments(TRUE);
21192 #endif //MULTIPLE_HEAPS
21196 //fix the start_segment for the ephemeral generations
21197 for (int i = 0; i < max_generation; i++)
21199 generation* gen = generation_of (i);
21200 generation_start_segment (gen) = ephemeral_heap_segment;
21201 generation_allocation_segment (gen) = ephemeral_heap_segment;
21207 #ifdef FEATURE_PREMORTEM_FINALIZATION
21208 finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
21209 (!settings.demotion && settings.promotion));
21210 #endif // FEATURE_PREMORTEM_FINALIZATION
21212 #ifdef MULTIPLE_HEAPS
21213 dprintf(3, ("Joining after end of compaction"));
21214 gc_t_join.join(this, gc_join_adjust_handle_age_compact);
21215 if (gc_t_join.joined())
21216 #endif //MULTIPLE_HEAPS
21218 #ifdef MULTIPLE_HEAPS
21219 //join all threads to make sure they are synchronized
21220 dprintf(3, ("Restarting after Promotion granted"));
21221 gc_t_join.restart();
21222 #endif //MULTIPLE_HEAPS
21226 sc.thread_number = heap_number;
21227 sc.promotion = FALSE;
21228 sc.concurrent = FALSE;
21229 // new generations bounds are set can call this guy
21230 if (settings.promotion && !settings.demotion)
21232 dprintf (2, ("Promoting EE roots for gen %d",
21233 condemned_gen_number));
21234 CNameSpace::GcPromotionsGranted(condemned_gen_number,
21235 max_generation, &sc);
21237 else if (settings.demotion)
21239 dprintf (2, ("Demoting EE roots for gen %d",
21240 condemned_gen_number));
21241 CNameSpace::GcDemote (condemned_gen_number, max_generation, &sc);
21246 gen0_big_free_spaces = 0;
21248 reset_pinned_queue_bos();
21249 unsigned int gen_number = min (max_generation, 1 + condemned_gen_number);
21250 generation* gen = generation_of (gen_number);
21251 BYTE* low = generation_allocation_start (generation_of (gen_number-1));
21252 BYTE* high = heap_segment_allocated (ephemeral_heap_segment);
21254 while (!pinned_plug_que_empty_p())
21256 mark* m = pinned_plug_of (deque_pinned_plug());
21257 size_t len = pinned_len (m);
21258 BYTE* arr = (pinned_plug (m) - len);
21259 dprintf(3,("free [%Ix %Ix[ pin",
21260 (size_t)arr, (size_t)arr + len));
21263 assert (len >= Align (min_obj_size));
21264 make_unused_array (arr, len);
21265 // fix fully contained bricks + first one
21266 // if the array goes beyong the first brick
21267 size_t start_brick = brick_of (arr);
21268 size_t end_brick = brick_of (arr + len);
21269 if (end_brick != start_brick)
21272 ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
21273 start_brick, end_brick, (size_t)arr));
21274 set_brick (start_brick,
21275 arr - brick_address (start_brick));
21276 size_t brick = start_brick+1;
21277 while (brick < end_brick)
21279 set_brick (brick, start_brick - brick);
21284 //when we take an old segment to make the new
21285 //ephemeral segment. we can have a bunch of
21286 //pinned plugs out of order going to the new ephemeral seg
21287 //and then the next plugs go back to max_generation
21288 if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
21289 (heap_segment_reserved (ephemeral_heap_segment) > arr))
21292 while ((low <= arr) && (high > arr))
21295 assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
21296 settings.demotion || !settings.promotion);
21297 dprintf (3, ("new free list generation %d", gen_number));
21299 gen = generation_of (gen_number);
21300 if (gen_number >= 1)
21301 low = generation_allocation_start (generation_of (gen_number-1));
21308 dprintf (3, ("new free list generation %d", max_generation));
21309 gen_number = max_generation;
21310 gen = generation_of (gen_number);
21313 dprintf(3,("threading it into generation %d", gen_number));
21314 thread_gap (arr, len, gen);
21315 add_gen_free (gen_number, len);
21321 for (int x = 0; x <= max_generation; x++)
21323 assert (generation_allocation_start (generation_of (x)));
21327 if (!settings.demotion && settings.promotion)
21329 //clear card for generation 1. generation 0 is empty
21330 clear_card_for_addresses (
21331 generation_allocation_start (generation_of (1)),
21332 generation_allocation_start (generation_of (0)));
21334 if (settings.promotion && !settings.demotion)
21336 BYTE* start = generation_allocation_start (youngest_generation);
21337 MAYBE_UNUSED_VAR(start);
21338 assert (heap_segment_allocated (ephemeral_heap_segment) ==
21339 (start + Align (size (start))));
21344 //force promotion for sweep
21345 settings.promotion = TRUE;
21346 settings.compaction = FALSE;
21349 sc.thread_number = heap_number;
21350 sc.promotion = FALSE;
21351 sc.concurrent = FALSE;
21353 dprintf (2, ("**** Doing Mark and Sweep GC****"));
21355 if ((condemned_gen_number < max_generation))
21357 generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
21358 generation_free_list_space (older_gen) = r_free_list_space;
21359 generation_free_obj_space (older_gen) = r_free_obj_space;
21360 generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
21361 generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
21362 generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
21363 generation_allocation_limit (older_gen) = r_allocation_limit;
21364 generation_allocation_pointer (older_gen) = r_allocation_pointer;
21365 generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
21366 generation_allocation_segment (older_gen) = r_allocation_segment;
21369 if ((condemned_gen_number < max_generation))
21371 // Fix the allocation area of the older generation
21372 fix_older_allocation_area (older_gen);
21375 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21376 if (ShouldTrackMovementForProfilerOrEtw())
21378 record_survived_for_profiler(condemned_gen_number, first_condemned_address);
21380 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21382 gen0_big_free_spaces = 0;
21383 make_free_lists (condemned_gen_number);
21384 recover_saved_pinned_info();
21386 #ifdef FEATURE_PREMORTEM_FINALIZATION
21387 finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
21388 #endif // FEATURE_PREMORTEM_FINALIZATION
21389 // MTHTS: leave single thread for HT processing on plan_phase
21390 #ifdef MULTIPLE_HEAPS
21391 dprintf(3, ("Joining after end of sweep"));
21392 gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
21393 if (gc_t_join.joined())
21394 #endif //MULTIPLE_HEAPS
21396 CNameSpace::GcPromotionsGranted(condemned_gen_number,
21397 max_generation, &sc);
21398 if (condemned_gen_number >= (max_generation -1))
21400 #ifdef MULTIPLE_HEAPS
21401 for (int i = 0; i < n_heaps; i++)
21403 g_heaps[i]->rearrange_heap_segments(FALSE);
21406 rearrange_heap_segments(FALSE);
21407 #endif //MULTIPLE_HEAPS
21410 #ifdef MULTIPLE_HEAPS
21411 //join all threads to make sure they are synchronized
21412 dprintf(3, ("Restarting after Promotion granted"));
21413 gc_t_join.restart();
21414 #endif //MULTIPLE_HEAPS
21418 for (int x = 0; x <= max_generation; x++)
21420 assert (generation_allocation_start (generation_of (x)));
21424 //clear card for generation 1. generation 0 is empty
21425 clear_card_for_addresses (
21426 generation_allocation_start (generation_of (1)),
21427 generation_allocation_start (generation_of (0)));
21428 assert ((heap_segment_allocated (ephemeral_heap_segment) ==
21429 (generation_allocation_start (youngest_generation) +
21430 Align (min_obj_size))));
21433 //verify_partial();
21436 #pragma warning(pop)
21440 /*****************************
21441 Called after compact phase to fix all generation gaps
21442 ********************************/
21443 void gc_heap::fix_generation_bounds (int condemned_gen_number,
21444 generation* consing_gen)
21446 assert (generation_allocation_segment (consing_gen) ==
21447 ephemeral_heap_segment);
21449 //assign the planned allocation start to the generation
21450 int gen_number = condemned_gen_number;
21451 int bottom_gen = 0;
21453 while (gen_number >= bottom_gen)
21455 generation* gen = generation_of (gen_number);
21456 dprintf(3,("Fixing generation pointers for %Ix", gen_number));
21457 if ((gen_number < max_generation) && ephemeral_promotion)
21459 make_unused_array (saved_ephemeral_plan_start[gen_number],
21460 saved_ephemeral_plan_start_size[gen_number]);
21462 reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
21463 make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
21464 dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
21467 #ifdef MULTIPLE_HEAPS
21468 if (ephemeral_promotion)
21470 //we are creating a generation fault. set the cards.
21471 // and we are only doing this for multiple heaps because in the single heap scenario the
21472 // new ephemeral generations will be empty and there'll be no need to set cards for the
21473 // old ephemeral generations that got promoted into max_generation.
21474 ptrdiff_t delta = 0;
21475 #ifdef SEG_MAPPING_TABLE
21476 heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
21477 #else //SEG_MAPPING_TABLE
21478 heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
21479 #endif //SEG_MAPPING_TABLE
21481 assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
21482 size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
21483 size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
21484 while (card != end_card)
21490 #endif //MULTIPLE_HEAPS
21492 alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
21493 //reset the allocated size
21494 BYTE* start = generation_allocation_start (youngest_generation);
21495 MAYBE_UNUSED_VAR(start);
21496 if (settings.promotion && !settings.demotion)
21497 assert ((start + Align (size (start))) ==
21498 heap_segment_plan_allocated(ephemeral_heap_segment));
21500 heap_segment_allocated(ephemeral_heap_segment)=
21501 heap_segment_plan_allocated(ephemeral_heap_segment);
21505 BYTE* gc_heap::generation_limit (int gen_number)
21507 if (settings.promotion)
21509 if (gen_number <= 1)
21510 return heap_segment_reserved (ephemeral_heap_segment);
21512 return generation_allocation_start (generation_of ((gen_number - 2)));
21516 if (gen_number <= 0)
21517 return heap_segment_reserved (ephemeral_heap_segment);
21519 return generation_allocation_start (generation_of ((gen_number - 1)));
21523 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
21525 BYTE* start = heap_segment_allocated (ephemeral_heap_segment);
21526 size_t size = Align (min_obj_size)*(condemned_gen_number+1);
21527 assert ((start + size) <=
21528 heap_segment_reserved (ephemeral_heap_segment));
21529 if ((start + size) >
21530 heap_segment_committed (ephemeral_heap_segment))
21532 if (!grow_heap_segment (ephemeral_heap_segment, start + size))
21541 BYTE* gc_heap::allocate_at_end (size_t size)
21543 BYTE* start = heap_segment_allocated (ephemeral_heap_segment);
21544 size = Align (size);
21545 BYTE* result = start;
21546 // only called to allocate a min obj so can't overflow here.
21547 assert ((start + size) <=
21548 heap_segment_reserved (ephemeral_heap_segment));
21549 //ensure_gap_allocation took care of it
21550 assert ((start + size) <=
21551 heap_segment_committed (ephemeral_heap_segment));
21552 heap_segment_allocated (ephemeral_heap_segment) += size;
21557 void gc_heap::make_free_lists (int condemned_gen_number)
21562 start = GetCycleCount32();
21565 //Promotion has to happen in sweep case.
21566 assert (settings.promotion);
21568 generation* condemned_gen = generation_of (condemned_gen_number);
21569 BYTE* start_address = generation_allocation_start (condemned_gen);
21571 size_t current_brick = brick_of (start_address);
21572 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
21574 PREFIX_ASSUME(current_heap_segment != NULL);
21576 BYTE* end_address = heap_segment_allocated (current_heap_segment);
21577 size_t end_brick = brick_of (end_address-1);
21578 make_free_args args;
21579 args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
21580 args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
21582 (generation_limit (args.free_list_gen_number)));
21583 args.free_list_gen = generation_of (args.free_list_gen_number);
21584 args.highest_plug = 0;
21586 if ((start_address < end_address) ||
21587 (condemned_gen_number == max_generation))
21591 if ((current_brick > end_brick))
21593 if (args.current_gen_limit == MAX_PTR)
21595 //We had an empty segment
21596 //need to allocate the generation start
21598 generation* gen = generation_of (max_generation);
21600 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21602 PREFIX_ASSUME(start_seg != NULL);
21604 BYTE* gap = heap_segment_mem (start_seg);
21606 generation_allocation_start (gen) = gap;
21607 heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
21608 make_unused_array (gap, Align (min_obj_size));
21609 reset_allocation_pointers (gen, gap);
21610 dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
21611 max_generation, (size_t)gap));
21612 args.current_gen_limit = generation_limit (args.free_list_gen_number);
21614 if (heap_segment_next_rw (current_heap_segment))
21616 current_heap_segment = heap_segment_next_rw (current_heap_segment);
21617 current_brick = brick_of (heap_segment_mem (current_heap_segment));
21618 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
21628 int brick_entry = brick_table [ current_brick ];
21629 if ((brick_entry >= 0))
21631 make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
21632 dprintf(3,("Fixing brick entry %Ix to %Ix",
21633 current_brick, (size_t)args.highest_plug));
21634 set_brick (current_brick,
21635 (args.highest_plug - brick_address (current_brick)));
21639 if ((brick_entry > -32768))
21643 ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
21644 if ((brick_entry != -32767) && (! ((offset == brick_entry))))
21646 assert ((brick_entry == -1));
21649 //init to -1 for faster find_first_object
21650 set_brick (current_brick, -1);
21658 int bottom_gen = 0;
21659 args.free_list_gen_number--;
21660 while (args.free_list_gen_number >= bottom_gen)
21663 generation* gen2 = generation_of (args.free_list_gen_number);
21664 gap = allocate_at_end (Align(min_obj_size));
21665 generation_allocation_start (gen2) = gap;
21666 reset_allocation_pointers (gen2, gap);
21667 dprintf(3,("Fixing generation start of %d to: %Ix",
21668 args.free_list_gen_number, (size_t)gap));
21669 PREFIX_ASSUME(gap != NULL);
21670 make_unused_array (gap, Align (min_obj_size));
21672 args.free_list_gen_number--;
21675 //reset the allocated size
21676 BYTE* start2 = generation_allocation_start (youngest_generation);
21677 alloc_allocated = start2 + Align (size (start2));
21681 finish = GetCycleCount32();
21682 sweep_time = finish - start;
21686 void gc_heap::make_free_list_in_brick (BYTE* tree, make_free_args* args)
21688 assert ((tree >= 0));
21690 int right_node = node_right_child (tree);
21691 int left_node = node_left_child (tree);
21692 args->highest_plug = 0;
21695 if (! (0 == left_node))
21697 make_free_list_in_brick (tree + left_node, args);
21702 size_t gap_size = node_gap_size (tree);
21703 BYTE* gap = (plug - gap_size);
21704 dprintf (3,("Making free list %Ix len %d in %d",
21705 //dprintf (3,("F: %Ix len %Ix in %d",
21706 (size_t)gap, gap_size, args->free_list_gen_number));
21707 args->highest_plug = tree;
21709 if (is_plug_padded (plug))
21711 dprintf (3, ("%Ix padded", plug));
21712 clear_plug_padded (plug);
21714 #endif //SHORT_PLUGS
21717 if ((args->current_gen_limit == MAX_PTR) ||
21718 ((plug >= args->current_gen_limit) &&
21719 ephemeral_pointer_p (plug)))
21721 dprintf(3,(" Crossing Generation boundary at %Ix",
21722 (size_t)args->current_gen_limit));
21723 if (!(args->current_gen_limit == MAX_PTR))
21725 args->free_list_gen_number--;
21726 args->free_list_gen = generation_of (args->free_list_gen_number);
21728 dprintf(3,( " Fixing generation start of %d to: %Ix",
21729 args->free_list_gen_number, (size_t)gap));
21731 reset_allocation_pointers (args->free_list_gen, gap);
21732 args->current_gen_limit = generation_limit (args->free_list_gen_number);
21734 if ((gap_size >= (2*Align (min_obj_size))))
21736 dprintf(3,(" Splitting the gap in two %Id left",
21738 make_unused_array (gap, Align(min_obj_size));
21739 gap_size = (gap_size - Align(min_obj_size));
21740 gap = (gap + Align(min_obj_size));
21744 make_unused_array (gap, gap_size);
21751 thread_gap (gap, gap_size, args->free_list_gen);
21752 add_gen_free (args->free_list_gen->gen_num, gap_size);
21754 if (! (0 == right_node))
21756 make_free_list_in_brick (tree + right_node, args);
21762 void gc_heap::thread_gap (BYTE* gap_start, size_t size, generation* gen)
21764 assert (generation_allocation_start (gen));
21767 if ((gen->gen_num == 0) && (size > CLR_SIZE))
21769 gen0_big_free_spaces += size;
21772 assert ((heap_segment_rw (generation_start_segment (gen))!=
21773 ephemeral_heap_segment) ||
21774 (gap_start > generation_allocation_start (gen)));
21775 // The beginning of a segment gap is not aligned
21776 assert (size >= Align (min_obj_size));
21777 make_unused_array (gap_start, size,
21778 (!settings.concurrent && (gen != youngest_generation)),
21779 (gen->gen_num == max_generation));
21780 dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
21782 if ((size >= min_free_list))
21784 generation_free_list_space (gen) += size;
21785 generation_allocator (gen)->thread_item (gap_start, size);
21789 generation_free_obj_space (gen) += size;
21794 void gc_heap::loh_thread_gap_front (BYTE* gap_start, size_t size, generation* gen)
21796 assert (generation_allocation_start (gen));
21797 if (size >= min_free_list)
21799 generation_free_list_space (gen) += size;
21800 generation_allocator (gen)->thread_item_front (gap_start, size);
21804 void gc_heap::make_unused_array (BYTE* x, size_t size, BOOL clearp, BOOL resetp)
21806 dprintf (3, ("Making unused array [%Ix, %Ix[",
21807 (size_t)x, (size_t)(x+size)));
21808 assert (size >= Align (min_obj_size));
21810 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
21811 // check_batch_mark_array_bits (x, x+size);
21812 //#endif //VERIFY_HEAP && BACKGROUND_GC
21815 reset_memory (x, size);
21817 ((CObjectHeader*)x)->SetFree(size);
21822 #error "This won't work on big endian platforms"
21825 size_t size_as_object = (UINT32)(size - free_object_base_size) + free_object_base_size;
21827 if (size_as_object < size)
21830 // If the size is more than 4GB, we need to create multiple objects because of
21831 // the Array::m_NumComponents is DWORD and the high 32 bits of unused array
21832 // size is ignored in regular object size computation.
21834 BYTE * tmp = x + size_as_object;
21835 size_t remaining_size = size - size_as_object;
21837 while (remaining_size > UINT32_MAX)
21839 // Make sure that there will be at least Align(min_obj_size) left
21840 size_t current_size = UINT32_MAX - get_alignment_constant (FALSE)
21841 - Align (min_obj_size, get_alignment_constant (FALSE));
21843 ((CObjectHeader*)tmp)->SetFree(current_size);
21845 remaining_size -= current_size;
21846 tmp += current_size;
21849 ((CObjectHeader*)tmp)->SetFree(remaining_size);
21854 clear_card_for_addresses (x, x + Align(size));
21857 // Clear memory set by make_unused_array.
21858 void gc_heap::clear_unused_array (BYTE* x, size_t size)
21860 // Also clear the sync block
21861 *(((PTR_PTR)x)-1) = 0;
21863 ((CObjectHeader*)x)->UnsetFree();
21868 #error "This won't work on big endian platforms"
21871 // The memory could have been cleared in the meantime. We have to mirror the algorithm
21872 // from make_unused_array since we cannot depend on the object sizes in memory.
21873 size_t size_as_object = (UINT32)(size - free_object_base_size) + free_object_base_size;
21875 if (size_as_object < size)
21877 BYTE * tmp = x + size_as_object;
21878 size_t remaining_size = size - size_as_object;
21880 while (remaining_size > UINT32_MAX)
21882 size_t current_size = UINT32_MAX - get_alignment_constant (FALSE)
21883 - Align (min_obj_size, get_alignment_constant (FALSE));
21885 ((CObjectHeader*)tmp)->UnsetFree();
21887 remaining_size -= current_size;
21888 tmp += current_size;
21891 ((CObjectHeader*)tmp)->UnsetFree();
21897 BYTE* tree_search (BYTE* tree, BYTE* old_address)
21899 BYTE* candidate = 0;
21903 if (tree < old_address)
21905 if ((cn = node_right_child (tree)) != 0)
21907 assert (candidate < tree);
21910 Prefetch (tree - 8);
21916 else if (tree > old_address)
21918 if ((cn = node_left_child (tree)) != 0)
21921 Prefetch (tree - 8);
21929 if (tree <= old_address)
21931 else if (candidate)
21937 void gc_heap::relocate_address (BYTE** pold_address THREAD_NUMBER_DCL)
21939 BYTE* old_address = *pold_address;
21940 if (!((old_address >= gc_low) && (old_address < gc_high)))
21941 #ifdef MULTIPLE_HEAPS
21943 if (old_address == 0)
21945 gc_heap* hp = heap_of (old_address);
21946 if ((hp == this) ||
21947 !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
21950 #else //MULTIPLE_HEAPS
21952 #endif //MULTIPLE_HEAPS
21953 // delta translates old_address into address_gc (old_address);
21954 size_t brick = brick_of (old_address);
21955 int brick_entry = brick_table [ brick ];
21956 BYTE* new_address = old_address;
21957 if (! ((brick_entry == 0)))
21961 while (brick_entry < 0)
21963 brick = (brick + brick_entry);
21964 brick_entry = brick_table [ brick ];
21966 BYTE* old_loc = old_address;
21968 BYTE* node = tree_search ((brick_address (brick) + brick_entry-1),
21970 if ((node <= old_loc))
21971 new_address = (old_address + node_relocation_distance (node));
21974 if (node_left_p (node))
21976 dprintf(3,(" L: %Ix", (size_t)node));
21977 new_address = (old_address +
21978 (node_relocation_distance (node) +
21979 node_gap_size (node)));
21984 brick_entry = brick_table [ brick ];
21990 *pold_address = new_address;
21994 #ifdef FEATURE_LOH_COMPACTION
21995 if (loh_compacted_p)
21997 *pold_address = old_address + loh_node_relocation_distance (old_address);
22000 #endif //FEATURE_LOH_COMPACTION
22002 *pold_address = new_address;
22007 gc_heap::check_class_object_demotion (BYTE* obj)
22009 #ifdef COLLECTIBLE_CLASS
22010 if (is_collectible(obj))
22012 check_class_object_demotion_internal (obj);
22014 #endif //COLLECTIBLE_CLASS
22017 #ifdef COLLECTIBLE_CLASS
22019 gc_heap::check_class_object_demotion_internal (BYTE* obj)
22021 if (settings.demotion)
22023 #ifdef MULTIPLE_HEAPS
22024 // We set the card without checking the demotion range 'cause at this point
22025 // the handle that points to the loader allocator object may or may not have
22026 // been relocated by other GC threads.
22027 set_card (card_of (obj));
22030 BYTE* class_obj = get_class_object (obj);
22031 dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
22032 BYTE* temp_class_obj = class_obj;
22033 BYTE** temp = &temp_class_obj;
22034 relocate_address (temp THREAD_NUMBER_ARG);
22036 check_demotion_helper (temp, obj);
22037 #endif //MULTIPLE_HEAPS
22041 #endif //COLLECTIBLE_CLASS
22044 gc_heap::check_demotion_helper (BYTE** pval, BYTE* parent_obj)
22046 // detect if we are demoting an object
22047 if ((*pval < demotion_high) &&
22048 (*pval >= demotion_low))
22050 dprintf(3, ("setting card %Ix:%Ix",
22051 card_of((BYTE*)pval),
22054 set_card (card_of (parent_obj));
22056 #ifdef MULTIPLE_HEAPS
22057 else if (settings.demotion)
22059 dprintf (4, ("Demotion active, computing heap_of object"));
22060 gc_heap* hp = heap_of (*pval);
22061 if ((*pval < hp->demotion_high) &&
22062 (*pval >= hp->demotion_low))
22064 dprintf(3, ("setting card %Ix:%Ix",
22065 card_of((BYTE*)pval),
22068 set_card (card_of (parent_obj));
22071 #endif //MULTIPLE_HEAPS
22075 gc_heap::reloc_survivor_helper (BYTE** pval)
22078 relocate_address (pval THREAD_NUMBER_ARG);
22080 check_demotion_helper (pval, (BYTE*)pval);
22084 gc_heap::relocate_obj_helper (BYTE* x, size_t s)
22087 if (contain_pointers (x))
22089 dprintf (3, ("$%Ix$", (size_t)x));
22091 go_through_object_nostart (method_table(x), x, s, pval,
22093 BYTE* child = *pval;
22094 reloc_survivor_helper (pval);
22097 dprintf (3, ("%Ix->%Ix->%Ix", (BYTE*)pval, child, *pval));
22102 check_class_object_demotion (x);
22106 void gc_heap::reloc_ref_in_shortened_obj (BYTE** address_to_set_card, BYTE** address_to_reloc)
22110 BYTE* old_val = (address_to_reloc ? *address_to_reloc : 0);
22111 relocate_address (address_to_reloc THREAD_NUMBER_ARG);
22112 if (address_to_reloc)
22114 dprintf (3, ("SR %Ix: %Ix->%Ix", (BYTE*)address_to_reloc, old_val, *address_to_reloc));
22117 //check_demotion_helper (current_saved_info_to_relocate, (BYTE*)pval);
22118 BYTE* relocated_addr = *address_to_reloc;
22119 if ((relocated_addr < demotion_high) &&
22120 (relocated_addr >= demotion_low))
22122 dprintf (3, ("set card for location %Ix(%Ix)",
22123 (size_t)address_to_set_card, card_of((BYTE*)address_to_set_card)));
22125 set_card (card_of ((BYTE*)address_to_set_card));
22127 #ifdef MULTIPLE_HEAPS
22128 else if (settings.demotion)
22130 gc_heap* hp = heap_of (relocated_addr);
22131 if ((relocated_addr < hp->demotion_high) &&
22132 (relocated_addr >= hp->demotion_low))
22134 dprintf (3, ("%Ix on h#d, set card for location %Ix(%Ix)",
22135 relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((BYTE*)address_to_set_card)));
22137 set_card (card_of ((BYTE*)address_to_set_card));
22140 #endif //MULTIPLE_HEAPS
22143 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
22146 BYTE* plug = pinned_plug (pinned_plug_entry);
22147 BYTE* pre_plug_start = plug - sizeof (plug_and_gap);
22148 // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
22149 // address. Consider this scenario:
22150 // gen1 start | 3-ptr sized NP | PP
22152 // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
22153 // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
22154 // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree).
22155 pre_plug_start += sizeof (BYTE*);
22156 BYTE** old_address = &pre_plug_start;
22158 BYTE* old_val = (old_address ? *old_address : 0);
22159 relocate_address (old_address THREAD_NUMBER_ARG);
22162 dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix",
22163 (BYTE*)old_address, old_val, *old_address, (pre_plug_start - sizeof (BYTE*))));
22166 pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (BYTE*));
22170 void gc_heap::relocate_shortened_obj_helper (BYTE* x, size_t s, BYTE* end, mark* pinned_plug_entry, BOOL is_pinned)
22173 BYTE* plug = pinned_plug (pinned_plug_entry);
22177 //// Temporary - we just wanna make sure we are doing things right when padding is needed.
22178 //if ((x + s) < plug)
22180 // dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix",
22181 // x, (x + s), (plug- (x + s)), plug));
22185 relocate_pre_plug_info (pinned_plug_entry);
22188 verify_pins_with_post_plug_info("after relocate_pre_plug_info");
22190 BYTE* saved_plug_info_start = 0;
22191 BYTE** saved_info_to_relocate = 0;
22195 saved_plug_info_start = (BYTE*)(pinned_plug_entry->get_post_plug_info_start());
22196 saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_post_plug_reloc_info());
22200 saved_plug_info_start = (plug - sizeof (plug_and_gap));
22201 saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_pre_plug_reloc_info());
22204 BYTE** current_saved_info_to_relocate = 0;
22207 dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
22209 if (contain_pointers (x))
22211 dprintf (3,("$%Ix$", (size_t)x));
22213 go_through_object_nostart (method_table(x), x, s, pval,
22215 dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (BYTE*)pval, *pval));
22217 if ((BYTE*)pval >= end)
22219 current_saved_info_to_relocate = saved_info_to_relocate + ((BYTE*)pval - saved_plug_info_start) / sizeof (BYTE**);
22220 child = *current_saved_info_to_relocate;
22221 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
22222 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
22223 (BYTE*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
22227 reloc_survivor_helper (pval);
22232 check_class_object_demotion (x);
22235 void gc_heap::relocate_survivor_helper (BYTE* plug, BYTE* plug_end)
22238 while (x < plug_end)
22240 size_t s = size (x);
22241 BYTE* next_obj = x + Align (s);
22242 Prefetch (next_obj);
22243 relocate_obj_helper (x, s);
22249 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
22250 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
22252 #if defined (_DEBUG) && defined (VERIFY_HEAP)
22253 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
22255 if (!verify_pinned_queue_p)
22258 if (settings.heap_expansion)
22261 for (size_t i = 0; i < mark_stack_tos; i++)
22263 mark& m = mark_stack_array[i];
22265 mark* pinned_plug_entry = pinned_plug_of(i);
22267 if (pinned_plug_entry->has_post_plug_info() &&
22268 pinned_plug_entry->post_short_p() &&
22269 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
22271 BYTE* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
22272 // object after pin
22273 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d",
22274 next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
22275 (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
22277 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
22279 if (node_gap_size (next_obj) != *post_plug_debug)
22281 dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix",
22282 next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
22286 // can't do node_relocation_distance here as it clears the left bit.
22287 //if (node_relocation_distance (next_obj) != *post_plug_debug)
22288 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
22290 dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix",
22291 next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
22294 if (node_left_child (next_obj) > 0)
22296 dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
22302 dprintf (3, ("%s verified", msg));
22304 #endif // _DEBUG && VERIFY_HEAP
22307 #ifdef COLLECTIBLE_CLASS
22308 // We don't want to burn another ptr size space for pinned plugs to record this so just
22309 // set the card unconditionally for collectible objects if we are demoting.
22311 gc_heap::unconditional_set_card_collectible (BYTE* obj)
22313 if (settings.demotion)
22315 set_card (card_of (obj));
22318 #endif //COLLECTIBLE_CLASS
22320 void gc_heap::relocate_shortened_survivor_helper (BYTE* plug, BYTE* plug_end, mark* pinned_plug_entry)
22323 BYTE* p_plug = pinned_plug (pinned_plug_entry);
22324 BOOL is_pinned = (plug == p_plug);
22325 BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
22327 plug_end += sizeof (gap_reloc_pair);
22329 //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
22330 dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
22332 verify_pins_with_post_plug_info("begin reloc short surv");
22334 while (x < plug_end)
22336 if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
22338 dprintf (3, ("last obj %Ix is short", x));
22342 #ifdef COLLECTIBLE_CLASS
22343 if (pinned_plug_entry->post_short_collectible_p())
22344 unconditional_set_card_collectible (x);
22345 #endif //COLLECTIBLE_CLASS
22347 // Relocate the saved references based on bits set.
22348 BYTE** saved_plug_info_start = (BYTE**)(pinned_plug_entry->get_post_plug_info_start());
22349 BYTE** saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_post_plug_reloc_info());
22350 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
22352 if (pinned_plug_entry->post_short_bit_p (i))
22354 reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
22360 #ifdef COLLECTIBLE_CLASS
22361 if (pinned_plug_entry->pre_short_collectible_p())
22362 unconditional_set_card_collectible (x);
22363 #endif //COLLECTIBLE_CLASS
22365 relocate_pre_plug_info (pinned_plug_entry);
22367 // Relocate the saved references based on bits set.
22368 BYTE** saved_plug_info_start = (BYTE**)(p_plug - sizeof (plug_and_gap));
22369 BYTE** saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_pre_plug_reloc_info());
22370 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
22372 if (pinned_plug_entry->pre_short_bit_p (i))
22374 reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
22382 size_t s = size (x);
22383 BYTE* next_obj = x + Align (s);
22384 Prefetch (next_obj);
22386 if (next_obj >= plug_end)
22388 dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix",
22389 next_obj, plug, plug_end));
22391 verify_pins_with_post_plug_info("before reloc short obj");
22393 relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
22397 relocate_obj_helper (x, s);
22404 verify_pins_with_post_plug_info("end reloc short surv");
22407 void gc_heap::relocate_survivors_in_plug (BYTE* plug, BYTE* plug_end,
22408 BOOL check_last_object_p,
22409 mark* pinned_plug_entry)
22411 //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
22412 dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
22414 if (check_last_object_p)
22416 relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
22420 relocate_survivor_helper (plug, plug_end);
22424 void gc_heap::relocate_survivors_in_brick (BYTE* tree, relocate_args* args)
22426 assert ((tree != 0));
22428 dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
22429 tree, args->last_plug,
22430 (tree + node_left_child (tree)),
22431 (tree + node_right_child (tree)),
22432 node_gap_size (tree)));
22434 if (node_left_child (tree))
22436 relocate_survivors_in_brick (tree + node_left_child (tree), args);
22440 BOOL has_post_plug_info_p = FALSE;
22441 BOOL has_pre_plug_info_p = FALSE;
22443 if (tree == oldest_pinned_plug)
22445 args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
22446 &has_post_plug_info_p);
22447 assert (tree == pinned_plug (args->pinned_plug_entry));
22449 dprintf (3, ("tree is the oldest pin: %Ix", tree));
22451 if (args->last_plug)
22453 size_t gap_size = node_gap_size (tree);
22454 BYTE* gap = (plug - gap_size);
22455 dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
22456 assert (gap_size >= Align (min_obj_size));
22457 BYTE* last_plug_end = gap;
22459 BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
22462 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
22467 assert (!has_pre_plug_info_p);
22470 args->last_plug = plug;
22471 args->is_shortened = has_post_plug_info_p;
22472 if (has_post_plug_info_p)
22474 dprintf (3, ("setting %Ix as shortened", plug));
22476 dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
22478 if (node_right_child (tree))
22480 relocate_survivors_in_brick (tree + node_right_child (tree), args);
22485 void gc_heap::update_oldest_pinned_plug()
22487 oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
22490 void gc_heap::relocate_survivors (int condemned_gen_number,
22491 BYTE* first_condemned_address)
22493 generation* condemned_gen = generation_of (condemned_gen_number);
22494 BYTE* start_address = first_condemned_address;
22495 size_t current_brick = brick_of (start_address);
22496 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
22498 PREFIX_ASSUME(current_heap_segment != NULL);
22500 BYTE* end_address = 0;
22502 reset_pinned_queue_bos();
22503 update_oldest_pinned_plug();
22505 end_address = heap_segment_allocated (current_heap_segment);
22507 size_t end_brick = brick_of (end_address - 1);
22508 relocate_args args;
22510 args.high = gc_high;
22511 args.is_shortened = FALSE;
22512 args.pinned_plug_entry = 0;
22513 args.last_plug = 0;
22516 if (current_brick > end_brick)
22518 if (args.last_plug)
22521 assert (!(args.is_shortened));
22522 relocate_survivors_in_plug (args.last_plug,
22523 heap_segment_allocated (current_heap_segment),
22525 args.pinned_plug_entry);
22528 args.last_plug = 0;
22531 if (heap_segment_next_rw (current_heap_segment))
22533 current_heap_segment = heap_segment_next_rw (current_heap_segment);
22534 current_brick = brick_of (heap_segment_mem (current_heap_segment));
22535 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
22544 int brick_entry = brick_table [ current_brick ];
22546 if (brick_entry >= 0)
22548 relocate_survivors_in_brick (brick_address (current_brick) +
22557 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
22558 void gc_heap::walk_plug (BYTE* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args, size_t profiling_context)
22560 if (check_last_object_p)
22562 size += sizeof (gap_reloc_pair);
22563 mark* entry = args->pinned_plug_entry;
22565 if (args->is_shortened)
22567 assert (entry->has_post_plug_info());
22568 entry->swap_post_plug_and_saved_for_profiler();
22572 assert (entry->has_pre_plug_info());
22573 entry->swap_pre_plug_and_saved_for_profiler();
22577 ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
22578 ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
22580 STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
22582 #ifdef FEATURE_EVENT_TRACE
22583 ETW::GCLog::MovedReference(plug,
22587 settings.compaction);
22590 if (check_last_object_p)
22592 mark* entry = args->pinned_plug_entry;
22594 if (args->is_shortened)
22596 entry->swap_post_plug_and_saved_for_profiler();
22600 entry->swap_pre_plug_and_saved_for_profiler();
22605 void gc_heap::walk_relocation_in_brick (BYTE* tree, walk_relocate_args* args, size_t profiling_context)
22607 assert ((tree != 0));
22608 if (node_left_child (tree))
22610 walk_relocation_in_brick (tree + node_left_child (tree), args, profiling_context);
22614 BOOL has_pre_plug_info_p = FALSE;
22615 BOOL has_post_plug_info_p = FALSE;
22617 if (tree == oldest_pinned_plug)
22619 args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
22620 &has_post_plug_info_p);
22621 assert (tree == pinned_plug (args->pinned_plug_entry));
22624 if (args->last_plug != 0)
22626 size_t gap_size = node_gap_size (tree);
22627 BYTE* gap = (plug - gap_size);
22628 BYTE* last_plug_end = gap;
22629 size_t last_plug_size = (last_plug_end - args->last_plug);
22630 dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix",
22631 tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
22633 BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
22634 if (!check_last_object_p)
22635 assert (last_plug_size >= Align (min_obj_size));
22637 walk_plug (args->last_plug, last_plug_size, check_last_object_p, args, profiling_context);
22641 assert (!has_pre_plug_info_p);
22644 dprintf (3, ("set args last plug to plug: %Ix", plug));
22645 args->last_plug = plug;
22646 args->is_shortened = has_post_plug_info_p;
22648 if (node_right_child (tree))
22650 walk_relocation_in_brick (tree + node_right_child (tree), args, profiling_context);
22655 void gc_heap::walk_relocation (int condemned_gen_number,
22656 BYTE* first_condemned_address,
22657 size_t profiling_context)
22660 generation* condemned_gen = generation_of (condemned_gen_number);
22661 BYTE* start_address = first_condemned_address;
22662 size_t current_brick = brick_of (start_address);
22663 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
22665 PREFIX_ASSUME(current_heap_segment != NULL);
22667 reset_pinned_queue_bos();
22668 update_oldest_pinned_plug();
22669 size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
22670 walk_relocate_args args;
22671 args.is_shortened = FALSE;
22672 args.pinned_plug_entry = 0;
22673 args.last_plug = 0;
22677 if (current_brick > end_brick)
22679 if (args.last_plug)
22681 walk_plug (args.last_plug,
22682 (heap_segment_allocated (current_heap_segment) - args.last_plug),
22684 &args, profiling_context);
22685 args.last_plug = 0;
22687 if (heap_segment_next_rw (current_heap_segment))
22689 current_heap_segment = heap_segment_next_rw (current_heap_segment);
22690 current_brick = brick_of (heap_segment_mem (current_heap_segment));
22691 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
22700 int brick_entry = brick_table [ current_brick ];
22701 if (brick_entry >= 0)
22703 walk_relocation_in_brick (brick_address (current_brick) +
22706 profiling_context);
22713 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
22714 void gc_heap::walk_relocation_for_bgc(size_t profiling_context)
22716 // This should only be called for BGCs
22717 assert(settings.concurrent);
22719 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
22721 BOOL small_object_segments = TRUE;
22722 int align_const = get_alignment_constant (small_object_segments);
22728 if (small_object_segments)
22730 //switch to large segment
22731 small_object_segments = FALSE;
22733 align_const = get_alignment_constant (small_object_segments);
22734 seg = heap_segment_rw (generation_start_segment (large_object_generation));
22736 PREFIX_ASSUME(seg != NULL);
22744 BYTE* o = heap_segment_mem (seg);
22745 BYTE* end = heap_segment_allocated (seg);
22750 if (method_table(o) == g_pFreeObjectMethodTable)
22752 o += Align (size (o), align_const);
22756 // It's survived. Make a fake plug, starting at o,
22757 // and send the event
22759 BYTE* plug_start = o;
22761 while (method_table(o) != g_pFreeObjectMethodTable)
22763 o += Align (size (o), align_const);
22770 BYTE* plug_end = o;
22772 // Note on last parameter: since this is for bgc, only ETW
22773 // should be sending these events so that existing profapi profilers
22774 // don't get confused.
22775 ETW::GCLog::MovedReference(
22778 0, // Reloc distance == 0 as this is non-compacting
22780 FALSE, // Non-compacting
22781 FALSE); // fAllowProfApiNotification
22784 seg = heap_segment_next (seg);
22788 void gc_heap::make_free_lists_for_profiler_for_bgc ()
22790 assert(settings.concurrent);
22792 size_t profiling_context = 0;
22793 ETW::GCLog::BeginMovedReferences(&profiling_context);
22795 // This provides the profiler with information on what blocks of
22796 // memory are moved during a gc.
22798 walk_relocation_for_bgc(profiling_context);
22800 // Notify the EE-side profiling code that all the references have been traced for
22801 // this heap, and that it needs to flush all cached data it hasn't sent to the
22802 // profiler and release resources it no longer needs. Since this is for bgc, only
22803 // ETW should be sending these events so that existing profapi profilers don't get confused.
22804 ETW::GCLog::EndMovedReferences(profiling_context, FALSE /* fAllowProfApiNotification */);
22806 #ifdef MULTIPLE_HEAPS
22807 bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
22808 if (bgc_t_join.joined())
22810 bgc_t_join.restart();
22812 #endif // MULTIPLE_HEAPS
22815 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
22816 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
22818 void gc_heap::relocate_phase (int condemned_gen_number,
22819 BYTE* first_condemned_address)
22822 sc.thread_number = heap_number;
22823 sc.promotion = FALSE;
22824 sc.concurrent = FALSE;
22830 start = GetCycleCount32();
22833 // %type% category = quote (relocate);
22834 dprintf (2,("---- Relocate phase -----"));
22836 #ifdef MULTIPLE_HEAPS
22837 //join all threads to make sure they are synchronized
22838 dprintf(3, ("Joining after end of plan"));
22839 gc_t_join.join(this, gc_join_begin_relocate_phase);
22840 if (gc_t_join.joined())
22841 #endif //MULTIPLE_HEAPS
22844 #ifdef MULTIPLE_HEAPS
22846 //join all threads to make sure they are synchronized
22847 dprintf(3, ("Restarting for relocation"));
22848 gc_t_join.restart();
22849 #endif //MULTIPLE_HEAPS
22852 dprintf(3,("Relocating roots"));
22853 CNameSpace::GcScanRoots(GCHeap::Relocate,
22854 condemned_gen_number, max_generation, &sc);
22856 verify_pins_with_post_plug_info("after reloc stack");
22858 #ifdef BACKGROUND_GC
22859 if (recursive_gc_sync::background_running_p())
22861 scan_background_roots (GCHeap::Relocate, heap_number, &sc);
22863 #endif //BACKGROUND_GC
22865 if (condemned_gen_number != max_generation)
22867 dprintf(3,("Relocating cross generation pointers"));
22868 mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
22869 verify_pins_with_post_plug_info("after reloc cards");
22871 if (condemned_gen_number != max_generation)
22873 dprintf(3,("Relocating cross generation pointers for large objects"));
22874 mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
22878 #ifdef FEATURE_LOH_COMPACTION
22879 if (loh_compacted_p)
22881 assert (settings.condemned_generation == max_generation);
22882 relocate_in_loh_compact();
22885 #endif //FEATURE_LOH_COMPACTION
22887 relocate_in_large_objects ();
22891 dprintf(3,("Relocating survivors"));
22892 relocate_survivors (condemned_gen_number,
22893 first_condemned_address);
22896 #ifdef FEATURE_PREMORTEM_FINALIZATION
22897 dprintf(3,("Relocating finalization data"));
22898 finalize_queue->RelocateFinalizationData (condemned_gen_number,
22900 #endif // FEATURE_PREMORTEM_FINALIZATION
22905 dprintf(3,("Relocating handle table"));
22906 CNameSpace::GcScanHandles(GCHeap::Relocate,
22907 condemned_gen_number, max_generation, &sc);
22910 #ifdef MULTIPLE_HEAPS
22911 //join all threads to make sure they are synchronized
22912 dprintf(3, ("Joining after end of relocation"));
22913 gc_t_join.join(this, gc_join_relocate_phase_done);
22915 #endif //MULTIPLE_HEAPS
22918 finish = GetCycleCount32();
22919 reloc_time = finish - start;
22922 dprintf(2,( "---- End of Relocate phase ----"));
22925 // This compares to see if tree is the current pinned plug and returns info
22926 // for this pinned plug. Also advances the pinned queue if that's the case.
22928 // We don't change the values of the plug info if tree is not the same as
22929 // the current pinned plug - the caller is responsible for setting the right
22930 // values to begin with.
22932 // POPO TODO: We are keeping this temporarily as this is also used by realloc
22933 // where it passes FALSE to deque_p, change it to use the same optimization
22934 // as relocate. Not as essential since realloc is already a slow path.
22935 mark* gc_heap::get_next_pinned_entry (BYTE* tree,
22936 BOOL* has_pre_plug_info_p,
22937 BOOL* has_post_plug_info_p,
22940 if (!pinned_plug_que_empty_p())
22942 mark* oldest_entry = oldest_pin();
22943 BYTE* oldest_plug = pinned_plug (oldest_entry);
22944 if (tree == oldest_plug)
22946 *has_pre_plug_info_p = oldest_entry->has_pre_plug_info();
22947 *has_post_plug_info_p = oldest_entry->has_post_plug_info();
22951 deque_pinned_plug();
22954 dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d",
22956 (*has_pre_plug_info_p ? 1 : 0),
22957 (*has_post_plug_info_p ? 1 : 0)));
22959 return oldest_entry;
22966 // This also deques the oldest entry and update the oldest plug
22967 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p,
22968 BOOL* has_post_plug_info_p)
22970 mark* oldest_entry = oldest_pin();
22971 *has_pre_plug_info_p = oldest_entry->has_pre_plug_info();
22972 *has_post_plug_info_p = oldest_entry->has_post_plug_info();
22974 deque_pinned_plug();
22975 update_oldest_pinned_plug();
22976 return oldest_entry;
22980 void gc_heap::copy_cards_range (BYTE* dest, BYTE* src, size_t len, BOOL copy_cards_p)
22983 copy_cards_for_addresses (dest, src, len);
22985 clear_card_for_addresses (dest, dest + len);
22988 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
22989 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
22990 // we won't need to individually recover each overwritten part of plugs.
22992 void gc_heap::gcmemcopy (BYTE* dest, BYTE* src, size_t len, BOOL copy_cards_p)
22996 #ifdef BACKGROUND_GC
22997 if (current_c_gc_state == c_gc_state_marking)
22999 //TODO: should look to see whether we should consider changing this
23000 // to copy a consecutive region of the mark array instead.
23001 copy_mark_bits_for_addresses (dest, src, len);
23003 #endif //BACKGROUND_GC
23004 //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
23005 dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
23006 memcopy (dest - plug_skew, src - plug_skew, (int)len);
23007 copy_cards_range (dest, src, len, copy_cards_p);
23011 void gc_heap::compact_plug (BYTE* plug, size_t size, BOOL check_last_object_p, compact_args* args)
23014 BYTE* reloc_plug = plug + args->last_plug_relocation;
23016 if (check_last_object_p)
23018 size += sizeof (gap_reloc_pair);
23019 mark* entry = args->pinned_plug_entry;
23021 if (args->is_shortened)
23023 assert (entry->has_post_plug_info());
23024 entry->swap_post_plug_and_saved();
23028 assert (entry->has_pre_plug_info());
23029 entry->swap_pre_plug_and_saved();
23033 int old_brick_entry = brick_table [brick_of (plug)];
23035 assert (node_relocation_distance (plug) == args->last_plug_relocation);
23037 #ifdef FEATURE_STRUCTALIGN
23038 ptrdiff_t alignpad = node_alignpad(plug);
23041 make_unused_array (reloc_plug - alignpad, alignpad);
23042 if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
23044 // The alignment padding is straddling one or more bricks;
23045 // it has to be the last "object" of its first brick.
23046 fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
23049 #else // FEATURE_STRUCTALIGN
23050 size_t unused_arr_size = 0;
23051 BOOL already_padded_p = FALSE;
23053 if (is_plug_padded (plug))
23055 already_padded_p = TRUE;
23056 clear_plug_padded (plug);
23057 unused_arr_size = Align (min_obj_size);
23059 #endif //SHORT_PLUGS
23060 if (node_realigned (plug))
23062 unused_arr_size += switch_alignment_size (already_padded_p);
23065 if (unused_arr_size != 0)
23067 make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
23069 if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
23071 dprintf (3, ("fix B for padding: %Id: %Ix->%Ix",
23072 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
23073 // The alignment padding is straddling one or more bricks;
23074 // it has to be the last "object" of its first brick.
23075 fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
23078 #endif // FEATURE_STRUCTALIGN
23081 if (is_plug_padded (plug))
23083 make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
23085 if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
23087 // The alignment padding is straddling one or more bricks;
23088 // it has to be the last "object" of its first brick.
23089 fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
23092 #endif //SHORT_PLUGS
23094 gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
23096 if (args->check_gennum_p)
23098 int src_gennum = args->src_gennum;
23099 if (src_gennum == -1)
23101 src_gennum = object_gennum (plug);
23104 int dest_gennum = object_gennum_plan (reloc_plug);
23106 if (src_gennum < dest_gennum)
23108 generation_allocation_size (generation_of (dest_gennum)) += size;
23112 size_t current_reloc_brick = args->current_compacted_brick;
23114 if (brick_of (reloc_plug) != current_reloc_brick)
23116 dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix",
23117 current_reloc_brick, brick_of (reloc_plug)));
23119 if (args->before_last_plug)
23121 dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
23122 current_reloc_brick,
23123 args->before_last_plug,
23124 (args->before_last_plug - brick_address (current_reloc_brick))));
23127 set_brick (current_reloc_brick,
23128 args->before_last_plug - brick_address (current_reloc_brick));
23131 current_reloc_brick = brick_of (reloc_plug);
23133 size_t end_brick = brick_of (reloc_plug + size-1);
23134 if (end_brick != current_reloc_brick)
23136 // The plug is straddling one or more bricks
23137 // It has to be the last plug of its first brick
23138 dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
23139 current_reloc_brick, (size_t)reloc_plug,
23140 (reloc_plug - brick_address (current_reloc_brick))));
23143 set_brick (current_reloc_brick,
23144 reloc_plug - brick_address (current_reloc_brick));
23146 // update all intervening brick
23147 size_t brick = current_reloc_brick + 1;
23148 dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
23149 brick, (end_brick - 1)));
23150 while (brick < end_brick)
23152 set_brick (brick, -1);
23155 // code last brick offset as a plug address
23156 args->before_last_plug = brick_address (end_brick) -1;
23157 current_reloc_brick = end_brick;
23158 dprintf (3, ("setting before last to %Ix, last brick to %Ix",
23159 args->before_last_plug, current_reloc_brick));
23163 dprintf (3, ("still in the same brick: %Ix", end_brick));
23164 args->before_last_plug = reloc_plug;
23166 args->current_compacted_brick = current_reloc_brick;
23168 if (check_last_object_p)
23170 mark* entry = args->pinned_plug_entry;
23172 if (args->is_shortened)
23174 entry->swap_post_plug_and_saved();
23178 entry->swap_pre_plug_and_saved();
23183 void gc_heap::compact_in_brick (BYTE* tree, compact_args* args)
23185 assert (tree >= 0);
23186 int left_node = node_left_child (tree);
23187 int right_node = node_right_child (tree);
23188 ptrdiff_t relocation = node_relocation_distance (tree);
23194 dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
23195 compact_in_brick ((tree + left_node), args);
23199 BOOL has_pre_plug_info_p = FALSE;
23200 BOOL has_post_plug_info_p = FALSE;
23202 if (tree == oldest_pinned_plug)
23204 args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
23205 &has_post_plug_info_p);
23206 assert (tree == pinned_plug (args->pinned_plug_entry));
23209 if (args->last_plug != 0)
23211 size_t gap_size = node_gap_size (tree);
23212 BYTE* gap = (plug - gap_size);
23213 BYTE* last_plug_end = gap;
23214 size_t last_plug_size = (last_plug_end - args->last_plug);
23215 dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix",
23216 tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
23218 BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
23219 if (!check_last_object_p)
23220 assert (last_plug_size >= Align (min_obj_size));
23222 compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
23226 assert (!has_pre_plug_info_p);
23229 dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
23230 args->last_plug = plug;
23231 args->last_plug_relocation = relocation;
23232 args->is_shortened = has_post_plug_info_p;
23236 dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
23237 compact_in_brick ((tree + right_node), args);
23241 void gc_heap::recover_saved_pinned_info()
23243 reset_pinned_queue_bos();
23245 while (!(pinned_plug_que_empty_p()))
23247 mark* oldest_entry = oldest_pin();
23248 oldest_entry->recover_plug_info();
23249 deque_pinned_plug();
23253 void gc_heap::compact_phase (int condemned_gen_number,
23254 BYTE* first_condemned_address,
23257 // %type% category = quote (compact);
23261 start = GetCycleCount32();
23263 generation* condemned_gen = generation_of (condemned_gen_number);
23264 BYTE* start_address = first_condemned_address;
23265 size_t current_brick = brick_of (start_address);
23266 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23268 PREFIX_ASSUME(current_heap_segment != NULL);
23270 reset_pinned_queue_bos();
23271 update_oldest_pinned_plug();
23273 BOOL reused_seg = FALSE;
23274 int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
23275 if ((heap_expand_mechanism == expand_reuse_bestfit) ||
23276 (heap_expand_mechanism == expand_reuse_normal))
23280 for (int i = 1; i <= max_generation; i++)
23282 generation_allocation_size (generation_of (i)) = 0;
23286 BYTE* end_address = heap_segment_allocated (current_heap_segment);
23288 size_t end_brick = brick_of (end_address-1);
23290 args.last_plug = 0;
23291 args.before_last_plug = 0;
23292 args.current_compacted_brick = ~((size_t)1);
23293 args.is_shortened = FALSE;
23294 args.pinned_plug_entry = 0;
23295 args.copy_cards_p = (condemned_gen_number >= 1) || !clear_cards;
23296 args.check_gennum_p = reused_seg;
23297 if (args.check_gennum_p)
23299 args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
23302 dprintf (2,("---- Compact Phase: %Ix(%Ix)----",
23303 first_condemned_address, brick_of (first_condemned_address)));
23305 #ifdef MULTIPLE_HEAPS
23307 if (gc_t_join.joined())
23309 #endif //MULTIPLE_HEAPS
23311 #ifdef MULTIPLE_HEAPS
23312 dprintf(3, ("Restarting for compaction"));
23313 gc_t_join.restart();
23315 #endif //MULTIPLE_HEAPS
23317 reset_pinned_queue_bos();
23319 #ifdef FEATURE_LOH_COMPACTION
23320 if (loh_compacted_p)
23324 #endif //FEATURE_LOH_COMPACTION
23326 if ((start_address < end_address) ||
23327 (condemned_gen_number == max_generation))
23331 if (current_brick > end_brick)
23333 if (args.last_plug != 0)
23335 dprintf (3, ("compacting last plug: %Ix", args.last_plug))
23336 compact_plug (args.last_plug,
23337 (heap_segment_allocated (current_heap_segment) - args.last_plug),
23342 if (heap_segment_next_rw (current_heap_segment))
23344 current_heap_segment = heap_segment_next_rw (current_heap_segment);
23345 current_brick = brick_of (heap_segment_mem (current_heap_segment));
23346 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23347 args.last_plug = 0;
23348 if (args.check_gennum_p)
23350 args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
23356 if (args.before_last_plug !=0)
23358 dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
23359 args.current_compacted_brick, (size_t)args.before_last_plug));
23360 assert (args.current_compacted_brick != ~1u);
23361 set_brick (args.current_compacted_brick,
23362 args.before_last_plug - brick_address (args.current_compacted_brick));
23368 int brick_entry = brick_table [ current_brick ];
23369 dprintf (3, ("B: %Ix(%Ix)->%Ix",
23370 current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
23372 if (brick_entry >= 0)
23374 compact_in_brick ((brick_address (current_brick) + brick_entry -1),
23383 recover_saved_pinned_info();
23386 finish = GetCycleCount32();
23387 compact_time = finish - start;
23390 concurrent_print_time_delta ("compact end");
23392 dprintf(2,("---- End of Compact phase ----"));
23395 #ifndef FEATURE_REDHAWK
23396 // This function is the filter function for the "__except" setup in the server and
23397 // concurrent gc thread base (gc_heap::gc_thread_stub()) in gc.cpp. If an
23398 // exception leaks out during GC, or from the implementation of gc_thread_function,
23399 // this filter will be invoked and we will kick in our unhandled exception processing
23400 // without relying on the OS UEF invocation mechanism.
23402 // Also, any exceptions that escape out of the GC thread are fatal. Thus, once
23403 // we do our unhandled exception processing, we shall failfast.
23404 inline LONG GCUnhandledExceptionFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID pv)
23406 WRAPPER_NO_CONTRACT;
23408 LONG result = CLRVectoredExceptionHandler(pExceptionPointers);
23409 if (result == EXCEPTION_CONTINUE_EXECUTION)
23411 // Since VEH has asked to continue execution, lets do that...
23415 if ((pExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) ||
23416 (pExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP))
23418 // We dont want to fail fast on debugger exceptions
23422 // VEH shouldnt be returning EXCEPTION_EXECUTE_HANDLER for a fault
23423 // in the GC thread!
23424 _ASSERTE(result != EXCEPTION_EXECUTE_HANDLER);
23426 // Exceptions in GC threads are fatal - invoke our unhandled exception
23428 result = InternalUnhandledExceptionFilter_Worker(pExceptionPointers);
23430 #ifdef FEATURE_UEF_CHAINMANAGER
23431 if (g_pUEFManager && (result == EXCEPTION_CONTINUE_SEARCH))
23433 // Since the "UEF" of this runtime instance didnt handle the exception,
23434 // invoke the other registered UEF callbacks as well
23435 result = g_pUEFManager->InvokeUEFCallbacks(pExceptionPointers);
23437 #endif // FEATURE_UEF_CHAINMANAGER
23439 // ...and then proceed to failfast.
23440 EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
23441 _ASSERTE(!"We shouldnt reach here incase of exceptions in GC threads!");
23443 // Returning this will ensure our filter handler gets executed so that
23444 // it can failfast the runtime.
23445 return EXCEPTION_EXECUTE_HANDLER;
23447 #endif // FEATURE_REDHAWK
23449 #ifdef MULTIPLE_HEAPS
23452 #pragma warning(push)
23453 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
23455 DWORD __stdcall gc_heap::gc_thread_stub (void* arg)
23457 ClrFlsSetThreadType (ThreadType_GC);
23458 STRESS_LOG_RESERVE_MEM (GC_STRESSLOG_MULTIPLY);
23460 // We commit the thread's entire stack to ensure we're robust in low memory conditions.
23461 BOOL fSuccess = Thread::CommitThreadStack(NULL);
23465 #ifdef BACKGROUND_GC
23466 // For background GC we revert to doing a blocking GC.
23469 STRESS_LOG0(LF_GC, LL_ALWAYS, "Thread::CommitThreadStack failed.");
23470 _ASSERTE(!"Thread::CommitThreadStack failed.");
23471 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_STACKOVERFLOW);
23472 #endif //BACKGROUND_GC
23475 #ifndef NO_CATCH_HANDLERS
23478 #endif // NO_CATCH_HANDLERS
23479 gc_heap* heap = (gc_heap*)arg;
23480 _alloca (256*heap->heap_number);
23481 return heap->gc_thread_function();
23483 #ifndef NO_CATCH_HANDLERS
23485 PAL_EXCEPT_FILTER(GCUnhandledExceptionFilter, NULL)
23487 ASSERTE(!"Exception caught escaping out of the GC thread!");
23488 EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
23491 #endif // NO_CATCH_HANDLERS
23494 #pragma warning(pop)
23497 #endif //MULTIPLE_HEAPS
23499 #ifdef BACKGROUND_GC
23502 #pragma warning(push)
23503 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
23505 DWORD __stdcall gc_heap::bgc_thread_stub (void* arg)
23507 gc_heap* heap = (gc_heap*)arg;
23509 // TODO: need to check if we are still fine for these APIs:
23510 // Thread::BeginThreadAffinity
23511 // Thread::EndThreadAffinity()
23512 // since now GC threads can be managed threads.
23513 ClrFlsSetThreadType (ThreadType_GC);
23514 assert (heap->bgc_thread != NULL);
23515 heap->bgc_thread->SetGCSpecial(true);
23516 STRESS_LOG_RESERVE_MEM (GC_STRESSLOG_MULTIPLY);
23518 // We commit the thread's entire stack to ensure we're robust in low memory conditions.
23520 BOOL fSuccess = Thread::CommitThreadStack();
23524 // For background GC we revert to doing a blocking GC.
23529 #ifndef NO_CATCH_HANDLERS
23532 #endif // NO_CATCH_HANDLERS
23533 return heap->bgc_thread_function();
23535 #ifndef NO_CATCH_HANDLERS
23537 PAL_EXCEPT_FILTER(GCUnhandledExceptionFilter, NULL)
23539 ASSERTE(!"Exception caught escaping out of the GC thread!");
23540 EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
23543 #endif // NO_CATCH_HANDLERS
23546 #pragma warning(pop)
23549 #endif //BACKGROUND_GC
23551 /*------------------ Background GC ----------------------------*/
23553 #ifdef BACKGROUND_GC
23555 void gc_heap::background_drain_mark_list (int thread)
23557 size_t saved_c_mark_list_index = c_mark_list_index;
23559 if (saved_c_mark_list_index)
23561 concurrent_print_time_delta ("SML");
23563 while (c_mark_list_index != 0)
23565 size_t current_index = c_mark_list_index - 1;
23566 BYTE* o = c_mark_list [current_index];
23567 background_mark_object (o THREAD_NUMBER_ARG);
23568 c_mark_list_index--;
23570 if (saved_c_mark_list_index)
23573 concurrent_print_time_delta ("EML");
23576 fire_drain_mark_list_event (saved_c_mark_list_index);
23580 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
23581 #ifdef MULTIPLE_HEAPS
23582 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
23583 // them. So we can use the same static variables.
23584 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
23586 // Whenever we call this method there may have been preceding object promotions. So set
23587 // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
23588 // based on the how the scanning proceeded).
23589 s_fUnscannedPromotions = TRUE;
23591 // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
23592 // the state of this thread's portion of the dependent handle table. That's because promotions on other
23593 // threads could cause handle promotions to become necessary here. Even if there are definitely no more
23594 // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
23595 // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
23596 // as all the others or they'll get out of step).
23599 // The various worker threads are all currently racing in this code. We need to work out if at least
23600 // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
23601 // dependent handle table when both of the following conditions apply:
23602 // 1) At least one (arbitrary) object might have been promoted since the last scan (because if this
23603 // object happens to correspond to a primary in one of our handles we might potentially have to
23604 // promote the associated secondary).
23605 // 2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
23607 // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
23608 // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
23609 // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
23610 // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
23611 // follows below. Note that we can't read this outside of the join since on any iteration apart from
23612 // the first threads will be racing between reading this value and completing their previous
23613 // iteration's table scan.
23615 // The second condition is tracked by the dependent handle code itself on a per worker thread basis
23616 // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
23617 // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
23618 // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
23619 // we're safely joined.
23620 if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
23621 s_fUnpromotedHandles = TRUE;
23623 // Synchronize all the threads so we can read our state variables safely. The following shared
23624 // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
23625 // single thread inside the join.
23626 bgc_t_join.join(this, gc_join_scan_dependent_handles);
23627 if (bgc_t_join.joined())
23629 // We're synchronized so it's safe to read our shared state variables. We update another shared
23630 // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
23631 // the loop. We scan if there has been at least one object promotion since last time and at least
23632 // one thread has a dependent handle table with a potential handle promotion possible.
23633 s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
23635 // Reset our shared state variables (ready to be set again on this scan or with a good initial
23636 // value for the next call if we're terminating the loop).
23637 s_fUnscannedPromotions = FALSE;
23638 s_fUnpromotedHandles = FALSE;
23640 if (!s_fScanRequired)
23642 BYTE* all_heaps_max = 0;
23643 BYTE* all_heaps_min = MAX_PTR;
23645 for (i = 0; i < n_heaps; i++)
23647 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
23648 all_heaps_max = g_heaps[i]->background_max_overflow_address;
23649 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
23650 all_heaps_min = g_heaps[i]->background_min_overflow_address;
23652 for (i = 0; i < n_heaps; i++)
23654 g_heaps[i]->background_max_overflow_address = all_heaps_max;
23655 g_heaps[i]->background_min_overflow_address = all_heaps_min;
23659 // Restart all the workers.
23660 dprintf(2, ("Starting all gc thread mark stack overflow processing"));
23661 bgc_t_join.restart();
23664 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
23665 // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
23666 // global flag indicating that at least one object promotion may have occurred (the usual comment
23667 // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
23668 // exit the method since we unconditionally set this variable on method entry anyway).
23669 if (background_process_mark_overflow (sc->concurrent))
23670 s_fUnscannedPromotions = TRUE;
23672 // If we decided that no scan was required we can terminate the loop now.
23673 if (!s_fScanRequired)
23676 // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
23677 // processed before we start scanning dependent handle tables (if overflows remain while we scan we
23678 // could miss noting the promotion of some primary objects).
23679 bgc_t_join.join(this, gc_join_rescan_dependent_handles);
23680 if (bgc_t_join.joined())
23682 // Restart all the workers.
23683 dprintf(3, ("Starting all gc thread for dependent handle promotion"));
23684 bgc_t_join.restart();
23687 // If the portion of the dependent handle table managed by this worker has handles that could still be
23688 // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
23689 // could require a rescan of handles on this or other workers.
23690 if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
23691 if (CNameSpace::GcDhReScan(sc))
23692 s_fUnscannedPromotions = TRUE;
23696 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
23698 // Whenever we call this method there may have been preceding object promotions. So set
23699 // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
23700 // based on the how the scanning proceeded).
23701 bool fUnscannedPromotions = true;
23703 // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
23704 // scan without performing any new promotions.
23705 while (CNameSpace::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
23707 // On each iteration of the loop start with the assumption that no further objects have been promoted.
23708 fUnscannedPromotions = false;
23710 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
23711 // being visible. If there was an overflow (background_process_mark_overflow returned true) then
23712 // additional objects now appear to be promoted and we should set the flag.
23713 if (background_process_mark_overflow (sc->concurrent))
23714 fUnscannedPromotions = true;
23716 // Perform the scan and set the flag if any promotions resulted.
23717 if (CNameSpace::GcDhReScan (sc))
23718 fUnscannedPromotions = true;
23721 // Perform a last processing of any overflowed mark stack.
23722 background_process_mark_overflow (sc->concurrent);
23724 #endif //MULTIPLE_HEAPS
23726 void gc_heap::recover_bgc_settings()
23728 if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
23730 dprintf (2, ("restoring bgc settings"));
23731 settings = saved_bgc_settings;
23732 GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
23737 void gc_heap::save_bgc_data_per_heap()
23739 if (!bgc_data_saved_p)
23741 memset (&saved_bgc_data_per_heap, 0, sizeof (saved_bgc_data_per_heap));
23742 memcpy (&saved_bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
23743 bgc_data_saved_p = TRUE;
23747 void gc_heap::allow_fgc()
23749 assert (bgc_thread == GetThread());
23751 if (bgc_thread->PreemptiveGCDisabled() && bgc_thread->CatchAtSafePoint())
23753 bgc_thread->EnablePreemptiveGC();
23754 bgc_thread->DisablePreemptiveGC();
23758 BOOL gc_heap::should_commit_mark_array()
23760 return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
23763 void gc_heap::clear_commit_flag()
23765 generation* gen = generation_of (max_generation);
23766 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
23771 if (gen != large_object_generation)
23773 gen = large_object_generation;
23774 seg = heap_segment_in_range (generation_start_segment (gen));
23782 if (seg->flags & heap_segment_flags_ma_committed)
23784 seg->flags &= ~heap_segment_flags_ma_committed;
23787 if (seg->flags & heap_segment_flags_ma_pcommitted)
23789 seg->flags &= ~heap_segment_flags_ma_pcommitted;
23792 seg = heap_segment_next (seg);
23796 void gc_heap::clear_commit_flag_global()
23798 #ifdef MULTIPLE_HEAPS
23799 for (int i = 0; i < n_heaps; i++)
23801 g_heaps[i]->clear_commit_flag();
23804 clear_commit_flag();
23805 #endif //MULTIPLE_HEAPS
23808 void gc_heap::verify_mark_array_cleared (BYTE* begin, BYTE* end, DWORD* mark_array_addr)
23811 size_t markw = mark_word_of (begin);
23812 size_t markw_end = mark_word_of (end);
23814 while (markw < markw_end)
23816 if (mark_array_addr[markw])
23818 dprintf (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
23819 markw, mark_array_addr[markw], mark_word_address (markw)));
23827 void gc_heap::verify_mark_array_cleared (heap_segment* seg, DWORD* mark_array_addr)
23829 verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
23832 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp,
23834 BYTE* new_lowest_address)
23836 BYTE* start = (BYTE*)seg;
23837 BYTE* end = heap_segment_reserved (seg);
23839 BYTE* lowest = hp->background_saved_lowest_address;
23840 BYTE* highest = hp->background_saved_highest_address;
23842 BYTE* commit_start = NULL;
23843 BYTE* commit_end = NULL;
23844 size_t commit_flag = 0;
23846 if ((highest >= start) &&
23849 if ((start >= lowest) && (end <= highest))
23851 dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
23852 start, end, lowest, highest));
23853 commit_flag = heap_segment_flags_ma_committed;
23857 dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
23858 start, end, lowest, highest));
23859 commit_flag = heap_segment_flags_ma_pcommitted;
23862 commit_start = max (lowest, start);
23863 commit_end = min (highest, end);
23865 if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
23870 if (hp->card_table != g_card_table)
23872 if (new_lowest_address == 0)
23874 new_lowest_address = g_lowest_address;
23877 DWORD* ct = &g_card_table[card_word (gcard_of (new_lowest_address))];
23878 DWORD* ma = (DWORD*)((BYTE*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
23880 dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix",
23881 hp->card_table, g_card_table,
23882 hp->mark_array, ma));
23884 if (!commit_mark_array_by_range (commit_start, commit_end, ma))
23890 seg->flags |= commit_flag;
23896 BOOL gc_heap::commit_mark_array_by_range (BYTE* begin, BYTE* end, DWORD* mark_array_addr)
23898 size_t beg_word = mark_word_of (begin);
23899 size_t end_word = mark_word_of (align_on_mark_word (end));
23900 BYTE* commit_start = align_lower_page ((BYTE*)&mark_array_addr[beg_word]);
23901 BYTE* commit_end = align_on_page ((BYTE*)&mark_array_addr[end_word]);
23902 size_t size = (size_t)(commit_end - commit_start);
23904 #ifdef SIMPLE_DPRINTF
23905 dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
23907 beg_word, end_word,
23908 (end_word - beg_word) * sizeof (DWORD),
23909 &mark_array_addr[beg_word],
23910 &mark_array_addr[end_word],
23911 (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
23912 commit_start, commit_end,
23914 #endif //SIMPLE_DPRINTF
23916 if (VirtualAlloc (commit_start, size, MEM_COMMIT, PAGE_READWRITE))
23918 // We can only verify the mark array is cleared from begin to end, the first and the last
23919 // page aren't necessarily all cleared 'cause they could be used by other segments or
23921 verify_mark_array_cleared (begin, end, mark_array_addr);
23926 dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (DWORD)));
23931 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, DWORD* new_mark_array_addr)
23933 BYTE* start = (BYTE*)seg;
23934 BYTE* end = heap_segment_reserved (seg);
23936 #ifdef MULTIPLE_HEAPS
23937 BYTE* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
23938 BYTE* highest = heap_segment_heap (seg)->background_saved_highest_address;
23940 BYTE* lowest = background_saved_lowest_address;
23941 BYTE* highest = background_saved_highest_address;
23942 #endif //MULTIPLE_HEAPS
23944 if ((highest >= start) &&
23947 start = max (lowest, start);
23948 end = min (highest, end);
23949 if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
23958 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, DWORD* mark_array_addr)
23960 dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
23962 heap_segment_reserved (seg),
23964 return commit_mark_array_by_range ((BYTE*)seg, heap_segment_reserved (seg), mark_array_addr);
23967 BOOL gc_heap::commit_mark_array_bgc_init (DWORD* mark_array_addr)
23969 dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix",
23970 lowest_address, highest_address, mark_array));
23972 generation* gen = generation_of (max_generation);
23973 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
23978 if (gen != large_object_generation)
23980 gen = large_object_generation;
23981 seg = heap_segment_in_range (generation_start_segment (gen));
23989 dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
23991 if (!(seg->flags & heap_segment_flags_ma_committed))
23993 // For ro segments they could always be only partially in range so we'd
23994 // be calling this at the beginning of every BGC. We are not making this
23995 // more efficient right now - ro segments are currently only used by redhawk.
23996 if (heap_segment_read_only_p (seg))
23998 if ((heap_segment_mem (seg) >= lowest_address) &&
23999 (heap_segment_reserved (seg) <= highest_address))
24001 if (commit_mark_array_by_seg (seg, mark_array))
24003 seg->flags |= heap_segment_flags_ma_committed;
24012 BYTE* start = max (lowest_address, (BYTE*)seg);
24013 BYTE* end = min (highest_address, heap_segment_reserved (seg));
24014 if (commit_mark_array_by_range (start, end, mark_array))
24016 seg->flags |= heap_segment_flags_ma_pcommitted;
24026 // For normal segments they are by design completely in range so just
24027 // commit the whole mark array for each seg.
24028 if (commit_mark_array_by_seg (seg, mark_array))
24030 if (seg->flags & heap_segment_flags_ma_pcommitted)
24032 seg->flags &= ~heap_segment_flags_ma_pcommitted;
24034 seg->flags |= heap_segment_flags_ma_committed;
24043 seg = heap_segment_next (seg);
24049 // This function doesn't check the commit flag since it's for a new array -
24050 // the mark_array flag for these segments will remain the same.
24051 BOOL gc_heap::commit_new_mark_array (DWORD* new_mark_array_addr)
24053 dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
24054 generation* gen = generation_of (max_generation);
24055 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
24060 if (gen != large_object_generation)
24062 gen = large_object_generation;
24063 seg = heap_segment_in_range (generation_start_segment (gen));
24071 if (!commit_mark_array_with_check (seg, new_mark_array_addr))
24076 seg = heap_segment_next (seg);
24079 #ifdef MULTIPLE_HEAPS
24080 if (new_heap_segment)
24082 if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
24087 #endif //MULTIPLE_HEAPS
24092 BOOL gc_heap::commit_new_mark_array_global (DWORD* new_mark_array)
24094 #ifdef MULTIPLE_HEAPS
24095 for (int i = 0; i < n_heaps; i++)
24097 if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
24103 if (!commit_new_mark_array (new_mark_array))
24107 #endif //MULTIPLE_HEAPS
24112 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
24114 // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
24115 // been set to NULL.
24116 if (mark_array == NULL)
24121 dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
24123 size_t flags = seg->flags;
24125 if ((flags & heap_segment_flags_ma_committed) ||
24126 (flags & heap_segment_flags_ma_pcommitted))
24128 BYTE* start = (BYTE*)seg;
24129 BYTE* end = heap_segment_reserved (seg);
24131 if (flags & heap_segment_flags_ma_pcommitted)
24133 start = max (lowest_address, start);
24134 end = min (highest_address, end);
24137 size_t beg_word = mark_word_of (start);
24138 size_t end_word = mark_word_of (align_on_mark_word (end));
24139 BYTE* decommit_start = align_on_page ((BYTE*)&mark_array[beg_word]);
24140 BYTE* decommit_end = align_lower_page ((BYTE*)&mark_array[end_word]);
24141 size_t size = (size_t)(decommit_end - decommit_start);
24143 #ifdef SIMPLE_DPRINTF
24144 dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
24146 beg_word, end_word,
24147 (end_word - beg_word) * sizeof (DWORD),
24148 &mark_array[beg_word],
24149 &mark_array[end_word],
24150 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
24151 decommit_start, decommit_end,
24153 #endif //SIMPLE_DPRINTF
24155 if (decommit_start < decommit_end)
24157 if (!VirtualFree (decommit_start, size, MEM_DECOMMIT))
24159 dprintf (GC_TABLE_LOG, ("VirtualFree on %Ix for %Id bytes failed: %d",
24160 decommit_start, size, GetLastError()));
24161 assert (!"decommit failed");
24165 dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
24169 void gc_heap::background_mark_phase ()
24171 verify_mark_array_cleared();
24174 sc.thread_number = heap_number;
24175 sc.promotion = TRUE;
24176 sc.concurrent = FALSE;
24179 Thread* current_thread = GetThread();
24180 BOOL cooperative_mode = TRUE;
24181 #ifndef MULTIPLE_HEAPS
24182 const int thread = heap_number;
24183 #endif //!MULTIPLE_HEAPS
24185 dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
24187 assert (settings.concurrent);
24192 start = GetCycleCount32();
24195 #ifdef FFIND_OBJECT
24196 if (gen0_must_clear_bricks > 0)
24197 gen0_must_clear_bricks--;
24198 #endif //FFIND_OBJECT
24200 background_soh_alloc_count = 0;
24201 background_loh_alloc_count = 0;
24202 bgc_overflow_count = 0;
24204 bpromoted_bytes (heap_number) = 0;
24205 static DWORD num_sizedrefs = 0;
24207 background_min_overflow_address = MAX_PTR;
24208 background_max_overflow_address = 0;
24209 background_min_soh_overflow_address = MAX_PTR;
24210 background_max_soh_overflow_address = 0;
24211 processed_soh_overflow_p = FALSE;
24214 //set up the mark lists from g_mark_list
24215 assert (g_mark_list);
24216 mark_list = g_mark_list;
24217 //dont use the mark list for full gc
24218 //because multiple segments are more complex to handle and the list
24219 //is likely to overflow
24220 mark_list_end = &mark_list [0];
24221 mark_list_index = &mark_list [0];
24223 c_mark_list_index = 0;
24228 generation* gen = generation_of (max_generation);
24230 dprintf(3,("BGC: stack marking"));
24231 sc.concurrent = TRUE;
24233 CNameSpace::GcScanRoots(background_promote_callback,
24234 max_generation, max_generation,
24239 dprintf(3,("BGC: finalization marking"));
24240 finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
24243 size_t total_loh_size = generation_size (max_generation + 1);
24244 bgc_begin_loh_size = total_loh_size;
24245 bgc_alloc_spin_loh = 0;
24246 bgc_loh_size_increased = 0;
24247 bgc_loh_allocated_in_free = 0;
24248 size_t total_soh_size = generation_sizes (generation_of (max_generation));
24250 dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
24253 //concurrent_print_time_delta ("copying stack roots");
24254 concurrent_print_time_delta ("CS");
24256 fire_bgc_event (BGC1stNonConEnd);
24258 expanded_in_fgc = FALSE;
24259 saved_overflow_ephemeral_seg = 0;
24260 current_bgc_state = bgc_reset_ww;
24262 // we don't need a join here - just whichever thread that gets here
24263 // first can change the states and call restart_vm.
24264 // this is not true - we can't let the EE run when we are scanning stack.
24265 // since we now allow reset ww to run concurrently and have a join for it,
24266 // we can do restart ee on the 1st thread that got here. Make sure we handle the
24267 // sizedref handles correctly.
24268 #ifdef MULTIPLE_HEAPS
24269 bgc_t_join.join(this, gc_join_restart_ee);
24270 if (bgc_t_join.joined())
24271 #endif //MULTIPLE_HEAPS
24273 num_sizedrefs = SystemDomain::System()->GetTotalNumSizedRefHandles();
24275 // this c_write is not really necessary because restart_vm
24276 // has an instruction that will flush the cpu cache (interlocked
24277 // or whatever) but we don't want to rely on that.
24278 dprintf (BGC_LOG, ("setting cm_in_progress"));
24279 c_write (cm_in_progress, TRUE);
24281 //restart all thread, doing the marking from the array
24282 assert (dont_restart_ee_p);
24283 dont_restart_ee_p = FALSE;
24286 __SwitchToThread (0, CALLER_LIMITS_SPINNING);
24287 #ifdef MULTIPLE_HEAPS
24288 dprintf(3, ("Starting all gc threads for gc"));
24289 bgc_t_join.restart();
24290 #endif //MULTIPLE_HEAPS
24293 #ifdef MULTIPLE_HEAPS
24294 bgc_t_join.join(this, gc_join_after_reset);
24295 if (bgc_t_join.joined())
24296 #endif //MULTIPLE_HEAPS
24298 disable_preemptive (current_thread, TRUE);
24301 concurrent_print_time_delta ("CRWW begin");
24303 #ifdef MULTIPLE_HEAPS
24305 for (i = 0; i < n_heaps; i++)
24307 g_heaps[i]->reset_write_watch (TRUE);
24310 reset_write_watch (TRUE);
24311 #endif //MULTIPLE_HEAPS
24313 concurrent_print_time_delta ("CRWW");
24314 #endif //WRITE_WATCH
24316 #ifdef MULTIPLE_HEAPS
24317 for (i = 0; i < n_heaps; i++)
24319 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
24322 revisit_written_pages (TRUE, TRUE);
24323 #endif //MULTIPLE_HEAPS
24325 concurrent_print_time_delta ("CRW");
24327 #ifdef MULTIPLE_HEAPS
24328 for (i = 0; i < n_heaps; i++)
24330 g_heaps[i]->current_bgc_state = bgc_mark_handles;
24333 current_bgc_state = bgc_mark_handles;
24334 #endif //MULTIPLE_HEAPS
24336 current_c_gc_state = c_gc_state_marking;
24338 enable_preemptive (current_thread);
24340 #ifdef MULTIPLE_HEAPS
24341 dprintf(3, ("Joining BGC threads after resetting writewatch"));
24342 bgc_t_join.restart();
24343 #endif //MULTIPLE_HEAPS
24346 disable_preemptive (current_thread, TRUE);
24348 if (num_sizedrefs > 0)
24350 CNameSpace::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
24352 enable_preemptive (current_thread);
24354 #ifdef MULTIPLE_HEAPS
24355 bgc_t_join.join(this, gc_join_scan_sizedref_done);
24356 if (bgc_t_join.joined())
24358 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
24359 bgc_t_join.restart();
24361 #endif //MULTIPLE_HEAPS
24363 disable_preemptive (current_thread, TRUE);
24366 dprintf (3,("BGC: handle table marking"));
24367 CNameSpace::GcScanHandles(background_promote,
24368 max_generation, max_generation,
24370 //concurrent_print_time_delta ("concurrent marking handle table");
24371 concurrent_print_time_delta ("CRH");
24373 current_bgc_state = bgc_mark_stack;
24374 dprintf (2,("concurrent draining mark list"));
24375 background_drain_mark_list (thread);
24376 //concurrent_print_time_delta ("concurrent marking stack roots");
24377 concurrent_print_time_delta ("CRS");
24379 dprintf (2,("concurrent revisiting dirtied pages"));
24380 revisit_written_pages (TRUE);
24381 revisit_written_pages (TRUE);
24382 //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
24383 concurrent_print_time_delta ("CRre");
24385 enable_preemptive (current_thread);
24387 #ifdef MULTIPLE_HEAPS
24388 bgc_t_join.join(this, gc_join_concurrent_overflow);
24389 if (bgc_t_join.joined())
24391 BYTE* all_heaps_max = 0;
24392 BYTE* all_heaps_min = MAX_PTR;
24394 for (i = 0; i < n_heaps; i++)
24396 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix",
24398 g_heaps[i]->background_max_overflow_address,
24399 g_heaps[i]->background_min_overflow_address));
24400 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
24401 all_heaps_max = g_heaps[i]->background_max_overflow_address;
24402 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
24403 all_heaps_min = g_heaps[i]->background_min_overflow_address;
24405 for (i = 0; i < n_heaps; i++)
24407 g_heaps[i]->background_max_overflow_address = all_heaps_max;
24408 g_heaps[i]->background_min_overflow_address = all_heaps_min;
24410 dprintf(3, ("Starting all bgc threads after updating the overflow info"));
24411 bgc_t_join.restart();
24413 #endif //MULTIPLE_HEAPS
24415 disable_preemptive (current_thread, TRUE);
24417 dprintf (2, ("before CRov count: %d", bgc_overflow_count));
24418 bgc_overflow_count = 0;
24419 background_process_mark_overflow (TRUE);
24420 dprintf (2, ("after CRov count: %d", bgc_overflow_count));
24421 bgc_overflow_count = 0;
24422 //concurrent_print_time_delta ("concurrent processing mark overflow");
24423 concurrent_print_time_delta ("CRov");
24425 // Stop all threads, crawl all stacks and revisit changed pages.
24426 fire_bgc_event (BGC1stConEnd);
24428 dprintf (2, ("Stopping the EE"));
24430 enable_preemptive (current_thread);
24432 #ifdef MULTIPLE_HEAPS
24433 bgc_t_join.join(this, gc_join_suspend_ee);
24434 if (bgc_t_join.joined())
24436 bgc_threads_sync_event.Reset();
24438 dprintf(3, ("Joining BGC threads for non concurrent final marking"));
24439 bgc_t_join.restart();
24441 #endif //MULTIPLE_HEAPS
24443 if (heap_number == 0)
24445 enter_spin_lock (&gc_lock);
24449 bgc_threads_sync_event.Set();
24453 bgc_threads_sync_event.Wait(INFINITE, FALSE);
24454 dprintf (2, ("bgc_threads_sync_event is signalled"));
24457 assert (settings.concurrent);
24458 assert (settings.condemned_generation == max_generation);
24460 dprintf (2, ("clearing cm_in_progress"));
24461 c_write (cm_in_progress, FALSE);
24463 bgc_alloc_lock->check();
24465 current_bgc_state = bgc_final_marking;
24467 //concurrent_print_time_delta ("concurrent marking ended");
24468 concurrent_print_time_delta ("CR");
24470 fire_bgc_event (BGC2ndNonConBegin);
24472 mark_absorb_new_alloc();
24474 // We need a join here 'cause find_object would complain if the gen0
24475 // bricks of another heap haven't been fixed up. So we need to make sure
24476 // that every heap's gen0 bricks are fixed up before we proceed.
24477 #ifdef MULTIPLE_HEAPS
24478 bgc_t_join.join(this, gc_join_after_absorb);
24479 if (bgc_t_join.joined())
24481 dprintf(3, ("Joining BGC threads after absorb"));
24482 bgc_t_join.restart();
24484 #endif //MULTIPLE_HEAPS
24486 // give VM a chance to do work
24487 GCToEEInterface::GcBeforeBGCSweepWork();
24489 //reset the flag, indicating that the EE no longer expect concurrent
24491 sc.concurrent = FALSE;
24493 total_loh_size = generation_size (max_generation + 1);
24494 total_soh_size = generation_sizes (generation_of (max_generation));
24496 dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
24498 dprintf (2, ("nonconcurrent marking stack roots"));
24499 CNameSpace::GcScanRoots(background_promote,
24500 max_generation, max_generation,
24502 //concurrent_print_time_delta ("nonconcurrent marking stack roots");
24503 concurrent_print_time_delta ("NRS");
24505 // finalize_queue->EnterFinalizeLock();
24506 finalize_queue->GcScanRoots(background_promote, heap_number, 0);
24507 // finalize_queue->LeaveFinalizeLock();
24509 dprintf (2, ("nonconcurrent marking handle table"));
24510 CNameSpace::GcScanHandles(background_promote,
24511 max_generation, max_generation,
24513 //concurrent_print_time_delta ("nonconcurrent marking handle table");
24514 concurrent_print_time_delta ("NRH");
24516 dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
24517 revisit_written_pages (FALSE);
24518 //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
24519 concurrent_print_time_delta ("NRre LOH");
24521 dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
24522 bgc_overflow_count = 0;
24524 // Dependent handles need to be scanned with a special algorithm (see the header comment on
24525 // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
24526 // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
24527 // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
24528 // The call to background_scan_dependent_handles is what will cycle through more iterations if
24529 // required and will also perform processing of any mark stack overflow once the dependent handle
24530 // table has been fully promoted.
24531 dprintf (2, ("1st dependent handle scan and process mark overflow"));
24532 CNameSpace::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
24533 background_scan_dependent_handles (&sc);
24534 //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
24535 concurrent_print_time_delta ("NR 1st Hov");
24537 dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
24538 bgc_overflow_count = 0;
24540 #ifdef MULTIPLE_HEAPS
24541 bgc_t_join.join(this, gc_join_null_dead_short_weak);
24542 if (bgc_t_join.joined())
24543 #endif //MULTIPLE_HEAPS
24545 GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
24547 #ifdef MULTIPLE_HEAPS
24548 dprintf(3, ("Joining BGC threads for short weak handle scan"));
24549 bgc_t_join.restart();
24550 #endif //MULTIPLE_HEAPS
24553 // null out the target of short weakref that were not promoted.
24554 CNameSpace::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
24556 //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
24557 concurrent_print_time_delta ("NR GcShortWeakPtrScan");
24561 #ifdef MULTIPLE_HEAPS
24562 bgc_t_join.join(this, gc_join_scan_finalization);
24563 if (bgc_t_join.joined())
24565 dprintf(3, ("Joining BGC threads for finalization"));
24566 bgc_t_join.restart();
24568 #endif //MULTIPLE_HEAPS
24570 //Handle finalization.
24571 dprintf(3,("Marking finalization data"));
24572 //concurrent_print_time_delta ("bgc joined to mark finalization");
24573 concurrent_print_time_delta ("NRj");
24575 // finalize_queue->EnterFinalizeLock();
24576 finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
24577 // finalize_queue->LeaveFinalizeLock();
24579 concurrent_print_time_delta ("NRF");
24582 dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
24583 bgc_overflow_count = 0;
24585 // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
24586 // for finalization. As before background_scan_dependent_handles will also process any mark stack
24588 dprintf (2, ("2nd dependent handle scan and process mark overflow"));
24589 background_scan_dependent_handles (&sc);
24590 //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
24591 concurrent_print_time_delta ("NR 2nd Hov");
24593 #ifdef MULTIPLE_HEAPS
24594 bgc_t_join.join(this, gc_join_null_dead_long_weak);
24595 if (bgc_t_join.joined())
24597 dprintf(2, ("Joining BGC threads for weak pointer deletion"));
24598 bgc_t_join.restart();
24600 #endif //MULTIPLE_HEAPS
24602 // null out the target of long weakref that were not promoted.
24603 CNameSpace::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
24604 concurrent_print_time_delta ("NR GcWeakPtrScan");
24606 #ifdef MULTIPLE_HEAPS
24607 bgc_t_join.join(this, gc_join_null_dead_syncblk);
24608 if (bgc_t_join.joined())
24609 #endif //MULTIPLE_HEAPS
24611 dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
24612 // scan for deleted entries in the syncblk cache
24613 CNameSpace::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
24614 concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
24615 #ifdef MULTIPLE_HEAPS
24616 dprintf(2, ("Starting BGC threads for end of background mark phase"));
24617 bgc_t_join.restart();
24618 #endif //MULTIPLE_HEAPS
24621 gen0_bricks_cleared = FALSE;
24623 dprintf (2, ("end of bgc mark: loh: %d, soh: %d",
24624 generation_size (max_generation + 1),
24625 generation_sizes (generation_of (max_generation))));
24627 for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
24629 generation* gen = generation_of (gen_idx);
24630 dynamic_data* dd = dynamic_data_of (gen_idx);
24631 dd_begin_data_size (dd) = generation_size (gen_idx) -
24632 (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
24633 Align (size (generation_allocation_start (gen)));
24634 dd_survived_size (dd) = 0;
24635 dd_pinned_survived_size (dd) = 0;
24636 dd_artificial_pinned_survived_size (dd) = 0;
24637 dd_added_pinned_size (dd) = 0;
24640 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24641 PREFIX_ASSUME(seg != NULL);
24645 seg->flags &= ~heap_segment_flags_swept;
24647 if (heap_segment_allocated (seg) == heap_segment_mem (seg))
24649 // This can't happen...
24653 if (seg == ephemeral_heap_segment)
24655 heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
24659 heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
24662 dprintf (2, ("seg %Ix background allocated is %Ix",
24663 heap_segment_mem (seg),
24664 heap_segment_background_allocated (seg)));
24665 seg = heap_segment_next_rw (seg);
24668 // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
24669 // we can't let the user code consume the left over parts in these alloc contexts.
24670 repair_allocation_contexts (FALSE);
24673 finish = GetCycleCount32();
24674 mark_time = finish - start;
24677 dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d",
24678 generation_free_list_space (generation_of (max_generation)),
24679 generation_free_obj_space (generation_of (max_generation))));
24681 dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
24685 gc_heap::suspend_EE ()
24687 dprintf (2, ("suspend_EE"));
24688 #ifdef MULTIPLE_HEAPS
24689 gc_heap* hp = gc_heap::g_heaps[0];
24690 GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24692 GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24693 #endif //MULTIPLE_HEAPS
24696 #ifdef MULTIPLE_HEAPS
24698 gc_heap::bgc_suspend_EE ()
24700 for (int i = 0; i < n_heaps; i++)
24702 gc_heap::g_heaps[i]->reset_gc_done();
24705 dprintf (2, ("bgc_suspend_EE"));
24706 GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24708 gc_started = FALSE;
24709 for (int i = 0; i < n_heaps; i++)
24711 gc_heap::g_heaps[i]->set_gc_done();
24716 gc_heap::bgc_suspend_EE ()
24720 dprintf (2, ("bgc_suspend_EE"));
24721 GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24722 gc_started = FALSE;
24725 #endif //MULTIPLE_HEAPS
24728 gc_heap::restart_EE ()
24730 dprintf (2, ("restart_EE"));
24731 #ifdef MULTIPLE_HEAPS
24732 GCToEEInterface::RestartEE(FALSE);
24734 GCToEEInterface::RestartEE(FALSE);
24735 #endif //MULTIPLE_HEAPS
24738 inline BYTE* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
24742 BYTE* end = ((seg == ephemeral_heap_segment) ?
24743 generation_allocation_start (generation_of (max_generation-1)) :
24744 heap_segment_allocated (seg));
24745 return align_lower_page (end);
24749 return heap_segment_allocated (seg);
24753 void gc_heap::revisit_written_page (BYTE* page,
24758 BYTE*& last_object,
24759 BOOL large_objects_p,
24760 size_t& num_marked_objects)
24762 BYTE* start_address = page;
24764 int align_const = get_alignment_constant (!large_objects_p);
24765 BYTE* high_address = end;
24766 BYTE* current_lowest_address = background_saved_lowest_address;
24767 BYTE* current_highest_address = background_saved_highest_address;
24768 BOOL no_more_loop_p = FALSE;
24771 #ifndef MULTIPLE_HEAPS
24772 const int thread = heap_number;
24773 #endif //!MULTIPLE_HEAPS
24775 if (large_objects_p)
24781 if (((last_page + OS_PAGE_SIZE) == page)
24782 || (start_address <= last_object))
24788 o = find_first_object (start_address, last_object);
24789 // We can visit the same object again, but on a different page.
24790 assert (o >= last_object);
24794 dprintf (3,("page %Ix start: %Ix, %Ix[ ",
24795 (size_t)page, (size_t)o,
24796 (size_t)(min (high_address, page + OS_PAGE_SIZE))));
24798 while (o < (min (high_address, page + OS_PAGE_SIZE)))
24802 if (concurrent_p && large_objects_p)
24804 bgc_alloc_lock->bgc_mark_set (o);
24806 if (((CObjectHeader*)o)->IsFree())
24808 s = unused_array_size (o);
24820 dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
24822 assert (Align (s) >= Align (min_obj_size));
24824 BYTE* next_o = o + Align (s, align_const);
24826 if (next_o >= start_address)
24828 #ifdef MULTIPLE_HEAPS
24831 // We set last_object here for SVR BGC here because SVR BGC has more than
24832 // one GC thread. When we have more than one GC thread we would run into this
24833 // situation if we skipped unmarked objects:
24834 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it
24836 // bgc thread 2 marks X and all its current children.
24837 // user thread comes along and dirties more (and later) pages in X.
24838 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
24839 // on them because it had already skipped X. We need to detect that this object is now
24840 // marked and mark the children on the dirtied pages.
24841 // In the future if we have less BGC threads than we have heaps we should add
24842 // the check to the number of BGC threads.
24845 #endif //MULTIPLE_HEAPS
24847 if (contain_pointers (o) &&
24848 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
24849 background_marked (o)))
24851 dprintf (3, ("going through %Ix", (size_t)o));
24852 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
24853 if ((BYTE*)poo >= min (high_address, page + OS_PAGE_SIZE))
24855 no_more_loop_p = TRUE;
24860 num_marked_objects++;
24861 background_mark_object (oo THREAD_NUMBER_ARG);
24864 else if (concurrent_p && large_objects_p && ((CObjectHeader*)o)->IsFree() && (next_o > min (high_address, page + OS_PAGE_SIZE)))
24866 // We need to not skip the object here because of this corner scenario:
24867 // A large object was being allocated during BGC mark so we first made it
24868 // into a free object, then cleared its memory. In this loop we would detect
24869 // that it's a free object which normally we would skip. But by the next time
24870 // we call GetWriteWatch we could still be on this object and the object had
24871 // been made into a valid object and some of its memory was changed. We need
24872 // to be sure to process those written pages so we can't skip the object just
24874 no_more_loop_p = TRUE;
24879 if (concurrent_p && large_objects_p)
24881 bgc_alloc_lock->bgc_mark_done ();
24883 if (no_more_loop_p)
24890 #ifdef MULTIPLE_HEAPS
24893 assert (last_object < (min (high_address, page + OS_PAGE_SIZE)));
24896 #endif //MULTIPLE_HEAPS
24901 dprintf (3,("Last object: %Ix", (size_t)last_object));
24902 last_page = align_lower_page (o);
24905 // When reset_only_p is TRUE, we should only reset pages that are in range
24906 // because we need to consider the segments or part of segments that were
24907 // allocated out of range all live.
24908 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
24911 if (concurrent_p && !reset_only_p)
24913 current_bgc_state = bgc_revisit_soh;
24916 size_t total_dirtied_pages = 0;
24917 size_t total_marked_objects = 0;
24919 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24921 PREFIX_ASSUME(seg != NULL);
24924 int mode = concurrent_p ? 1 : 0;
24925 BOOL small_object_segments = TRUE;
24926 int align_const = get_alignment_constant (small_object_segments);
24932 if (small_object_segments)
24934 //switch to large segment
24935 if (concurrent_p && !reset_only_p)
24937 current_bgc_state = bgc_revisit_loh;
24942 dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
24943 fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
24944 concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
24945 total_dirtied_pages = 0;
24946 total_marked_objects = 0;
24949 small_object_segments = FALSE;
24950 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
24952 dprintf (3, ("now revisiting large object segments"));
24953 align_const = get_alignment_constant (small_object_segments);
24954 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24956 PREFIX_ASSUME(seg != NULL);
24964 dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
24968 dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
24969 fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
24974 BYTE* base_address = (BYTE*)heap_segment_mem (seg);
24975 //we need to truncate to the base of the page because
24976 //some newly allocated could exist beyond heap_segment_allocated
24977 //and if we reset the last page write watch status,
24978 // they wouldn't be guaranteed to be visited -> gc hole.
24979 ULONG_PTR bcount = array_size;
24980 BYTE* last_page = 0;
24981 BYTE* last_object = heap_segment_mem (seg);
24982 BYTE* high_address = 0;
24984 BOOL skip_seg_p = FALSE;
24988 if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
24989 (heap_segment_reserved (seg) <= background_saved_highest_address))
24991 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number,
24992 heap_segment_mem (seg), heap_segment_reserved (seg)));
24999 dprintf (3, ("looking at seg %Ix", (size_t)last_object));
25003 base_address = max (base_address, background_saved_lowest_address);
25004 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
25007 dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address,
25008 heap_segment_mem (seg), heap_segment_reserved (seg)));
25015 high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
25016 high_address = min (high_address, background_saved_highest_address);
25020 high_address = high_page (seg, concurrent_p);
25023 if ((base_address < high_address) &&
25024 (bcount >= array_size))
25026 ptrdiff_t region_size = high_address - base_address;
25027 dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
25029 UINT status = GetWriteWatch (mode, base_address, region_size,
25030 (PVOID*)background_written_addresses,
25031 &bcount, &granularity);
25036 printf ("GetWriteWatch Error ");
25037 printf ("Probing pages [%Ix, %Ix[\n", (size_t)base_address, (size_t)high_address);
25040 assert (status == 0);
25041 assert (granularity == OS_PAGE_SIZE);
25045 total_dirtied_pages += bcount;
25047 dprintf (3, ("Found %d pages [%Ix, %Ix[",
25048 bcount, (size_t)base_address, (size_t)high_address));
25053 for (unsigned i = 0; i < bcount; i++)
25055 #ifdef NO_WRITE_BARRIER
25056 card_table [card_word (card_of (background_written_addresses [i]))] = ~0u;
25057 dprintf (3,("Set Cards [%p:%p, %p:%p[",
25058 card_of (background_written_addresses [i]), g_addresses [i],
25059 card_of (background_written_addresses [i]+OS_PAGE_SIZE), background_written_addresses [i]+OS_PAGE_SIZE));
25060 #endif //NO_WRITE_BARRIER
25061 BYTE* page = (BYTE*)background_written_addresses[i];
25062 dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i,
25063 (size_t)page, (size_t)high_address));
25064 if (page < high_address)
25066 //search for marked objects in the page
25067 revisit_written_page (page, high_address, concurrent_p,
25068 seg, last_page, last_object,
25069 !small_object_segments,
25070 total_marked_objects);
25074 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
25075 assert (!"page shouldn't have exceeded limit");
25080 if (bcount >= array_size){
25081 base_address = background_written_addresses [array_size-1] + OS_PAGE_SIZE;
25082 bcount = array_size;
25092 seg = heap_segment_next_rw (seg);
25095 #endif //WRITE_WATCH
25098 void gc_heap::background_grow_c_mark_list()
25100 assert (c_mark_list_index >= c_mark_list_length);
25101 BOOL should_drain_p = FALSE;
25103 #ifndef MULTIPLE_HEAPS
25104 const int thread = heap_number;
25105 #endif //!MULTIPLE_HEAPS
25107 dprintf (2, ("stack copy buffer overflow"));
25108 BYTE** new_c_mark_list = 0;
25111 if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (BYTE*))))
25113 should_drain_p = TRUE;
25117 new_c_mark_list = new (nothrow) (BYTE*[c_mark_list_length*2]);
25118 if (new_c_mark_list == 0)
25120 should_drain_p = TRUE;
25124 if (should_drain_p)
25127 dprintf (2, ("No more memory for the stacks copy, draining.."));
25128 //drain the list by marking its elements
25129 background_drain_mark_list (thread);
25133 assert (new_c_mark_list);
25134 memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(BYTE*));
25135 c_mark_list_length = c_mark_list_length*2;
25136 delete c_mark_list;
25137 c_mark_list = new_c_mark_list;
25141 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
25145 //in order to save space on the array, mark the object,
25146 //knowing that it will be visited later
25147 assert (settings.concurrent);
25149 THREAD_NUMBER_FROM_CONTEXT;
25150 #ifndef MULTIPLE_HEAPS
25151 const int thread = 0;
25152 #endif //!MULTIPLE_HEAPS
25154 BYTE* o = (BYTE*)*ppObject;
25161 gc_heap* hp = gc_heap::heap_of (o);
25163 if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
25168 #ifdef INTERIOR_POINTERS
25169 if (flags & GC_CALL_INTERIOR)
25171 o = hp->find_object (o, hp->background_saved_lowest_address);
25175 #endif //INTERIOR_POINTERS
25177 #ifdef FEATURE_CONSERVATIVE_GC
25178 // For conservative GC, a value on stack may point to middle of a free object.
25179 // In this case, we don't need to promote the pointer.
25180 if (g_pConfig->GetGCConservative() && ((CObjectHeader*)o)->IsFree())
25184 #endif //FEATURE_CONSERVATIVE_GC
25187 ((CObjectHeader*)o)->Validate();
25190 dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
25191 if (o && (size (o) > LARGE_OBJECT_SIZE))
25193 dprintf (3, ("Brc %Ix", (size_t)o));
25196 if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
25198 hpt->background_grow_c_mark_list();
25200 dprintf (3, ("pushing %08x into mark_list", (size_t)o));
25201 hpt->c_mark_list [hpt->c_mark_list_index++] = o;
25203 STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, " GCHeap::Background Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetMethodTable() : NULL);
25206 void gc_heap::mark_absorb_new_alloc()
25208 fix_allocation_contexts (FALSE);
25210 gen0_bricks_cleared = FALSE;
25212 clear_gen0_bricks();
25215 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
25217 BOOL success = FALSE;
25218 BOOL thread_created = FALSE;
25219 dprintf (2, ("Preparing gc thread"));
25221 EnterCriticalSection (&(gh->bgc_threads_timeout_cs));
25222 if (!(gh->bgc_thread_running))
25224 dprintf (2, ("GC thread not runnning"));
25225 if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
25228 thread_created = TRUE;
25233 dprintf (3, ("GC thread already running"));
25236 LeaveCriticalSection (&(gh->bgc_threads_timeout_cs));
25239 FireEtwGCCreateConcurrentThread_V1(GetClrInstanceId());
25244 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
25248 assert (background_gc_done_event.IsValid());
25250 //dprintf (2, ("Creating BGC thread"));
25252 #ifdef FEATURE_REDHAWK
25254 // Thread creation is handled a little differently in Redhawk. We create the thread by a call to the OS
25255 // (via the PAL) and it starts running immediately. We place a wrapper around the start routine to
25256 // initialize the Redhawk Thread context (since that must be done from the thread itself) and also destroy
25257 // it as the thread exits. We also set gh->bgc_thread from this wrapper since otherwise we'd have to
25258 // search the thread store for one with the matching ID. gc->bgc_thread will be valid by the time we've
25259 // finished the event wait below.
25261 rh_bgc_thread_ctx sContext;
25262 sContext.m_pRealStartRoutine = gh->bgc_thread_stub;
25263 sContext.m_pRealContext = gh;
25265 if (!PalStartBackgroundGCThread(gh->rh_bgc_thread_stub, &sContext))
25270 #else // FEATURE_REDHAWK
25272 Thread* current_bgc_thread;
25274 gh->bgc_thread = SetupUnstartedThread(FALSE);
25275 if (!(gh->bgc_thread))
25280 current_bgc_thread = gh->bgc_thread;
25281 if (!current_bgc_thread->CreateNewThread (0, &(gh->bgc_thread_stub), gh))
25286 current_bgc_thread->SetBackground (TRUE, FALSE);
25288 // wait for the thread to be in its main loop, this is to detect the situation
25289 // where someone triggers a GC during dll loading where the loader lock is
25291 current_bgc_thread->StartThread();
25293 #endif // FEATURE_REDHAWK
25296 dprintf (2, ("waiting for the thread to reach its main loop"));
25297 // In chk builds this can easily time out since
25298 // now we need to set the thread up into a managed thead.
25299 // And since it's a managed thread we also need to make sure that we don't
25300 // clean up here and are still executing code on that thread (it'll
25301 // trigger all sorts of asserts.
25302 //DWORD res = gh->background_gc_create_event.Wait(20,FALSE);
25303 DWORD res = gh->background_gc_create_event.Wait(INFINITE,FALSE);
25304 if (res == WAIT_TIMEOUT)
25306 dprintf (2, ("waiting for the thread to reach its main loop Timeout."));
25309 if (!gh->bgc_thread_running)
25311 dprintf(2, ("background GC thread failed to start."));
25314 //dprintf (2, ("waiting for the thread to reach its main loop Done."));
25323 if (gh->bgc_thread)
25325 gh->bgc_thread = 0;
25331 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
25334 dprintf (3, ("Creating concurrent GC thread for the first time"));
25335 background_gc_done_event.CreateManualEvent(TRUE);
25336 if (!background_gc_done_event.IsValid())
25340 bgc_threads_sync_event.CreateManualEvent(FALSE);
25341 if (!bgc_threads_sync_event.IsValid())
25345 ee_proceed_event.CreateAutoEvent(FALSE);
25346 if (!ee_proceed_event.IsValid())
25350 bgc_start_event.CreateManualEvent(FALSE);
25351 if (!bgc_start_event.IsValid())
25356 #ifdef MULTIPLE_HEAPS
25357 bgc_t_join.init (number_of_heaps, join_flavor_bgc);
25358 #endif //MULTIPLE_HEAPS
25366 if (background_gc_done_event.IsValid())
25368 background_gc_done_event.CloseEvent();
25370 if (bgc_threads_sync_event.IsValid())
25372 bgc_threads_sync_event.CloseEvent();
25374 if (ee_proceed_event.IsValid())
25376 ee_proceed_event.CloseEvent();
25378 if (bgc_start_event.IsValid())
25380 bgc_start_event.CloseEvent();
25387 BOOL gc_heap::create_bgc_thread_support()
25392 gc_lh_block_event.CreateManualEvent(FALSE);
25393 if (!gc_lh_block_event.IsValid())
25398 background_gc_create_event.CreateAutoEvent(FALSE);
25399 if (!background_gc_create_event.IsValid())
25404 //needs to have room for enough smallest objects fitting on a page
25405 parr = new (nothrow) (BYTE* [1 + page_size / MIN_OBJECT_SIZE]);
25411 make_c_mark_list (parr);
25419 if (gc_lh_block_event.IsValid())
25421 gc_lh_block_event.CloseEvent();
25423 if (background_gc_create_event.IsValid())
25425 background_gc_create_event.CloseEvent();
25432 int gc_heap::check_for_ephemeral_alloc()
25434 int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
25438 #ifdef MULTIPLE_HEAPS
25439 for (int heap_index = 0; heap_index < n_heaps; heap_index++)
25440 #endif //MULTIPLE_HEAPS
25442 for (int i = 0; i <= (max_generation - 1); i++)
25444 #ifdef MULTIPLE_HEAPS
25445 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
25447 if (get_new_allocation (i) <= 0)
25448 #endif //MULTIPLE_HEAPS
25450 gen = max (gen, i);
25461 // Wait for gc to finish sequential part
25462 void gc_heap::wait_to_proceed()
25464 assert (background_gc_done_event.IsValid());
25465 assert (bgc_start_event.IsValid());
25467 user_thread_wait(&ee_proceed_event, FALSE);
25470 // Start a new concurrent gc
25471 void gc_heap::start_c_gc()
25473 assert (background_gc_done_event.IsValid());
25474 assert (bgc_start_event.IsValid());
25476 //Need to make sure that the gc thread is in the right place.
25477 background_gc_done_event.Wait(INFINITE, FALSE);
25478 background_gc_done_event.Reset();
25479 bgc_start_event.Set();
25482 void gc_heap::do_background_gc()
25484 dprintf (2, ("starting a BGC"));
25485 #ifdef MULTIPLE_HEAPS
25486 for (int i = 0; i < n_heaps; i++)
25488 g_heaps[i]->init_background_gc();
25491 init_background_gc();
25492 #endif //MULTIPLE_HEAPS
25493 //start the background gc
25496 //wait until we get restarted by the BGC.
25500 void gc_heap::kill_gc_thread()
25502 //assert (settings.concurrent == FALSE);
25504 // We are doing a two-stage shutdown now.
25505 // In the first stage, we do minimum work, and call ExitProcess at the end.
25506 // In the secodn stage, we have the Loader lock and only one thread is
25507 // alive. Hence we do not need to kill gc thread.
25508 DestroyThread (bgc_thread);
25509 background_gc_done_event.CloseEvent();
25510 gc_lh_block_event.CloseEvent();
25511 bgc_start_event.CloseEvent();
25512 DeleteCriticalSection (&bgc_threads_timeout_cs);
25514 recursive_gc_sync::shutdown();
25517 DWORD gc_heap::bgc_thread_function()
25519 assert (background_gc_done_event.IsValid());
25520 assert (bgc_start_event.IsValid());
25522 dprintf (3, ("gc_thread thread starting..."));
25524 BOOL do_exit = FALSE;
25525 Thread* thread_to_destroy = 0;
25527 #ifndef FEATURE_REDHAWK
25528 // see comments in create_bgc_thread - we need
25529 // to make sure that thread doesn't clean up this thread
25530 // while we run code here.
25531 if (!bgc_thread->HasStarted(FALSE))
25533 dprintf (2, ("HasStarted failed"));
25534 bgc_thread_running = FALSE;
25535 background_gc_create_event.Set();
25538 #endif //FEATURE_REDHAWK
25540 bgc_thread_running = TRUE;
25541 Thread* current_thread = GetThread();
25542 BOOL cooperative_mode = TRUE;
25543 bgc_thread_id = GetCurrentThreadId();
25544 dprintf (1, ("bgc_thread_id is set to %Ix", bgc_thread_id));
25545 //this also indicates that the thread is ready.
25546 background_gc_create_event.Set();
25549 // Wait for work to do...
25550 dprintf (3, ("bgc thread: waiting..."));
25552 cooperative_mode = enable_preemptive (current_thread);
25553 //current_thread->m_fPreemptiveGCDisabled = 0;
25555 DWORD result = bgc_start_event.Wait(
25557 #ifdef MULTIPLE_HEAPS
25561 #endif //MULTIPLE_HEAPS
25563 #ifdef MULTIPLE_HEAPS
25567 #endif //MULTIPLE_HEAPS
25570 dprintf (2, ("gc thread: finished waiting"));
25572 // not calling disable_preemptive here 'cause we
25573 // can't wait for GC complete here - RestartEE will be called
25574 // when we've done the init work.
25576 if (result == WAIT_TIMEOUT)
25578 // Should join the bgc threads and terminate all of them
25580 dprintf (1, ("GC thread timeout"));
25581 EnterCriticalSection (&bgc_threads_timeout_cs);
25582 if (!keep_bgc_threads_p)
25584 dprintf (2, ("GC thread exiting"));
25585 bgc_thread_running = FALSE;
25586 // We can't call DestroyThread here 'cause EnterCriticalSection
25587 // increases the thread's m_dwLockCount and DestroyThread will
25588 // assert if the lock count is not 0.
25589 thread_to_destroy = bgc_thread;
25594 LeaveCriticalSection (&bgc_threads_timeout_cs);
25599 dprintf (3, ("GC thread needed, not exiting"));
25603 // if we signal the thread with no concurrent work to do -> exit
25604 if (!settings.concurrent)
25606 dprintf (3, ("no concurrent GC needed, exiting"));
25612 recursive_gc_sync::begin_background();
25613 dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d",
25614 generation_free_list_space (generation_of (max_generation)),
25615 generation_free_obj_space (generation_of (max_generation)),
25616 dd_fragmentation (dynamic_data_of (max_generation))));
25620 current_bgc_state = bgc_not_in_process;
25623 //trace_gc = FALSE;
25626 enable_preemptive (current_thread);
25627 #ifdef MULTIPLE_HEAPS
25628 bgc_t_join.join(this, gc_join_done);
25629 if (bgc_t_join.joined())
25630 #endif //MULTIPLE_HEAPS
25632 enter_spin_lock (&gc_lock);
25633 dprintf (SPINLOCK_LOG, ("bgc Egc"));
25635 bgc_start_event.Reset();
25637 #ifdef MULTIPLE_HEAPS
25638 for (int gen = max_generation; gen <= (max_generation + 1); gen++)
25640 size_t desired_per_heap = 0;
25641 size_t total_desired = 0;
25644 for (int i = 0; i < n_heaps; i++)
25647 dd = hp->dynamic_data_of (gen);
25648 size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
25649 if (temp_total_desired < total_desired)
25652 total_desired = (size_t)MAX_PTR;
25655 total_desired = temp_total_desired;
25658 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
25660 for (int i = 0; i < n_heaps; i++)
25662 hp = gc_heap::g_heaps[i];
25663 dd = hp->dynamic_data_of (gen);
25664 dd_desired_allocation (dd) = desired_per_heap;
25665 dd_gc_new_allocation (dd) = desired_per_heap;
25666 dd_new_allocation (dd) = desired_per_heap;
25669 #endif //MULTIPLE_HEAPS
25670 #ifdef MULTIPLE_HEAPS
25672 #endif //MULTIPLE_HEAPS
25674 c_write (settings.concurrent, FALSE);
25675 recursive_gc_sync::end_background();
25676 keep_bgc_threads_p = FALSE;
25677 background_gc_done_event.Set();
25679 dprintf (SPINLOCK_LOG, ("bgc Lgc"));
25680 leave_spin_lock (&gc_lock);
25681 #ifdef MULTIPLE_HEAPS
25682 dprintf(1, ("End of BGC - starting all BGC threads"));
25683 bgc_t_join.restart();
25684 #endif //MULTIPLE_HEAPS
25686 // We can't disable preempt here because there might've been a GC already
25687 // started and decided to do a BGC and waiting for a BGC thread to restart
25688 // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
25689 // to restart the VM so we deadlock.
25690 //gc_heap::disable_preemptive (current_thread, TRUE);
25693 if (thread_to_destroy)
25695 DestroyThread(thread_to_destroy);
25698 FireEtwGCTerminateConcurrentThread_V1(GetClrInstanceId());
25700 dprintf (3, ("bgc_thread thread exiting"));
25704 #endif //BACKGROUND_GC
25706 //Clear the cards [start_card, end_card[
25707 void gc_heap::clear_cards (size_t start_card, size_t end_card)
25709 if (start_card < end_card)
25711 size_t start_word = card_word (start_card);
25712 size_t end_word = card_word (end_card);
25713 if (start_word < end_word)
25715 unsigned bits = card_bit (start_card);
25716 card_table [start_word] &= lowbits (~0, bits);
25717 for (size_t i = start_word+1; i < end_word; i++)
25718 card_table [i] = 0;
25719 bits = card_bit (end_card);
25720 // Don't write beyond end_card (and possibly uncommitted card table space).
25723 card_table [end_word] &= highbits (~0, bits);
25728 card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
25729 highbits (~0, card_bit (end_card)));
25731 #ifdef VERYSLOWDEBUG
25732 size_t card = start_card;
25733 while (card < end_card)
25735 assert (! (card_set_p (card)));
25738 #endif //VERYSLOWDEBUG
25739 dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
25740 start_card, (size_t)card_address (start_card),
25741 end_card, (size_t)card_address (end_card)));
25745 void gc_heap::clear_card_for_addresses (BYTE* start_address, BYTE* end_address)
25747 size_t start_card = card_of (align_on_card (start_address));
25748 size_t end_card = card_of (align_lower_card (end_address));
25749 clear_cards (start_card, end_card);
25752 // copy [srccard, ...[ to [dst_card, end_card[
25753 // This will set the same bit twice. Can be optimized.
25755 void gc_heap::copy_cards (size_t dst_card, size_t src_card,
25756 size_t end_card, BOOL nextp)
25758 // If the range is empty, this function is a no-op - with the subtlety that
25759 // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
25760 // outside the committed region. To avoid the access, leave early.
25761 if (!(dst_card < end_card))
25764 unsigned int srcbit = card_bit (src_card);
25765 unsigned int dstbit = card_bit (dst_card);
25766 size_t srcwrd = card_word (src_card);
25767 size_t dstwrd = card_word (dst_card);
25768 unsigned int srctmp = card_table[srcwrd];
25769 unsigned int dsttmp = card_table[dstwrd];
25770 for (size_t card = dst_card; card < end_card; card++)
25772 if (srctmp & (1 << srcbit))
25773 dsttmp |= 1 << dstbit;
25775 dsttmp &= ~(1 << dstbit);
25776 if (!(++srcbit % 32))
25778 srctmp = card_table[++srcwrd];
25783 if (srctmp & (1 << srcbit))
25784 dsttmp |= 1 << dstbit;
25786 if (!(++dstbit % 32))
25788 card_table[dstwrd] = dsttmp;
25790 dsttmp = card_table[dstwrd];
25794 card_table[dstwrd] = dsttmp;
25797 void gc_heap::copy_cards_for_addresses (BYTE* dest, BYTE* src, size_t len)
25799 ptrdiff_t relocation_distance = src - dest;
25800 size_t start_dest_card = card_of (align_on_card (dest));
25801 size_t end_dest_card = card_of (dest + len - 1);
25802 size_t dest_card = start_dest_card;
25803 size_t src_card = card_of (card_address (dest_card)+relocation_distance);
25804 dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
25805 src_card, (size_t)src, dest_card, (size_t)dest));
25806 dprintf (3,(" %Ix->%Ix:%Ix[",
25807 (size_t)src+len, end_dest_card, (size_t)dest+len));
25809 dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
25810 dest, src, len, relocation_distance, (align_on_card (dest))));
25812 dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
25813 start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
25815 //First card has two boundaries
25816 if (start_dest_card != card_of (dest))
25818 if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
25819 card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
25821 dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
25822 (card_address (start_dest_card) + relocation_distance),
25823 card_of (card_address (start_dest_card) + relocation_distance),
25825 card_of (src + len - 1)));
25827 dprintf (3, ("setting card: %Ix", card_of (dest)));
25828 set_card (card_of (dest));
25832 if (card_set_p (card_of (src)))
25833 set_card (card_of (dest));
25836 copy_cards (dest_card, src_card, end_dest_card,
25837 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
25839 //Last card has two boundaries.
25840 if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
25841 card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
25843 dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
25844 (card_address (end_dest_card) + relocation_distance),
25845 card_of (card_address (end_dest_card) + relocation_distance),
25849 dprintf (3, ("setting card: %Ix", end_dest_card));
25850 set_card (end_dest_card);
25853 if (card_set_p (card_of (src + len - 1)))
25854 set_card (end_dest_card);
25857 #ifdef BACKGROUND_GC
25858 // this does not need the Interlocked version of mark_array_set_marked.
25859 void gc_heap::copy_mark_bits_for_addresses (BYTE* dest, BYTE* src, size_t len)
25861 dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
25862 (size_t)src, (size_t)dest,
25863 (size_t)src+len, (size_t)dest+len));
25867 BYTE* src_end = src + len;
25868 int align_const = get_alignment_constant (TRUE);
25869 ptrdiff_t reloc = dest - src;
25871 while (src_o < src_end)
25873 BYTE* next_o = src_o + Align (size (src_o), align_const);
25875 if (background_object_marked (src_o, TRUE))
25877 dest_o = src_o + reloc;
25879 //if (background_object_marked (dest_o, FALSE))
25881 // dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
25882 // FATAL_GC_ERROR();
25885 background_mark (dest_o,
25886 background_saved_lowest_address,
25887 background_saved_highest_address);
25888 dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
25894 #endif //BACKGROUND_GC
25896 void gc_heap::fix_brick_to_highest (BYTE* o, BYTE* next_o)
25898 size_t new_current_brick = brick_of (o);
25899 set_brick (new_current_brick,
25900 (o - brick_address (new_current_brick)));
25901 size_t b = 1 + new_current_brick;
25902 size_t limit = brick_of (next_o);
25903 //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
25904 dprintf(3,("b:%Ix->%Ix-%Ix",
25905 new_current_brick, (size_t)o, (size_t)next_o, limit));
25908 set_brick (b,(new_current_brick - b));
25913 // start can not be >= heap_segment_allocated for the segment.
25914 BYTE* gc_heap::find_first_object (BYTE* start, BYTE* first_object)
25916 size_t brick = brick_of (start);
25918 //last_object == null -> no search shortcut needed
25919 if ((brick == brick_of (first_object) || (start <= first_object)))
25925 ptrdiff_t min_brick = (ptrdiff_t)brick_of (first_object);
25926 ptrdiff_t prev_brick = (ptrdiff_t)brick - 1;
25927 int brick_entry = 0;
25930 if (prev_brick < min_brick)
25934 if ((brick_entry = brick_table [ prev_brick ]) >= 0)
25938 assert (! ((brick_entry == 0)));
25939 prev_brick = (brick_entry + prev_brick);
25942 o = ((prev_brick < min_brick) ? first_object :
25943 brick_address (prev_brick) + brick_entry - 1);
25944 assert (o <= start);
25947 assert (Align (size (o)) >= Align (min_obj_size));
25948 BYTE* next_o = o + Align (size (o));
25949 size_t curr_cl = (size_t)next_o / brick_size;
25950 size_t min_cl = (size_t)first_object / brick_size;
25952 //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
25954 unsigned int n_o = 1;
25957 BYTE* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
25959 while (next_o <= start)
25967 assert (Align (size (o)) >= Align (min_obj_size));
25968 next_o = o + Align (size (o));
25970 }while (next_o < next_b);
25972 if (((size_t)next_o / brick_size) != curr_cl)
25974 if (curr_cl >= min_cl)
25976 fix_brick_to_highest (o, next_o);
25978 curr_cl = (size_t) next_o / brick_size;
25980 next_b = min (align_lower_brick (next_o) + brick_size, start+1);
25983 size_t bo = brick_of (o);
25984 //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix",
25985 dprintf (3, ("%Id o, [%Ix-[%Ix",
25989 set_brick (bo, (o - brick_address(bo)));
26003 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
26005 dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
26006 dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
26008 if (card_bundles_enabled())
26010 size_t cardb = cardw_card_bundle (cardw);
26011 size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
26014 //find a non null bundle
26015 while ((cardb < end_cardb) &&
26016 (card_bundle_set_p (cardb)==0))
26020 if (cardb == end_cardb)
26022 //find a non empty card word
26024 DWORD* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
26025 DWORD* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
26026 while ((card_word < card_word_end) &&
26031 if (card_word != card_word_end)
26033 cardw = (card_word - &card_table [0]);
26036 else if ((cardw <= card_bundle_cardw (cardb)) &&
26037 (card_word == &card_table [card_bundle_cardw (cardb+1)]))
26039 // a whole bundle was explored and is empty
26040 dprintf (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
26041 dd_collection_count (dynamic_data_of (0)),
26042 cardb, card_bundle_cardw (cardb),
26043 card_bundle_cardw (cardb+1)));
26044 card_bundle_clear (cardb);
26051 DWORD* card_word = &card_table[cardw];
26052 DWORD* card_word_end = &card_table [cardw_end];
26054 while (card_word < card_word_end)
26056 if ((*card_word) != 0)
26058 cardw = (card_word - &card_table [0]);
26069 #endif //CARD_BUNDLE
26071 BOOL gc_heap::find_card (DWORD* card_table, size_t& card,
26072 size_t card_word_end, size_t& end_card)
26074 DWORD* last_card_word;
26077 // Find the first card which is set
26079 last_card_word = &card_table [card_word (card)];
26080 z = card_bit (card);
26081 y = (*last_card_word) >> z;
26086 size_t lcw = card_word(card)+1;
26087 if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
26091 last_card_word = &card_table [lcw];
26092 y = *last_card_word;
26095 #else //CARD_BUNDLE
26101 while ((last_card_word < &card_table [card_word_end]) &&
26102 !(*last_card_word));
26103 if (last_card_word < &card_table [card_word_end])
26104 y = *last_card_word;
26107 #endif //CARD_BUNDLE
26111 // Look for the lowest bit set
26120 card = (last_card_word - &card_table [0])* card_word_width + z;
26125 if ((z == card_word_width) &&
26126 (last_card_word < &card_table [card_word_end]))
26131 y = *(++last_card_word);
26132 }while ((last_card_word < &card_table [card_word_end]) &&
26134 (y == (1 << card_word_width)-1)
26136 // if left shift count >= width of type,
26137 // gcc reports error.
26145 end_card = (last_card_word - &card_table [0])* card_word_width + z;
26146 //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
26147 dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
26152 //because of heap expansion, computing end is complicated.
26153 BYTE* compute_next_end (heap_segment* seg, BYTE* low)
26155 if ((low >= heap_segment_mem (seg)) &&
26156 (low < heap_segment_allocated (seg)))
26159 return heap_segment_allocated (seg);
26163 gc_heap::compute_next_boundary (BYTE* low, int gen_number,
26166 //when relocating, the fault line is the plan start of the younger
26167 //generation because the generation is promoted.
26168 if (relocating && (gen_number == (settings.condemned_generation + 1)))
26170 generation* gen = generation_of (gen_number - 1);
26171 BYTE* gen_alloc = generation_plan_allocation_start (gen);
26172 assert (gen_alloc);
26177 assert (gen_number > settings.condemned_generation);
26178 return generation_allocation_start (generation_of (gen_number - 1 ));
26184 gc_heap::keep_card_live (BYTE* o, size_t& n_gen,
26185 size_t& cg_pointers_found)
26188 if ((gc_low <= o) && (gc_high > o))
26192 #ifdef MULTIPLE_HEAPS
26195 gc_heap* hp = heap_of (o);
26198 if ((hp->gc_low <= o) &&
26205 #endif //MULTIPLE_HEAPS
26206 cg_pointers_found ++;
26207 dprintf (4, ("keep card live for %Ix", o));
26211 gc_heap::mark_through_cards_helper (BYTE** poo, size_t& n_gen,
26212 size_t& cg_pointers_found,
26213 card_fn fn, BYTE* nhigh,
26214 BYTE* next_boundary)
26217 if ((gc_low <= *poo) && (gc_high > *poo))
26220 call_fn(fn) (poo THREAD_NUMBER_ARG);
26222 #ifdef MULTIPLE_HEAPS
26225 gc_heap* hp = heap_of_gc (*poo);
26228 if ((hp->gc_low <= *poo) &&
26229 (hp->gc_high > *poo))
26232 call_fn(fn) (poo THREAD_NUMBER_ARG);
26234 if ((fn == &gc_heap::relocate_address) ||
26235 ((hp->ephemeral_low <= *poo) &&
26236 (hp->ephemeral_high > *poo)))
26238 cg_pointers_found++;
26242 #endif //MULTIPLE_HEAPS
26243 if ((next_boundary <= *poo) && (nhigh > *poo))
26245 cg_pointers_found ++;
26246 dprintf (4, ("cg pointer %Ix found, %Id so far",
26247 (size_t)*poo, cg_pointers_found ));
26252 BOOL gc_heap::card_transition (BYTE* po, BYTE* end, size_t card_word_end,
26253 size_t& cg_pointers_found,
26254 size_t& n_eph, size_t& n_card_set,
26255 size_t& card, size_t& end_card,
26256 BOOL& foundp, BYTE*& start_address,
26257 BYTE*& limit, size_t& n_cards_cleared)
26259 dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
26260 dprintf (3, ("ct: %Id cg", cg_pointers_found));
26261 BOOL passed_end_card_p = FALSE;
26264 if (cg_pointers_found == 0)
26266 //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
26267 dprintf(3,(" CC [%Ix, %Ix[ ",
26268 (size_t)card_address(card), (size_t)po));
26269 clear_cards (card, card_of(po));
26270 n_card_set -= (card_of (po) - card);
26271 n_cards_cleared += (card_of (po) - card);
26274 n_eph +=cg_pointers_found;
26275 cg_pointers_found = 0;
26276 card = card_of (po);
26277 if (card >= end_card)
26279 passed_end_card_p = TRUE;
26280 dprintf (3, ("card %Ix exceeding end_card %Ix",
26281 (size_t)card, (size_t)end_card));
26282 foundp = find_card (card_table, card, card_word_end, end_card);
26285 n_card_set+= end_card - card;
26286 start_address = card_address (card);
26287 dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
26288 (size_t)card, (size_t)start_address,
26289 (size_t)card_address (end_card)));
26291 limit = min (end, card_address (end_card));
26293 assert (!((limit == card_address (end_card))&&
26294 card_set_p (end_card)));
26297 return passed_end_card_p;
26300 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
26302 #ifdef BACKGROUND_GC
26303 dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
26304 current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
26305 heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26306 PREFIX_ASSUME(soh_seg != NULL);
26309 dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix",
26311 heap_segment_background_allocated (soh_seg),
26312 heap_segment_allocated (soh_seg)));
26313 soh_seg = heap_segment_next_rw (soh_seg);
26315 #endif //BACKGROUND_GC
26317 BYTE* low = gc_low;
26318 BYTE* high = gc_high;
26319 size_t end_card = 0;
26320 generation* oldest_gen = generation_of (max_generation);
26321 int curr_gen_number = max_generation;
26322 BYTE* gen_boundary = generation_allocation_start
26323 (generation_of (curr_gen_number - 1));
26324 BYTE* next_boundary = (compute_next_boundary
26325 (gc_low, curr_gen_number, relocating));
26326 heap_segment* seg = heap_segment_rw (generation_start_segment (oldest_gen));
26328 PREFIX_ASSUME(seg != NULL);
26330 BYTE* beg = generation_allocation_start (oldest_gen);
26331 BYTE* end = compute_next_end (seg, low);
26332 BYTE* last_object = beg;
26334 size_t cg_pointers_found = 0;
26336 size_t card_word_end = (card_of (align_on_card_word (end)) /
26341 size_t n_card_set = 0;
26342 BYTE* nhigh = (relocating ?
26343 heap_segment_plan_allocated (ephemeral_heap_segment) : high);
26345 BOOL foundp = FALSE;
26346 BYTE* start_address = 0;
26348 size_t card = card_of (beg);
26349 #ifdef BACKGROUND_GC
26350 BOOL consider_bgc_mark_p = FALSE;
26351 BOOL check_current_sweep_p = FALSE;
26352 BOOL check_saved_sweep_p = FALSE;
26353 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
26354 #endif //BACKGROUND_GC
26356 dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
26357 size_t total_cards_cleared = 0;
26361 if (card_of(last_object) > card)
26363 dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
26364 if (cg_pointers_found == 0)
26366 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
26367 clear_cards (card, card_of(last_object));
26368 n_card_set -= (card_of (last_object) - card);
26369 total_cards_cleared += (card_of (last_object) - card);
26371 n_eph +=cg_pointers_found;
26372 cg_pointers_found = 0;
26373 card = card_of (last_object);
26375 if (card >= end_card)
26377 foundp = find_card (card_table, card, card_word_end, end_card);
26380 n_card_set+= end_card - card;
26381 start_address = max (beg, card_address (card));
26383 limit = min (end, card_address (end_card));
26385 if ((!foundp) || (last_object >= end) || (card_address (card) >= end))
26387 if ((foundp) && (cg_pointers_found == 0))
26389 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
26391 clear_cards (card, card_of (end));
26392 n_card_set -= (card_of (end) - card);
26393 total_cards_cleared += (card_of (end) - card);
26395 n_eph +=cg_pointers_found;
26396 cg_pointers_found = 0;
26397 if ((seg = heap_segment_next_in_range (seg)) != 0)
26399 #ifdef BACKGROUND_GC
26400 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
26401 #endif //BACKGROUND_GC
26402 beg = heap_segment_mem (seg);
26403 end = compute_next_end (seg, low);
26404 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
26405 card = card_of (beg);
26416 assert (card_set_p (card));
26418 BYTE* o = last_object;
26420 o = find_first_object (start_address, last_object);
26421 //Never visit an object twice.
26422 assert (o >= last_object);
26424 //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
26425 dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
26426 card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
26430 assert (Align (size (o)) >= Align (min_obj_size));
26431 size_t s = size (o);
26433 BYTE* next_o = o + Align (s);
26436 if ((o >= gen_boundary) &&
26437 (seg == ephemeral_heap_segment))
26439 dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
26441 assert ((curr_gen_number > 0));
26442 gen_boundary = generation_allocation_start
26443 (generation_of (curr_gen_number - 1));
26444 next_boundary = (compute_next_boundary
26445 (low, curr_gen_number, relocating));
26448 dprintf (4, ("|%Ix|", (size_t)o));
26450 if (next_o < start_address)
26455 #ifdef BACKGROUND_GC
26456 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
26460 #endif //BACKGROUND_GC
26462 #ifdef COLLECTIBLE_CLASS
26463 if (is_collectible(o))
26465 BOOL passed_end_card_p = FALSE;
26467 if (card_of (o) > card)
26469 passed_end_card_p = card_transition (o, end, card_word_end,
26473 foundp, start_address,
26474 limit, total_cards_cleared);
26477 if ((!passed_end_card_p || foundp) && (card_of (o) == card))
26479 // card is valid and it covers the head of the object
26480 if (fn == &gc_heap::relocate_address)
26482 keep_card_live (o, n_gen, cg_pointers_found);
26486 BYTE* class_obj = get_class_object (o);
26487 mark_through_cards_helper (&class_obj, n_gen,
26488 cg_pointers_found, fn,
26489 nhigh, next_boundary);
26493 if (passed_end_card_p)
26495 if (foundp && (card_address (card) < next_o))
26497 goto go_through_refs;
26499 else if (foundp && (start_address < limit))
26501 next_o = find_first_object (start_address, o);
26510 #endif //COLLECTIBLE_CLASS
26512 if (contain_pointers (o))
26514 dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
26517 dprintf (4, ("normal object path"));
26519 (method_table(o), o, s, poo,
26520 start_address, use_start, (o + s),
26522 dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
26523 if (card_of ((BYTE*)poo) > card)
26525 BOOL passed_end_card_p = card_transition ((BYTE*)poo, end,
26530 foundp, start_address,
26531 limit, total_cards_cleared);
26533 if (passed_end_card_p)
26535 if (foundp && (card_address (card) < next_o))
26539 if (ppstop <= (BYTE**)start_address)
26541 else if (poo < (BYTE**)start_address)
26542 {poo = (BYTE**)start_address;}
26545 else if (foundp && (start_address < limit))
26547 next_o = find_first_object (start_address, o);
26555 mark_through_cards_helper (poo, n_gen,
26556 cg_pointers_found, fn,
26557 nhigh, next_boundary);
26564 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
26566 if (brick_table [brick_of (o)] <0)
26567 fix_brick_to_highest (o, next_o);
26575 // compute the efficiency ratio of the card table
26578 generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
26579 dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d",
26580 n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
26584 dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d",
26585 n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
26589 #ifdef SEG_REUSE_STATS
26590 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
26592 size_t total_items = 0;
26594 for (int i = 0; i < count; i++)
26596 total_items += ordered_indices[i];
26597 *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
26598 dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
26600 dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
26601 return total_items;
26603 #endif // SEG_REUSE_STATS
26605 void gc_heap::count_plug (size_t last_plug_size, BYTE*& last_plug)
26607 // detect pinned plugs
26608 if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
26610 deque_pinned_plug();
26611 update_oldest_pinned_plug();
26612 dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
26616 size_t plug_size = last_plug_size + Align(min_obj_size);
26617 BOOL is_padded = FALSE;
26620 plug_size += Align (min_obj_size);
26622 #endif //SHORT_PLUGS
26624 #ifdef RESPECT_LARGE_ALIGNMENT
26625 plug_size += switch_alignment_size (is_padded);
26626 #endif //RESPECT_LARGE_ALIGNMENT
26628 total_ephemeral_plugs += plug_size;
26629 size_t plug_size_power2 = round_up_power2 (plug_size);
26630 ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
26631 dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array",
26635 (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
26639 void gc_heap::count_plugs_in_brick (BYTE* tree, BYTE*& last_plug)
26641 assert ((tree != 0));
26642 if (node_left_child (tree))
26644 count_plugs_in_brick (tree + node_left_child (tree), last_plug);
26647 if (last_plug != 0)
26650 size_t gap_size = node_gap_size (plug);
26651 BYTE* gap = (plug - gap_size);
26652 BYTE* last_plug_end = gap;
26653 size_t last_plug_size = (last_plug_end - last_plug);
26654 dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
26655 tree, last_plug, gap_size, gap, last_plug_size));
26657 if (tree == oldest_pinned_plug)
26659 dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
26660 tree, last_plug, last_plug_size));
26661 mark* m = oldest_pin();
26662 if (m->has_pre_plug_info())
26664 last_plug_size += sizeof (gap_reloc_pair);
26665 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
26668 // Can't assert here - if it's a pinned plug it can be less.
26669 //assert (last_plug_size >= Align (min_obj_size));
26671 count_plug (last_plug_size, last_plug);
26676 if (node_right_child (tree))
26678 count_plugs_in_brick (tree + node_right_child (tree), last_plug);
26682 void gc_heap::build_ordered_plug_indices ()
26684 memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
26685 memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
26687 BYTE* start_address = generation_limit (max_generation);
26688 BYTE* end_address = heap_segment_allocated (ephemeral_heap_segment);
26689 size_t current_brick = brick_of (start_address);
26690 size_t end_brick = brick_of (end_address - 1);
26691 BYTE* last_plug = 0;
26693 //Look for the right pinned plug to start from.
26694 reset_pinned_queue_bos();
26695 while (!pinned_plug_que_empty_p())
26697 mark* m = oldest_pin();
26698 if ((m->first >= start_address) && (m->first < end_address))
26700 dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
26705 deque_pinned_plug();
26708 update_oldest_pinned_plug();
26710 while (current_brick <= end_brick)
26712 int brick_entry = brick_table [ current_brick ];
26713 if (brick_entry >= 0)
26715 count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
26723 count_plug (end_address - last_plug, last_plug);
26726 // we need to make sure that after fitting all the existing plugs, we
26727 // have big enough free space left to guarantee that the next allocation
26729 size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
26730 total_ephemeral_plugs += extra_size;
26731 dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
26732 ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
26734 memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
26736 #ifdef SEG_REUSE_STATS
26737 dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
26738 size_t total_plug_power2 = 0;
26739 dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
26740 dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))",
26741 total_ephemeral_plugs,
26743 (total_ephemeral_plugs ?
26744 (total_plug_power2 * 100 / total_ephemeral_plugs) :
26746 dprintf (SEG_REUSE_LOG_0, ("-------------------"));
26747 #endif // SEG_REUSE_STATS
26750 void gc_heap::init_ordered_free_space_indices ()
26752 memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
26753 memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
26756 void gc_heap::trim_free_spaces_indices ()
26758 trimmed_free_space_index = -1;
26759 size_t max_count = max_free_space_items - 1;
26762 for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
26764 count += ordered_free_space_indices[i];
26766 if (count >= max_count)
26772 SSIZE_T extra_free_space_items = count - max_count;
26774 if (extra_free_space_items > 0)
26776 ordered_free_space_indices[i] -= extra_free_space_items;
26777 free_space_items = max_count;
26778 trimmed_free_space_index = i;
26782 free_space_items = count;
26790 free_space_buckets = MAX_NUM_BUCKETS - i;
26792 for (--i; i >= 0; i--)
26794 ordered_free_space_indices[i] = 0;
26797 memcpy (saved_ordered_free_space_indices,
26798 ordered_free_space_indices,
26799 sizeof(ordered_free_space_indices));
26802 // We fit as many plugs as we can and update the number of plugs left and the number
26803 // of free spaces left.
26804 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
26806 assert (small_index <= big_index);
26807 assert (big_index < MAX_NUM_BUCKETS);
26809 size_t small_blocks = ordered_blocks[small_index];
26811 if (small_blocks == 0)
26816 size_t big_spaces = ordered_spaces[big_index];
26818 if (big_spaces == 0)
26823 dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces",
26825 small_blocks, (small_index + MIN_INDEX_POWER2),
26826 big_spaces, (big_index + MIN_INDEX_POWER2)));
26828 size_t big_to_small = big_spaces << (big_index - small_index);
26830 SSIZE_T extra_small_spaces = big_to_small - small_blocks;
26831 dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks",
26833 big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
26834 BOOL can_fit = (extra_small_spaces >= 0);
26838 dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks",
26840 extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
26845 dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
26846 ordered_spaces[big_index] = 0;
26847 if (extra_small_spaces > 0)
26849 dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
26850 ordered_blocks[small_index] = 0;
26851 for (i = small_index; i < big_index; i++)
26853 if (extra_small_spaces & 1)
26855 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d",
26857 (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
26858 ordered_spaces[i] += 1;
26860 extra_small_spaces >>= 1;
26863 dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d",
26865 (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
26866 ordered_spaces[i] += extra_small_spaces;
26870 dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d",
26872 (small_index + MIN_INDEX_POWER2),
26873 ordered_blocks[small_index],
26874 (ordered_blocks[small_index] - big_to_small)));
26875 ordered_blocks[small_index] -= big_to_small;
26878 #ifdef SEG_REUSE_STATS
26880 dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
26881 dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
26883 dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
26884 dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
26885 #endif //SEG_REUSE_STATS
26890 // space_index gets updated to the biggest available space index.
26891 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
26893 assert (*space_index >= block_index);
26895 while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
26898 if (*space_index < block_index)
26907 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
26909 #ifdef FEATURE_STRUCTALIGN
26910 // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
26912 #endif // FEATURE_STRUCTALIGN
26913 int space_index = count - 1;
26914 for (int block_index = (count - 1); block_index >= 0; block_index--)
26916 if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
26925 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
26927 assert (bestfit_seg);
26929 //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2,
26930 // ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets),
26931 // free_space_buckets,
26932 // free_space_items);
26934 bestfit_seg->add_buckets (MIN_INDEX_POWER2,
26935 ordered_free_space_indices,
26939 assert (settings.condemned_generation == max_generation);
26941 BYTE* first_address = heap_segment_mem (seg);
26942 BYTE* end_address = heap_segment_reserved (seg);
26943 //look through the pinned plugs for relevant ones.
26944 //Look for the right pinned plug to start from.
26945 reset_pinned_queue_bos();
26947 // See comment in can_expand_into_p why we need (max_generation + 1).
26948 size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
26949 BOOL has_fit_gen_starts = FALSE;
26951 while (!pinned_plug_que_empty_p())
26954 if ((pinned_plug (m) >= first_address) &&
26955 (pinned_plug (m) < end_address) &&
26956 (pinned_len (m) >= eph_gen_starts))
26959 assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
26964 deque_pinned_plug();
26968 if (!pinned_plug_que_empty_p())
26970 bestfit_seg->add ((void*)m, TRUE, TRUE);
26971 deque_pinned_plug();
26973 has_fit_gen_starts = TRUE;
26976 while (!pinned_plug_que_empty_p() &&
26977 ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
26979 bestfit_seg->add ((void*)m, TRUE, FALSE);
26980 deque_pinned_plug();
26984 if (commit_end_of_seg)
26986 if (!has_fit_gen_starts)
26988 assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
26990 bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
26994 bestfit_seg->check();
26998 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
27000 if (!end_of_segment_p)
27002 trim_free_spaces_indices ();
27005 BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices,
27006 ordered_free_space_indices,
27009 return can_bestfit;
27012 BOOL gc_heap::best_fit (size_t free_space,
27013 size_t largest_free_space,
27014 size_t additional_space,
27015 BOOL* use_additional_space)
27017 dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
27019 assert (!additional_space || (additional_space && use_additional_space));
27020 if (use_additional_space)
27022 *use_additional_space = FALSE;
27025 if (ordered_plug_indices_init == FALSE)
27027 total_ephemeral_plugs = 0;
27028 build_ordered_plug_indices();
27029 ordered_plug_indices_init = TRUE;
27033 memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
27036 if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
27038 dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
27039 size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
27040 BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
27041 if (!can_fit_empty_eph)
27043 can_fit_empty_eph = (additional_space >= empty_eph);
27045 if (can_fit_empty_eph)
27047 *use_additional_space = TRUE;
27051 return can_fit_empty_eph;
27054 if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
27056 dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
27060 if ((free_space + additional_space) == 0)
27062 dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
27066 #ifdef SEG_REUSE_STATS
27067 dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
27068 size_t total_free_space_power2 = 0;
27069 size_t total_free_space_items =
27070 dump_buckets (ordered_free_space_indices,
27072 &total_free_space_power2);
27073 dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
27075 dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
27076 total_ephemeral_plugs,
27078 total_free_space_power2,
27079 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
27080 additional_space));
27082 size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
27083 memcpy (saved_all_free_space_indices,
27084 ordered_free_space_indices,
27085 sizeof(saved_all_free_space_indices));
27087 #endif // SEG_REUSE_STATS
27089 if (total_ephemeral_plugs > (free_space + additional_space))
27094 use_bestfit = try_best_fit(FALSE);
27096 if (!use_bestfit && additional_space)
27098 int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
27100 if (relative_free_space_index != -1)
27102 int relative_plug_index = 0;
27103 size_t plugs_to_fit = 0;
27105 for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
27107 plugs_to_fit = ordered_plug_indices[relative_plug_index];
27108 if (plugs_to_fit != 0)
27114 if ((relative_plug_index > relative_free_space_index) ||
27115 ((relative_plug_index == relative_free_space_index) &&
27116 (plugs_to_fit > 1)))
27118 #ifdef SEG_REUSE_STATS
27119 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
27120 (relative_free_space_index + MIN_INDEX_POWER2),
27122 (relative_plug_index + MIN_INDEX_POWER2)));
27123 #endif // SEG_REUSE_STATS
27127 dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
27128 ordered_free_space_indices[relative_free_space_index]++;
27129 use_bestfit = try_best_fit(TRUE);
27132 free_space_items++;
27133 // Since we might've trimmed away some of the free spaces we had, we should see
27134 // if we really need to use end of seg space - if it's the same or smaller than
27135 // the largest space we trimmed we can just add that one back instead of
27136 // using end of seg.
27137 if (relative_free_space_index > trimmed_free_space_index)
27139 *use_additional_space = TRUE;
27143 // If the addition space is <= than the last trimmed space, we
27144 // should just use that last trimmed space instead.
27145 saved_ordered_free_space_indices[trimmed_free_space_index]++;
27155 dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
27157 #ifdef SEG_REUSE_STATS
27158 size_t saved_max = max_free_space_items;
27159 BOOL temp_bestfit = FALSE;
27161 dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
27162 dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
27164 // TODO: need to take the end of segment into consideration.
27165 while (max_free_space_items <= total_free_space_items)
27167 max_free_space_items += max_free_space_items / 2;
27168 dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
27169 memcpy (ordered_free_space_indices,
27170 saved_all_free_space_indices,
27171 sizeof(ordered_free_space_indices));
27172 if (try_best_fit(FALSE))
27174 temp_bestfit = TRUE;
27181 dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
27185 dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
27188 dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
27189 max_free_space_items = saved_max;
27190 #endif // SEG_REUSE_STATS
27191 if (free_space_items)
27193 max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
27194 max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
27198 max_free_space_items = MAX_NUM_FREE_SPACES;
27202 dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
27203 dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
27205 return use_bestfit;
27208 BOOL gc_heap::process_free_space (heap_segment* seg,
27210 size_t min_free_size,
27211 size_t min_cont_size,
27212 size_t* total_free_space,
27213 size_t* largest_free_space)
27215 *total_free_space += free_space;
27216 *largest_free_space = max (*largest_free_space, free_space);
27218 #ifdef SIMPLE_DPRINTF
27219 dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix",
27220 free_space, *total_free_space, *largest_free_space));
27221 #endif //SIMPLE_DPRINTF
27223 if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
27225 #ifdef SIMPLE_DPRINTF
27226 dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit",
27227 settings.condemned_generation,
27228 *total_free_space, min_free_size, *largest_free_space, min_cont_size,
27230 #endif //SIMPLE_DPRINTF
27234 int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
27235 if (free_space_index != -1)
27237 ordered_free_space_indices[free_space_index]++;
27242 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
27243 allocator* gen_allocator)
27245 min_cont_size += END_SPACE_AFTER_GC;
27246 use_bestfit = FALSE;
27247 commit_end_of_seg = FALSE;
27248 bestfit_first_pin = 0;
27249 BYTE* first_address = heap_segment_mem (seg);
27250 BYTE* end_address = heap_segment_reserved (seg);
27251 size_t end_extra_space = end_space_after_gc();
27253 if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
27255 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
27256 first_address, end_address, end_extra_space));
27260 end_address -= end_extra_space;
27262 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix",
27263 settings.condemned_generation, min_free_size, min_cont_size));
27264 size_t eph_gen_starts = eph_gen_starts_size;
27266 if (settings.condemned_generation == max_generation)
27268 size_t free_space = 0;
27269 size_t largest_free_space = free_space;
27270 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
27271 //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from.
27272 //We are going to allocate the generation starts in the 1st free space,
27273 //so start from the first free space that's big enough for gen starts and a min object size.
27274 // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it -
27275 // we could use it by allocating the last generation start a bit bigger but
27276 // the complexity isn't worth the effort (those plugs are from gen2
27277 // already anyway).
27278 reset_pinned_queue_bos();
27280 BOOL has_fit_gen_starts = FALSE;
27282 init_ordered_free_space_indices ();
27283 while (!pinned_plug_que_empty_p())
27286 if ((pinned_plug (m) >= first_address) &&
27287 (pinned_plug (m) < end_address) &&
27288 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
27294 deque_pinned_plug();
27298 if (!pinned_plug_que_empty_p())
27300 bestfit_first_pin = pinned_plug (m) - pinned_len (m);
27302 if (process_free_space (seg,
27303 pinned_len (m) - eph_gen_starts,
27304 min_free_size, min_cont_size,
27305 &free_space, &largest_free_space))
27310 deque_pinned_plug();
27312 has_fit_gen_starts = TRUE;
27315 dprintf (3, ("first pin is %Ix", pinned_plug (m)));
27317 //tally up free space
27318 while (!pinned_plug_que_empty_p() &&
27319 ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
27321 dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
27322 if (process_free_space (seg,
27324 min_free_size, min_cont_size,
27325 &free_space, &largest_free_space))
27330 deque_pinned_plug();
27334 //try to find space at the end of the segment.
27335 size_t end_space = (end_address - heap_segment_plan_allocated (seg));
27336 size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0);
27337 dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
27338 if (end_space >= additional_space)
27340 BOOL can_fit = TRUE;
27341 commit_end_of_seg = TRUE;
27343 if (largest_free_space < min_cont_size)
27345 if (end_space >= min_cont_size)
27347 additional_space = max (min_cont_size, additional_space);
27348 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph",
27353 if (settings.concurrent)
27356 commit_end_of_seg = FALSE;
27360 size_t additional_space_bestfit = additional_space;
27361 if (!has_fit_gen_starts)
27363 if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
27365 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
27366 additional_space_bestfit));
27370 bestfit_first_pin = heap_segment_plan_allocated (seg);
27371 additional_space_bestfit -= eph_gen_starts;
27374 can_fit = best_fit (free_space,
27375 largest_free_space,
27376 additional_space_bestfit,
27377 &commit_end_of_seg);
27381 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg",
27382 seg, (commit_end_of_seg ? "with" : "without")));
27386 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
27393 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
27396 assert (additional_space <= end_space);
27397 if (commit_end_of_seg)
27399 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
27401 dprintf (2, ("Couldn't commit end of segment?!"));
27402 use_bestfit = FALSE;
27409 // We increase the index here because growing heap segment could create a discrepency with
27410 // the additional space we used (could be bigger).
27411 size_t free_space_end_of_seg =
27412 heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
27413 int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
27414 saved_ordered_free_space_indices[relative_free_space_index]++;
27420 memcpy (ordered_free_space_indices,
27421 saved_ordered_free_space_indices,
27422 sizeof(ordered_free_space_indices));
27423 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
27424 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
27425 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
27431 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
27436 assert (settings.condemned_generation == (max_generation-1));
27437 size_t free_space = (end_address - heap_segment_plan_allocated (seg));
27438 size_t largest_free_space = free_space;
27439 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
27440 //find the first free list in range of the current segment
27441 size_t sz_list = gen_allocator->first_bucket_size();
27442 unsigned int a_l_idx = 0;
27443 BYTE* free_list = 0;
27444 for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
27446 if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
27448 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
27451 if ((free_list >= first_address) &&
27452 (free_list < end_address) &&
27453 (unused_array_size (free_list) >= eph_gen_starts))
27459 free_list = free_list_slot (free_list);
27467 init_ordered_free_space_indices ();
27468 if (process_free_space (seg,
27469 unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size),
27470 min_free_size, min_cont_size,
27471 &free_space, &largest_free_space))
27476 free_list = free_list_slot (free_list);
27480 dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
27484 //tally up free space
27490 if ((free_list >= first_address) && (free_list < end_address) &&
27491 process_free_space (seg,
27492 unused_array_size (free_list),
27493 min_free_size, min_cont_size,
27494 &free_space, &largest_free_space))
27499 free_list = free_list_slot (free_list);
27502 if (a_l_idx < gen_allocator->number_of_buckets())
27504 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
27510 dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
27514 BOOL can_fit = best_fit (free_space, 0, NULL);
27517 dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
27521 dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
27529 void gc_heap::realloc_plug (size_t last_plug_size, BYTE*& last_plug,
27530 generation* gen, BYTE* start_address,
27531 unsigned int& active_new_gen_number,
27532 BYTE*& last_pinned_gap, BOOL& leftp,
27535 , mark* pinned_plug_entry
27536 #endif //SHORT_PLUGS
27539 // detect generation boundaries
27540 // make sure that active_new_gen_number is not the youngest generation.
27541 // because the generation_limit wouldn't return the right thing in this case.
27544 if ((active_new_gen_number > 1) &&
27545 (last_plug >= generation_limit (active_new_gen_number)))
27547 assert (last_plug >= start_address);
27548 active_new_gen_number--;
27549 realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
27550 assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
27555 // detect pinned plugs
27556 if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
27558 size_t entry = deque_pinned_plug();
27559 mark* m = pinned_plug_of (entry);
27561 size_t saved_pinned_len = pinned_len(m);
27562 pinned_len(m) = last_plug - last_pinned_gap;
27563 //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
27565 if (m->has_post_plug_info())
27567 last_plug_size += sizeof (gap_reloc_pair);
27568 dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
27571 last_pinned_gap = last_plug + last_plug_size;
27572 dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
27573 pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
27576 //we are creating a generation fault. set the cards.
27578 size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
27579 size_t card = card_of (last_plug);
27580 while (card != end_card)
27587 else if (last_plug >= start_address)
27591 clear_plug_padded (last_plug);
27592 #endif //SHORT_PLUGS
27594 #ifdef FEATURE_STRUCTALIGN
27595 int requiredAlignment;
27597 node_aligninfo (last_plug, requiredAlignment, pad);
27599 // from how we previously aligned the plug's destination address,
27600 // compute the actual alignment offset.
27601 BYTE* reloc_plug = last_plug + node_relocation_distance (last_plug);
27602 ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
27603 if (!alignmentOffset)
27605 // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
27606 alignmentOffset = requiredAlignment;
27609 //clear the alignment info because we are reallocating
27610 clear_node_aligninfo (last_plug);
27611 #else // FEATURE_STRUCTALIGN
27612 //clear the realignment flag because we are reallocating
27613 clear_node_realigned (last_plug);
27614 #endif // FEATURE_STRUCTALIGN
27615 BOOL adjacentp = FALSE;
27616 BOOL set_padding_on_saved_p = FALSE;
27620 assert (pinned_plug_entry != NULL);
27622 last_plug_size += sizeof (gap_reloc_pair);
27625 if (last_plug_size <= sizeof (plug_and_gap))
27627 set_padding_on_saved_p = TRUE;
27629 #endif //SHORT_PLUGS
27631 dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug_size))
27634 BYTE* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
27636 set_padding_on_saved_p,
27638 #endif //SHORT_PLUGS
27639 TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
27641 dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
27642 assert (new_address);
27643 set_node_relocation_distance (last_plug, new_address - last_plug);
27644 #ifdef FEATURE_STRUCTALIGN
27645 if (leftp && node_alignpad (last_plug) == 0)
27646 #else // FEATURE_STRUCTALIGN
27647 if (leftp && !node_realigned (last_plug))
27648 #endif // FEATURE_STRUCTALIGN
27650 // TODO - temporarily disable L optimization because of a bug in it.
27651 //set_node_left (last_plug);
27653 dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
27658 void gc_heap::realloc_in_brick (BYTE* tree, BYTE*& last_plug,
27659 BYTE* start_address,
27661 unsigned int& active_new_gen_number,
27662 BYTE*& last_pinned_gap, BOOL& leftp)
27664 assert (tree >= 0);
27665 int left_node = node_left_child (tree);
27666 int right_node = node_right_child (tree);
27668 dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d",
27669 tree, last_pinned_gap, last_plug, left_node, right_node));
27673 dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
27674 realloc_in_brick ((tree + left_node), last_plug, start_address,
27675 gen, active_new_gen_number, last_pinned_gap,
27679 if (last_plug != 0)
27683 BOOL has_pre_plug_info_p = FALSE;
27684 BOOL has_post_plug_info_p = FALSE;
27685 mark* pinned_plug_entry = get_next_pinned_entry (tree,
27686 &has_pre_plug_info_p,
27687 &has_post_plug_info_p,
27690 // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
27691 // The pinned plugs are handled in realloc_plug.
27692 size_t gap_size = node_gap_size (plug);
27693 BYTE* gap = (plug - gap_size);
27694 BYTE* last_plug_end = gap;
27695 size_t last_plug_size = (last_plug_end - last_plug);
27696 // Cannot assert this - a plug could be less than that due to the shortened ones.
27697 //assert (last_plug_size >= Align (min_obj_size));
27698 dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
27699 plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
27700 realloc_plug (last_plug_size, last_plug, gen, start_address,
27701 active_new_gen_number, last_pinned_gap,
27702 leftp, has_pre_plug_info_p
27704 , pinned_plug_entry
27705 #endif //SHORT_PLUGS
27713 dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
27714 realloc_in_brick ((tree + right_node), last_plug, start_address,
27715 gen, active_new_gen_number, last_pinned_gap,
27721 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
27722 BYTE* start_address, BYTE* end_address,
27723 unsigned active_new_gen_number)
27725 dprintf (3, ("--- Reallocing ---"));
27729 //make sure that every generation has a planned allocation start
27730 int gen_number = max_generation - 1;
27731 while (gen_number >= 0)
27733 generation* gen = generation_of (gen_number);
27734 if (0 == generation_plan_allocation_start (gen))
27736 generation_plan_allocation_start (gen) =
27737 bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
27738 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
27739 assert (generation_plan_allocation_start (gen));
27745 BYTE* first_address = start_address;
27746 //Look for the right pinned plug to start from.
27747 reset_pinned_queue_bos();
27748 BYTE* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
27749 while (!pinned_plug_que_empty_p())
27751 mark* m = oldest_pin();
27752 if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
27754 if (pinned_plug (m) < first_address)
27756 first_address = pinned_plug (m);
27761 deque_pinned_plug();
27764 size_t current_brick = brick_of (first_address);
27765 size_t end_brick = brick_of (end_address-1);
27766 BYTE* last_plug = 0;
27768 BYTE* last_pinned_gap = heap_segment_plan_allocated (seg);
27769 BOOL leftp = FALSE;
27771 dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
27772 start_address, first_address, pinned_plug (oldest_pin())));
27774 while (current_brick <= end_brick)
27776 int brick_entry = brick_table [ current_brick ];
27777 if (brick_entry >= 0)
27779 realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
27780 last_plug, start_address, consing_gen,
27781 active_new_gen_number, last_pinned_gap,
27787 if (last_plug != 0)
27789 realloc_plug (end_address - last_plug, last_plug, consing_gen,
27791 active_new_gen_number, last_pinned_gap,
27795 #endif //SHORT_PLUGS
27799 //Fix the old segment allocated size
27800 assert (last_pinned_gap >= heap_segment_mem (seg));
27801 assert (last_pinned_gap <= heap_segment_committed (seg));
27802 heap_segment_plan_allocated (seg) = last_pinned_gap;
27805 void gc_heap::verify_no_pins (BYTE* start, BYTE* end)
27808 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
27810 BOOL contains_pinned_plugs = FALSE;
27813 while (mi != mark_stack_tos)
27815 m = pinned_plug_of (mi);
27816 if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
27818 contains_pinned_plugs = TRUE;
27825 if (contains_pinned_plugs)
27830 #endif //VERIFY_HEAP
27833 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
27835 if (!should_expand_in_full_gc)
27837 if ((condemned_gen_number != max_generation) &&
27838 (settings.pause_mode != pause_low_latency) &&
27839 (settings.pause_mode != pause_sustained_low_latency))
27841 should_expand_in_full_gc = TRUE;
27846 void gc_heap::save_ephemeral_generation_starts()
27848 for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
27850 saved_ephemeral_plan_start[ephemeral_generation] =
27851 generation_plan_allocation_start (generation_of (ephemeral_generation));
27852 saved_ephemeral_plan_start_size[ephemeral_generation] =
27853 generation_plan_allocation_start_size (generation_of (ephemeral_generation));
27857 generation* gc_heap::expand_heap (int condemned_generation,
27858 generation* consing_gen,
27859 heap_segment* new_heap_segment)
27861 assert (condemned_generation >= (max_generation -1));
27862 unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
27863 BYTE* start_address = generation_limit (max_generation);
27864 BYTE* end_address = heap_segment_allocated (ephemeral_heap_segment);
27865 BOOL should_promote_ephemeral = FALSE;
27866 ptrdiff_t eph_size = total_ephemeral_size;
27867 #ifdef BACKGROUND_GC
27868 dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
27869 #endif //BACKGROUND_GC
27870 settings.heap_expansion = TRUE;
27872 #ifdef BACKGROUND_GC
27873 if (cm_in_progress)
27875 if (!expanded_in_fgc)
27877 expanded_in_fgc = TRUE;
27880 #endif //BACKGROUND_GC
27882 //reset the elevation state for next time.
27883 dprintf (2, ("Elevation: elevation = el_none"));
27884 settings.should_lock_elevation = FALSE;
27886 heap_segment* new_seg = new_heap_segment;
27889 return consing_gen;
27891 //copy the card and brick tables
27892 if (g_card_table!= card_table)
27893 copy_brick_card_table (TRUE);
27895 BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
27896 dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
27898 assert (generation_plan_allocation_start (generation_of (max_generation-1)));
27899 assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
27900 heap_segment_mem (ephemeral_heap_segment));
27901 assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
27902 heap_segment_committed (ephemeral_heap_segment));
27904 assert (generation_plan_allocation_start (youngest_generation));
27905 assert (generation_plan_allocation_start (youngest_generation) <
27906 heap_segment_plan_allocated (ephemeral_heap_segment));
27910 should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
27913 if (should_promote_ephemeral)
27915 ephemeral_promotion = TRUE;
27916 gc_data_per_heap.clear_mechanism (gc_heap_expand);
27917 gc_data_per_heap.set_mechanism (gc_heap_expand, expand_new_seg_ep);
27918 dprintf (2, ("promoting ephemeral"));
27919 save_ephemeral_generation_starts();
27923 // commit the new ephemeral segment all at once if it is a new one.
27924 if ((eph_size > 0) && new_segment_p)
27926 #ifdef FEATURE_STRUCTALIGN
27927 // The destination may require a larger alignment padding than the source.
27928 // Assume the worst possible alignment padding.
27929 eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
27930 #endif // FEATURE_STRUCTALIGN
27931 #ifdef RESPECT_LARGE_ALIGNMENT
27932 //Since the generation start can be larger than min_obj_size
27933 //The alignment could be switched.
27934 eph_size += switch_alignment_size(FALSE);
27935 #endif //RESPECT_LARGE_ALIGNMENT
27936 //Since the generation start can be larger than min_obj_size
27937 //Compare the alignemnt of the first object in gen1
27938 if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
27940 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
27941 return consing_gen;
27943 heap_segment_used (new_seg) = heap_segment_committed (new_seg);
27946 //Fix the end of the old ephemeral heap segment
27947 heap_segment_plan_allocated (ephemeral_heap_segment) =
27948 generation_plan_allocation_start (generation_of (max_generation-1));
27950 dprintf (3, ("Old ephemeral allocated set to %Ix",
27951 (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
27956 // TODO - Is this really necessary? We should think about it.
27957 //initialize the first brick
27958 size_t first_brick = brick_of (heap_segment_mem (new_seg));
27959 set_brick (first_brick,
27960 heap_segment_mem (new_seg) - brick_address (first_brick));
27963 //From this point on, we cannot run out of memory
27965 //reset the allocation of the consing generation back to the end of the
27966 //old ephemeral segment
27967 generation_allocation_limit (consing_gen) =
27968 heap_segment_plan_allocated (ephemeral_heap_segment);
27969 generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
27970 generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
27972 //clear the generation gap for all of the ephemeral generations
27974 int generation_num = max_generation-1;
27975 while (generation_num >= 0)
27977 generation* gen = generation_of (generation_num);
27978 generation_plan_allocation_start (gen) = 0;
27983 heap_segment* old_seg = ephemeral_heap_segment;
27984 ephemeral_heap_segment = new_seg;
27986 //Note: the ephemeral segment shouldn't be threaded onto the segment chain
27987 //because the relocation and compact phases shouldn't see it
27989 // set the generation members used by allocate_in_expanded_heap
27990 // and switch to ephemeral generation
27991 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
27993 if (!should_promote_ephemeral)
27995 realloc_plugs (consing_gen, old_seg, start_address, end_address,
27996 active_new_gen_number);
28001 repair_allocation_in_expanded_heap (consing_gen);
28004 // assert that the generation gap for all of the ephemeral generations were allocated.
28007 int generation_num = max_generation-1;
28008 while (generation_num >= 0)
28010 generation* gen = generation_of (generation_num);
28011 assert (generation_plan_allocation_start (gen));
28017 if (!new_segment_p)
28019 dprintf (2, ("Demoting ephemeral segment"));
28020 //demote the entire segment.
28021 settings.demotion = TRUE;
28022 demotion_low = heap_segment_mem (ephemeral_heap_segment);
28023 demotion_high = heap_segment_reserved (ephemeral_heap_segment);
28027 demotion_low = MAX_PTR;
28029 #ifndef MULTIPLE_HEAPS
28030 settings.demotion = FALSE;
28031 #endif //!MULTIPLE_HEAPS
28033 ptrdiff_t eph_size1 = total_ephemeral_size;
28034 MAYBE_UNUSED_VAR(eph_size1);
28036 if (!should_promote_ephemeral && new_segment_p)
28038 assert (eph_size1 <= eph_size);
28041 if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
28043 // This is to catch when we accidently delete a segment that has pins.
28044 verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
28047 verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
28049 dprintf(2,("---- End of Heap Expansion ----"));
28050 return consing_gen;
28053 bool gc_heap::init_dynamic_data()
28056 if (!QueryPerformanceFrequency(&qpf))
28061 if (!QueryPerformanceCounter(&ts))
28066 DWORD now = (DWORD)(ts.QuadPart/(qpf.QuadPart/1000));
28068 //clear some fields
28069 for (int i = 0; i < max_generation+1; i++)
28071 dynamic_data* dd = dynamic_data_of (i);
28073 dd->time_clock = now;
28076 // get the registry setting for generation 0 size
28077 size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
28079 dprintf (2, ("gen 0 size: %Id", gen0size));
28081 dynamic_data* dd = dynamic_data_of (0);
28082 dd->current_size = 0;
28083 dd->promoted_size = 0;
28084 dd->collection_count = 0;
28085 // dd->limit = 3.0f;
28086 #ifdef MULTIPLE_HEAPS
28087 dd->limit = 20.0f; // be more aggressive on server gc
28088 dd->max_limit = 40.0f;
28091 // dd->max_limit = 15.0f; //10.0f;
28092 dd->max_limit = 20.0f;
28093 #endif //MULTIPLE_HEAPS
28094 dd->min_gc_size = Align(gen0size / 8 * 5);
28095 dd->min_size = dd->min_gc_size;
28096 //dd->max_size = Align (gen0size);
28097 //g_pConfig->GetGCconcurrent() is not necessarily 0 for server builds
28098 #ifdef MULTIPLE_HEAPS
28099 dd->max_size = max (6*1024*1024, min ( Align(get_valid_segment_size()/2), 200*1024*1024));
28100 #else //MULTIPLE_HEAPS
28101 dd->max_size = ((g_pConfig->GetGCconcurrent()!=0) ?
28103 max (6*1024*1024, min ( Align(get_valid_segment_size()/2), 200*1024*1024)));
28104 #endif //MULTIPLE_HEAPS
28105 dd->new_allocation = dd->min_gc_size;
28106 dd->gc_new_allocation = dd->new_allocation;
28107 dd->desired_allocation = dd->new_allocation;
28108 dd->default_new_allocation = dd->min_gc_size;
28109 dd->fragmentation = 0;
28110 dd->fragmentation_limit = 40000;
28111 dd->fragmentation_burden_limit = 0.5f;
28113 dd = dynamic_data_of (1);
28114 dd->current_size = 0;
28115 dd->promoted_size = 0;
28116 dd->collection_count = 0;
28118 // dd->max_limit = 15.0f;
28119 dd->max_limit = 7.0f;
28120 dd->min_gc_size = 9*32*1024;
28121 dd->min_size = dd->min_gc_size;
28122 // dd->max_size = 2397152;
28123 #ifdef MULTIPLE_HEAPS
28124 dd->max_size = max (6*1024*1024, Align(get_valid_segment_size()/2));
28125 #else //MULTIPLE_HEAPS
28126 dd->max_size = ((g_pConfig->GetGCconcurrent()!=0) ?
28128 max (6*1024*1024, Align(get_valid_segment_size()/2)));
28129 #endif //MULTIPLE_HEAPS
28130 dd->new_allocation = dd->min_gc_size;
28131 dd->gc_new_allocation = dd->new_allocation;
28132 dd->desired_allocation = dd->new_allocation;
28133 dd->default_new_allocation = dd->min_gc_size;
28134 dd->fragmentation = 0;
28135 dd->fragmentation_limit = 80000;
28136 dd->fragmentation_burden_limit = 0.5f;
28138 dd = dynamic_data_of (2);
28139 dd->current_size = 0;
28140 dd->promoted_size = 0;
28141 dd->collection_count = 0;
28143 dd->max_limit = 1.8f;
28144 dd->min_gc_size = 256*1024;
28145 dd->min_size = dd->min_gc_size;
28146 dd->max_size = SSIZE_T_MAX;
28147 dd->new_allocation = dd->min_gc_size;
28148 dd->gc_new_allocation = dd->new_allocation;
28149 dd->desired_allocation = dd->new_allocation;
28150 dd->default_new_allocation = dd->min_gc_size;
28151 dd->fragmentation = 0;
28152 dd->fragmentation_limit = 200000;
28153 dd->fragmentation_burden_limit = 0.25f;
28155 //dynamic data for large objects
28156 dd = dynamic_data_of (3);
28157 dd->current_size = 0;
28158 dd->promoted_size = 0;
28159 dd->collection_count = 0;
28161 dd->max_limit = 4.5f;
28162 dd->min_gc_size = 3*1024*1024;
28163 dd->min_size = dd->min_gc_size;
28164 dd->max_size = SSIZE_T_MAX;
28165 dd->new_allocation = dd->min_gc_size;
28166 dd->gc_new_allocation = dd->new_allocation;
28167 dd->desired_allocation = dd->new_allocation;
28168 dd->default_new_allocation = dd->min_gc_size;
28169 dd->fragmentation = 0;
28170 dd->fragmentation_limit = 0;
28171 dd->fragmentation_burden_limit = 0.0f;
28176 // This returns a time stamp in milliseconds that is used throughout GC.
28177 // TODO: Replace all calls to QueryPerformanceCounter with this function.
28178 size_t gc_heap::get_time_now()
28181 if (!QueryPerformanceCounter(&ts))
28184 return (size_t)(ts.QuadPart/(qpf.QuadPart/1000));
28187 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
28189 if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
28190 return ((limit - limit*cst) / (1.0f - (cst * limit)));
28196 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may
28197 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous
28198 //value of the budget
28199 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation,
28200 size_t previous_desired_allocation, size_t collection_count)
28202 if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
28204 dprintf (2, ("allocation fraction: %d%", (int)(allocation_fraction/100.0)));
28205 new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
28208 size_t smoothing = 3; // exponential smoothing factor
28209 if (smoothing > collection_count)
28210 smoothing = collection_count;
28211 new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
28213 return new_allocation;
28216 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
28217 size_t out, int gen_number,
28220 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
28222 if (dd_begin_data_size (dd) == 0)
28224 size_t new_allocation = dd_default_new_allocation (dd);
28225 current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;
28226 if ((gen_number == 0) && (pass == 1))
28228 current_gc_data_per_heap->gen_data[max_generation+2].new_allocation = new_allocation;
28231 return new_allocation;
28236 size_t previous_desired_allocation = dd_desired_allocation (dd);
28237 //ptrdiff_t allocation = (previous_desired_allocation - dd_gc_new_allocation (dd));
28238 ptrdiff_t allocation = (previous_desired_allocation - dd_new_allocation (dd));
28239 size_t current_size = dd_current_size (dd);
28240 float max_limit = dd_max_limit (dd);
28241 float limit = dd_limit (dd);
28242 size_t min_gc_size = dd_min_gc_size (dd);
28244 size_t max_size = dd_max_size (dd);
28245 size_t new_allocation = 0;
28246 float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
28247 if (gen_number >= max_generation)
28249 size_t new_size = 0;
28251 cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
28253 f = surv_to_growth (cst, limit, max_limit);
28254 size_t max_growth_size = (size_t)(max_size / f);
28255 if (current_size >= max_growth_size)
28257 new_size = max_size;
28261 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
28264 assert ((new_size >= current_size) || (new_size == max_size));
28266 if (gen_number == max_generation)
28268 new_allocation = max((new_size - current_size), min_gc_size);
28270 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
28271 dd_desired_allocation (dd), dd_collection_count (dd));
28273 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
28275 //reducing allocation in case of fragmentation
28276 size_t new_allocation1 = max (min_gc_size,
28278 (size_t)((float)new_allocation * current_size /
28279 ((float)current_size + 2*dd_fragmentation (dd))));
28280 dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
28281 new_allocation, new_allocation1));
28282 new_allocation = new_allocation1;
28285 else //large object heap
28288 GetProcessMemoryLoad (&ms);
28289 ULONGLONG available_ram = ms.ullAvailPhys;
28291 if (ms.ullAvailPhys > 1024*1024)
28292 available_ram -= 1024*1024;
28294 ULONGLONG available_free = available_ram + (ULONGLONG)generation_free_list_space (generation_of (gen_number));
28295 if (available_free > (ULONGLONG)MAX_PTR)
28297 available_free = (ULONGLONG)MAX_PTR;
28300 //try to avoid OOM during large object allocation
28301 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))),
28302 (size_t)available_free),
28303 max ((current_size/4), min_gc_size));
28305 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
28306 dd_desired_allocation (dd), dd_collection_count (dd));
28312 size_t survivors = out;
28313 cst = float (survivors) / float (dd_begin_data_size (dd));
28314 f = surv_to_growth (cst, limit, max_limit);
28315 new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
28317 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
28318 dd_desired_allocation (dd), dd_collection_count (dd));
28320 if (gen_number == 0)
28325 //printf ("%f, %Id\n", cst, new_allocation);
28326 size_t free_space = generation_free_list_space (generation_of (gen_number));
28327 // DTREVIEW - is min_gc_size really a good choice?
28328 // on 64-bit this will almost always be true.
28329 if (free_space > min_gc_size)
28331 dprintf (2, ("Detected excessive Fragmentation"));
28332 settings.gen0_reduction_count = 2;
28336 if (settings.gen0_reduction_count > 0)
28337 settings.gen0_reduction_count--;
28340 if (settings.gen0_reduction_count > 0)
28342 dprintf (2, ("Reducing new allocation based on fragmentation"));
28343 new_allocation = min (new_allocation,
28344 max (min_gc_size, (max_size/3)));
28350 size_t new_allocation_ret =
28351 Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
28352 int gen_data_index = gen_number;
28353 if ((gen_number == 0) && (pass == 1))
28355 gen_data_index = max_generation+2;
28357 gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
28358 gen_data->surv = (size_t)(cst*100);
28359 gen_data->new_allocation = new_allocation_ret;
28361 dd_surv (dd) = cst;
28363 #ifdef SIMPLE_DPRINTF
28364 dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
28365 heap_number, gen_number, out, current_size, allocation,
28366 (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
28368 dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
28369 dprintf (1,("current: %Id alloc: %Id ", current_size, allocation));
28370 dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
28371 (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
28372 #endif //SIMPLE_DPRINTF
28374 return new_allocation_ret;
28378 //returns the planned size of a generation (including free list element)
28379 size_t gc_heap::generation_plan_size (int gen_number)
28381 if (0 == gen_number)
28382 return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
28383 generation_plan_allocation_start (generation_of (gen_number))),
28384 (int)Align (min_obj_size));
28387 generation* gen = generation_of (gen_number);
28388 if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
28389 return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
28390 generation_plan_allocation_start (generation_of (gen_number)));
28393 size_t gensize = 0;
28394 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
28396 PREFIX_ASSUME(seg != NULL);
28398 while (seg && (seg != ephemeral_heap_segment))
28400 gensize += heap_segment_plan_allocated (seg) -
28401 heap_segment_mem (seg);
28402 seg = heap_segment_next_rw (seg);
28406 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
28407 heap_segment_mem (ephemeral_heap_segment));
28415 //returns the size of a generation (including free list element)
28416 size_t gc_heap::generation_size (int gen_number)
28418 if (0 == gen_number)
28419 return max((heap_segment_allocated (ephemeral_heap_segment) -
28420 generation_allocation_start (generation_of (gen_number))),
28421 (int)Align (min_obj_size));
28424 generation* gen = generation_of (gen_number);
28425 if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
28426 return (generation_allocation_start (generation_of (gen_number - 1)) -
28427 generation_allocation_start (generation_of (gen_number)));
28430 size_t gensize = 0;
28431 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
28433 PREFIX_ASSUME(seg != NULL);
28435 while (seg && (seg != ephemeral_heap_segment))
28437 gensize += heap_segment_allocated (seg) -
28438 heap_segment_mem (seg);
28439 seg = heap_segment_next_rw (seg);
28443 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
28444 heap_segment_mem (ephemeral_heap_segment));
28453 size_t gc_heap::compute_in (int gen_number)
28455 assert (gen_number != 0);
28456 dynamic_data* dd = dynamic_data_of (gen_number);
28458 size_t in = generation_allocation_size (generation_of (gen_number));
28460 if (gen_number == max_generation && ephemeral_promotion)
28463 for (int i = 0; i <= max_generation; i++)
28465 dynamic_data* dd = dynamic_data_of (i);
28466 in += dd_survived_size (dd);
28467 if (i != max_generation)
28469 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
28474 dd_gc_new_allocation (dd) -= in;
28476 generation_allocation_size (generation_of (gen_number)) = 0;
28480 void gc_heap::compute_promoted_allocation (int gen_number)
28482 compute_in (gen_number);
28487 size_t gc_heap::trim_youngest_desired (DWORD memory_load,
28488 size_t total_new_allocation,
28489 size_t total_min_allocation)
28491 if (memory_load < MAX_ALLOWED_MEM_LOAD)
28493 // If the total of memory load and gen0 budget exceeds
28494 // our max memory load limit, trim the gen0 budget so the total
28495 // is the max memory load limit.
28496 size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
28497 return min (total_new_allocation, remain_memory_load);
28501 return max (mem_one_percent, total_min_allocation);
28505 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
28507 dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
28509 size_t final_new_allocation = new_allocation;
28510 if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
28512 DWORD num_heaps = 1;
28514 #ifdef MULTIPLE_HEAPS
28515 num_heaps = gc_heap::n_heaps;
28516 #endif //MULTIPLE_HEAPS
28518 size_t total_new_allocation = new_allocation * num_heaps;
28519 size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
28521 if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
28522 (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
28524 DWORD dwMemoryLoad = 0;
28526 GetProcessMemoryLoad(&ms);
28527 dprintf (2, ("Current memory load: %d", ms.dwMemoryLoad));
28528 dwMemoryLoad = ms.dwMemoryLoad;
28530 size_t final_total =
28531 trim_youngest_desired (dwMemoryLoad, total_new_allocation, total_min_allocation);
28532 final_new_allocation = Align ((final_total / num_heaps), get_alignment_constant (TRUE));
28536 if (final_new_allocation < new_allocation)
28538 settings.gen0_reduction_count = 2;
28541 return final_new_allocation;
28546 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
28548 #ifdef BACKGROUND_GC
28549 return (settings.concurrent ?
28550 (bgc_data_saved_p ? &saved_bgc_data_per_heap : &gc_data_per_heap) :
28551 &gc_data_per_heap);
28553 return &gc_data_per_heap;
28554 #endif //BACKGROUND_GC
28557 void gc_heap::compute_new_dynamic_data (int gen_number)
28559 PREFIX_ASSUME(gen_number >= 0);
28560 PREFIX_ASSUME(gen_number <= max_generation);
28562 dynamic_data* dd = dynamic_data_of (gen_number);
28563 generation* gen = generation_of (gen_number);
28564 size_t in = (gen_number==0) ? 0 : compute_in (gen_number);
28566 size_t total_gen_size = generation_size (gen_number);
28567 //keep track of fragmentation
28568 dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
28569 dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
28571 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
28573 size_t out = dd_survived_size (dd);
28575 gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
28576 gen_data->size_after = total_gen_size;
28577 gen_data->free_list_space_after = generation_free_list_space (gen);
28578 gen_data->free_obj_space_after = generation_free_obj_space (gen);
28581 if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
28583 // When we are in the low latency mode, we can still be
28584 // condemning more than gen1's 'cause of induced GCs.
28585 dd_desired_allocation (dd) = low_latency_alloc;
28589 if (gen_number == 0)
28591 //compensate for dead finalizable objects promotion.
28592 //they shoudn't be counted for growth.
28593 size_t final_promoted = 0;
28594 final_promoted = min (promoted_bytes (heap_number), out);
28595 // Prefast: this is clear from above but prefast needs to be told explicitly
28596 PREFIX_ASSUME(final_promoted <= out);
28598 dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
28599 dd_freach_previous_promotion (dd) = final_promoted;
28600 size_t lower_bound = desired_new_allocation (dd, out-final_promoted, gen_number, 0);
28601 gen_data->out = out - final_promoted;
28603 if (settings.condemned_generation == 0)
28605 //there is no noise.
28606 dd_desired_allocation (dd) = lower_bound;
28610 current_gc_data_per_heap->gen_data[max_generation+2] = *gen_data;
28611 current_gc_data_per_heap->gen_data[max_generation+2].out = out;
28613 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
28615 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
28616 //assert ( lower_bound <= higher_bound);
28618 //discount the noise. Change the desired allocation
28619 //only if the previous value is outside of the range.
28620 if (dd_desired_allocation (dd) < lower_bound)
28622 dd_desired_allocation (dd) = lower_bound;
28624 else if (dd_desired_allocation (dd) > higher_bound)
28626 dd_desired_allocation (dd) = higher_bound;
28628 #if defined (_WIN64) && !defined (MULTIPLE_HEAPS)
28629 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
28630 #endif //_WIN64 && !MULTIPLE_HEAPS
28631 trim_youngest_desired_low_memory();
28632 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
28637 gen_data->out = out;
28638 dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
28642 dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
28644 dd_promoted_size (dd) = out;
28645 if (gen_number == max_generation)
28647 dd = dynamic_data_of (max_generation+1);
28648 total_gen_size = generation_size (max_generation + 1);
28649 dd_fragmentation (dd) = generation_free_list_space (large_object_generation) +
28650 generation_free_obj_space (large_object_generation);
28651 dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
28652 dd_survived_size (dd) = dd_current_size (dd);
28654 out = dd_current_size (dd);
28655 dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
28656 dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
28657 get_alignment_constant (FALSE));
28659 gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
28660 gen_data->size_after = total_gen_size;
28661 gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
28662 gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
28664 gen_data->out = out;
28665 #ifdef BACKGROUND_GC
28666 end_loh_size = total_gen_size;
28667 #endif //BACKGROUND_GC
28669 dd_promoted_size (dd) = out;
28673 void gc_heap::trim_youngest_desired_low_memory()
28675 if (g_low_memory_status)
28677 size_t committed_mem = 0;
28678 heap_segment* seg = generation_start_segment (generation_of (max_generation));
28681 committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
28682 seg = heap_segment_next (seg);
28684 seg = generation_start_segment (generation_of (max_generation + 1));
28687 committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
28688 seg = heap_segment_next (seg);
28691 dynamic_data* dd = dynamic_data_of (0);
28692 size_t current = dd_desired_allocation (dd);
28693 size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_gc_size (dd));
28695 dd_desired_allocation (dd) = min (current, candidate);
28699 void gc_heap::decommit_ephemeral_segment_pages()
28701 if (settings.concurrent)
28706 BOOL trim_p = FALSE;
28707 size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
28708 dynamic_data* dd = dynamic_data_of (0);
28710 if (settings.condemned_generation >= (max_generation-1))
28713 size_t new_slack_space =
28715 max(min(min(get_valid_segment_size()/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
28717 #ifdef FEATURE_CORECLR
28718 dd_desired_allocation (dd);
28721 #endif //FEATURE_CORECLR
28724 slack_space = min (slack_space, new_slack_space);
28727 #ifndef MULTIPLE_HEAPS
28728 size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
28729 size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
28730 size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
28732 if (dd_desired_allocation (dynamic_data_of(0)) > gc_gen0_desired_high)
28734 gc_gen0_desired_high = dd_desired_allocation (dynamic_data_of(0)) + extra_space;
28737 if (ephemeral_elapsed >= decommit_timeout)
28739 slack_space = min (slack_space, gc_gen0_desired_high);
28741 gc_last_ephemeral_decommit_time = dd_time_clock(dynamic_data_of(0));
28742 gc_gen0_desired_high = 0;
28744 #endif //!MULTIPLE_HEAPS
28746 size_t saved_slack_space = slack_space;
28747 size_t current_slack_space = ((slack_space < gen0_big_free_spaces) ? 0 : (slack_space - gen0_big_free_spaces));
28748 slack_space = current_slack_space;
28750 dprintf (1, ("ss: %Id->%Id", saved_slack_space, slack_space));
28751 decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);
28753 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
28754 current_gc_data_per_heap->extra_gen0_committed = (ULONGLONG)(heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment));
28757 size_t gc_heap::new_allocation_limit (size_t size, size_t free_size, int gen_number)
28759 dynamic_data* dd = dynamic_data_of (gen_number);
28760 ptrdiff_t new_alloc = dd_new_allocation (dd);
28761 assert (new_alloc == (ptrdiff_t)Align (new_alloc,
28762 get_alignment_constant (!(gen_number == (max_generation+1)))));
28763 size_t limit = min (max (new_alloc, (SSIZE_T)size), (SSIZE_T)free_size);
28764 assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
28765 dd_new_allocation (dd) = (new_alloc - limit );
28769 //This is meant to be called by decide_on_compacting.
28771 size_t gc_heap::generation_fragmentation (generation* gen,
28772 generation* consing_gen,
28776 BYTE* alloc = generation_allocation_pointer (consing_gen);
28777 // If the allocation pointer has reached the ephemeral segment
28778 // fine, otherwise the whole ephemeral segment is considered
28780 if (in_range_for_segment (alloc, ephemeral_heap_segment))
28782 if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
28783 frag = end - alloc;
28786 // case when no survivors, allocated set to beginning
28789 dprintf (3, ("ephemeral frag: %Id", frag));
28792 frag = (heap_segment_allocated (ephemeral_heap_segment) -
28793 heap_segment_mem (ephemeral_heap_segment));
28794 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
28796 PREFIX_ASSUME(seg != NULL);
28798 while (seg != ephemeral_heap_segment)
28800 frag += (heap_segment_allocated (seg) -
28801 heap_segment_plan_allocated (seg));
28802 dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
28803 (heap_segment_allocated (seg) -
28804 heap_segment_plan_allocated (seg))));
28806 seg = heap_segment_next_rw (seg);
28809 dprintf (3, ("frag: %Id discounting pinned plugs", frag));
28810 //add the length of the dequeued plug free space
28812 while (bos < mark_stack_bos)
28814 frag += (pinned_len (pinned_plug_of (bos)));
28821 // for SOH this returns the total sizes of the generation and its
28822 // younger generation(s).
28823 // for LOH this returns just LOH size.
28824 size_t gc_heap::generation_sizes (generation* gen)
28827 if (generation_start_segment (gen ) == ephemeral_heap_segment)
28828 result = (heap_segment_allocated (ephemeral_heap_segment) -
28829 generation_allocation_start (gen));
28832 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
28834 PREFIX_ASSUME(seg != NULL);
28838 result += (heap_segment_allocated (seg) -
28839 heap_segment_mem (seg));
28840 seg = heap_segment_next_in_range (seg);
28847 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
28848 size_t fragmentation,
28849 BOOL& should_expand)
28851 BOOL should_compact = FALSE;
28852 should_expand = FALSE;
28853 generation* gen = generation_of (condemned_gen_number);
28854 dynamic_data* dd = dynamic_data_of (condemned_gen_number);
28855 size_t gen_sizes = generation_sizes(gen);
28856 float fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
28857 (float (fragmentation) / gen_sizes) );
28860 // for pure GC stress runs we need compaction, for GC stress "mix"
28861 // we need to ensure a better mix of compacting and sweeping collections
28862 if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
28863 && !g_pConfig->IsGCStressMix())
28864 should_compact = TRUE;
28867 // in GC stress "mix" mode, for stress induced collections make sure we
28868 // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
28869 // against the GC's determination, as it may lead to premature OOMs.
28870 if (g_pConfig->IsGCStressMix() && settings.stress_induced)
28872 int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
28873 int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
28874 if (compactions < sweeps / 10)
28876 should_compact = TRUE;
28880 #endif //STRESS_HEAP
28882 if (g_pConfig->GetGCForceCompact())
28883 should_compact = TRUE;
28885 if ((condemned_gen_number == max_generation) && last_gc_before_oom)
28887 should_compact = TRUE;
28888 last_gc_before_oom = FALSE;
28891 if (settings.reason == reason_induced_compacting)
28893 dprintf (2, ("induced compacting GC"));
28894 should_compact = TRUE;
28897 dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
28898 fragmentation, (int) (100*fragmentation_burden)));
28900 if (!should_compact)
28902 if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
28904 dprintf(GTC_LOG, ("compacting due to low ephemeral"));
28905 should_compact = TRUE;
28906 gc_data_per_heap.set_mechanism (gc_compact, compact_low_ephemeral);
28910 if (should_compact)
28912 if ((condemned_gen_number >= (max_generation - 1)))
28914 if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
28916 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
28917 should_expand = TRUE;
28923 BOOL high_memory = FALSE;
28926 if (!should_compact)
28928 // We are not putting this in dt_high_frag_p because it's not exactly
28929 // high fragmentation - it's just enough planned fragmentation for us to
28930 // want to compact. Also the "fragmentation" we are talking about here
28931 // is different from anywhere else.
28932 BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
28933 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
28937 #ifdef BACKGROUND_GC
28938 // do not force compaction if this was a stress-induced GC
28939 IN_STRESS_HEAP(if (!settings.stress_induced))
28941 #endif // BACKGROUND_GC
28942 assert (settings.concurrent == FALSE);
28943 dprintf(GTC_LOG,("compacting due to fragmentation"));
28944 should_compact = TRUE;
28945 #ifdef BACKGROUND_GC
28947 #endif // BACKGROUND_GC
28951 // check for high memory situation
28952 if(!should_compact)
28954 DWORD num_heaps = 1;
28955 #ifdef MULTIPLE_HEAPS
28956 num_heaps = gc_heap::n_heaps;
28957 #endif // MULTIPLE_HEAPS
28959 SSIZE_T reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
28960 if((settings.entry_memory_load >= 90) && (settings.entry_memory_load < 97))
28962 if(reclaim_space > (LONGLONG)(min_high_fragmentation_threshold(available_physical_mem, num_heaps)))
28964 dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
28965 should_compact = TRUE;
28967 high_memory = TRUE;
28969 else if(settings.entry_memory_load >= 97)
28971 if(reclaim_space > (SSIZE_T)(min_reclaim_fragmentation_threshold(total_physical_mem, num_heaps)))
28973 dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
28974 should_compact = TRUE;
28976 high_memory = TRUE;
28982 // The purpose of calling ensure_gap_allocation here is to make sure
28983 // that we actually are able to commit the memory to allocate generation
28985 if ((should_compact == FALSE) &&
28986 (ensure_gap_allocation (condemned_gen_number) == FALSE))
28988 should_compact = TRUE;
28989 gc_data_per_heap.set_mechanism (gc_compact, compact_no_gaps);
28992 if (settings.condemned_generation == max_generation)
28994 //check the progress
28997 (high_memory && !should_compact) ||
28999 generation_size (max_generation) <= generation_plan_size (max_generation))
29001 dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
29002 generation_size (max_generation),
29003 generation_plan_size (max_generation)));
29004 //no progress -> lock
29005 settings.should_lock_elevation = TRUE;
29009 dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
29010 return should_compact;
29013 size_t align_lower_good_size_allocation (size_t size)
29015 return (size/64)*64;
29018 size_t gc_heap::approximate_new_allocation()
29020 dynamic_data* dd0 = dynamic_data_of (0);
29021 return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
29024 // After we did a GC we expect to have at least this
29025 // much space at the end of the segment to satisfy
29026 // a reasonable amount of allocation requests.
29027 size_t gc_heap::end_space_after_gc()
29029 return max ((dd_min_gc_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
29032 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
29036 if ((tp == tuning_deciding_condemned_gen) ||
29037 (tp == tuning_deciding_compaction))
29039 start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
29040 if (settings.concurrent)
29042 dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)",
29043 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
29047 dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)",
29048 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
29051 else if (tp == tuning_deciding_expansion)
29053 start = heap_segment_plan_allocated (ephemeral_heap_segment);
29054 dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan",
29055 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
29059 assert (tp == tuning_deciding_full_gc);
29060 dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)",
29061 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
29062 start = alloc_allocated;
29065 if (start == 0) // empty ephemeral generations
29067 assert (tp == tuning_deciding_expansion);
29068 // if there are no survivors in the ephemeral segment,
29069 // this should be the beginning of ephemeral segment.
29070 start = generation_allocation_pointer (generation_of (max_generation));
29071 assert (start == heap_segment_mem (ephemeral_heap_segment));
29074 if (tp == tuning_deciding_expansion)
29076 assert (settings.condemned_generation >= (max_generation-1));
29077 size_t gen0size = approximate_new_allocation();
29078 size_t eph_size = gen0size;
29080 for (int j = 1; j <= max_generation-1; j++)
29082 eph_size += 2*dd_min_size (dynamic_data_of(j));
29085 // We must find room for one large object and enough room for gen0size
29086 if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
29088 dprintf (3, ("Enough room before end of segment"));
29093 size_t room = align_lower_good_size_allocation
29094 (heap_segment_reserved (ephemeral_heap_segment) - start);
29095 size_t end_seg = room;
29097 //look at the plug free space
29098 size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
29099 bool large_chunk_found = FALSE;
29101 BYTE* gen0start = generation_plan_allocation_start (youngest_generation);
29102 dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
29103 if (gen0start == 0)
29105 dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
29107 while ((bos < mark_stack_bos) &&
29108 !((room >= gen0size) && large_chunk_found))
29110 BYTE* plug = pinned_plug (pinned_plug_of (bos));
29111 if (in_range_for_segment (plug, ephemeral_heap_segment))
29113 if (plug >= gen0start)
29115 size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
29117 if (!large_chunk_found)
29119 large_chunk_found = (chunk >= largest_alloc);
29121 dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
29122 room, large_chunk_found));
29128 if (room >= gen0size)
29130 if (large_chunk_found)
29132 dprintf (3, ("Enough room"));
29137 // now we need to find largest_alloc at the end of the segment.
29138 if (end_seg >= end_space_after_gc())
29140 dprintf (3, ("Enough room (may need end of seg)"));
29146 dprintf (3, ("Not enough room"));
29152 size_t end_space = 0;
29153 dynamic_data* dd = dynamic_data_of (0);
29154 if ((tp == tuning_deciding_condemned_gen) ||
29155 (tp == tuning_deciding_full_gc))
29157 end_space = 2*dd_min_size (dd);
29161 assert (tp == tuning_deciding_compaction);
29162 end_space = approximate_new_allocation();
29165 if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space))
29167 dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction"));
29169 return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space);
29173 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, __int64& alloc_bytes)
29175 //create a new alloc context because gen3context is shared.
29176 alloc_context acontext;
29177 acontext.alloc_ptr = 0;
29178 acontext.alloc_limit = 0;
29179 acontext.alloc_bytes = 0;
29180 #ifdef MULTIPLE_HEAPS
29181 acontext.alloc_heap = vm_heap;
29182 #endif //MULTIPLE_HEAPS
29185 BYTE* current_lowest_address = lowest_address;
29186 BYTE* current_highest_address = highest_address;
29187 #ifdef BACKGROUND_GC
29188 if (recursive_gc_sync::background_running_p())
29190 current_lowest_address = background_saved_lowest_address;
29191 current_highest_address = background_saved_highest_address;
29193 #endif //BACKGROUND_GC
29194 #endif // MARK_ARRAY
29196 SIZE_T maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
29199 if (g_pConfig->GetGCAllowVeryLargeObjects())
29201 maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
29205 if (jsize >= maxObjectSize)
29207 if (g_pConfig->IsGCBreakOnOOMEnabled())
29212 #ifndef FEATURE_REDHAWK
29213 ThrowOutOfMemoryDimensionsExceeded();
29219 size_t size = AlignQword (jsize);
29220 int align_const = get_alignment_constant (FALSE);
29221 #ifdef FEATURE_LOH_COMPACTION
29222 size_t pad = Align (loh_padding_obj_size, align_const);
29225 #endif //FEATURE_LOH_COMPACTION
29227 assert (size >= Align (min_obj_size, align_const));
29229 #pragma inline_depth(0)
29231 if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
29237 #pragma inline_depth(20)
29240 #ifdef FEATURE_LOH_COMPACTION
29241 // The GC allocator made a free object already in this alloc context and
29242 // adjusted the alloc_ptr accordingly.
29243 #endif //FEATURE_LOH_COMPACTION
29245 BYTE* result = acontext.alloc_ptr;
29247 assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
29249 CObjectHeader* obj = (CObjectHeader*)result;
29252 if (recursive_gc_sync::background_running_p())
29254 if ((result < current_highest_address) && (result >= current_lowest_address))
29256 dprintf (3, ("Clearing mark bit at address %Ix",
29257 (size_t)(&mark_array [mark_word_of (result)])));
29259 mark_array_clear_marked (result);
29261 #ifdef BACKGROUND_GC
29262 //the object has to cover one full mark DWORD
29263 assert (size > mark_word_size);
29264 if (current_c_gc_state == c_gc_state_marking)
29266 dprintf (3, ("Concurrent allocation of a large object %Ix",
29268 //mark the new block specially so we know it is a new object
29269 if ((result < current_highest_address) && (result >= current_lowest_address))
29271 dprintf (3, ("Setting mark bit at address %Ix",
29272 (size_t)(&mark_array [mark_word_of (result)])));
29274 mark_array_set_marked (result);
29277 #endif //BACKGROUND_GC
29279 #endif //MARK_ARRAY
29282 assert ((size_t)obj == Align ((size_t)obj, align_const));
29284 alloc_bytes += acontext.alloc_bytes;
29288 void reset_memory (BYTE* o, size_t sizeo)
29290 #ifndef FEATURE_PAL
29291 if (sizeo > 128 * 1024)
29293 // We cannot reset the memory for the useful part of a free object.
29294 size_t size_to_skip = min_free_list - plug_skew;
29296 size_t page_start = align_on_page ((size_t)(o + size_to_skip));
29297 size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
29298 VirtualAlloc ((char*)page_start, size, MEM_RESET, PAGE_READWRITE);
29299 VirtualUnlock ((char*)page_start, size);
29301 #endif //!FEATURE_PAL
29304 void gc_heap::reset_large_object (BYTE* o)
29306 // If it's a large object, allow the O/S to discard the backing store for these pages.
29307 reset_memory (o, size(o));
29310 BOOL gc_heap::large_object_marked (BYTE* o, BOOL clearp)
29313 // It shouldn't be necessary to do these comparisons because this is only used for blocking
29314 // GCs and LOH segments cannot be out of range.
29315 if ((o >= lowest_address) && (o < highest_address))
29335 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
29336 void gc_heap::record_survived_for_profiler(int condemned_gen_number, BYTE * start_address)
29338 size_t profiling_context = 0;
29340 ETW::GCLog::BeginMovedReferences(&profiling_context);
29342 // Now walk the portion of memory that is actually being relocated.
29343 walk_relocation(condemned_gen_number, start_address, profiling_context);
29345 #ifdef FEATURE_LOH_COMPACTION
29346 if (loh_compacted_p)
29348 walk_relocation_loh (profiling_context);
29350 #endif //FEATURE_LOH_COMPACTION
29352 // Notify the EE-side profiling code that all the references have been traced for
29353 // this heap, and that it needs to flush all cached data it hasn't sent to the
29354 // profiler and release resources it no longer needs.
29355 ETW::GCLog::EndMovedReferences(profiling_context);
29358 void gc_heap::notify_profiler_of_surviving_large_objects ()
29360 size_t profiling_context = 0;
29362 ETW::GCLog::BeginMovedReferences(&profiling_context);
29364 generation* gen = large_object_generation;
29365 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));;
29367 PREFIX_ASSUME(seg != NULL);
29369 BYTE* o = generation_allocation_start (gen);
29370 BYTE* plug_end = o;
29371 BYTE* plug_start = o;
29373 // Generally, we can only get here if this is TRUE:
29374 // (CORProfilerTrackGC() || ETW::GCLog::ShouldTrackMovementForEtw())
29375 // But we can't always assert that, as races could theoretically cause GC profiling
29376 // or ETW to turn off just before we get here. This is harmless (we do checks later
29377 // on, under appropriate locks, before actually calling into profilers), though it's
29378 // a slowdown to determine these plugs for nothing.
29382 if (o >= heap_segment_allocated (seg))
29384 seg = heap_segment_next (seg);
29388 o = heap_segment_mem (seg);
29390 if (large_object_marked(o, FALSE))
29397 o = o + AlignQword (size (o));
29398 if (o >= heap_segment_allocated (seg))
29402 m = large_object_marked (o, FALSE);
29407 ETW::GCLog::MovedReference(
29416 while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
29418 o = o + AlignQword (size (o));
29422 ETW::GCLog::EndMovedReferences(profiling_context);
29424 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
29426 #ifdef BACKGROUND_GC
29428 BOOL gc_heap::background_object_marked (BYTE* o, BOOL clearp)
29431 if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
29433 if (mark_array_marked (o))
29437 mark_array_clear_marked (o);
29438 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
29439 dprintf (3, ("CM: %Ix", o));
29449 dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
29453 BYTE* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
29456 (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
29459 void gc_heap::set_mem_verify (BYTE* start, BYTE* end, BYTE b)
29464 if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC) &&
29465 !(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_NO_MEM_FILL))
29467 dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
29468 memset (start, b, (end - start));
29471 #endif //VERIFY_HEAP
29474 void gc_heap::generation_delete_heap_segment (generation* gen,
29476 heap_segment* prev_seg,
29477 heap_segment* next_seg)
29479 dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
29480 if (gen == large_object_generation)
29482 heap_segment_next (prev_seg) = next_seg;
29484 dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
29486 heap_segment_next (seg) = freeable_large_heap_segment;
29487 freeable_large_heap_segment = seg;
29491 if (seg == ephemeral_heap_segment)
29496 heap_segment_next (next_seg) = prev_seg;
29498 dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
29499 heap_segment_next (seg) = freeable_small_heap_segment;
29500 freeable_small_heap_segment = seg;
29503 decommit_heap_segment (seg);
29504 seg->flags |= heap_segment_flags_decommitted;
29506 set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
29509 void gc_heap::process_background_segment_end (heap_segment* seg,
29511 BYTE* last_plug_end,
29512 heap_segment* start_seg,
29516 BYTE* allocated = heap_segment_allocated (seg);
29517 BYTE* background_allocated = heap_segment_background_allocated (seg);
29519 dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)",
29520 (size_t)heap_segment_mem (seg), background_allocated, allocated));
29523 if (allocated != background_allocated)
29525 if (gen == large_object_generation)
29530 dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[",
29531 (size_t)last_plug_end, background_allocated));
29532 thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
29534 fix_brick_to_highest (last_plug_end, background_allocated);
29536 // When we allowed fgc's during going through gaps, we could have erased the brick
29537 // that corresponds to bgc_allocated 'cause we had to update the brick there,
29538 // recover it here.
29539 fix_brick_to_highest (background_allocated, background_allocated);
29543 // by default, if allocated == background_allocated, it can't
29544 // be the ephemeral segment.
29545 if (seg == ephemeral_heap_segment)
29550 if (allocated == heap_segment_mem (seg))
29552 // this can happen with LOH segments when multiple threads
29553 // allocate new segments and not all of them were needed to
29554 // satisfy allocation requests.
29555 assert (gen == large_object_generation);
29558 if (last_plug_end == heap_segment_mem (seg))
29560 dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
29561 (size_t)allocated, (*delete_p ? "should" : "should not")));
29563 if (seg != start_seg)
29570 dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
29571 heap_segment_allocated (seg) = last_plug_end;
29572 set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
29574 decommit_heap_segment_pages (seg, 0);
29578 dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
29579 bgc_verify_mark_array_cleared (seg);
29582 void gc_heap::process_n_background_segments (heap_segment* seg,
29583 heap_segment* prev_seg,
29586 assert (gen != large_object_generation);
29590 dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
29591 heap_segment* next_seg = heap_segment_next (seg);
29593 if (heap_segment_read_only_p (seg))
29599 if (heap_segment_allocated (seg) == heap_segment_mem (seg))
29601 // This can happen - if we have a LOH segment where nothing survived
29602 // or a SOH segment allocated by a gen1 GC when BGC was going where
29603 // nothing survived last time we did a gen1 GC.
29604 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
29612 verify_soh_segment_list();
29618 BOOL gc_heap::fgc_should_consider_object (BYTE* o,
29620 BOOL consider_bgc_mark_p,
29621 BOOL check_current_sweep_p,
29622 BOOL check_saved_sweep_p)
29624 // the logic for this function must be kept in sync with the analogous function
29625 // in ToolBox\SOS\Strike\gc.cpp
29627 // TRUE means we don't need to check the bgc mark bit
29628 // FALSE means we do.
29629 BOOL no_bgc_mark_p = FALSE;
29631 if (consider_bgc_mark_p)
29633 if (check_current_sweep_p && (o < current_sweep_pos))
29635 dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
29636 no_bgc_mark_p = TRUE;
29639 if (!no_bgc_mark_p)
29641 if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
29643 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
29644 no_bgc_mark_p = TRUE;
29647 if (!check_saved_sweep_p)
29649 BYTE* background_allocated = heap_segment_background_allocated (seg);
29650 // if this was the saved ephemeral segment, check_saved_sweep_p
29651 // would've been true.
29652 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
29653 // background_allocated could be 0 for the new segments acquired during bgc
29654 // sweep and we still want no_bgc_mark_p to be true.
29655 if (o >= background_allocated)
29657 dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
29658 no_bgc_mark_p = TRUE;
29665 no_bgc_mark_p = TRUE;
29668 dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
29669 return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
29672 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
29673 // if it's TRUE, check_current_sweep_p tells you if you should consider the
29674 // current sweep position or not.
29675 void gc_heap::should_check_bgc_mark (heap_segment* seg,
29676 BOOL* consider_bgc_mark_p,
29677 BOOL* check_current_sweep_p,
29678 BOOL* check_saved_sweep_p)
29680 // the logic for this function must be kept in sync with the analogous function
29681 // in ToolBox\SOS\Strike\gc.cpp
29682 *consider_bgc_mark_p = FALSE;
29683 *check_current_sweep_p = FALSE;
29684 *check_saved_sweep_p = FALSE;
29686 if (current_c_gc_state == c_gc_state_planning)
29688 // We are doing the current_sweep_pos comparison here because we have yet to
29689 // turn on the swept flag for the segment but in_range_for_segment will return
29690 // FALSE if the address is the same as reserved.
29691 if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
29693 dprintf (3, ("seg %Ix is already swept by bgc"));
29697 *consider_bgc_mark_p = TRUE;
29699 dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
29701 if (seg == saved_sweep_ephemeral_seg)
29703 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
29704 *check_saved_sweep_p = TRUE;
29707 if (in_range_for_segment (current_sweep_pos, seg))
29709 dprintf (3, ("current sweep pos is %Ix and within seg %Ix",
29710 current_sweep_pos, seg));
29711 *check_current_sweep_p = TRUE;
29717 void gc_heap::background_ephemeral_sweep()
29719 dprintf (3, ("bgc ephemeral sweep"));
29721 int align_const = get_alignment_constant (TRUE);
29723 saved_sweep_ephemeral_seg = ephemeral_heap_segment;
29724 saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
29726 // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
29727 // we thread onto a list first then publish it when we are done.
29728 allocator youngest_free_list;
29729 size_t youngest_free_list_space = 0;
29730 size_t youngest_free_obj_space = 0;
29732 youngest_free_list.clear();
29734 for (int i = 0; i <= (max_generation - 1); i++)
29736 generation* gen_to_reset = generation_of (i);
29737 assert (generation_free_list_space (gen_to_reset) == 0);
29738 assert (generation_free_obj_space (gen_to_reset) == 0);
29741 for (int i = (max_generation - 1); i >= 0; i--)
29743 generation* current_gen = generation_of (i);
29744 BYTE* o = generation_allocation_start (current_gen);
29745 //Skip the generation gap object
29746 o = o + Align(size (o), align_const);
29747 BYTE* end = ((i > 0) ?
29748 generation_allocation_start (generation_of (i - 1)) :
29749 heap_segment_allocated (ephemeral_heap_segment));
29751 BYTE* plug_end = o;
29752 BYTE* plug_start = o;
29753 BOOL marked_p = FALSE;
29757 marked_p = background_object_marked (o, TRUE);
29761 size_t plug_size = plug_start - plug_end;
29765 thread_gap (plug_end, plug_size, current_gen);
29771 make_unused_array (plug_end, plug_size);
29772 if (plug_size >= min_free_list)
29774 youngest_free_list_space += plug_size;
29775 youngest_free_list.thread_item (plug_end, plug_size);
29779 youngest_free_obj_space += plug_size;
29784 fix_brick_to_highest (plug_end, plug_start);
29785 fix_brick_to_highest (plug_start, plug_start);
29790 o = o + Align (size (o), align_const);
29796 m = background_object_marked (o, TRUE);
29799 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
29803 while ((o < end) && !background_object_marked (o, FALSE))
29805 o = o + Align (size (o), align_const);
29810 if (plug_end != end)
29814 thread_gap (plug_end, end - plug_end, current_gen);
29815 fix_brick_to_highest (plug_end, end);
29819 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
29820 // the following line is temporary.
29821 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
29823 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
29825 make_unused_array (plug_end, (end - plug_end));
29827 #endif //VERIFY_HEAP
29831 dd_fragmentation (dynamic_data_of (i)) =
29832 generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
29835 generation* youngest_gen = generation_of (0);
29836 generation_free_list_space (youngest_gen) = youngest_free_list_space;
29837 generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
29838 dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
29839 generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
29842 void gc_heap::background_sweep()
29844 Thread* current_thread = GetThread();
29845 generation* gen = generation_of (max_generation);
29846 dynamic_data* dd = dynamic_data_of (max_generation);
29847 // For SOH segments we go backwards.
29848 heap_segment* start_seg = ephemeral_heap_segment;
29849 PREFIX_ASSUME(start_seg != NULL);
29850 heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
29851 heap_segment* seg = start_seg;
29852 BYTE* o = heap_segment_mem (seg);
29854 heap_segment* prev_seg = heap_segment_next (seg);
29855 int align_const = get_alignment_constant (TRUE);
29858 assert (o == generation_allocation_start (generation_of (max_generation)));
29859 o = o + Align(size (o), align_const);
29862 BYTE* plug_end = o;
29863 BYTE* plug_start = o;
29864 next_sweep_obj = o;
29865 current_sweep_pos = o;
29867 //BYTE* end = background_next_end (seg, (gen == large_object_generation));
29868 BYTE* end = heap_segment_background_allocated (seg);
29869 BOOL delete_p = FALSE;
29871 //concurrent_print_time_delta ("finished with mark and start with sweep");
29872 concurrent_print_time_delta ("Sw");
29873 dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
29875 //block concurrent allocation for large objects
29876 dprintf (3, ("lh state: planning"));
29877 if (gc_lh_block_event.IsValid())
29879 gc_lh_block_event.Reset();
29882 for (int i = 0; i <= (max_generation + 1); i++)
29884 generation* gen_to_reset = generation_of (i);
29885 generation_allocator (gen_to_reset)->clear();
29886 generation_free_list_space (gen_to_reset) = 0;
29887 generation_free_obj_space (gen_to_reset) = 0;
29888 generation_free_list_allocated (gen_to_reset) = 0;
29889 generation_end_seg_allocated (gen_to_reset) = 0;
29890 generation_condemned_allocated (gen_to_reset) = 0;
29891 //reset the allocation so foreground gc can allocate into older generation
29892 generation_allocation_pointer (gen_to_reset)= 0;
29893 generation_allocation_limit (gen_to_reset) = 0;
29894 generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
29897 fire_bgc_event (BGC2ndNonConEnd);
29899 current_bgc_state = bgc_sweep_soh;
29900 verify_soh_segment_list();
29902 if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
29903 ro_segments_in_range)
29905 sweep_ro_segments (generation_start_segment (gen));
29908 //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
29909 if (current_c_gc_state != c_gc_state_planning)
29911 current_c_gc_state = c_gc_state_planning;
29914 concurrent_print_time_delta ("Swe");
29916 heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
29917 PREFIX_ASSUME(loh_seg != NULL);
29920 loh_seg->flags &= ~heap_segment_flags_swept;
29921 heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
29922 loh_seg = heap_segment_next_rw (loh_seg);
29925 #ifdef MULTIPLE_HEAPS
29926 bgc_t_join.join(this, gc_join_restart_ee);
29927 if (bgc_t_join.joined())
29928 #endif //MULTIPLE_HEAPS
29930 #ifdef MULTIPLE_HEAPS
29931 dprintf(2, ("Starting BGC threads for resuming EE"));
29932 bgc_t_join.restart();
29933 #endif //MULTIPLE_HEAPS
29936 if (heap_number == 0)
29941 fire_bgc_event (BGC2ndConBegin);
29943 background_ephemeral_sweep();
29945 #ifdef MULTIPLE_HEAPS
29946 bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
29947 if (bgc_t_join.joined())
29948 #endif //MULTIPLE_HEAPS
29950 leave_spin_lock (&gc_lock);
29952 #ifdef MULTIPLE_HEAPS
29953 dprintf(2, ("Starting BGC threads for BGC sweeping"));
29954 bgc_t_join.restart();
29955 #endif //MULTIPLE_HEAPS
29958 disable_preemptive (current_thread, TRUE);
29960 dprintf (2, ("bgs: sweeping gen2 objects"));
29961 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
29962 (size_t)heap_segment_mem (seg),
29963 (size_t)heap_segment_allocated (seg),
29964 (size_t)heap_segment_background_allocated (seg)));
29966 int num_objs = 256;
29967 int current_num_objs = 0;
29968 heap_segment* next_seg = 0;
29974 if (gen == large_object_generation)
29976 next_seg = heap_segment_next (seg);
29980 next_seg = heap_segment_prev (fseg, seg);
29985 if (!heap_segment_read_only_p (seg))
29987 if (gen == large_object_generation)
29989 // we can treat all LOH segments as in the bgc domain
29990 // regardless of whether we saw in bgc mark or not
29991 // because we don't allow LOH allocations during bgc
29992 // sweep anyway - the LOH segments can't change.
29993 process_background_segment_end (seg, gen, plug_end,
29994 start_seg, &delete_p);
29998 assert (heap_segment_background_allocated (seg) != 0);
29999 process_background_segment_end (seg, gen, plug_end,
30000 start_seg, &delete_p);
30002 assert (next_seg || !delete_p);
30008 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
30013 dprintf (2, ("seg %Ix has been swept", seg));
30014 seg->flags |= heap_segment_flags_swept;
30017 verify_soh_segment_list();
30021 dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
30025 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
30027 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
30029 if (gen != large_object_generation)
30031 dprintf (2, ("bgs: sweeping gen3 objects"));
30032 current_bgc_state = bgc_sweep_loh;
30033 gen = generation_of (max_generation+1);
30034 start_seg = heap_segment_rw (generation_start_segment (gen));
30036 PREFIX_ASSUME(start_seg != NULL);
30040 o = generation_allocation_start (gen);
30041 assert (method_table (o) == g_pFreeObjectMethodTable);
30042 align_const = get_alignment_constant (FALSE);
30043 o = o + Align(size (o), align_const);
30045 end = heap_segment_allocated (seg);
30046 dprintf (2, ("sweeping gen3 objects"));
30047 generation_free_obj_space (gen) = 0;
30048 generation_allocator (gen)->clear();
30049 generation_free_list_space (gen) = 0;
30051 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
30052 (size_t)heap_segment_mem (seg),
30053 (size_t)heap_segment_allocated (seg),
30054 (size_t)heap_segment_background_allocated (seg)));
30061 o = heap_segment_mem (seg);
30064 assert (gen != large_object_generation);
30065 assert (o == generation_allocation_start (generation_of (max_generation)));
30066 align_const = get_alignment_constant (TRUE);
30067 o = o + Align(size (o), align_const);
30071 current_sweep_pos = o;
30072 next_sweep_obj = o;
30075 end = background_next_end (seg, (gen == large_object_generation));
30076 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
30077 (size_t)heap_segment_mem (seg),
30078 (size_t)heap_segment_allocated (seg),
30079 (size_t)heap_segment_background_allocated (seg)));
30083 if ((o < end) && background_object_marked (o, TRUE))
30086 if (gen == large_object_generation)
30088 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
30091 thread_gap (plug_end, plug_start-plug_end, gen);
30092 if (gen != large_object_generation)
30094 add_gen_free (max_generation, plug_start-plug_end);
30095 fix_brick_to_highest (plug_end, plug_start);
30096 // we need to fix the brick for the next plug here 'cause an FGC can
30097 // happen and can't read a stale brick.
30098 fix_brick_to_highest (plug_start, plug_start);
30105 next_sweep_obj = o + Align(size (o), align_const);
30106 current_num_objs++;
30107 if (current_num_objs >= num_objs)
30109 current_sweep_pos = next_sweep_obj;
30112 current_num_objs = 0;
30115 o = next_sweep_obj;
30121 m = background_object_marked (o, TRUE);
30124 if (gen != large_object_generation)
30126 add_gen_plug (max_generation, plug_end-plug_start);
30127 dd_survived_size (dd) += (plug_end - plug_start);
30129 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
30133 while ((o < end) && !background_object_marked (o, FALSE))
30135 next_sweep_obj = o + Align(size (o), align_const);;
30136 current_num_objs++;
30137 if (current_num_objs >= num_objs)
30139 current_sweep_pos = plug_end;
30140 dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
30142 current_num_objs = 0;
30145 o = next_sweep_obj;
30150 size_t total_loh_size = generation_size (max_generation + 1);
30151 size_t total_soh_size = generation_sizes (generation_of (max_generation));
30153 dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
30155 dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id",
30156 generation_free_list_space (generation_of (max_generation)),
30157 generation_free_obj_space (generation_of (max_generation))));
30158 dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id",
30160 generation_free_list_space (generation_of (max_generation + 1)),
30161 generation_free_obj_space (generation_of (max_generation + 1))));
30163 fire_bgc_event (BGC2ndConEnd);
30164 concurrent_print_time_delta ("background sweep");
30166 heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
30167 PREFIX_ASSUME(reset_seg != NULL);
30171 heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
30172 heap_segment_background_allocated (reset_seg) = 0;
30173 reset_seg = heap_segment_next_rw (reset_seg);
30176 // We calculate dynamic data here because if we wait till we signal the lh event,
30177 // the allocation thread can change the fragmentation and we may read an intermediate
30178 // value (which can be greater than the generation size). Plus by that time it won't
30180 compute_new_dynamic_data (max_generation);
30182 enable_preemptive (current_thread);
30184 #ifdef MULTIPLE_HEAPS
30185 bgc_t_join.join(this, gc_join_set_state_free);
30186 if (bgc_t_join.joined())
30187 #endif //MULTIPLE_HEAPS
30189 // TODO: We are using this join just to set the state. Should
30190 // look into eliminating it - check to make sure things that use
30191 // this state can live with per heap state like should_check_bgc_mark.
30192 current_c_gc_state = c_gc_state_free;
30194 #ifdef MULTIPLE_HEAPS
30195 dprintf(2, ("Starting BGC threads after background sweep phase"));
30196 bgc_t_join.restart();
30197 #endif //MULTIPLE_HEAPS
30200 disable_preemptive (current_thread, TRUE);
30202 if (gc_lh_block_event.IsValid())
30204 gc_lh_block_event.Set();
30207 //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
30208 dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
30210 #endif //BACKGROUND_GC
30212 void gc_heap::sweep_large_objects ()
30214 //this min value is for the sake of the dynamic tuning.
30215 //so we know that we are not starting even if we have no
30217 generation* gen = large_object_generation;
30218 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
30220 PREFIX_ASSUME(start_seg != NULL);
30222 heap_segment* seg = start_seg;
30223 heap_segment* prev_seg = 0;
30224 BYTE* o = generation_allocation_start (gen);
30225 int align_const = get_alignment_constant (FALSE);
30227 //Skip the generation gap object
30228 o = o + Align(size (o), align_const);
30230 BYTE* plug_end = o;
30231 BYTE* plug_start = o;
30233 generation_allocator (gen)->clear();
30234 generation_free_list_space (gen) = 0;
30235 generation_free_obj_space (gen) = 0;
30238 dprintf (3, ("sweeping large objects"));
30239 dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix",
30241 (size_t)heap_segment_mem (seg),
30242 (size_t)heap_segment_allocated (seg),
30247 if (o >= heap_segment_allocated (seg))
30249 heap_segment* next_seg = heap_segment_next (seg);
30250 //delete the empty segment if not the only one
30251 if ((plug_end == heap_segment_mem (seg)) &&
30252 (seg != start_seg) && !heap_segment_read_only_p (seg))
30254 //prepare for deletion
30255 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
30257 heap_segment_next (prev_seg) = next_seg;
30258 heap_segment_next (seg) = freeable_large_heap_segment;
30259 freeable_large_heap_segment = seg;
30263 if (!heap_segment_read_only_p (seg))
30265 dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
30266 heap_segment_allocated (seg) = plug_end;
30267 decommit_heap_segment_pages (seg, 0);
30276 o = heap_segment_mem (seg);
30278 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
30279 (size_t)heap_segment_mem (seg),
30280 (size_t)heap_segment_allocated (seg)));
30283 if (large_object_marked(o, TRUE))
30286 //everything between plug_end and plug_start is free
30287 thread_gap (plug_end, plug_start-plug_end, gen);
30292 o = o + AlignQword (size (o));
30293 if (o >= heap_segment_allocated (seg))
30297 m = large_object_marked (o, TRUE);
30300 dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
30304 while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
30306 o = o + AlignQword (size (o));
30311 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
30313 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
30316 void gc_heap::relocate_in_large_objects ()
30318 relocate_args args;
30320 args.high = gc_high;
30321 args.last_plug = 0;
30323 generation* gen = large_object_generation;
30325 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30327 PREFIX_ASSUME(seg != NULL);
30329 BYTE* o = generation_allocation_start (gen);
30333 if (o >= heap_segment_allocated (seg))
30335 seg = heap_segment_next_rw (seg);
30340 o = heap_segment_mem (seg);
30343 while (o < heap_segment_allocated (seg))
30345 check_class_object_demotion (o);
30346 if (contain_pointers (o))
30348 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
30349 go_through_object_nostart (method_table (o), o, size(o), pval,
30351 reloc_survivor_helper (pval);
30354 o = o + AlignQword (size (o));
30359 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
30362 BYTE* low = gc_low;
30363 size_t end_card = 0;
30364 generation* oldest_gen = generation_of (max_generation+1);
30365 heap_segment* seg = heap_segment_rw (generation_start_segment (oldest_gen));
30367 PREFIX_ASSUME(seg != NULL);
30369 BYTE* beg = generation_allocation_start (oldest_gen);
30370 BYTE* end = heap_segment_allocated (seg);
30372 size_t cg_pointers_found = 0;
30374 size_t card_word_end = (card_of (align_on_card_word (end)) /
30379 size_t n_card_set = 0;
30380 BYTE* next_boundary = (relocating ?
30381 generation_plan_allocation_start (generation_of (max_generation -1)) :
30384 BYTE* nhigh = (relocating ?
30385 heap_segment_plan_allocated (ephemeral_heap_segment) :
30388 BOOL foundp = FALSE;
30389 BYTE* start_address = 0;
30391 size_t card = card_of (beg);
30393 #ifdef BACKGROUND_GC
30394 BOOL consider_bgc_mark_p = FALSE;
30395 BOOL check_current_sweep_p = FALSE;
30396 BOOL check_saved_sweep_p = FALSE;
30397 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
30398 #endif //BACKGROUND_GC
30400 size_t total_cards_cleared = 0;
30402 //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
30403 dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
30406 if ((o < end) && (card_of(o) > card))
30408 dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
30409 if (cg_pointers_found == 0)
30411 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
30412 clear_cards (card, card_of((BYTE*)o));
30413 total_cards_cleared += (card_of((BYTE*)o) - card);
30415 n_eph +=cg_pointers_found;
30416 cg_pointers_found = 0;
30417 card = card_of ((BYTE*)o);
30419 if ((o < end) &&(card >= end_card))
30421 foundp = find_card (card_table, card, card_word_end, end_card);
30424 n_card_set+= end_card - card;
30425 start_address = max (beg, card_address (card));
30427 limit = min (end, card_address (end_card));
30429 if ((!foundp) || (o >= end) || (card_address (card) >= end))
30431 if ((foundp) && (cg_pointers_found == 0))
30433 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
30434 (size_t)card_address(card+1)));
30435 clear_cards (card, card+1);
30436 total_cards_cleared += 1;
30438 n_eph +=cg_pointers_found;
30439 cg_pointers_found = 0;
30440 if ((seg = heap_segment_next_rw (seg)) != 0)
30442 #ifdef BACKGROUND_GC
30443 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
30444 #endif //BACKGROUND_GC
30445 beg = heap_segment_mem (seg);
30446 end = compute_next_end (seg, low);
30447 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
30448 card = card_of (beg);
30459 assert (card_set_p (card));
30461 dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
30462 card, (size_t)o, (size_t)limit));
30464 assert (Align (size (o)) >= Align (min_obj_size));
30465 size_t s = size (o);
30466 BYTE* next_o = o + AlignQword (s);
30472 assert (Align (s) >= Align (min_obj_size));
30473 next_o = o + AlignQword (s);
30476 dprintf (4, ("|%Ix|", (size_t)o));
30477 if (next_o < start_address)
30482 #ifdef BACKGROUND_GC
30483 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
30487 #endif //BACKGROUND_GC
30489 #ifdef COLLECTIBLE_CLASS
30490 if (is_collectible(o))
30492 BOOL passed_end_card_p = FALSE;
30494 if (card_of (o) > card)
30496 passed_end_card_p = card_transition (o, end, card_word_end,
30500 foundp, start_address,
30501 limit, total_cards_cleared);
30504 if ((!passed_end_card_p || foundp) && (card_of (o) == card))
30506 // card is valid and it covers the head of the object
30507 if (fn == &gc_heap::relocate_address)
30509 keep_card_live (o, n_gen, cg_pointers_found);
30513 BYTE* class_obj = get_class_object (o);
30514 mark_through_cards_helper (&class_obj, n_gen,
30515 cg_pointers_found, fn,
30516 nhigh, next_boundary);
30520 if (passed_end_card_p)
30522 if (foundp && (card_address (card) < next_o))
30524 goto go_through_refs;
30534 #endif //COLLECTIBLE_CLASS
30536 if (contain_pointers (o))
30538 dprintf(3,("Going through %Ix", (size_t)o));
30540 go_through_object (method_table(o), o, s, poo,
30541 start_address, use_start, (o + s),
30543 if (card_of ((BYTE*)poo) > card)
30545 BOOL passed_end_card_p = card_transition ((BYTE*)poo, end,
30550 foundp, start_address,
30551 limit, total_cards_cleared);
30553 if (passed_end_card_p)
30555 if (foundp && (card_address (card) < next_o))
30559 if (ppstop <= (BYTE**)start_address)
30561 else if (poo < (BYTE**)start_address)
30562 {poo = (BYTE**)start_address;}
30572 mark_through_cards_helper (poo, n_gen,
30573 cg_pointers_found, fn,
30574 nhigh, next_boundary);
30586 // compute the efficiency ratio of the card table
30589 generation_skip_ratio = min (((n_eph > 800) ?
30590 (int)(((float)n_gen / (float)n_eph) * 100) : 100),
30591 generation_skip_ratio);
30593 dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d",
30594 n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
30598 dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d",
30599 n_eph, n_gen, n_card_set, generation_skip_ratio));
30603 void gc_heap::descr_segment (heap_segment* seg )
30607 BYTE* x = heap_segment_mem (seg);
30608 while (x < heap_segment_allocated (seg))
30610 dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
30611 x = x + Align(size (x));
30616 void gc_heap::descr_card_table ()
30619 if (trace_gc && (print_level >= 4))
30621 ptrdiff_t min = -1;
30622 dprintf(3,("Card Table set at: "));
30623 for (size_t i = card_of (lowest_address); i < card_of (highest_address); i++)
30625 if (card_set_p (i))
30634 if (! ((min == -1)))
30636 dprintf (3,("[%Ix %Ix[, ",
30637 (size_t)card_address (min), (size_t)card_address (i)));
30646 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
30647 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
30649 #ifdef MULTIPLE_HEAPS
30650 int n_heaps = GCHeap::GetGCHeap()->GetNumberOfHeaps ();
30651 for (int i = 0; i < n_heaps; i++)
30653 gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
30654 #else //MULTIPLE_HEAPS
30656 gc_heap* hp = NULL;
30658 // prefix complains about us dereferencing hp in wks build even though we only access static members
30659 // this way. not sure how to shut it up except for this ugly workaround:
30660 PREFIX_ASSUME(hp != NULL);
30661 #endif // _PREFAST_
30662 #endif //MULTIPLE_HEAPS
30664 int curr_gen_number0 = max_generation+1;
30665 while (curr_gen_number0 >= 0)
30667 generation* gen = hp->generation_of (curr_gen_number0);
30668 heap_segment* seg = generation_start_segment (gen);
30669 while (seg && (seg != hp->ephemeral_heap_segment))
30671 assert (curr_gen_number0 > 0);
30673 // report bounds from heap_segment_mem (seg) to
30674 // heap_segment_allocated (seg);
30675 // for generation # curr_gen_number0
30676 // for heap # heap_no
30678 fn(context, curr_gen_number0, heap_segment_mem (seg),
30679 heap_segment_allocated (seg),
30680 curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
30682 seg = heap_segment_next (seg);
30686 assert (seg == hp->ephemeral_heap_segment);
30687 assert (curr_gen_number0 <= max_generation);
30689 if ((curr_gen_number0 == max_generation))
30691 if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
30693 // report bounds from heap_segment_mem (seg) to
30694 // generation_allocation_start (generation_of (max_generation-1))
30695 // for heap # heap_number
30697 fn(context, curr_gen_number0, heap_segment_mem (seg),
30698 generation_allocation_start (hp->generation_of (max_generation-1)),
30699 generation_allocation_start (hp->generation_of (max_generation-1)) );
30702 else if (curr_gen_number0 != 0)
30704 //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
30705 // to generation_allocation_start (generation_of (curr_gen_number0-1))
30706 // for heap # heap_number
30708 fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
30709 generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
30710 generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
30714 //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
30715 // to heap_segment_allocated (ephemeral_heap_segment);
30716 // for heap # heap_number
30718 fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
30719 heap_segment_allocated (hp->ephemeral_heap_segment),
30720 heap_segment_reserved (hp->ephemeral_heap_segment) );
30723 curr_gen_number0--;
30727 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
30730 // Note that when logging is on it can take a long time to go through the free items.
30731 void gc_heap::print_free_list (int gen, heap_segment* seg)
30734 if (settings.concurrent == FALSE)
30736 BYTE* seg_start = heap_segment_mem (seg);
30737 BYTE* seg_end = heap_segment_allocated (seg);
30739 dprintf (3, ("Free list in seg %Ix:", seg_start));
30741 size_t total_free_item = 0;
30743 allocator* gen_allocator = generation_allocator (generation_of (gen));
30744 for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
30746 BYTE* fo = gen_allocator->alloc_list_head_of (b);
30749 if (fo >= seg_start && fo < seg_end)
30753 size_t free_item_len = size(fo);
30755 dprintf (3, ("[%Ix, %Ix[:%Id",
30757 (size_t)(fo + free_item_len),
30761 fo = free_list_slot (fo);
30765 dprintf (3, ("total %Id free items", total_free_item));
30771 void gc_heap::descr_generations (BOOL begin_gc_p)
30774 if (StressLog::StressLogOn(LF_GC, LL_INFO10))
30777 #ifdef MULTIPLE_HEAPS
30779 #endif //MULTIPLE_HEAPS
30781 STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
30782 for (int n = max_generation; n >= 0; --n)
30784 STRESS_LOG4(LF_GC, LL_INFO10, " Generation %d [%p, %p] cur = %p\n",
30786 generation_allocation_start(generation_of(n)),
30787 generation_allocation_limit(generation_of(n)),
30788 generation_allocation_pointer(generation_of(n)));
30790 heap_segment* seg = generation_start_segment(generation_of(n));
30793 STRESS_LOG4(LF_GC, LL_INFO10, " Segment mem %p alloc = %p used %p committed %p\n",
30794 heap_segment_mem(seg),
30795 heap_segment_allocated(seg),
30796 heap_segment_used(seg),
30797 heap_segment_committed(seg));
30798 seg = heap_segment_next(seg);
30802 #endif // STRESS_LOG
30805 dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
30806 (size_t) lowest_address, (size_t) highest_address));
30807 #ifdef BACKGROUND_GC
30808 dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
30809 (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
30810 #endif //BACKGROUND_GC
30812 if (heap_number == 0)
30814 dprintf (1, ("soh size: %Id", get_total_heap_size()));
30817 int curr_gen_number = max_generation+1;
30818 while (curr_gen_number >= 0)
30820 size_t total_gen_size = generation_size (curr_gen_number);
30821 #ifdef SIMPLE_DPRINTF
30822 dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
30823 (begin_gc_p ? "BEG" : "END"),
30824 settings.condemned_generation,
30827 dd_fragmentation (dynamic_data_of (curr_gen_number)),
30828 generation_free_list_space (generation_of (curr_gen_number)),
30829 generation_free_obj_space (generation_of (curr_gen_number)),
30831 (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
30833 (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
30834 (settings.heap_expansion ? "(EX)" : " "),
30835 (settings.promotion ? "Promotion" : "NoPromotion")));
30837 dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
30839 size (generation_allocation_start (generation_of (curr_gen_number))),
30841 dd_fragmentation (dynamic_data_of (curr_gen_number))));
30842 #endif //SIMPLE_DPRINTF
30844 generation* gen = generation_of (curr_gen_number);
30845 heap_segment* seg = generation_start_segment (gen);
30846 while (seg && (seg != ephemeral_heap_segment))
30848 dprintf (GTC_LOG,("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
30850 (size_t)heap_segment_mem (seg),
30851 (size_t)heap_segment_allocated (seg),
30852 (size_t)heap_segment_committed (seg),
30853 (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
30854 (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
30855 print_free_list (curr_gen_number, seg);
30856 seg = heap_segment_next (seg);
30858 if (seg && (seg != generation_start_segment (gen)))
30860 dprintf (GTC_LOG,("g%d: [%Ix %Ix[",
30862 (size_t)heap_segment_mem (seg),
30863 (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
30864 print_free_list (curr_gen_number, seg);
30869 dprintf (GTC_LOG,("g%d: [%Ix %Ix[",
30871 (size_t)generation_allocation_start (generation_of (curr_gen_number)),
30872 (size_t)(((curr_gen_number == 0)) ?
30873 (heap_segment_allocated
30874 (generation_start_segment
30875 (generation_of (curr_gen_number)))) :
30876 (generation_allocation_start
30877 (generation_of (curr_gen_number - 1))))
30879 print_free_list (curr_gen_number, seg);
30891 //-----------------------------------------------------------------------------
30893 // VM Specific support
30895 //-----------------------------------------------------------------------------
30900 unsigned int PromotedObjectCount = 0;
30901 unsigned int CreatedObjectCount = 0;
30902 unsigned int AllocDuration = 0;
30903 unsigned int AllocCount = 0;
30904 unsigned int AllocBigCount = 0;
30905 unsigned int AllocSmallCount = 0;
30906 unsigned int AllocStart = 0;
30909 //Static member variables.
30910 VOLATILE(BOOL) GCHeap::GcInProgress = FALSE;
30912 //CMCSafeLock* GCHeap::fGcLock;
30913 CLREvent *GCHeap::WaitForGCEvent = NULL;
30916 unsigned int GCHeap::GcDuration;
30918 unsigned GCHeap::GcCondemnedGeneration = 0;
30919 size_t GCHeap::totalSurvivedSize = 0;
30920 #ifdef FEATURE_PREMORTEM_FINALIZATION
30921 CFinalize* GCHeap::m_Finalize = 0;
30922 BOOL GCHeap::GcCollectClasses = FALSE;
30923 VOLATILE(LONG) GCHeap::m_GCFLock = 0;
30925 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
30927 #ifdef BACKGROUND_GC
30928 int GCHeap::gc_stress_fgcs_in_bgc = 0;
30929 #endif // BACKGROUND_GC
30930 #ifndef MULTIPLE_HEAPS
30931 OBJECTHANDLE GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
30932 int GCHeap::m_CurStressObj = 0;
30933 #endif // !MULTIPLE_HEAPS
30934 #endif // STRESS_HEAP
30935 #endif // FEATURE_REDHAWK
30937 #endif //FEATURE_PREMORTEM_FINALIZATION
30939 static void spin_lock ()
30941 enter_spin_lock_noinstru (&m_GCLock);
30945 void EnterAllocLock()
30947 #if defined(_TARGET_X86_)
30949 inc dword ptr m_GCLock
30954 #else //_TARGET_X86_
30956 #endif //_TARGET_X86_
30960 void LeaveAllocLock()
30963 leave_spin_lock_noinstru (&m_GCLock);
30966 class AllocLockHolder
30980 // An explanation of locking for finalization:
30982 // Multiple threads allocate objects. During the allocation, they are serialized by
30983 // the AllocLock above. But they release that lock before they register the object
30984 // for finalization. That's because there is much contention for the alloc lock, but
30985 // finalization is presumed to be a rare case.
30987 // So registering an object for finalization must be protected by the FinalizeLock.
30989 // There is another logical queue that involves finalization. When objects registered
30990 // for finalization become unreachable, they are moved from the "registered" queue to
30991 // the "unreachable" queue. Note that this only happens inside a GC, so no other
30992 // threads can be manipulating either queue at that time. Once the GC is over and
30993 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
30994 // queue and call their finalizers. This dequeue operation is also protected with
30995 // the finalize lock.
30997 // At first, this seems unnecessary. Only one thread is ever enqueuing or dequeuing
30998 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
30999 // when a GC is not in progress). The reason we share a lock with threads enqueuing
31000 // on the "registered" queue is that the "registered" and "unreachable" queues are
31003 // They are actually two regions of a longer list, which can only grow at one end.
31004 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
31005 // object at the boundary between the logical queues, out to the other end of the
31006 // unreachable queue -- where all growing takes place. Then you move the boundary
31007 // pointer so that the gap we created at the boundary is now on the "registered"
31008 // side rather than the "unreachable" side. Now the object can be placed into the
31009 // "registered" side at that point. This is much more efficient than doing moves
31010 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
31012 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock. Instead, it relies
31013 // on the fact that the lock will only be taken for a brief period and that it will
31014 // never provoke or allow a GC while the lock is held. This is critical. If the
31015 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
31016 // allow a GC), then the Alloc client would have to GC protect a finalizable object
31017 // to protect against that eventuality. That is too slow!
31021 BOOL IsValidObject99(BYTE *pObject)
31024 if (!((CObjectHeader*)pObject)->IsFree())
31025 ((CObjectHeader *) pObject)->Validate();
31026 #endif //VERIFY_HEAP
31030 #ifdef BACKGROUND_GC
31031 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg,
31036 BYTE* seg_start = heap_segment_mem (seg);
31037 BYTE* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
31039 if ((seg_start < background_saved_highest_address) &&
31040 (seg_end > background_saved_lowest_address))
31042 *range_beg = max (seg_start, background_saved_lowest_address);
31043 *range_end = min (seg_end, background_saved_highest_address);
31052 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
31054 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31055 if (recursive_gc_sync::background_running_p() && g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31057 BYTE* range_beg = 0;
31058 BYTE* range_end = 0;
31060 if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
31062 size_t markw = mark_word_of (range_beg);
31063 size_t markw_end = mark_word_of (range_end);
31064 while (markw < markw_end)
31066 if (mark_array [markw])
31068 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
31069 markw, mark_array [markw], mark_word_address (markw)));
31074 BYTE* p = mark_word_address (markw_end);
31075 while (p < range_end)
31077 assert (!(mark_array_marked (p)));
31082 #endif //VERIFY_HEAP && MARK_ARRAY
31085 void gc_heap::verify_mark_bits_cleared (BYTE* obj, size_t s)
31087 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31088 size_t start_mark_bit = mark_bit_of (obj) + 1;
31089 size_t end_mark_bit = mark_bit_of (obj + s);
31090 unsigned int startbit = mark_bit_bit (start_mark_bit);
31091 unsigned int endbit = mark_bit_bit (end_mark_bit);
31092 size_t startwrd = mark_bit_word (start_mark_bit);
31093 size_t endwrd = mark_bit_word (end_mark_bit);
31094 unsigned int result = 0;
31096 unsigned int firstwrd = ~(lowbits (~0, startbit));
31097 unsigned int lastwrd = ~(highbits (~0, endbit));
31099 if (startwrd == endwrd)
31101 unsigned int wrd = firstwrd & lastwrd;
31102 result = mark_array[startwrd] & wrd;
31110 // verify the first mark word is cleared.
31113 result = mark_array[startwrd] & firstwrd;
31121 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
31123 result = mark_array[wrdtmp];
31130 // set the last mark word.
31133 result = mark_array[endwrd] & lastwrd;
31139 #endif //VERIFY_HEAP && MARK_ARRAY
31142 void gc_heap::clear_all_mark_array()
31145 //size_t num_dwords_written = 0;
31146 //LARGE_INTEGER ts;
31147 //if (!QueryPerformanceCounter(&ts))
31148 // FATAL_GC_ERROR();
31150 //size_t begin_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
31152 generation* gen = generation_of (max_generation);
31153 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31159 if (gen != large_object_generation)
31161 gen = generation_of (max_generation+1);
31162 seg = heap_segment_rw (generation_start_segment (gen));
31170 BYTE* range_beg = 0;
31171 BYTE* range_end = 0;
31173 if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
31175 size_t markw = mark_word_of (range_beg);
31176 size_t markw_end = mark_word_of (range_end);
31177 size_t size_total = (markw_end - markw) * sizeof (DWORD);
31178 //num_dwords_written = markw_end - markw;
31180 size_t size_left = 0;
31182 assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
31184 if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
31186 size = (size_total & ~(sizeof(PTR_PTR) - 1));
31187 size_left = size_total - size;
31188 assert ((size_left & (sizeof (DWORD) - 1)) == 0);
31195 memclr ((BYTE*)&mark_array[markw], size);
31197 if (size_left != 0)
31199 DWORD* markw_to_clear = &mark_array[markw + size / sizeof (DWORD)];
31200 for (size_t i = 0; i < (size_left / sizeof (DWORD)); i++)
31202 *markw_to_clear = 0;
31208 seg = heap_segment_next_rw (seg);
31211 //if (!QueryPerformanceCounter(&ts))
31212 // FATAL_GC_ERROR();
31214 //size_t end_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000)) - begin_time;
31216 //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(DWORD));
31218 #endif //MARK_ARRAY
31221 #endif //BACKGROUND_GC
31223 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
31225 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31226 assert (card_table == g_card_table);
31227 size_t markw = mark_word_of (heap_segment_mem (seg));
31228 size_t markw_end = mark_word_of (heap_segment_reserved (seg));
31230 while (markw < markw_end)
31232 if (mark_array [markw])
31234 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
31235 markw, mark_array [markw], mark_word_address (markw)));
31240 #endif //VERIFY_HEAP && MARK_ARRAY
31243 void gc_heap::verify_mark_array_cleared ()
31245 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31246 if (recursive_gc_sync::background_running_p() && g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31248 generation* gen = generation_of (max_generation);
31249 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31255 if (gen != large_object_generation)
31257 gen = generation_of (max_generation+1);
31258 seg = heap_segment_rw (generation_start_segment (gen));
31266 bgc_verify_mark_array_cleared (seg);
31267 seg = heap_segment_next_rw (seg);
31270 #endif //VERIFY_HEAP && MARK_ARRAY
31273 void gc_heap::verify_seg_end_mark_array_cleared()
31275 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31276 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31278 generation* gen = generation_of (max_generation);
31279 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31285 if (gen != large_object_generation)
31287 gen = generation_of (max_generation+1);
31288 seg = heap_segment_rw (generation_start_segment (gen));
31296 // We already cleared all mark array bits for ephemeral generations
31297 // at the beginning of bgc sweep
31298 BYTE* from = ((seg == ephemeral_heap_segment) ?
31299 generation_allocation_start (generation_of (max_generation - 1)) :
31300 heap_segment_allocated (seg));
31301 size_t markw = mark_word_of (align_on_mark_word (from));
31302 size_t markw_end = mark_word_of (heap_segment_reserved (seg));
31304 while (from < mark_word_address (markw))
31306 if (is_mark_bit_set (from))
31308 dprintf (3, ("mark bit for %Ix was not cleared", from));
31312 from += mark_bit_pitch;
31315 while (markw < markw_end)
31317 if (mark_array [markw])
31319 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
31320 markw, mark_array [markw], mark_word_address (markw)));
31325 seg = heap_segment_next_rw (seg);
31328 #endif //VERIFY_HEAP && MARK_ARRAY
31331 // This function is called to make sure we don't mess up the segment list
31332 // in SOH. It's called by:
31333 // 1) begin and end of ephemeral GCs
31334 // 2) during bgc sweep when we switch segments.
31335 void gc_heap::verify_soh_segment_list()
31338 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31340 generation* gen = generation_of (max_generation);
31341 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31342 heap_segment* last_seg = 0;
31346 seg = heap_segment_next_rw (seg);
31348 if (last_seg != ephemeral_heap_segment)
31353 #endif //VERIFY_HEAP
31356 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
31357 // it can be called at the end of the final marking; and at any point during background
31359 // NOTE - to be able to call this function during background sweep, we need to temporarily
31360 // NOT clear the mark array bits as we go.
31361 void gc_heap::verify_partial ()
31363 #ifdef BACKGROUND_GC
31364 //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
31365 //generation* gen = large_object_generation;
31366 generation* gen = generation_of (max_generation);
31367 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31368 int align_const = get_alignment_constant (gen != large_object_generation);
31374 // Different ways to fail.
31375 BOOL mark_missed_p = FALSE;
31376 BOOL bad_ref_p = FALSE;
31377 BOOL free_ref_p = FALSE;
31383 if (gen != large_object_generation)
31386 gen = large_object_generation;
31387 align_const = get_alignment_constant (gen != large_object_generation);
31388 seg = heap_segment_rw (generation_start_segment (gen));
31397 o = heap_segment_mem (seg);
31398 end = heap_segment_allocated (seg);
31399 //printf ("validating [%Ix-[%Ix\n", o, end);
31404 BOOL marked_p = background_object_marked (o, FALSE);
31408 go_through_object_cl (method_table (o), o, s, oo,
31412 //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
31413 MethodTable *pMT = method_table (*oo);
31415 if (pMT == g_pFreeObjectMethodTable)
31421 if (!pMT->SanityCheck())
31424 dprintf (3, ("Bad member of %Ix %Ix",
31425 (size_t)oo, (size_t)*oo));
31429 if (current_bgc_state == bgc_final_marking)
31431 if (marked_p && !background_object_marked (*oo, FALSE))
31433 mark_missed_p = TRUE;
31442 o = o + Align(s, align_const);
31444 seg = heap_segment_next_rw (seg);
31447 //printf ("didn't find any large object large enough...\n");
31448 //printf ("finished verifying loh\n");
31449 #endif //BACKGROUND_GC
31455 gc_heap::verify_free_lists ()
31457 for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
31459 dprintf (3, ("Verifying free list for gen:%d", gen_num));
31460 allocator* gen_alloc = generation_allocator (generation_of (gen_num));
31461 size_t sz = gen_alloc->first_bucket_size();
31462 bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
31464 for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
31466 BYTE* free_list = gen_alloc->alloc_list_head_of (a_l_number);
31470 if (!((CObjectHeader*)free_list)->IsFree())
31472 dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
31473 (size_t)free_list));
31476 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
31477 || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
31479 dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
31480 (size_t)free_list));
31483 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
31485 dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
31486 (size_t)free_list));
31489 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
31491 dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
31492 (size_t)free_list));
31497 free_list = free_list_slot (free_list);
31499 //verify the sanity of the tail
31500 BYTE* tail = gen_alloc->alloc_list_tail_of (a_l_number);
31501 if (!((tail == 0) || (tail == prev)))
31503 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
31508 BYTE* head = gen_alloc->alloc_list_head_of (a_l_number);
31509 if ((head != 0) && (free_list_slot (head) != 0))
31511 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
31522 gc_heap::verify_heap (BOOL begin_gc_p)
31524 int heap_verify_level = g_pConfig->GetHeapVerifyLevel();
31525 size_t last_valid_brick = 0;
31526 BOOL bCurrentBrickInvalid = FALSE;
31527 BOOL large_brick_p = TRUE;
31528 size_t curr_brick = 0;
31529 size_t prev_brick = (size_t)-1;
31530 int curr_gen_num = max_generation+1;
31531 heap_segment* seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
31533 PREFIX_ASSUME(seg != NULL);
31535 BYTE* curr_object = heap_segment_mem (seg);
31536 BYTE* prev_object = 0;
31537 BYTE* begin_youngest = generation_allocation_start(generation_of(0));
31538 BYTE* end_youngest = heap_segment_allocated (ephemeral_heap_segment);
31539 BYTE* next_boundary = generation_allocation_start (generation_of (max_generation - 1));
31540 int align_const = get_alignment_constant (FALSE);
31541 size_t total_objects_verified = 0;
31542 size_t total_objects_verified_deep = 0;
31544 #ifdef BACKGROUND_GC
31545 BOOL consider_bgc_mark_p = FALSE;
31546 BOOL check_current_sweep_p = FALSE;
31547 BOOL check_saved_sweep_p = FALSE;
31548 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31549 #endif //BACKGROUND_GC
31551 #ifdef MULTIPLE_HEAPS
31552 t_join* current_join = &gc_t_join;
31553 #ifdef BACKGROUND_GC
31554 if (settings.concurrent && (GetCurrentThreadId() == bgc_thread_id))
31556 // We always call verify_heap on entry of GC on the SVR GC threads.
31557 current_join = &bgc_t_join;
31559 #endif //BACKGROUND_GC
31560 #endif //MULTIPLE_HEAPS
31562 #ifdef BACKGROUND_GC
31563 dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin",
31564 (begin_gc_p ? "BEG" : "END"),
31565 VolatileLoad(&settings.gc_index),
31566 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
31568 dprintf (2,("[%s]GC#%d: Verifying heap - begin",
31569 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
31570 #endif //BACKGROUND_GC
31572 #ifndef MULTIPLE_HEAPS
31573 if ((g_ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
31574 (g_ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
31578 #endif //MULTIPLE_HEAPS
31580 #ifdef BACKGROUND_GC
31581 //don't touch the memory because the program is allocating from it.
31582 if (!settings.concurrent)
31583 #endif //BACKGROUND_GC
31585 if (!(heap_verify_level & EEConfig::HEAPVERIFY_NO_MEM_FILL))
31587 //uninit the unused portions of segments.
31588 generation* gen1 = large_object_generation;
31589 heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
31590 PREFIX_ASSUME(seg1 != NULL);
31596 BYTE* clear_start = heap_segment_allocated (seg1) - plug_skew;
31597 if (heap_segment_used (seg1) > clear_start)
31599 dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa",
31600 heap_segment_mem (seg1),
31602 heap_segment_used (seg1)));
31603 memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
31604 (heap_segment_used (seg1) - clear_start));
31606 seg1 = heap_segment_next_rw (seg1);
31610 if (gen1 == large_object_generation)
31612 gen1 = generation_of (max_generation);
31613 seg1 = heap_segment_rw (generation_start_segment (gen1));
31614 PREFIX_ASSUME(seg1 != NULL);
31625 #ifdef MULTIPLE_HEAPS
31626 current_join->join(this, gc_join_verify_copy_table);
31627 if (current_join->joined())
31629 // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
31630 for (int i = 0; i < n_heaps; i++)
31632 //copy the card and brick tables
31633 if (g_card_table != g_heaps[i]->card_table)
31635 g_heaps[i]->copy_brick_card_table (FALSE);
31639 current_join->restart();
31642 if (g_card_table != card_table)
31643 copy_brick_card_table (FALSE);
31644 #endif //MULTIPLE_HEAPS
31646 //verify that the generation structures makes sense
31648 generation* gen = generation_of (max_generation);
31650 assert (generation_allocation_start (gen) ==
31651 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
31652 int gen_num = max_generation-1;
31653 generation* prev_gen = gen;
31654 while (gen_num >= 0)
31656 gen = generation_of (gen_num);
31657 assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
31658 assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
31659 assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
31661 if (generation_start_segment (prev_gen ) ==
31662 generation_start_segment (gen))
31664 assert (generation_allocation_start (prev_gen) <
31665 generation_allocation_start (gen));
31674 // Handle segment transitions
31675 if (curr_object >= heap_segment_allocated (seg))
31677 if (curr_object > heap_segment_allocated(seg))
31679 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
31680 (size_t)curr_object, (size_t)seg));
31683 seg = heap_segment_next_in_range (seg);
31686 #ifdef BACKGROUND_GC
31687 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31688 #endif //BACKGROUND_GC
31689 curr_object = heap_segment_mem(seg);
31695 if (curr_gen_num == (max_generation+1))
31698 seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
31700 PREFIX_ASSUME(seg != NULL);
31702 #ifdef BACKGROUND_GC
31703 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31704 #endif //BACKGROUND_GC
31705 curr_object = heap_segment_mem (seg);
31707 large_brick_p = FALSE;
31708 align_const = get_alignment_constant (TRUE);
31711 break; // Done Verifying Heap -- no more segments
31715 // Are we at the end of the youngest_generation?
31716 if ((seg == ephemeral_heap_segment))
31718 if (curr_object >= end_youngest)
31720 // prev_object length is too long if we hit this int3
31721 if (curr_object > end_youngest)
31723 dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
31724 (size_t)curr_object, (size_t)end_youngest));
31730 if ((curr_object >= next_boundary) && (curr_gen_num > 0))
31733 if (curr_gen_num > 0)
31735 next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
31740 //if (is_mark_set (curr_object))
31742 // printf ("curr_object: %Ix is marked!",(size_t)curr_object);
31743 // FATAL_GC_ERROR();
31746 size_t s = size (curr_object);
31747 dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
31750 dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
31754 // If object is not in the youngest generation, then lets
31755 // verify that the brick table is correct....
31756 if (((seg != ephemeral_heap_segment) ||
31757 (brick_of(curr_object) < brick_of(begin_youngest))))
31759 curr_brick = brick_of(curr_object);
31761 // Brick Table Verification...
31763 // On brick transition
31764 // if brick is negative
31765 // verify that brick indirects to previous valid brick
31767 // set current brick invalid flag to be flipped if we
31768 // encounter an object at the correct place
31770 if (curr_brick != prev_brick)
31772 // If the last brick we were examining had positive
31773 // entry but we never found the matching object, then
31774 // we have a problem
31775 // If prev_brick was the last one of the segment
31776 // it's ok for it to be invalid because it is never looked at
31777 if (bCurrentBrickInvalid &&
31778 (curr_brick != brick_of (heap_segment_mem (seg))) &&
31779 !heap_segment_read_only_p (seg))
31781 dprintf (3, ("curr brick %Ix invalid", curr_brick));
31787 //large objects verify the table only if they are in
31789 if ((heap_segment_reserved (seg) <= highest_address) &&
31790 (heap_segment_mem (seg) >= lowest_address) &&
31791 brick_table [curr_brick] != 0)
31793 dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
31794 curr_brick, (size_t)curr_object));
31799 bCurrentBrickInvalid = FALSE;
31804 // If the current brick contains a negative value make sure
31805 // that the indirection terminates at the last valid brick
31806 if (brick_table [curr_brick] < 0)
31808 if (brick_table [curr_brick] == 0)
31810 dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
31811 curr_brick, (size_t)curr_object));
31814 ptrdiff_t i = curr_brick;
31815 while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
31816 (brick_table[i] < 0))
31818 i = i + brick_table[i];
31820 if (i < ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
31822 dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
31823 i, brick_of (heap_segment_mem (seg)),
31827 // if (i != last_valid_brick)
31828 // FATAL_GC_ERROR();
31829 bCurrentBrickInvalid = FALSE;
31831 else if (!heap_segment_read_only_p (seg))
31833 bCurrentBrickInvalid = TRUE;
31838 if (bCurrentBrickInvalid)
31840 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
31842 bCurrentBrickInvalid = FALSE;
31843 last_valid_brick = curr_brick;
31848 if (*((BYTE**)curr_object) != (BYTE *) g_pFreeObjectMethodTable)
31850 #ifdef FEATURE_LOH_COMPACTION
31851 if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
31853 assert (method_table (prev_object) == g_pFreeObjectMethodTable);
31855 #endif //FEATURE_LOH_COMPACTION
31857 total_objects_verified++;
31859 BOOL can_verify_deep = TRUE;
31860 #ifdef BACKGROUND_GC
31861 can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
31862 #endif //BACKGROUND_GC
31864 BOOL deep_verify_obj = can_verify_deep;
31865 if ((heap_verify_level & EEConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
31866 deep_verify_obj = FALSE;
31868 ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
31870 if (can_verify_deep)
31872 if (curr_gen_num > 0)
31874 BOOL need_card_p = FALSE;
31875 if (contain_pointers_or_collectible (curr_object))
31877 dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
31878 size_t crd = card_of (curr_object);
31879 BOOL found_card_p = card_set_p (crd);
31881 #ifdef COLLECTIBLE_CLASS
31882 if (is_collectible(curr_object))
31884 BYTE* class_obj = get_class_object (curr_object);
31885 if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
31889 dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
31890 card_of (curr_object), (size_t)curr_object, class_obj));
31896 #endif //COLLECTIBLE_CLASS
31898 if (contain_pointers(curr_object))
31900 go_through_object_nostart
31901 (method_table(curr_object), curr_object, s, oo,
31903 if ((crd != card_of ((BYTE*)oo)) && !found_card_p)
31905 crd = card_of ((BYTE*)oo);
31906 found_card_p = card_set_p (crd);
31907 need_card_p = FALSE;
31909 if ((*oo < ephemeral_high) && (*oo >= next_boundary))
31911 need_card_p = TRUE;
31914 if (need_card_p && !found_card_p)
31917 dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
31918 card_of (curr_object), (size_t)curr_object,
31919 card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
31925 if (need_card_p && !found_card_p)
31927 dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
31928 card_of (curr_object), (size_t)curr_object,
31929 card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
31934 total_objects_verified_deep++;
31938 prev_object = curr_object;
31939 prev_brick = curr_brick;
31940 curr_object = curr_object + Align(s, align_const);
31941 if (curr_object < prev_object)
31943 dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
31948 #ifdef BACKGROUND_GC
31949 dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id",
31950 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
31951 (begin_gc_p ? "BEG" : "END"),
31952 ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
31953 total_objects_verified, total_objects_verified_deep));
31954 if (current_c_gc_state != c_gc_state_planning)
31956 assert (total_objects_verified == total_objects_verified_deep);
31958 #endif //BACKGROUND_GC
31960 verify_free_lists();
31962 #ifdef FEATURE_PREMORTEM_FINALIZATION
31963 finalize_queue->CheckFinalizerObjects();
31964 #endif // FEATURE_PREMORTEM_FINALIZATION
31967 // to be consistent with handle table APIs pass a ScanContext*
31968 // to provide the heap number. the SC isn't complete though so
31969 // limit its scope to handle table verification.
31971 sc.thread_number = heap_number;
31972 CNameSpace::VerifyHandleTable(max_generation, max_generation, &sc);
31975 #ifdef MULTIPLE_HEAPS
31976 current_join->join(this, gc_join_verify_objects_done);
31977 if (current_join->joined())
31978 #endif //MULTIPLE_HEAPS
31980 SyncBlockCache::GetSyncBlockCache()->VerifySyncTableEntry();
31981 #ifdef MULTIPLE_HEAPS
31982 current_join->restart();
31983 #endif //MULTIPLE_HEAPS
31986 #ifdef BACKGROUND_GC
31987 if (!settings.concurrent)
31989 if (current_c_gc_state == c_gc_state_planning)
31991 // temporarily commenting this out 'cause an FGC
31992 // could be triggered before we sweep ephemeral.
31993 //verify_seg_end_mark_array_cleared();
31997 if (settings.concurrent)
31999 verify_mark_array_cleared();
32001 dprintf (2,("GC%d(%s): Verifying heap - end",
32002 VolatileLoad(&settings.gc_index),
32003 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
32005 dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
32006 #endif //BACKGROUND_GC
32009 void GCHeap::ValidateObjectMember (Object* obj)
32011 size_t s = size (obj);
32012 BYTE* o = (BYTE*)obj;
32014 go_through_object_cl (method_table (obj), o, s, oo,
32016 BYTE* child_o = *oo;
32019 dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
32020 MethodTable *pMT = method_table (child_o);
32021 if (!pMT->SanityCheck()) {
32022 dprintf (3, ("Bad member of %Ix %Ix",
32023 (size_t)oo, (size_t)child_o));
32030 #endif //VERIFY_HEAP
32032 void DestructObject (CObjectHeader* hdr)
32034 hdr->~CObjectHeader();
32037 HRESULT GCHeap::Shutdown ()
32041 CNameSpace::GcRuntimeStructuresValid (FALSE);
32043 // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
32044 // threads except the one performing the shutdown.
32045 // ASSERT( !GcInProgress );
32047 // Guard against any more GC occurring and against any threads blocking
32048 // for GC to complete when the GC heap is gone. This fixes a race condition
32049 // where a thread in GC is destroyed as part of process destruction and
32050 // the remaining threads block for GC complete.
32053 //EnterAllocLock();
32055 //EnterFinalizeLock();
32058 // during shutdown lot of threads are suspended
32059 // on this even, we don't want to wake them up just yet
32060 //CloseHandle (WaitForGCEvent);
32062 //find out if the global card table hasn't been used yet
32063 DWORD* ct = &g_card_table[card_word (gcard_of (g_lowest_address))];
32064 if (card_table_refcount (ct) == 0)
32066 destroy_card_table (ct);
32070 //destroy all segments on the standby list
32071 while(gc_heap::segment_standby_list != 0)
32073 heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
32074 #ifdef MULTIPLE_HEAPS
32075 (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
32076 #else //MULTIPLE_HEAPS
32077 pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
32078 #endif //MULTIPLE_HEAPS
32079 gc_heap::segment_standby_list = next_seg;
32083 #ifdef MULTIPLE_HEAPS
32085 for (int i = 0; i < gc_heap::n_heaps; i ++)
32087 delete gc_heap::g_heaps[i]->vm_heap;
32088 //destroy pure GC stuff
32089 gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
32092 gc_heap::destroy_gc_heap (pGenGCHeap);
32094 #endif //MULTIPLE_HEAPS
32095 gc_heap::shutdown_gc();
32100 //used by static variable implementation
32101 void CGCDescGcScan(LPVOID pvCGCDesc, promote_func* fn, ScanContext* sc)
32103 CGCDesc* map = (CGCDesc*)pvCGCDesc;
32105 CGCDescSeries *last = map->GetLowestSeries();
32106 CGCDescSeries *cur = map->GetHighestSeries();
32108 assert (cur >= last);
32111 BYTE** ppslot = (BYTE**)((PBYTE)pvCGCDesc + cur->GetSeriesOffset());
32112 BYTE**ppstop = (BYTE**)((PBYTE)ppslot + cur->GetSeriesSize());
32114 while (ppslot < ppstop)
32118 (fn) ((Object**)ppslot, sc, 0);
32126 while (cur >= last);
32129 // Wait until a garbage collection is complete
32130 // returns NOERROR if wait was OK, other error code if failure.
32131 // WARNING: This will not undo the must complete state. If you are
32132 // in a must complete when you call this, you'd better know what you're
32135 #ifdef FEATURE_PREMORTEM_FINALIZATION
32137 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
32139 *pCFinalize = new (nothrow) CFinalize();
32140 if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
32141 return E_OUTOFMEMORY;
32145 #endif // FEATURE_PREMORTEM_FINALIZATION
32147 // init the instance heap
32148 HRESULT GCHeap::Init(size_t hn)
32150 HRESULT hres = S_OK;
32152 //Initialize all of the instance members.
32154 #ifdef MULTIPLE_HEAPS
32156 #endif //MULTIPLE_HEAPS
32158 // Rest of the initialization
32160 #ifdef MULTIPLE_HEAPS
32161 if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
32162 hres = E_OUTOFMEMORY;
32164 if (!gc_heap::make_gc_heap())
32165 hres = E_OUTOFMEMORY;
32166 #endif //MULTIPLE_HEAPS
32172 //System wide initialization
32173 HRESULT GCHeap::Initialize ()
32178 //Initialize the static members.
32181 CreatedObjectCount = 0;
32184 size_t seg_size = get_valid_segment_size();
32185 size_t large_seg_size = get_valid_segment_size(TRUE);
32186 gc_heap::min_segment_size = min (seg_size, large_seg_size);
32188 #ifdef MULTIPLE_HEAPS
32189 // GetGCProcessCpuCount only returns up to 64 procs.
32190 unsigned nhp = CPUGroupInfo::CanEnableGCCPUGroups() ? CPUGroupInfo::GetNumActiveProcessors():
32191 GetCurrentProcessCpuCount();
32192 hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
32194 hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
32195 #endif //MULTIPLE_HEAPS
32200 #if defined(_WIN64)
32202 GetProcessMemoryLoad (&ms);
32203 gc_heap::total_physical_mem = ms.ullTotalPhys;
32204 gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
32205 gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
32208 WaitForGCEvent = new (nothrow) CLREvent;
32210 if (!WaitForGCEvent)
32212 return E_OUTOFMEMORY;
32215 WaitForGCEvent->CreateManualEvent(TRUE);
32217 StompWriteBarrierResize(FALSE);
32219 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32220 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
32221 if (GCStress<cfg_any>::IsEnabled()) {
32222 for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
32223 m_StressObjs[i] = CreateGlobalHandle(0);
32224 m_CurStressObj = 0;
32226 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
32227 #endif // FEATURE_REDHAWK
32229 initGCShadow(); // If we are debugging write barriers, initialize heap shadow
32231 #ifdef MULTIPLE_HEAPS
32233 for (unsigned i = 0; i < nhp; i++)
32235 GCHeap* Hp = new (nothrow) GCHeap();
32237 return E_OUTOFMEMORY;
32239 if ((hr = Hp->Init (i))!= S_OK)
32244 // initialize numa node to heap map
32245 heap_select::init_numa_node_to_heap_map(nhp);
32248 #endif //MULTIPLE_HEAPS
32252 CNameSpace::GcRuntimeStructuresValid (TRUE);
32254 #ifdef GC_PROFILING
32255 if (CORProfilerTrackGC())
32256 UpdateGenerationBounds();
32257 #endif // GC_PROFILING
32264 // GC callback functions
32265 BOOL GCHeap::IsPromoted(Object* object)
32268 ((CObjectHeader*)object)->Validate();
32271 BYTE* o = (BYTE*)object;
32273 if (gc_heap::settings.condemned_generation == max_generation)
32275 #ifdef MULTIPLE_HEAPS
32276 gc_heap* hp = gc_heap::g_heaps[0];
32278 gc_heap* hp = pGenGCHeap;
32279 #endif //MULTIPLE_HEAPS
32281 #ifdef BACKGROUND_GC
32282 if (gc_heap::settings.concurrent)
32284 BOOL is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
32285 hp->background_marked (o));
32289 #endif //BACKGROUND_GC
32291 return (!((o < hp->highest_address) && (o >= hp->lowest_address))
32292 || hp->is_mark_set (o));
32297 gc_heap* hp = gc_heap::heap_of (o);
32298 return (!((o < hp->gc_high) && (o >= hp->gc_low))
32299 || hp->is_mark_set (o));
32303 size_t GCHeap::GetPromotedBytes(int heap_index)
32305 #ifdef BACKGROUND_GC
32306 if (gc_heap::settings.concurrent)
32308 return gc_heap::bpromoted_bytes (heap_index);
32311 #endif //BACKGROUND_GC
32313 return gc_heap::promoted_bytes (heap_index);
32317 unsigned int GCHeap::WhichGeneration (Object* object)
32319 gc_heap* hp = gc_heap::heap_of ((BYTE*)object);
32320 unsigned int g = hp->object_gennum ((BYTE*)object);
32321 dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
32325 BOOL GCHeap::IsEphemeral (Object* object)
32327 BYTE* o = (BYTE*)object;
32328 gc_heap* hp = gc_heap::heap_of (o);
32329 return hp->ephemeral_pointer_p (o);
32333 // Return NULL if can't find next object. When EE is not suspended,
32334 // the result is not accurate: if the input arg is in gen0, the function could
32335 // return zeroed out memory as next object
32336 Object * GCHeap::NextObj (Object * object)
32338 BYTE* o = (BYTE*)object;
32339 heap_segment * hs = gc_heap::find_segment (o, FALSE);
32345 BOOL large_object_p = heap_segment_loh_p (hs);
32346 if (large_object_p)
32347 return NULL; //could be racing with another core allocating.
32348 #ifdef MULTIPLE_HEAPS
32349 gc_heap* hp = heap_segment_heap (hs);
32350 #else //MULTIPLE_HEAPS
32352 #endif //MULTIPLE_HEAPS
32353 unsigned int g = hp->object_gennum ((BYTE*)object);
32354 if ((g == 0) && hp->settings.demotion)
32355 return NULL;//could be racing with another core allocating.
32356 int align_const = get_alignment_constant (!large_object_p);
32357 BYTE* nextobj = o + Align (size (o), align_const);
32358 if (nextobj <= o) // either overflow or 0 sized object.
32363 if ((nextobj < heap_segment_mem(hs)) ||
32364 (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) ||
32365 (nextobj >= hp->alloc_allocated))
32370 return (Object *)nextobj;
32373 #ifdef FEATURE_BASICFREEZE
32374 BOOL GCHeap::IsInFrozenSegment (Object * object)
32376 BYTE* o = (BYTE*)object;
32377 heap_segment * hs = gc_heap::find_segment (o, FALSE);
32378 //We create a frozen object for each frozen segment before the segment is inserted
32379 //to segment list; during ngen, we could also create frozen objects in segments which
32380 //don't belong to current GC heap.
32381 //So we return true if hs is NULL. It might create a hole about detecting invalidate
32382 //object. But given all other checks present, the hole should be very small
32383 return !hs || heap_segment_read_only_p (hs);
32385 #endif //FEATURE_BASICFREEZE
32387 #endif //VERIFY_HEAP
32389 // returns TRUE if the pointer is in one of the GC heaps.
32390 BOOL GCHeap::IsHeapPointer (void* vpObject, BOOL small_heap_only)
32392 STATIC_CONTRACT_SO_TOLERANT;
32394 // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment
32395 // no longer calls CLREvent::Wait which eventually takes a lock.
32397 BYTE* object = (BYTE*) vpObject;
32398 #ifndef FEATURE_BASICFREEZE
32399 if (!((object < g_highest_address) && (object >= g_lowest_address)))
32401 #endif //!FEATURE_BASICFREEZE
32403 heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
32407 #ifdef STRESS_PINNING
32408 static n_promote = 0;
32409 #endif //STRESS_PINNING
32410 // promote an object
32411 void GCHeap::Promote(Object** ppObject, ScanContext* sc, DWORD flags)
32413 THREAD_NUMBER_FROM_CONTEXT;
32414 #ifndef MULTIPLE_HEAPS
32415 const int thread = 0;
32416 #endif //!MULTIPLE_HEAPS
32418 BYTE* o = (BYTE*)*ppObject;
32423 #ifdef DEBUG_DestroyedHandleValue
32424 // we can race with destroy handle during concurrent scan
32425 if (o == (BYTE*)DEBUG_DestroyedHandleValue)
32427 #endif //DEBUG_DestroyedHandleValue
32431 gc_heap* hp = gc_heap::heap_of (o);
32433 dprintf (3, ("Promote %Ix", (size_t)o));
32435 #ifdef INTERIOR_POINTERS
32436 if (flags & GC_CALL_INTERIOR)
32438 if ((o < hp->gc_low) || (o >= hp->gc_high))
32442 if ( (o = hp->find_object (o, hp->gc_low)) == 0)
32448 #endif //INTERIOR_POINTERS
32450 #ifdef FEATURE_CONSERVATIVE_GC
32451 // For conservative GC, a value on stack may point to middle of a free object.
32452 // In this case, we don't need to promote the pointer.
32453 if (g_pConfig->GetGCConservative()
32454 && ((CObjectHeader*)o)->IsFree())
32461 ((CObjectHeader*)o)->ValidatePromote(sc, flags);
32464 if (flags & GC_CALL_PINNED)
32465 hp->pin_object (o, (BYTE**) ppObject, hp->gc_low, hp->gc_high);
32467 #ifdef STRESS_PINNING
32468 if ((++n_promote % 20) == 1)
32469 hp->pin_object (o, (BYTE**) ppObject, hp->gc_low, hp->gc_high);
32470 #endif //STRESS_PINNING
32472 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
32473 size_t promoted_size_begin = hp->promoted_bytes (thread);
32474 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
32476 if ((o >= hp->gc_low) && (o < hp->gc_high))
32478 hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
32481 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
32482 size_t promoted_size_end = hp->promoted_bytes (thread);
32485 if (sc->pCurrentDomain)
32487 sc->pCurrentDomain->RecordSurvivedBytes ((promoted_size_end - promoted_size_begin), thread);
32490 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
32492 STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
32495 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
32498 BYTE* object = (BYTE*)(Object*)(*ppObject);
32500 THREAD_NUMBER_FROM_CONTEXT;
32502 //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
32503 dprintf (3, ("R: %Ix", (size_t)ppObject));
32508 gc_heap* hp = gc_heap::heap_of (object);
32511 if (!(flags & GC_CALL_INTERIOR))
32513 // We cannot validate this object if it's in the condemned gen because it could
32514 // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
32515 if (!((object >= hp->gc_low) && (object < hp->gc_high)))
32517 ((CObjectHeader*)object)->Validate(FALSE);
32522 dprintf (3, ("Relocate %Ix\n", (size_t)object));
32526 if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
32528 if (!((object >= hp->gc_low) && (object < hp->gc_high)))
32533 if (gc_heap::loh_object_p (object))
32535 pheader = hp->find_object (object, 0);
32541 ptrdiff_t ref_offset = object - pheader;
32542 hp->relocate_address(&pheader THREAD_NUMBER_ARG);
32543 *ppObject = (Object*)(pheader + ref_offset);
32550 hp->relocate_address(&pheader THREAD_NUMBER_ARG);
32551 *ppObject = (Object*)pheader;
32554 STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
32557 /*static*/ BOOL GCHeap::IsLargeObject(MethodTable *mt)
32559 return mt->GetBaseSize() >= LARGE_OBJECT_SIZE;
32562 /*static*/ BOOL GCHeap::IsObjectInFixedHeap(Object *pObj)
32564 // For now we simply look at the size of the object to determine if it in the
32565 // fixed heap or not. If the bit indicating this gets set at some point
32566 // we should key off that instead.
32567 return size( pObj ) >= LARGE_OBJECT_SIZE;
32570 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32573 void StressHeapDummy ();
32575 static LONG GCStressStartCount = -1;
32576 static LONG GCStressCurCount = 0;
32577 static LONG GCStressStartAtJit = -1;
32579 // the maximum number of foreground GCs we'll induce during one BGC
32580 // (this number does not include "naturally" occuring GCs).
32581 static LONG GCStressMaxFGCsPerBGC = -1;
32583 // CLRRandom implementation can produce FPU exceptions if
32584 // the test/application run by CLR is enabling any FPU exceptions.
32585 // We want to avoid any unexpected exception coming from stress
32586 // infrastructure, so CLRRandom is not an option.
32587 // The code below is a replicate of CRT rand() implementation.
32588 // Using CRT rand() is not an option because we will interfere with the user application
32589 // that may also use it.
32590 int StressRNG(int iMaxValue)
32592 static BOOL bisRandInit = FALSE;
32593 static int lHoldrand = 1L;
32597 lHoldrand = (int)time(NULL);
32598 bisRandInit = TRUE;
32600 int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
32601 return randValue % iMaxValue;
32604 // free up object so that things will move and then do a GC
32605 //return TRUE if GC actually happens, otherwise FALSE
32606 BOOL GCHeap::StressHeap(alloc_context * acontext)
32608 // if GC stress was dynamically disabled during this run we return FALSE
32609 if (!GCStressPolicy::IsEnabled())
32613 if (g_pConfig->FastGCStressLevel() && !GetThread()->StressHeapIsEnabled()) {
32619 if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
32621 || g_pConfig->FastGCStressLevel() > 1
32624 if (!Thread::UniqueStack(&acontext)) {
32629 #ifdef BACKGROUND_GC
32630 // don't trigger a GC from the GC threads but still trigger GCs from user threads.
32631 if (IsGCSpecialThread())
32635 #endif //BACKGROUND_GC
32637 if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
32639 GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
32640 GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
32643 if (GCStressMaxFGCsPerBGC == -1)
32645 GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
32646 if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
32647 GCStressMaxFGCsPerBGC = 6;
32651 if (g_JitCount < GCStressStartAtJit)
32655 // Allow programmer to skip the first N Stress GCs so that you can
32656 // get to the interesting ones faster.
32657 FastInterlockIncrement(&GCStressCurCount);
32658 if (GCStressCurCount < GCStressStartCount)
32661 // throttle the number of stress-induced GCs by a factor given by GCStressStep
32662 if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
32667 #ifdef BACKGROUND_GC
32668 if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
32670 // allow a maximum number of stress induced FGCs during one BGC
32671 if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
32673 ++gc_stress_fgcs_in_bgc;
32675 #endif // BACKGROUND_GC
32677 if (g_pStringClass == 0)
32679 // If the String class has not been loaded, dont do any stressing. This should
32680 // be kept to a minimum to get as complete coverage as possible.
32681 _ASSERTE(g_fEEInit);
32688 #ifndef MULTIPLE_HEAPS
32689 static LONG OneAtATime = -1;
32692 acontext = generation_alloc_context (pGenGCHeap->generation_of(0));
32694 // Only bother with this if the stress level is big enough and if nobody else is
32695 // doing it right now. Note that some callers are inside the AllocLock and are
32696 // guaranteed synchronized. But others are using AllocationContexts and have no
32697 // particular synchronization.
32699 // For this latter case, we want a very high-speed way of limiting this to one
32700 // at a time. A secondary advantage is that we release part of our StressObjs
32701 // buffer sparingly but just as effectively.
32703 if (FastInterlockIncrement((LONG *) &OneAtATime) == 0 &&
32704 !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
32708 // If the current string is used up
32709 if (ObjectFromHandle(m_StressObjs[m_CurStressObj]) == 0)
32711 // Populate handles with strings
32712 int i = m_CurStressObj;
32713 while(ObjectFromHandle(m_StressObjs[i]) == 0)
32715 _ASSERTE(m_StressObjs[i] != 0);
32716 unsigned strLen = (LARGE_OBJECT_SIZE - 32) / sizeof(WCHAR);
32717 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
32719 // update the cached type handle before allocating
32720 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
32721 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
32724 str->SetMethodTable (g_pStringClass);
32725 str->SetStringLength (strLen);
32727 #if CHECK_APP_DOMAIN_LEAKS
32728 if (g_pConfig->AppDomainLeaks())
32729 str->SetAppDomain();
32731 StoreObjectInHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
32733 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
32734 if (i == m_CurStressObj) break;
32737 // advance the current handle to the next string
32738 m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
32741 // Get the current string
32742 str = (StringObject*) OBJECTREFToObject(ObjectFromHandle(m_StressObjs[m_CurStressObj]));
32745 // Chop off the end of the string and form a new object out of it.
32746 // This will 'free' an object at the begining of the heap, which will
32747 // force data movement. Note that we can only do this so many times.
32748 // before we have to move on to the next string.
32749 unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
32750 if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
32752 unsigned sizeToNextObj = (unsigned)Align(size(str));
32753 BYTE* freeObj = ((BYTE*) str) + sizeToNextObj - sizeOfNewObj;
32754 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);
32755 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
32759 // Let the string itself become garbage.
32760 // will be realloced next time around
32761 StoreObjectInHandle(m_StressObjs[m_CurStressObj], 0);
32765 FastInterlockDecrement((LONG *) &OneAtATime);
32766 #endif // !MULTIPLE_HEAPS
32767 if (IsConcurrentGCEnabled())
32769 int rgen = StressRNG(10);
32771 // gen0:gen1:gen2 distribution: 40:40:20
32774 else if (rgen >= 4)
32779 GarbageCollectTry (rgen, FALSE, collection_gcstress);
32783 GarbageCollect(max_generation, FALSE, collection_gcstress);
32788 _ASSERTE (!"Exception happens during StressHeap");
32790 EX_END_CATCH(RethrowTerminalExceptions)
32795 #endif // STRESS_HEAP
32796 #endif // FEATURE_REDHAWK
32799 #ifdef FEATURE_PREMORTEM_FINALIZATION
32800 #define REGISTER_FOR_FINALIZATION(_object, _size) \
32801 hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
32802 #else // FEATURE_PREMORTEM_FINALIZATION
32803 #define REGISTER_FOR_FINALIZATION(_object, _size) true
32804 #endif // FEATURE_PREMORTEM_FINALIZATION
32806 #ifdef FEATURE_REDHAWK
32807 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do { \
32808 if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size))) \
32810 STRESS_LOG_OOM_STACK(_size); \
32814 #else // FEATURE_REDHAWK
32815 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do { \
32816 if ((_object) == NULL) \
32818 STRESS_LOG_OOM_STACK(_size); \
32819 ThrowOutOfMemory(); \
32823 REGISTER_FOR_FINALIZATION(_object, _size); \
32826 #endif // FEATURE_REDHAWK
32829 // Small Object Allocator
32833 GCHeap::Alloc( size_t size, DWORD flags REQD_ALIGN_DCL)
32836 #ifdef FEATURE_REDHAWK
32837 // Under Redhawk NULL is returned on failure.
32845 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
32846 if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
32848 char *a = new char;
32851 #endif //_DEBUG && !FEATURE_REDHAWK
32855 assert (!GCHeap::UseAllocationContexts());
32857 Object* newAlloc = NULL;
32860 #ifdef COUNT_CYCLES
32861 AllocStart = GetCycleCount32();
32863 #elif defined(ENABLE_INSTRUMENTATION)
32864 unsigned AllocStart = GetInstLogTime();
32866 #endif //COUNT_CYCLES
32869 #ifdef MULTIPLE_HEAPS
32870 //take the first heap....
32871 gc_heap* hp = gc_heap::g_heaps[0];
32873 gc_heap* hp = pGenGCHeap;
32875 // prefix complains about us dereferencing hp in wks build even though we only access static members
32876 // this way. not sure how to shut it up except for this ugly workaround:
32877 PREFIX_ASSUME(hp != NULL);
32879 #endif //MULTIPLE_HEAPS
32882 AllocLockHolder lh;
32884 #ifndef FEATURE_REDHAWK
32885 GCStress<gc_on_alloc>::MaybeTrigger(generation_alloc_context(hp->generation_of(0)));
32886 #endif // FEATURE_REDHAWK
32888 alloc_context* acontext = 0;
32890 if (size < LARGE_OBJECT_SIZE)
32892 acontext = generation_alloc_context (hp->generation_of (0));
32897 newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
32898 #ifdef FEATURE_STRUCTALIGN
32899 newAlloc = (Object*) hp->pad_for_alignment ((BYTE*) newAlloc, requiredAlignment, size, acontext);
32900 #endif // FEATURE_STRUCTALIGN
32901 // ASSERT (newAlloc);
32905 acontext = generation_alloc_context (hp->generation_of (max_generation+1));
32907 newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
32908 #ifdef FEATURE_STRUCTALIGN
32909 newAlloc = (Object*) hp->pad_for_alignment_large ((BYTE*) newAlloc, requiredAlignment, size);
32910 #endif // FEATURE_STRUCTALIGN
32914 CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
32917 #ifdef COUNT_CYCLES
32918 finish = GetCycleCount32();
32919 #elif defined(ENABLE_INSTRUMENTATION)
32920 finish = GetInstLogTime();
32921 #endif //COUNT_CYCLES
32922 AllocDuration += finish - AllocStart;
32928 #ifdef FEATURE_64BIT_ALIGNMENT
32929 // Allocate small object with an alignment requirement of 8-bytes. Non allocation context version.
32931 GCHeap::AllocAlign8( size_t size, DWORD flags)
32934 #ifdef FEATURE_REDHAWK
32935 // Under Redhawk NULL is returned on failure.
32943 assert (!GCHeap::UseAllocationContexts());
32945 Object* newAlloc = NULL;
32948 AllocLockHolder lh;
32950 #ifdef MULTIPLE_HEAPS
32951 //take the first heap....
32952 gc_heap* hp = gc_heap::g_heaps[0];
32954 gc_heap* hp = pGenGCHeap;
32955 #endif //MULTIPLE_HEAPS
32957 newAlloc = AllocAlign8Common(hp, generation_alloc_context (hp->generation_of (0)), size, flags);
32963 // Allocate small object with an alignment requirement of 8-bytes. Allocation context version.
32965 GCHeap::AllocAlign8(alloc_context* acontext, size_t size, DWORD flags )
32968 #ifdef FEATURE_REDHAWK
32969 // Under Redhawk NULL is returned on failure.
32977 #ifdef MULTIPLE_HEAPS
32978 if (acontext->alloc_heap == 0)
32980 AssignHeap (acontext);
32981 assert (acontext->alloc_heap);
32984 gc_heap* hp = acontext->alloc_heap->pGenGCHeap;
32986 gc_heap* hp = pGenGCHeap;
32987 #endif //MULTIPLE_HEAPS
32989 return AllocAlign8Common(hp, acontext, size, flags);
32992 // Common code used by both variants of AllocAlign8 above.
32994 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, DWORD flags)
32997 #ifdef FEATURE_REDHAWK
32998 // Under Redhawk NULL is returned on failure.
33006 gc_heap* hp = (gc_heap*)_hp;
33008 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
33009 if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
33011 char *a = new char;
33014 #endif //_DEBUG && !FEATURE_REDHAWK
33018 Object* newAlloc = NULL;
33021 #ifdef COUNT_CYCLES
33022 AllocStart = GetCycleCount32();
33024 #elif defined(ENABLE_INSTRUMENTATION)
33025 unsigned AllocStart = GetInstLogTime();
33027 #endif //COUNT_CYCLES
33030 #ifndef FEATURE_REDHAWK
33031 GCStress<gc_on_alloc>::MaybeTrigger(acontext);
33032 #endif // FEATURE_REDHAWK
33034 if (size < LARGE_OBJECT_SIZE)
33040 // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
33041 // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
33042 // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
33043 size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
33045 // Retrieve the address of the next allocation from the context (note that we're inside the alloc
33046 // lock at this point).
33047 BYTE* result = acontext->alloc_ptr;
33049 // Will an allocation at this point yield the correct alignment and fit into the remainder of the
33051 if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
33053 // Yes, we can just go ahead and make the allocation.
33054 newAlloc = (Object*) hp->allocate (size, acontext);
33055 ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
33059 // No, either the next available address is not aligned in the way we require it or there's
33060 // not enough space to allocate an object of the required size. In both cases we allocate a
33061 // padding object (marked as a free object). This object's size is such that it will reverse
33062 // the alignment of the next header (asserted below).
33064 // We allocate both together then decide based on the result whether we'll format the space as
33065 // free object + real object or real object + free object.
33066 ASSERT((Align(min_obj_size) & 7) == 4);
33067 CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
33070 if (((size_t)freeobj & 7) == desiredAlignment)
33072 // New allocation has desired alignment, return this one and place the free object at the
33073 // end of the allocated space.
33074 newAlloc = (Object*)freeobj;
33075 freeobj = (CObjectHeader*)((BYTE*)freeobj + Align(size));
33079 // New allocation is still mis-aligned, format the initial space as a free object and the
33080 // rest of the space should be correctly aligned for the real object.
33081 newAlloc = (Object*)((BYTE*)freeobj + Align(min_obj_size));
33082 ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
33084 freeobj->SetFree(min_obj_size);
33090 // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
33091 // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
33092 // we've managed to arrange things so the only case where we see a bias is for boxed value types and
33093 // these can never get large enough to be allocated on the LOH.
33094 ASSERT(65536 < LARGE_OBJECT_SIZE);
33095 ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
33097 alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
33099 newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
33100 ASSERT(((size_t)newAlloc & 7) == 0);
33103 CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
33106 #ifdef COUNT_CYCLES
33107 finish = GetCycleCount32();
33108 #elif defined(ENABLE_INSTRUMENTATION)
33109 finish = GetInstLogTime();
33110 #endif //COUNT_CYCLES
33111 AllocDuration += finish - AllocStart;
33116 #endif // FEATURE_64BIT_ALIGNMENT
33119 GCHeap::AllocLHeap( size_t size, DWORD flags REQD_ALIGN_DCL)
33122 #ifdef FEATURE_REDHAWK
33123 // Under Redhawk NULL is returned on failure.
33131 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
33132 if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
33134 char *a = new char;
33137 #endif //_DEBUG && !FEATURE_REDHAWK
33141 Object* newAlloc = NULL;
33144 #ifdef COUNT_CYCLES
33145 AllocStart = GetCycleCount32();
33147 #elif defined(ENABLE_INSTRUMENTATION)
33148 unsigned AllocStart = GetInstLogTime();
33150 #endif //COUNT_CYCLES
33153 #ifdef MULTIPLE_HEAPS
33154 //take the first heap....
33155 gc_heap* hp = gc_heap::g_heaps[0];
33157 gc_heap* hp = pGenGCHeap;
33159 // prefix complains about us dereferencing hp in wks build even though we only access static members
33160 // this way. not sure how to shut it up except for this ugly workaround:
33161 PREFIX_ASSUME(hp != NULL);
33163 #endif //MULTIPLE_HEAPS
33165 #ifndef FEATURE_REDHAWK
33166 GCStress<gc_on_alloc>::MaybeTrigger(generation_alloc_context(hp->generation_of(0)));
33167 #endif // FEATURE_REDHAWK
33169 alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
33171 newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
33172 #ifdef FEATURE_STRUCTALIGN
33173 newAlloc = (Object*) hp->pad_for_alignment_large ((BYTE*) newAlloc, requiredAlignment, size);
33174 #endif // FEATURE_STRUCTALIGN
33175 CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
33178 #ifdef COUNT_CYCLES
33179 finish = GetCycleCount32();
33180 #elif defined(ENABLE_INSTRUMENTATION)
33181 finish = GetInstLogTime();
33182 #endif //COUNT_CYCLES
33183 AllocDuration += finish - AllocStart;
33190 GCHeap::Alloc(alloc_context* acontext, size_t size, DWORD flags REQD_ALIGN_DCL)
33193 #ifdef FEATURE_REDHAWK
33194 // Under Redhawk NULL is returned on failure.
33202 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
33203 if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
33205 char *a = new char;
33208 #endif //_DEBUG && !FEATURE_REDHAWK
33212 Object* newAlloc = NULL;
33215 #ifdef COUNT_CYCLES
33216 AllocStart = GetCycleCount32();
33218 #elif defined(ENABLE_INSTRUMENTATION)
33219 unsigned AllocStart = GetInstLogTime();
33221 #endif //COUNT_CYCLES
33224 #ifdef MULTIPLE_HEAPS
33225 if (acontext->alloc_heap == 0)
33227 AssignHeap (acontext);
33228 assert (acontext->alloc_heap);
33230 #endif //MULTIPLE_HEAPS
33232 #ifndef FEATURE_REDHAWK
33233 GCStress<gc_on_alloc>::MaybeTrigger(acontext);
33234 #endif // FEATURE_REDHAWK
33236 #ifdef MULTIPLE_HEAPS
33237 gc_heap* hp = acontext->alloc_heap->pGenGCHeap;
33239 gc_heap* hp = pGenGCHeap;
33241 // prefix complains about us dereferencing hp in wks build even though we only access static members
33242 // this way. not sure how to shut it up except for this ugly workaround:
33243 PREFIX_ASSUME(hp != NULL);
33245 #endif //MULTIPLE_HEAPS
33247 if (size < LARGE_OBJECT_SIZE)
33253 newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
33254 #ifdef FEATURE_STRUCTALIGN
33255 newAlloc = (Object*) hp->pad_for_alignment ((BYTE*) newAlloc, requiredAlignment, size, acontext);
33256 #endif // FEATURE_STRUCTALIGN
33257 // ASSERT (newAlloc);
33261 newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
33262 #ifdef FEATURE_STRUCTALIGN
33263 newAlloc = (Object*) hp->pad_for_alignment_large ((BYTE*) newAlloc, requiredAlignment, size);
33264 #endif // FEATURE_STRUCTALIGN
33267 CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
33270 #ifdef COUNT_CYCLES
33271 finish = GetCycleCount32();
33272 #elif defined(ENABLE_INSTRUMENTATION)
33273 finish = GetInstLogTime();
33274 #endif //COUNT_CYCLES
33275 AllocDuration += finish - AllocStart;
33282 GCHeap::FixAllocContext (alloc_context* acontext, BOOL lockp, void* arg, void *heap)
33284 #ifdef MULTIPLE_HEAPS
33287 acontext->alloc_count = 0;
33289 BYTE * alloc_ptr = acontext->alloc_ptr;
33294 // The acontext->alloc_heap can be out of sync with the ptrs because
33295 // of heap re-assignment in allocate
33296 gc_heap* hp = gc_heap::heap_of (alloc_ptr);
33298 gc_heap* hp = pGenGCHeap;
33299 #endif //MULTIPLE_HEAPS
33301 if (heap == NULL || heap == hp)
33305 enter_spin_lock (&hp->more_space_lock);
33307 hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
33308 get_alignment_constant(TRUE));
33311 leave_spin_lock (&hp->more_space_lock);
33317 GCHeap::GetContainingObject (void *pInteriorPtr)
33319 BYTE *o = (BYTE*)pInteriorPtr;
33321 gc_heap* hp = gc_heap::heap_of (o);
33322 if (o >= hp->lowest_address && o < hp->highest_address)
33324 o = hp->find_object (o, hp->gc_low);
33331 return (Object *)o;
33334 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
33336 if (dd_new_allocation (dd) < 0)
33341 if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
33349 //----------------------------------------------------------------------------
33350 // #GarbageCollector
33352 // API to ensure that a complete new garbage collection takes place
33355 GCHeap::GarbageCollect (int generation, BOOL low_memory_p, int mode)
33357 #if defined(_WIN64)
33360 size_t total_allocated = 0;
33361 size_t total_desired = 0;
33362 #ifdef MULTIPLE_HEAPS
33364 for (hn = 0; hn < gc_heap::n_heaps; hn++)
33366 gc_heap* hp = gc_heap::g_heaps [hn];
33367 total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
33368 total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
33369 dd_new_allocation (hp->dynamic_data_of (0));
33372 gc_heap* hp = pGenGCHeap;
33373 total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
33374 total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
33375 dd_new_allocation (hp->dynamic_data_of (0));
33376 #endif //MULTIPLE_HEAPS
33378 if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
33380 dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
33381 total_allocated, total_desired));
33388 #ifdef MULTIPLE_HEAPS
33389 gc_heap* hpt = gc_heap::g_heaps[0];
33392 #endif //MULTIPLE_HEAPS
33394 generation = (generation < 0) ? max_generation : min (generation, max_generation);
33395 dynamic_data* dd = hpt->dynamic_data_of (generation);
33397 #ifdef BACKGROUND_GC
33398 if (recursive_gc_sync::background_running_p())
33400 if ((mode == collection_optimized) || (mode & collection_non_blocking))
33404 if (mode & collection_blocking)
33406 pGenGCHeap->background_gc_wait();
33407 if (mode & collection_optimized)
33413 #endif //BACKGROUND_GC
33415 if (mode & collection_optimized)
33417 if (pGenGCHeap->gc_started)
33423 BOOL should_collect = FALSE;
33424 BOOL should_check_loh = (generation == max_generation);
33425 #ifdef MULTIPLE_HEAPS
33426 for (int i = 0; i < gc_heap::n_heaps; i++)
33428 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
33429 dynamic_data* dd2 = (should_check_loh ?
33430 (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
33433 if (should_collect_optimized (dd1, low_memory_p))
33435 should_collect = TRUE;
33438 if (dd2 && should_collect_optimized (dd2, low_memory_p))
33440 should_collect = TRUE;
33445 should_collect = should_collect_optimized (dd, low_memory_p);
33446 if (!should_collect && should_check_loh)
33449 should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
33451 #endif //MULTIPLE_HEAPS
33452 if (!should_collect)
33459 size_t CollectionCountAtEntry = dd_collection_count (dd);
33460 size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
33461 size_t CurrentCollectionCount = 0;
33465 CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
33467 if ((mode & collection_blocking) &&
33468 (generation == max_generation) &&
33469 (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
33471 #ifdef BACKGROUND_GC
33472 if (recursive_gc_sync::background_running_p())
33474 pGenGCHeap->background_gc_wait();
33476 #endif //BACKGROUND_GC
33481 if (CollectionCountAtEntry == CurrentCollectionCount)
33490 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
33492 int gen = (generation < 0) ?
33493 max_generation : min (generation, max_generation);
33495 gc_reason reason = reason_empty;
33499 if (mode & collection_blocking)
33500 reason = reason_lowmemory_blocking;
33502 reason = reason_lowmemory;
33505 reason = reason_induced;
33507 if (reason == reason_induced)
33509 if (mode & collection_compacting)
33511 reason = reason_induced_compacting;
33513 else if (mode & collection_non_blocking)
33515 reason = reason_induced_noforce;
33518 else if (mode & collection_gcstress)
33520 reason = reason_gcstress;
33525 return GarbageCollectGeneration (gen, reason);
33528 void gc_heap::do_pre_gc()
33530 STRESS_LOG_GC_STACK;
33533 STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
33534 (ULONG)settings.condemned_generation,
33535 (ULONG)settings.reason);
33536 #endif // STRESS_LOG
33538 #ifdef BACKGROUND_GC
33539 settings.b_state = current_bgc_state;
33540 #endif //BACKGROUND_GC
33542 #ifdef BACKGROUND_GC
33543 dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)",
33544 VolatileLoad(&settings.gc_index),
33545 dd_collection_count (dynamic_data_of (0)),
33546 settings.condemned_generation,
33547 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
33548 VolatileLoad(¤t_bgc_state)));
33550 dprintf (1, ("*GC* %d(gen0:%d)(%d)",
33551 VolatileLoad(&settings.gc_index),
33552 dd_collection_count (dynamic_data_of (0)),
33553 settings.condemned_generation));
33554 #endif //BACKGROUND_GC
33556 // TODO: this can happen...it's because of the way we are calling
33557 // do_pre_gc, will fix later.
33558 //if (last_gc_index > VolatileLoad(&settings.gc_index))
33560 // FATAL_GC_ERROR();
33563 last_gc_index = VolatileLoad(&settings.gc_index);
33564 GCHeap::UpdatePreGCCounters();
33566 if (settings.concurrent)
33568 #ifdef BACKGROUND_GC
33569 full_gc_counts[gc_type_background]++;
33571 GCHeap::gc_stress_fgcs_in_bgc = 0;
33572 #endif // STRESS_HEAP
33573 #endif // BACKGROUND_GC
33577 if (settings.condemned_generation == max_generation)
33579 full_gc_counts[gc_type_blocking]++;
33583 #ifdef BACKGROUND_GC
33584 if (settings.background_p)
33586 ephemeral_fgc_counts[settings.condemned_generation]++;
33588 #endif //BACKGROUND_GC
33592 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33595 SystemDomain::ResetADSurvivedBytes();
33597 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33600 void gc_heap::do_post_gc()
33602 if (!settings.concurrent)
33604 GCProfileWalkHeap();
33609 #ifdef COUNT_CYCLES
33610 AllocStart = GetCycleCount32();
33612 AllocStart = clock();
33613 #endif //COUNT_CYCLES
33616 GCToEEInterface::GcDone(settings.condemned_generation);
33618 #ifdef GC_PROFILING
33619 if (!settings.concurrent)
33621 UpdateGenerationBounds();
33622 GarbageCollectionFinishedCallback();
33624 #endif // GC_PROFILING
33627 //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)",
33628 dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)",
33629 VolatileLoad(&settings.gc_index),
33630 dd_collection_count (dynamic_data_of (0)),
33631 settings.condemned_generation,
33632 (settings.concurrent ? "BGC" : "GC")));
33634 GCHeap::UpdatePostGCCounters();
33635 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33636 //if (g_fEnableARM)
33638 // SystemDomain::GetADSurvivedBytes();
33640 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33643 STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
33644 (ULONG)settings.condemned_generation,
33645 (ULONG)settings.reason);
33646 #endif // STRESS_LOG
33649 unsigned GCHeap::GetGcCount()
33651 return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
33655 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
33657 dprintf (2, ("triggered a GC!"));
33659 #ifdef MULTIPLE_HEAPS
33660 gc_heap* hpt = gc_heap::g_heaps[0];
33663 #endif //MULTIPLE_HEAPS
33664 Thread* current_thread = GetThread();
33665 BOOL cooperative_mode = TRUE;
33666 dynamic_data* dd = hpt->dynamic_data_of (gen);
33667 size_t localCount = dd_collection_count (dd);
33669 enter_spin_lock (&gc_heap::gc_lock);
33670 dprintf (SPINLOCK_LOG, ("GC Egc"));
33671 ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
33673 //don't trigger another GC if one was already in progress
33674 //while waiting for the lock
33676 size_t col_count = dd_collection_count (dd);
33678 if (localCount != col_count)
33680 #ifdef SYNCHRONIZATION_STATS
33681 gc_lock_contended++;
33682 #endif //SYNCHRONIZATION_STATS
33683 dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
33684 leave_spin_lock (&gc_heap::gc_lock);
33686 // We don't need to release msl here 'cause this means a GC
33687 // has happened and would have release all msl's.
33692 #ifdef COUNT_CYCLES
33693 int gc_start = GetCycleCount32();
33694 #endif //COUNT_CYCLES
33696 #if defined ( _DEBUG) && defined (CATCH_GC)
33698 #endif // _DEBUG && CATCH_GC
33701 #ifdef COUNT_CYCLES
33702 AllocDuration += GetCycleCount32() - AllocStart;
33704 AllocDuration += clock() - AllocStart;
33705 #endif //COUNT_CYCLES
33708 gc_heap::g_low_memory_status = (reason == reason_lowmemory) ||
33709 (reason == reason_lowmemory_blocking) ||
33710 g_bLowMemoryFromHost;
33711 gc_trigger_reason = reason;
33713 #ifdef MULTIPLE_HEAPS
33714 for (int i = 0; i < gc_heap::n_heaps; i++)
33716 gc_heap::g_heaps[i]->reset_gc_done();
33719 gc_heap::reset_gc_done();
33720 #endif //MULTIPLE_HEAPS
33722 gc_heap::gc_started = TRUE;
33725 init_sync_log_stats();
33727 #ifndef MULTIPLE_HEAPS
33728 cooperative_mode = gc_heap::enable_preemptive (current_thread);
33730 dprintf (2, ("Suspending EE"));
33731 BEGIN_TIMING(suspend_ee_during_log);
33732 GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC);
33733 END_TIMING(suspend_ee_during_log);
33734 pGenGCHeap->settings.init_mechanisms();
33735 gc_heap::disable_preemptive (current_thread, cooperative_mode);
33736 #endif //!MULTIPLE_HEAPS
33739 // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
33742 #ifdef COUNT_CYCLES
33745 start = GetCycleCount32();
33750 #endif //COUNT_CYCLES
33751 PromotedObjectCount = 0;
33754 unsigned int condemned_generation_number = gen;
33756 // We want to get a stack from the user thread that triggered the GC
33757 // instead of on the GC thread which is the case for Server GC.
33758 // But we are doing it for Workstation GC as well to be uniform.
33759 FireEtwGCTriggered((int) reason, GetClrInstanceId());
33761 #ifdef MULTIPLE_HEAPS
33762 GcCondemnedGeneration = condemned_generation_number;
33764 cooperative_mode = gc_heap::enable_preemptive (current_thread);
33766 BEGIN_TIMING(gc_during_log);
33767 gc_heap::ee_suspend_event.Set();
33768 gc_heap::wait_for_gc_done();
33769 END_TIMING(gc_during_log);
33771 gc_heap::disable_preemptive (current_thread, cooperative_mode);
33773 condemned_generation_number = GcCondemnedGeneration;
33775 BEGIN_TIMING(gc_during_log);
33776 pGenGCHeap->garbage_collect (condemned_generation_number);
33777 END_TIMING(gc_during_log);
33778 #endif //MULTIPLE_HEAPS
33781 #ifdef COUNT_CYCLES
33782 finish = GetCycleCount32();
33785 #endif //COUNT_CYCLES
33786 GcDuration += finish - start;
33788 ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
33789 VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
33790 finish - start, GcDuration,
33791 AllocCount ? (AllocDuration / AllocCount) : 0,
33792 AllocSmallCount, AllocBigCount));
33797 #ifdef BACKGROUND_GC
33798 // We are deciding whether we should fire the alloc wait end event here
33799 // because in begin_foreground we could be calling end_foreground
33800 // if we need to retry.
33801 if (gc_heap::alloc_wait_event_p)
33803 hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
33804 gc_heap::alloc_wait_event_p = FALSE;
33806 #endif //BACKGROUND_GC
33808 #ifndef MULTIPLE_HEAPS
33809 #ifdef BACKGROUND_GC
33810 if (!gc_heap::dont_restart_ee_p)
33812 #endif //BACKGROUND_GC
33813 BEGIN_TIMING(restart_ee_during_log);
33814 GCToEEInterface::RestartEE(TRUE);
33815 END_TIMING(restart_ee_during_log);
33816 #ifdef BACKGROUND_GC
33818 #endif //BACKGROUND_GC
33819 #endif //!MULTIPLE_HEAPS
33822 #if defined (_DEBUG) && defined (CATCH_GC)
33823 __except (CheckException(GetExceptionInformation(), NULL))
33825 _ASSERTE(!"Exception during GarbageCollectGeneration()");
33827 #endif // _DEBUG && CATCH_GC
33829 #ifdef COUNT_CYCLES
33830 printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
33831 GetCycleCount32() - gc_start);
33832 #endif //COUNT_CYCLES
33834 #ifndef MULTIPLE_HEAPS
33835 process_sync_log_stats();
33836 gc_heap::gc_started = FALSE;
33837 gc_heap::set_gc_done();
33838 dprintf (SPINLOCK_LOG, ("GC Lgc"));
33839 leave_spin_lock (&gc_heap::gc_lock);
33840 #endif //!MULTIPLE_HEAPS
33842 #ifdef FEATURE_PREMORTEM_FINALIZATION
33843 if (!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers ||
33844 FinalizerThread::HaveExtraWorkForFinalizer())
33846 FinalizerThread::EnableFinalization();
33848 #endif // FEATURE_PREMORTEM_FINALIZATION
33850 return dd_collection_count (dd);
33853 size_t GCHeap::GetTotalBytesInUse ()
33855 #ifdef MULTIPLE_HEAPS
33856 //enumarate all the heaps and get their size.
33857 size_t tot_size = 0;
33858 for (int i = 0; i < gc_heap::n_heaps; i++)
33860 GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
33861 tot_size += Hp->ApproxTotalBytesInUse (FALSE);
33865 return ApproxTotalBytesInUse ();
33866 #endif //MULTIPLE_HEAPS
33869 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
33871 if (get_bgc_fgc_count != 0)
33873 #ifdef BACKGROUND_GC
33874 if (generation == max_generation)
33876 return (int)(gc_heap::full_gc_counts[gc_type_background]);
33880 return (int)(gc_heap::ephemeral_fgc_counts[generation]);
33884 #endif //BACKGROUND_GC
33887 #ifdef MULTIPLE_HEAPS
33888 gc_heap* hp = gc_heap::g_heaps [0];
33889 #else //MULTIPLE_HEAPS
33890 gc_heap* hp = pGenGCHeap;
33891 #endif //MULTIPLE_HEAPS
33892 if (generation > max_generation)
33895 return (int)dd_collection_count (hp->dynamic_data_of (generation));
33898 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
33900 size_t totsize = 0;
33902 //ASSERT(InMustComplete());
33903 enter_spin_lock (&pGenGCHeap->gc_lock);
33905 heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
33906 // Get small block heap size info
33907 totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
33908 heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
33909 while (seg1 != eph_seg)
33911 totsize += heap_segment_allocated (seg1) -
33912 heap_segment_mem (seg1);
33913 seg1 = heap_segment_next (seg1);
33916 //discount the fragmentation
33917 for (int i = 0; i <= max_generation; i++)
33919 generation* gen = pGenGCHeap->generation_of (i);
33920 totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
33923 if (!small_heap_only)
33925 heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
33929 totsize += heap_segment_allocated (seg2) -
33930 heap_segment_mem (seg2);
33931 seg2 = heap_segment_next (seg2);
33934 //discount the fragmentation
33935 generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
33936 size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
33939 leave_spin_lock (&pGenGCHeap->gc_lock);
33943 #ifdef MULTIPLE_HEAPS
33944 void GCHeap::AssignHeap (alloc_context* acontext)
33946 // Assign heap based on processor
33947 acontext->alloc_heap = GetHeap(heap_select::select_heap(acontext, 0));
33948 acontext->home_heap = acontext->alloc_heap;
33950 GCHeap* GCHeap::GetHeap (int n)
33952 assert (n < gc_heap::n_heaps);
33953 return gc_heap::g_heaps [n]->vm_heap;
33955 #endif //MULTIPLE_HEAPS
33957 bool GCHeap::IsThreadUsingAllocationContextHeap(alloc_context* acontext, int thread_number)
33959 #ifdef MULTIPLE_HEAPS
33960 return ((acontext->home_heap == GetHeap(thread_number)) ||
33961 (acontext->home_heap == 0) && (thread_number == 0));
33964 #endif //MULTIPLE_HEAPS
33967 // Returns the number of processors required to trigger the use of thread based allocation contexts
33968 int GCHeap::GetNumberOfHeaps ()
33970 #ifdef MULTIPLE_HEAPS
33971 return gc_heap::n_heaps;
33974 #endif //MULTIPLE_HEAPS
33978 in this way we spend extra time cycling through all the heaps while create the handle
33979 it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
33981 int GCHeap::GetHomeHeapNumber ()
33983 #ifdef MULTIPLE_HEAPS
33984 Thread *pThread = GetThread();
33985 for (int i = 0; i < gc_heap::n_heaps; i++)
33989 GCHeap *hp = pThread->GetAllocContext()->home_heap;
33990 if (hp == gc_heap::g_heaps[i]->vm_heap) return i;
33996 #endif //MULTIPLE_HEAPS
33999 unsigned int GCHeap::GetCondemnedGeneration()
34001 return gc_heap::settings.condemned_generation;
34004 int GCHeap::GetGcLatencyMode()
34006 return (int)(pGenGCHeap->settings.pause_mode);
34009 int GCHeap::SetGcLatencyMode (int newLatencyMode)
34011 gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
34013 if (new_mode == pause_low_latency)
34015 #ifndef MULTIPLE_HEAPS
34016 pGenGCHeap->settings.pause_mode = new_mode;
34017 #endif //!MULTIPLE_HEAPS
34019 else if (new_mode == pause_sustained_low_latency)
34021 #ifdef BACKGROUND_GC
34022 if (gc_heap::gc_can_use_concurrent)
34024 pGenGCHeap->settings.pause_mode = new_mode;
34026 #endif //BACKGROUND_GC
34030 pGenGCHeap->settings.pause_mode = new_mode;
34033 #ifdef BACKGROUND_GC
34034 if (recursive_gc_sync::background_running_p())
34036 // If we get here, it means we are doing an FGC. If the pause
34037 // mode was altered we will need to save it in the BGC settings.
34038 if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
34040 gc_heap::saved_bgc_settings.pause_mode = new_mode;
34043 #endif //BACKGROUND_GC
34045 return (int)set_pause_mode_success;
34048 int GCHeap::GetLOHCompactionMode()
34050 return pGenGCHeap->loh_compaction_mode;
34053 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
34055 #ifdef FEATURE_LOH_COMPACTION
34056 pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
34057 #endif //FEATURE_LOH_COMPACTION
34060 BOOL GCHeap::RegisterForFullGCNotification(DWORD gen2Percentage,
34061 DWORD lohPercentage)
34063 #ifdef MULTIPLE_HEAPS
34064 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34066 gc_heap* hp = gc_heap::g_heaps [hn];
34067 hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
34069 #else //MULTIPLE_HEAPS
34070 pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
34071 #endif //MULTIPLE_HEAPS
34073 pGenGCHeap->full_gc_approach_event.Reset();
34074 pGenGCHeap->full_gc_end_event.Reset();
34075 pGenGCHeap->full_gc_approach_event_set = false;
34077 pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
34078 pGenGCHeap->fgn_loh_percent = lohPercentage;
34083 BOOL GCHeap::CancelFullGCNotification()
34085 pGenGCHeap->fgn_maxgen_percent = 0;
34086 pGenGCHeap->fgn_loh_percent = 0;
34088 pGenGCHeap->full_gc_approach_event.Set();
34089 pGenGCHeap->full_gc_end_event.Set();
34094 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
34096 dprintf (2, ("WFGA: Begin wait"));
34097 int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
34098 dprintf (2, ("WFGA: End wait"));
34102 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
34104 dprintf (2, ("WFGE: Begin wait"));
34105 int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
34106 dprintf (2, ("WFGE: End wait"));
34110 void GCHeap::PublishObject (BYTE* Obj)
34112 #ifdef BACKGROUND_GC
34113 gc_heap* hp = gc_heap::heap_of (Obj);
34114 hp->bgc_alloc_lock->loh_alloc_done (Obj);
34115 #endif //BACKGROUND_GC
34118 // The spec for this one isn't clear. This function
34119 // returns the size that can be allocated without
34120 // triggering a GC of any kind.
34121 size_t GCHeap::ApproxFreeBytes()
34124 //ASSERT(InMustComplete());
34125 enter_spin_lock (&pGenGCHeap->gc_lock);
34127 generation* gen = pGenGCHeap->generation_of (0);
34128 size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
34130 leave_spin_lock (&pGenGCHeap->gc_lock);
34135 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
34137 if ((gen < 0) || (gen > max_generation))
34139 #ifdef MULTIPLE_HEAPS
34140 counters->current_size = 0;
34141 counters->promoted_size = 0;
34142 counters->collection_count = 0;
34144 //enumarate all the heaps and get their counters.
34145 for (int i = 0; i < gc_heap::n_heaps; i++)
34147 dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
34149 counters->current_size += dd_current_size (dd);
34150 counters->promoted_size += dd_promoted_size (dd);
34152 counters->collection_count += dd_collection_count (dd);
34155 dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
34156 counters->current_size = dd_current_size (dd);
34157 counters->promoted_size = dd_promoted_size (dd);
34158 counters->collection_count = dd_collection_count (dd);
34159 #endif //MULTIPLE_HEAPS
34163 // Get the segment size to use, making sure it conforms.
34164 size_t GCHeap::GetValidSegmentSize(BOOL large_seg)
34166 return get_valid_segment_size (large_seg);
34169 // Get the max gen0 heap size, making sure it conforms.
34170 size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
34172 size_t gen0size = g_pConfig->GetGCgen0size();
34174 if ((gen0size == 0) || !GCHeap::IsValidGen0MaxSize(gen0size))
34176 #if !defined(FEATURE_REDHAWK)
34178 // performance data seems to indicate halving the size results
34179 // in optimal perf. Ask for adjusted gen0 size.
34180 gen0size = max(GetLargestOnDieCacheSize(FALSE)/GetLogicalCpuCount(),(256*1024));
34181 #if (defined(_TARGET_AMD64_))
34182 // if gen0 size is too large given the available memory, reduce it.
34183 // Get true cache size, as we don't want to reduce below this.
34184 size_t trueSize = max(GetLargestOnDieCacheSize(TRUE)/GetLogicalCpuCount(),(256*1024));
34185 dprintf (2, ("cache: %Id-%Id, cpu: %Id",
34186 GetLargestOnDieCacheSize(FALSE),
34187 GetLargestOnDieCacheSize(TRUE),
34188 GetLogicalCpuCount()));
34191 GetProcessMemoryLoad (&ms);
34192 // if the total min GC across heaps will exceed 1/6th of available memory,
34193 // then reduce the min GC size until it either fits or has been reduced to cache size.
34194 while ((gen0size * gc_heap::n_heaps) > (ms.ullAvailPhys / 6))
34196 gen0size = gen0size / 2;
34197 if (gen0size <= trueSize)
34199 gen0size = trueSize;
34203 #endif //_TARGET_AMD64_
34206 gen0size = max((4*GetLargestOnDieCacheSize(TRUE)/5),(256*1024));
34208 #else //!FEATURE_REDHAWK
34209 gen0size = (256*1024);
34210 #endif //!FEATURE_REDHAWK
34213 // Generation 0 must never be more than 1/2 the segment size.
34214 if (gen0size >= (seg_size / 2))
34215 gen0size = seg_size / 2;
34220 void GCHeap::SetReservedVMLimit (size_t vmlimit)
34222 gc_heap::reserved_memory_limit = vmlimit;
34226 //versions of same method on each heap
34228 #ifdef FEATURE_PREMORTEM_FINALIZATION
34230 Object* GCHeap::GetNextFinalizableObject()
34233 #ifdef MULTIPLE_HEAPS
34235 //return the first non critical one in the first queue.
34236 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34238 gc_heap* hp = gc_heap::g_heaps [hn];
34239 Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
34243 //return the first non crtitical/critical one in the first queue.
34244 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34246 gc_heap* hp = gc_heap::g_heaps [hn];
34247 Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
34254 #else //MULTIPLE_HEAPS
34255 return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
34256 #endif //MULTIPLE_HEAPS
34260 size_t GCHeap::GetNumberFinalizableObjects()
34262 #ifdef MULTIPLE_HEAPS
34264 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34266 gc_heap* hp = gc_heap::g_heaps [hn];
34267 cnt += hp->finalize_queue->GetNumberFinalizableObjects();
34272 #else //MULTIPLE_HEAPS
34273 return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
34274 #endif //MULTIPLE_HEAPS
34277 size_t GCHeap::GetFinalizablePromotedCount()
34279 #ifdef MULTIPLE_HEAPS
34282 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34284 gc_heap* hp = gc_heap::g_heaps [hn];
34285 cnt += hp->finalize_queue->GetPromotedCount();
34289 #else //MULTIPLE_HEAPS
34290 return pGenGCHeap->finalize_queue->GetPromotedCount();
34291 #endif //MULTIPLE_HEAPS
34294 BOOL GCHeap::FinalizeAppDomain(AppDomain *pDomain, BOOL fRunFinalizers)
34296 #ifdef MULTIPLE_HEAPS
34297 BOOL foundp = FALSE;
34298 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34300 gc_heap* hp = gc_heap::g_heaps [hn];
34301 if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
34306 #else //MULTIPLE_HEAPS
34307 return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
34308 #endif //MULTIPLE_HEAPS
34311 BOOL GCHeap::ShouldRestartFinalizerWatchDog()
34313 // This condition was historically used as part of the condition to detect finalizer thread timeouts
34314 return gc_heap::gc_lock.lock != -1;
34317 void GCHeap::SetFinalizeQueueForShutdown(BOOL fHasLock)
34319 #ifdef MULTIPLE_HEAPS
34320 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34322 gc_heap* hp = gc_heap::g_heaps [hn];
34323 hp->finalize_queue->SetSegForShutDown(fHasLock);
34326 #else //MULTIPLE_HEAPS
34327 pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
34328 #endif //MULTIPLE_HEAPS
34331 //---------------------------------------------------------------------------
34332 // Finalized class tracking
34333 //---------------------------------------------------------------------------
34335 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
34339 if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
34341 //just reset the bit
34342 ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
34347 gc_heap* hp = gc_heap::heap_of ((BYTE*)obj);
34348 return hp->finalize_queue->RegisterForFinalization (gen, obj);
34352 void GCHeap::SetFinalizationRun (Object* obj)
34354 ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
34357 #endif // FEATURE_PREMORTEM_FINALIZATION
34359 //----------------------------------------------------------------------------
34361 // Write Barrier Support for bulk copy ("Clone") operations
34363 // StartPoint is the target bulk copy start point
34364 // len is the length of the bulk copy (in bytes)
34367 // Performance Note:
34369 // This is implemented somewhat "conservatively", that is we
34370 // assume that all the contents of the bulk copy are object
34371 // references. If they are not, and the value lies in the
34372 // ephemeral range, we will set false positives in the card table.
34374 // We could use the pointer maps and do this more accurately if necessary
34376 #if defined(_MSC_VER) && defined(_TARGET_X86_)
34377 #pragma optimize("y", on) // Small critical routines, don't put in EBP frame
34378 #endif //_MSC_VER && _TARGET_X86_
34381 GCHeap::SetCardsAfterBulkCopy( Object **StartPoint, size_t len )
34386 // Target should aligned
34387 assert(Aligned ((size_t)StartPoint));
34390 // Don't optimize the Generation 0 case if we are checking for write barrier voilations
34391 // since we need to update the shadow heap even in the generation 0 case.
34392 #if defined (WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
34393 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
34394 for(unsigned i=0; i < len / sizeof(Object*); i++)
34395 updateGCShadow(&StartPoint[i], StartPoint[i]);
34396 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
34398 // If destination is in Gen 0 don't bother
34400 #ifdef BACKGROUND_GC
34401 (!gc_heap::settings.concurrent) &&
34402 #endif //BACKGROUND_GC
34403 (GCHeap::GetGCHeap()->WhichGeneration( (Object*) StartPoint ) == 0))
34406 rover = StartPoint;
34407 end = StartPoint + (len/sizeof(Object*));
34408 while (rover < end)
34410 if ( (((BYTE*)*rover) >= g_ephemeral_low) && (((BYTE*)*rover) < g_ephemeral_high) )
34412 // Set Bit For Card and advance to next card
34413 size_t card = gcard_of ((BYTE*)rover);
34415 FastInterlockOr ((DWORD RAW_KEYWORD(volatile) *)&g_card_table[card/card_word_width],
34416 (1 << (DWORD)(card % card_word_width)));
34417 // Skip to next card for the object
34418 rover = (Object**)align_on_card ((BYTE*)(rover+1));
34427 #if defined(_MSC_VER) && defined(_TARGET_X86_)
34428 #pragma optimize("", on) // Go back to command line default optimizations
34429 #endif //_MSC_VER && _TARGET_X86_
34432 #ifdef FEATURE_PREMORTEM_FINALIZATION
34434 //--------------------------------------------------------------------
34436 // Support for finalization
34438 //--------------------------------------------------------------------
34441 unsigned int gen_segment (int gen)
34443 assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
34444 return (NUMBERGENERATIONS - gen - 1);
34447 bool CFinalize::Initialize()
34454 m_Array = new (nothrow)(Object*[100]);
34459 STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
34460 if (g_pConfig->IsGCBreakOnOOMEnabled())
34466 m_EndArray = &m_Array[100];
34468 for (int i =0; i < FreeList; i++)
34470 SegQueueLimit (i) = m_Array;
34472 m_PromotedCount = 0;
34475 lockowner_threadid = (DWORD) -1;
34481 CFinalize::~CFinalize()
34486 size_t CFinalize::GetPromotedCount ()
34488 return m_PromotedCount;
34492 void CFinalize::EnterFinalizeLock()
34494 _ASSERTE(dbgOnly_IsSpecialEEThread() ||
34495 GetThread() == 0 ||
34496 GetThread()->PreemptiveGCDisabled());
34499 if (FastInterlockExchange (&lock, 0) >= 0)
34501 unsigned int i = 0;
34504 YieldProcessor(); // indicate to the processor that we are spining
34506 __SwitchToThread (0, CALLER_LIMITS_SPINNING);
34508 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
34514 lockowner_threadid = ::GetCurrentThreadId();
34519 void CFinalize::LeaveFinalizeLock()
34521 _ASSERTE(dbgOnly_IsSpecialEEThread() ||
34522 GetThread() == 0 ||
34523 GetThread()->PreemptiveGCDisabled());
34526 lockowner_threadid = (DWORD) -1;
34532 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
34535 #ifdef FEATURE_REDHAWK
34536 // Under Redhawk false is returned on failure.
34544 EnterFinalizeLock();
34546 unsigned int dest = 0;
34548 if (g_fFinalizerRunOnShutDown)
34550 //no method table available yet,
34551 //put it in the finalizer queue and sort out when
34553 dest = FinalizerListSeg;
34557 dest = gen_segment (gen);
34559 // Adjust boundary for segments so that GC will keep objects alive.
34560 Object*** s_i = &SegQueue (FreeList);
34561 if ((*s_i) == m_EndArray)
34565 LeaveFinalizeLock();
34566 if (method_table(obj) == NULL)
34568 // If the object is uninitialized, a valid size should have been passed.
34569 assert (size >= Align (min_obj_size));
34570 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
34571 ((CObjectHeader*)obj)->SetFree(size);
34573 STRESS_LOG_OOM_STACK(0);
34574 if (g_pConfig->IsGCBreakOnOOMEnabled())
34578 #ifdef FEATURE_REDHAWK
34581 ThrowOutOfMemory();
34585 Object*** end_si = &SegQueueLimit (dest);
34588 //is the segment empty?
34589 if (!(*s_i == *(s_i-1)))
34591 //no, swap the end elements.
34592 *(*s_i) = *(*(s_i-1));
34594 //increment the fill pointer
34596 //go to the next segment.
34598 } while (s_i > end_si);
34600 // We have reached the destination segment
34601 // store the object
34603 // increment the fill pointer
34606 LeaveFinalizeLock();
34612 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
34616 EnterFinalizeLock();
34619 if (!IsSegEmpty(FinalizerListSeg))
34621 if (g_fFinalizerRunOnShutDown)
34623 obj = *(SegQueueLimit (FinalizerListSeg)-1);
34624 if (method_table(obj)->HasCriticalFinalizer())
34626 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
34627 FinalizerListSeg, CriticalFinalizerListSeg);
34631 --SegQueueLimit (FinalizerListSeg);
34634 obj = *(--SegQueueLimit (FinalizerListSeg));
34637 else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
34639 //the FinalizerList is empty, we can adjust both
34640 // limit instead of moving the object to the free list
34641 obj = *(--SegQueueLimit (CriticalFinalizerListSeg));
34642 --SegQueueLimit (FinalizerListSeg);
34646 dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
34648 LeaveFinalizeLock();
34653 CFinalize::SetSegForShutDown(BOOL fHasLock)
34658 EnterFinalizeLock();
34659 for (i = 0; i <= max_generation; i++)
34661 unsigned int seg = gen_segment (i);
34662 Object** startIndex = SegQueueLimit (seg)-1;
34663 Object** stopIndex = SegQueue (seg);
34664 for (Object** po = startIndex; po >= stopIndex; po--)
34667 if (method_table(obj)->HasCriticalFinalizer())
34669 MoveItem (po, seg, CriticalFinalizerListSeg);
34673 MoveItem (po, seg, FinalizerListSeg);
34678 LeaveFinalizeLock();
34682 CFinalize::DiscardNonCriticalObjects()
34684 //empty the finalization queue
34685 Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
34686 Object** stopIndex = SegQueue (FinalizerListSeg);
34687 for (Object** po = startIndex; po >= stopIndex; po--)
34689 MoveItem (po, FinalizerListSeg, FreeList);
34694 CFinalize::GetNumberFinalizableObjects()
34696 return SegQueueLimit (FinalizerListSeg) -
34697 (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
34701 CFinalize::FinalizeSegForAppDomain (AppDomain *pDomain,
34702 BOOL fRunFinalizers,
34705 BOOL finalizedFound = FALSE;
34706 Object** endIndex = SegQueue (Seg);
34707 for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
34709 CObjectHeader* obj = (CObjectHeader*)*i;
34711 // Objects are put into the finalization queue before they are complete (ie their methodtable
34712 // may be null) so we must check that the object we found has a method table before checking
34713 // if it has the index we are looking for. If the methodtable is null, it can't be from the
34714 // unloading domain, so skip it.
34715 if (method_table(obj) == NULL)
34718 // eagerly finalize all objects except those that may be agile.
34719 if (obj->GetAppDomainIndex() != pDomain->GetIndex())
34722 #ifndef FEATURE_REDHAWK
34723 if (method_table(obj)->IsAgileAndFinalizable())
34725 // If an object is both agile & finalizable, we leave it in the
34726 // finalization queue during unload. This is OK, since it's agile.
34727 // Right now only threads can be this way, so if that ever changes, change
34728 // the assert to just continue if not a thread.
34729 _ASSERTE(method_table(obj) == g_pThreadClass);
34731 if (method_table(obj) == g_pThreadClass)
34733 // However, an unstarted thread should be finalized. It could be holding a delegate
34734 // in the domain we want to unload. Once the thread has been started, its
34735 // delegate is cleared so only unstarted threads are a problem.
34736 Thread *pThread = ((THREADBASEREF)ObjectToOBJECTREF(obj))->GetInternal();
34737 if (! pThread || ! pThread->IsUnstarted())
34739 // This appdomain is going to be gone soon so let us assign
34740 // it the appdomain that's guaranteed to exist
34741 // The object is agile and the delegate should be null so we can do it
34742 obj->GetHeader()->ResetAppDomainIndexNoFailure(SystemDomain::System()->DefaultDomain()->GetIndex());
34748 obj->GetHeader()->ResetAppDomainIndexNoFailure(SystemDomain::System()->DefaultDomain()->GetIndex());
34752 #endif //!FEATURE_REDHAWK
34754 if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
34756 //remove the object because we don't want to
34757 //run the finalizer
34758 MoveItem (i, Seg, FreeList);
34759 //Reset the bit so it will be put back on the queue
34760 //if resurrected and re-registered.
34761 obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
34765 if (method_table(obj)->HasCriticalFinalizer())
34767 finalizedFound = TRUE;
34768 MoveItem (i, Seg, CriticalFinalizerListSeg);
34772 if (pDomain->IsRudeUnload())
34774 MoveItem (i, Seg, FreeList);
34778 finalizedFound = TRUE;
34779 MoveItem (i, Seg, FinalizerListSeg);
34785 return finalizedFound;
34789 CFinalize::FinalizeAppDomain (AppDomain *pDomain, BOOL fRunFinalizers)
34791 BOOL finalizedFound = FALSE;
34793 unsigned int startSeg = gen_segment (max_generation);
34795 EnterFinalizeLock();
34797 for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
34799 if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
34801 finalizedFound = TRUE;
34805 LeaveFinalizeLock();
34807 return finalizedFound;
34811 CFinalize::MoveItem (Object** fromIndex,
34812 unsigned int fromSeg,
34813 unsigned int toSeg)
34817 ASSERT (fromSeg != toSeg);
34818 if (fromSeg > toSeg)
34822 // Place the element at the boundary closest to dest
34823 Object** srcIndex = fromIndex;
34824 for (unsigned int i = fromSeg; i != toSeg; i+= step)
34826 Object**& destFill = m_FillPointers[i+(step - 1 )/2];
34827 Object** destIndex = destFill - (step + 1)/2;
34828 if (srcIndex != destIndex)
34830 Object* tmp = *srcIndex;
34831 *srcIndex = *destIndex;
34835 srcIndex = destIndex;
34840 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
34846 pSC->thread_number = hn;
34848 //scan the finalization queue
34849 Object** startIndex = SegQueue (CriticalFinalizerListSeg);
34850 Object** stopIndex = SegQueueLimit (FinalizerListSeg);
34852 for (Object** po = startIndex; po < stopIndex; po++)
34855 //dprintf (3, ("scan freacheable %Ix", (size_t)o));
34856 dprintf (3, ("scan f %Ix", (size_t)o));
34857 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34860 pSC->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(o->GetAppDomainIndex());
34862 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34868 #ifdef GC_PROFILING
34869 void CFinalize::WalkFReachableObjects (gc_heap* hp)
34871 BEGIN_PIN_PROFILER(CORProfilerPresent());
34872 Object** startIndex = SegQueue (CriticalFinalizerListSeg);
34873 Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
34874 Object** stopIndex = SegQueueLimit (FinalizerListSeg);
34875 for (Object** po = startIndex; po < stopIndex; po++)
34878 g_profControlBlock.pProfInterface->FinalizeableObjectQueued(po < stopCriticalIndex, (ObjectID)*po);
34880 END_PIN_PROFILER();
34882 #endif //GC_PROFILING
34885 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
34889 sc.promotion = TRUE;
34890 #ifdef MULTIPLE_HEAPS
34891 sc.thread_number = hp->heap_number;
34892 #endif //MULTIPLE_HEAPS
34894 BOOL finalizedFound = FALSE;
34896 //start with gen and explore all the younger generations.
34897 unsigned int startSeg = gen_segment (gen);
34899 m_PromotedCount = 0;
34900 for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
34902 Object** endIndex = SegQueue (Seg);
34903 for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
34905 CObjectHeader* obj = (CObjectHeader*)*i;
34906 dprintf (3, ("scanning: %Ix", (size_t)obj));
34907 if (!GCHeap::GetGCHeap()->IsPromoted (obj))
34909 dprintf (3, ("freacheable: %Ix", (size_t)obj));
34911 assert (method_table(obj)->HasFinalizer());
34913 #ifndef FEATURE_REDHAWK
34914 if (method_table(obj) == pWeakReferenceMT || method_table(obj)->GetCanonicalMethodTable() == pWeakReferenceOfTCanonMT)
34916 //destruct the handle right there.
34917 FinalizeWeakReference (obj);
34918 MoveItem (i, Seg, FreeList);
34921 #endif //!FEATURE_REDHAWK
34922 if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
34924 //remove the object because we don't want to
34925 //run the finalizer
34927 MoveItem (i, Seg, FreeList);
34929 //Reset the bit so it will be put back on the queue
34930 //if resurrected and re-registered.
34931 obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
34938 if (method_table(obj)->HasCriticalFinalizer())
34940 MoveItem (i, Seg, CriticalFinalizerListSeg);
34944 MoveItem (i, Seg, FinalizerListSeg);
34948 #ifdef BACKGROUND_GC
34951 if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
34953 // TODO - fix the following line.
34954 //assert (gc_heap::background_object_marked ((BYTE*)obj, FALSE));
34955 dprintf (3, ("%Ix is marked", (size_t)obj));
34958 #endif //BACKGROUND_GC
34962 finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
34963 !IsSegEmpty(CriticalFinalizerListSeg);
34965 if (finalizedFound)
34967 //Promote the f-reachable objects
34969 #ifdef MULTIPLE_HEAPS
34973 #endif //MULTIPLE_HEAPS
34976 hp->settings.found_finalizers = TRUE;
34978 #ifdef BACKGROUND_GC
34979 if (hp->settings.concurrent)
34981 hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
34983 #endif //BACKGROUND_GC
34984 if (hp->settings.concurrent && hp->settings.found_finalizers)
34987 FinalizerThread::EnableFinalization();
34991 return finalizedFound;
34994 //Relocates all of the objects in the finalization array
34996 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
34999 sc.promotion = FALSE;
35000 #ifdef MULTIPLE_HEAPS
35001 sc.thread_number = hp->heap_number;
35002 #endif //MULTIPLE_HEAPS
35004 unsigned int Seg = gen_segment (gen);
35006 Object** startIndex = SegQueue (Seg);
35007 for (Object** po = startIndex; po < SegQueue (FreeList);po++)
35009 GCHeap::Relocate (po, &sc);
35014 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
35016 // update the generation fill pointers.
35017 // if gen_0_empty is FALSE, test each object to find out if
35018 // it was promoted or not
35021 for (int i = min (gen+1, max_generation); i > 0; i--)
35023 m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
35028 //Look for demoted or promoted plugs
35030 for (int i = gen; i >= 0; i--)
35032 unsigned int Seg = gen_segment (i);
35033 Object** startIndex = SegQueue (Seg);
35035 for (Object** po = startIndex;
35036 po < SegQueueLimit (gen_segment(i)); po++)
35038 int new_gen = GCHeap::GetGCHeap()->WhichGeneration (*po);
35044 MoveItem (po, gen_segment (i), gen_segment (new_gen));
35049 MoveItem (po, gen_segment (i), gen_segment (new_gen));
35050 //back down in order to see all objects.
35061 CFinalize::GrowArray()
35063 size_t oldArraySize = (m_EndArray - m_Array);
35064 size_t newArraySize = (size_t)(((float)oldArraySize / 10) * 12);
35066 Object** newArray = new (nothrow)(Object*[newArraySize]);
35069 // It's not safe to throw here, because of the FinalizeLock. Tell our caller
35070 // to throw for us.
35071 // ASSERT (newArray);
35074 memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
35076 //adjust the fill pointers
35077 for (int i = 0; i < FreeList; i++)
35079 m_FillPointers [i] += (newArray - m_Array);
35082 m_Array = newArray;
35083 m_EndArray = &m_Array [newArraySize];
35089 void CFinalize::CheckFinalizerObjects()
35091 for (int i = 0; i <= max_generation; i++)
35093 Object **startIndex = SegQueue (gen_segment (i));
35094 Object **stopIndex = SegQueueLimit (gen_segment (i));
35096 for (Object **po = startIndex; po < stopIndex; po++)
35098 if ((int)GCHeap::GetGCHeap()->WhichGeneration (*po) < i)
35100 ((CObjectHeader*)*po)->Validate();
35104 #endif //VERIFY_HEAP
35106 #endif // FEATURE_PREMORTEM_FINALIZATION
35109 //------------------------------------------------------------------------------
35111 // End of VM specific support
35113 //------------------------------------------------------------------------------
35115 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
35117 generation* gen = gc_heap::generation_of (gen_number);
35118 heap_segment* seg = generation_start_segment (gen);
35119 BYTE* x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
35120 generation_allocation_start (gen));
35122 BYTE* end = heap_segment_allocated (seg);
35123 BOOL small_object_segments = TRUE;
35124 int align_const = get_alignment_constant (small_object_segments);
35131 if ((seg = heap_segment_next (seg)) != 0)
35133 x = heap_segment_mem (seg);
35134 end = heap_segment_allocated (seg);
35139 if (small_object_segments && walk_large_object_heap_p)
35142 small_object_segments = FALSE;
35143 align_const = get_alignment_constant (small_object_segments);
35144 seg = generation_start_segment (large_object_generation);
35145 x = heap_segment_mem (seg);
35146 end = heap_segment_allocated (seg);
35156 size_t s = size (x);
35157 CObjectHeader* o = (CObjectHeader*)x;
35162 _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
35164 if (!fn (o->GetObjectBase(), context))
35167 x = x + Align (s, align_const);
35171 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
35172 void GCHeap::WalkObject (Object* obj, walk_fn fn, void* context)
35174 BYTE* o = (BYTE*)obj;
35177 go_through_object_cl (method_table (o), o, size(o), oo,
35181 Object *oh = (Object*)*oo;
35182 if (!fn (oh, context))
35189 #endif //defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
35191 // Go through and touch (read) each page straddled by a memory block.
35192 void TouchPages(LPVOID pStart, UINT cb)
35194 const UINT pagesize = OS_PAGE_SIZE;
35195 _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
35198 VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
35199 VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) - (((size_t)pStart) & (pagesize-1)));
35203 a = VolatileLoad(p);
35204 //printf("Touching page %lxh\n", (ULONG)p);
35210 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
35211 // This code is designed to catch the failure to update the write barrier
35212 // The way it works is to copy the whole heap right after every GC. The write
35213 // barrier code has been modified so that it updates the shadow as well as the
35214 // real GC heap. Before doing the next GC, we walk the heap, looking for pointers
35215 // that were updated in the real heap, but not the shadow. A mismatch indicates
35216 // an error. The offending code can be found by breaking after the correct GC,
35217 // and then placing a data breakpoint on the Heap location that was updated without
35218 // going through the write barrier.
35220 // Called at process shutdown
35221 void deleteGCShadow()
35223 if (g_GCShadow != 0)
35224 VirtualFree (g_GCShadow, 0, MEM_RELEASE);
35229 // Called at startup and right after a GC, get a snapshot of the GC Heap
35230 void initGCShadow()
35232 if (!(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK))
35235 size_t len = g_highest_address - g_lowest_address;
35236 if (len > (size_t)(g_GCShadowEnd - g_GCShadow))
35239 g_GCShadowEnd = g_GCShadow = (BYTE*) VirtualAlloc(0, len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
35242 g_GCShadowEnd += len;
35246 _ASSERTE(!"Not enough memory to run HeapVerify level 2");
35247 // If after the assert we decide to allow the program to continue
35248 // running we need to be in a state that will not trigger any
35249 // additional AVs while we fail to allocate a shadow segment, i.e.
35250 // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
35255 // save the value of g_lowest_address at this time. If this value changes before
35256 // the next call to checkGCWriteBarrier() it means we extended the heap (with a
35257 // large object segment most probably), and the whole shadow segment is inconsistent.
35258 g_shadow_lowest_address = g_lowest_address;
35260 //****** Copy the whole GC heap ******
35262 // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
35263 // can produce a NULL result. This is because the initialization has not completed.
35265 generation* gen = gc_heap::generation_of (max_generation);
35266 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
35268 ptrdiff_t delta = g_GCShadow - g_lowest_address;
35269 BOOL small_object_segments = TRUE;
35274 if (small_object_segments)
35276 small_object_segments = FALSE;
35277 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
35283 // Copy the segment
35284 BYTE* start = heap_segment_mem(seg);
35285 BYTE* end = heap_segment_allocated (seg);
35286 memcpy(start + delta, start, end - start);
35287 seg = heap_segment_next_rw (seg);
35291 #define INVALIDGCVALUE (LPVOID)((size_t)0xcccccccd)
35293 // test to see if 'ptr' was only updated via the write barrier.
35294 inline void testGCShadow(Object** ptr)
35296 Object** shadow = (Object**) &g_GCShadow[((BYTE*) ptr - g_lowest_address)];
35297 if (*ptr != 0 && (BYTE*) shadow < g_GCShadowEnd && *ptr != *shadow)
35300 // If you get this assertion, someone updated a GC poitner in the heap without
35301 // using the write barrier. To find out who, check the value of
35302 // dd_collection_count (dynamic_data_of (0)). Also
35303 // note the value of 'ptr'. Rerun the App that the previous GC just occured.
35304 // Then put a data breakpoint for the value of 'ptr' Then check every write
35305 // to pointer between the two GCs. The last one is not using the write barrier.
35307 // If the memory of interest does not exist at system startup,
35308 // you need to set the data breakpoint right after the memory gets committed
35309 // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
35310 // in the memory window. run until the memory gets mapped. Then you can set
35313 // Note a recent change, we've identified race conditions when updating the gc shadow.
35314 // Throughout the runtime, code will update an address in the gc heap, then erect the
35315 // write barrier, which calls updateGCShadow. With an app that pounds one heap location
35316 // from multiple threads, you can hit this assert even though all involved are using the
35317 // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
35318 // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
35319 // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
35320 // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
35321 // TODO: erroneous asserts in here.
35323 if(*shadow!=INVALIDGCVALUE)
35325 _ASSERTE(!"Pointer updated without using write barrier");
35330 printf("saw a INVALIDGCVALUE. (just to let you know)\n");
35336 void testGCShadowHelper (BYTE* x)
35338 size_t s = size (x);
35339 if (contain_pointers (x))
35341 go_through_object_nostart (method_table(x), x, s, oo,
35342 { testGCShadow((Object**) oo); });
35346 // Walk the whole heap, looking for pointers that were not updated with the write barrier.
35347 void checkGCWriteBarrier()
35349 // g_shadow_lowest_address != g_lowest_address means the GC heap was extended by a segment
35350 // and the GC shadow segment did not track that change!
35351 if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_lowest_address)
35353 // No shadow stack, nothing to check.
35358 generation* gen = gc_heap::generation_of (max_generation);
35359 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
35361 PREFIX_ASSUME(seg != NULL);
35365 BYTE* x = heap_segment_mem(seg);
35366 while (x < heap_segment_allocated (seg))
35368 size_t s = size (x);
35369 testGCShadowHelper (x);
35372 seg = heap_segment_next_rw (seg);
35377 // go through large object heap
35378 int alignment = get_alignment_constant(FALSE);
35379 generation* gen = gc_heap::generation_of (max_generation+1);
35380 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
35382 PREFIX_ASSUME(seg != NULL);
35386 BYTE* x = heap_segment_mem(seg);
35387 while (x < heap_segment_allocated (seg))
35389 size_t s = size (x);
35390 testGCShadowHelper (x);
35391 x = x + Align (s, alignment);
35393 seg = heap_segment_next_rw (seg);
35397 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
35399 #endif // !DACCESS_COMPILE
35401 #ifdef FEATURE_BASICFREEZE
35402 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
35404 #ifndef DACCESS_COMPILE
35405 BYTE *o = heap_segment_mem(seg);
35407 // small heap alignment constant
35408 int alignment = get_alignment_constant(TRUE);
35410 while (o < heap_segment_allocated(seg))
35412 pfnMethodTable(pvContext, o);
35414 if (contain_pointers (o))
35416 go_through_object_nostart (method_table (o), o, size(o), oo,
35419 pfnObjRef(pvContext, oo);
35424 o += Align(size(o), alignment);
35426 #endif //!DACCESS_COMPILE
35428 #endif // FEATURE_BASICFREEZE
35430 #ifndef DACCESS_COMPILE
35431 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
35433 #ifdef BACKGROUND_GC
35434 if (recursive_gc_sync::background_running_p())
35436 DWORD dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
35437 if (dwRet == WAIT_OBJECT_0)
35439 else if (dwRet == WAIT_TIMEOUT)
35440 return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
35442 return E_FAIL; // It is not clear if what the last error would be if the wait failed,
35443 // as there are too many layers in between. The best we can do is to return E_FAIL;
35449 #endif // !DACCESS_COMPILE
35451 void GCHeap::TemporaryEnableConcurrentGC()
35453 #ifdef BACKGROUND_GC
35454 gc_heap::temp_disable_concurrent_p = FALSE;
35455 #endif //BACKGROUND_GC
35458 void GCHeap::TemporaryDisableConcurrentGC()
35460 #ifdef BACKGROUND_GC
35461 gc_heap::temp_disable_concurrent_p = TRUE;
35462 #endif //BACKGROUND_GC
35465 BOOL GCHeap::IsConcurrentGCEnabled()
35467 #ifdef BACKGROUND_GC
35468 return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
35471 #endif //BACKGROUND_GC