1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | |
6 | // |
7 | // #Overview |
8 | // |
9 | // GC automatically manages memory allocated by managed code. |
10 | // The design doc for GC can be found at Documentation/botr/garbage-collection.md |
11 | // |
12 | // This file includes both the code for GC and the allocator. The most common |
13 | // case for a GC to be triggered is from the allocator code. See |
14 | // code:#try_allocate_more_space where it calls GarbageCollectGeneration. |
15 | // |
16 | // Entry points for the allocator are GCHeap::Alloc* which are called by the |
17 | // allocation helpers in gcscan.cpp |
18 | // |
19 | |
20 | #include "gcpriv.h" |
21 | |
22 | #define USE_INTROSORT |
23 | |
24 | // We just needed a simple random number generator for testing. |
25 | class gc_rand |
26 | { |
27 | public: |
28 | static uint64_t x; |
29 | |
30 | static uint64_t get_rand() |
31 | { |
32 | x = (314159269*x+278281) & 0x7FFFFFFF; |
33 | return x; |
34 | } |
35 | |
36 | // obtain random number in the range 0 .. r-1 |
37 | static uint64_t get_rand(uint64_t r) { |
38 | // require r >= 0 |
39 | uint64_t x = (uint64_t)((get_rand() * r) >> 31); |
40 | return x; |
41 | } |
42 | }; |
43 | |
44 | uint64_t gc_rand::x = 0; |
45 | |
46 | #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) |
47 | BOOL bgc_heap_walk_for_etw_p = FALSE; |
48 | #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE |
49 | |
50 | #if defined(FEATURE_REDHAWK) |
51 | #define MAYBE_UNUSED_VAR(v) v = v |
52 | #else |
53 | #define MAYBE_UNUSED_VAR(v) |
54 | #endif // FEATURE_REDHAWK |
55 | |
56 | #define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0)) |
57 | |
58 | #ifdef SERVER_GC |
59 | #define partial_size_th 100 |
60 | #define num_partial_refs 64 |
61 | #else //SERVER_GC |
62 | #define partial_size_th 100 |
63 | #define num_partial_refs 32 |
64 | #endif //SERVER_GC |
65 | |
66 | #define demotion_plug_len_th (6*1024*1024) |
67 | |
68 | #ifdef BIT64 |
69 | #define MARK_STACK_INITIAL_LENGTH 1024 |
70 | #else |
71 | #define MARK_STACK_INITIAL_LENGTH 128 |
72 | #endif // BIT64 |
73 | |
74 | #define LOH_PIN_QUEUE_LENGTH 100 |
75 | #define LOH_PIN_DECAY 10 |
76 | |
77 | #ifdef BIT64 |
78 | // Right now we support maximum 1024 procs - meaning that we will create at most |
79 | // that many GC threads and GC heaps. |
80 | #define MAX_SUPPORTED_CPUS 1024 |
81 | #else |
82 | #define MAX_SUPPORTED_CPUS 64 |
83 | #endif // BIT64 |
84 | |
85 | uint32_t yp_spin_count_unit = 0; |
86 | size_t loh_size_threshold = LARGE_OBJECT_SIZE; |
87 | |
88 | #ifdef GC_CONFIG_DRIVEN |
89 | int compact_ratio = 0; |
90 | #endif //GC_CONFIG_DRIVEN |
91 | |
92 | // See comments in reset_memory. |
93 | BOOL reset_mm_p = TRUE; |
94 | |
95 | bool g_fFinalizerRunOnShutDown = false; |
96 | |
97 | #ifdef FEATURE_SVR_GC |
98 | bool g_built_with_svr_gc = true; |
99 | #else |
100 | bool g_built_with_svr_gc = false; |
101 | #endif // FEATURE_SVR_GC |
102 | |
103 | #if defined(BUILDENV_DEBUG) |
104 | uint8_t g_build_variant = 0; |
105 | #elif defined(BUILDENV_CHECKED) |
106 | uint8_t g_build_variant = 1; |
107 | #else |
108 | uint8_t g_build_variant = 2; |
109 | #endif // defined(BUILDENV_DEBUG) |
110 | |
111 | VOLATILE(int32_t) g_no_gc_lock = -1; |
112 | |
113 | #if defined (TRACE_GC) && !defined (DACCESS_COMPILE) |
114 | const char * const allocation_state_str[] = { |
115 | "start" , |
116 | "can_allocate" , |
117 | "cant_allocate" , |
118 | "try_fit" , |
119 | "try_fit_new_seg" , |
120 | "try_fit_new_seg_after_cg" , |
121 | "try_fit_no_seg" , |
122 | "try_fit_after_cg" , |
123 | "try_fit_after_bgc" , |
124 | "try_free_full_seg_in_bgc" , |
125 | "try_free_after_bgc" , |
126 | "try_seg_end" , |
127 | "acquire_seg" , |
128 | "acquire_seg_after_cg" , |
129 | "acquire_seg_after_bgc" , |
130 | "check_and_wait_for_bgc" , |
131 | "trigger_full_compact_gc" , |
132 | "trigger_ephemeral_gc" , |
133 | "trigger_2nd_ephemeral_gc" , |
134 | "check_retry_seg" |
135 | }; |
136 | |
137 | const char * const msl_take_state_str[] = { |
138 | "get_large_seg" , |
139 | "bgc_loh_sweep" , |
140 | "wait_bgc" , |
141 | "block_gc" , |
142 | "clr_mem" , |
143 | "clr_large_mem" , |
144 | "t_eph_gc" , |
145 | "t_full_gc" , |
146 | "alloc_small" , |
147 | "alloc_large" , |
148 | "alloc_small_cant" , |
149 | "alloc_large_cant" , |
150 | "try_alloc" , |
151 | "try_budget" |
152 | }; |
153 | #endif //TRACE_GC && !DACCESS_COMPILE |
154 | |
155 | |
156 | // Keep this in sync with the definition of gc_reason |
157 | #if (defined(DT_LOG) || defined(TRACE_GC)) && !defined (DACCESS_COMPILE) |
158 | static const char* const str_gc_reasons[] = |
159 | { |
160 | "alloc_soh" , |
161 | "induced" , |
162 | "lowmem" , |
163 | "empty" , |
164 | "alloc_loh" , |
165 | "oos_soh" , |
166 | "oos_loh" , |
167 | "induced_noforce" , |
168 | "gcstress" , |
169 | "induced_lowmem" , |
170 | "induced_compacting" , |
171 | "lowmemory_host" , |
172 | "pm_full_gc" , |
173 | "lowmemory_host_blocking" |
174 | }; |
175 | |
176 | static const char* const str_gc_pause_modes[] = |
177 | { |
178 | "batch" , |
179 | "interactive" , |
180 | "low_latency" , |
181 | "sustained_low_latency" , |
182 | "no_gc" |
183 | }; |
184 | #endif // defined(DT_LOG) || defined(TRACE_GC) |
185 | |
186 | inline |
187 | BOOL is_induced (gc_reason reason) |
188 | { |
189 | return ((reason == reason_induced) || |
190 | (reason == reason_induced_noforce) || |
191 | (reason == reason_lowmemory) || |
192 | (reason == reason_lowmemory_blocking) || |
193 | (reason == reason_induced_compacting) || |
194 | (reason == reason_lowmemory_host) || |
195 | (reason == reason_lowmemory_host_blocking)); |
196 | } |
197 | |
198 | inline |
199 | BOOL is_induced_blocking (gc_reason reason) |
200 | { |
201 | return ((reason == reason_induced) || |
202 | (reason == reason_lowmemory_blocking) || |
203 | (reason == reason_induced_compacting) || |
204 | (reason == reason_lowmemory_host_blocking)); |
205 | } |
206 | |
207 | #ifndef DACCESS_COMPILE |
208 | int64_t qpf; |
209 | |
210 | size_t GetHighPrecisionTimeStamp() |
211 | { |
212 | int64_t ts = GCToOSInterface::QueryPerformanceCounter(); |
213 | |
214 | return (size_t)(ts / (qpf / 1000)); |
215 | } |
216 | #endif |
217 | |
218 | #ifdef GC_STATS |
219 | // There is a current and a prior copy of the statistics. This allows us to display deltas per reporting |
220 | // interval, as well as running totals. The 'min' and 'max' values require special treatment. They are |
221 | // Reset (zeroed) in the current statistics when we begin a new interval and they are updated via a |
222 | // comparison with the global min/max. |
223 | GCStatistics g_GCStatistics; |
224 | GCStatistics g_LastGCStatistics; |
225 | |
226 | char* GCStatistics::logFileName = NULL; |
227 | FILE* GCStatistics::logFile = NULL; |
228 | |
229 | void GCStatistics::AddGCStats(const gc_mechanisms& settings, size_t timeInMSec) |
230 | { |
231 | #ifdef BACKGROUND_GC |
232 | if (settings.concurrent) |
233 | { |
234 | bgc.Accumulate((uint32_t)timeInMSec*1000); |
235 | cntBGC++; |
236 | } |
237 | else if (settings.background_p) |
238 | { |
239 | fgc.Accumulate((uint32_t)timeInMSec*1000); |
240 | cntFGC++; |
241 | if (settings.compaction) |
242 | cntCompactFGC++; |
243 | assert(settings.condemned_generation < max_generation); |
244 | cntFGCGen[settings.condemned_generation]++; |
245 | } |
246 | else |
247 | #endif // BACKGROUND_GC |
248 | { |
249 | ngc.Accumulate((uint32_t)timeInMSec*1000); |
250 | cntNGC++; |
251 | if (settings.compaction) |
252 | cntCompactNGC++; |
253 | cntNGCGen[settings.condemned_generation]++; |
254 | } |
255 | |
256 | if (is_induced (settings.reason)) |
257 | cntReasons[(int)reason_induced]++; |
258 | else if (settings.stress_induced) |
259 | cntReasons[(int)reason_gcstress]++; |
260 | else |
261 | cntReasons[(int)settings.reason]++; |
262 | |
263 | #ifdef BACKGROUND_GC |
264 | if (settings.concurrent || !settings.background_p) |
265 | { |
266 | #endif // BACKGROUND_GC |
267 | RollOverIfNeeded(); |
268 | #ifdef BACKGROUND_GC |
269 | } |
270 | #endif // BACKGROUND_GC |
271 | } |
272 | |
273 | void GCStatistics::Initialize() |
274 | { |
275 | LIMITED_METHOD_CONTRACT; |
276 | // for efficiency sake we're taking a dependency on the layout of a C++ object |
277 | // with a vtable. protect against violations of our premise: |
278 | static_assert(offsetof(GCStatistics, cntDisplay) == sizeof(void*), |
279 | "The first field of GCStatistics follows the pointer sized vtable" ); |
280 | |
281 | int podOffs = offsetof(GCStatistics, cntDisplay); // offset of the first POD field |
282 | memset((uint8_t*)(&g_GCStatistics)+podOffs, 0, sizeof(g_GCStatistics)-podOffs); |
283 | memset((uint8_t*)(&g_LastGCStatistics)+podOffs, 0, sizeof(g_LastGCStatistics)-podOffs); |
284 | } |
285 | |
286 | void GCStatistics::DisplayAndUpdate() |
287 | { |
288 | LIMITED_METHOD_CONTRACT; |
289 | |
290 | if (logFileName == NULL || logFile == NULL) |
291 | return; |
292 | |
293 | { |
294 | if (cntDisplay == 0) |
295 | fprintf(logFile, "\nGCMix **** Initialize *****\n\n" ); |
296 | |
297 | fprintf(logFile, "GCMix **** Summary ***** %d\n" , cntDisplay); |
298 | |
299 | // NGC summary (total, timing info) |
300 | ngc.DisplayAndUpdate(logFile, "NGC " , &g_LastGCStatistics.ngc, cntNGC, g_LastGCStatistics.cntNGC, msec); |
301 | |
302 | // FGC summary (total, timing info) |
303 | fgc.DisplayAndUpdate(logFile, "FGC " , &g_LastGCStatistics.fgc, cntFGC, g_LastGCStatistics.cntFGC, msec); |
304 | |
305 | // BGC summary |
306 | bgc.DisplayAndUpdate(logFile, "BGC " , &g_LastGCStatistics.bgc, cntBGC, g_LastGCStatistics.cntBGC, msec); |
307 | |
308 | // NGC/FGC break out by generation & compacting vs. sweeping |
309 | fprintf(logFile, "NGC " ); |
310 | for (int i = max_generation; i >= 0; --i) |
311 | fprintf(logFile, "gen%d %d (%d). " , i, cntNGCGen[i]-g_LastGCStatistics.cntNGCGen[i], cntNGCGen[i]); |
312 | fprintf(logFile, "\n" ); |
313 | |
314 | fprintf(logFile, "FGC " ); |
315 | for (int i = max_generation-1; i >= 0; --i) |
316 | fprintf(logFile, "gen%d %d (%d). " , i, cntFGCGen[i]-g_LastGCStatistics.cntFGCGen[i], cntFGCGen[i]); |
317 | fprintf(logFile, "\n" ); |
318 | |
319 | // Compacting vs. Sweeping break out |
320 | int _cntSweep = cntNGC-cntCompactNGC; |
321 | int _cntLastSweep = g_LastGCStatistics.cntNGC-g_LastGCStatistics.cntCompactNGC; |
322 | fprintf(logFile, "NGC Sweeping %d (%d) Compacting %d (%d)\n" , |
323 | _cntSweep - _cntLastSweep, _cntSweep, |
324 | cntCompactNGC - g_LastGCStatistics.cntCompactNGC, cntCompactNGC); |
325 | |
326 | _cntSweep = cntFGC-cntCompactFGC; |
327 | _cntLastSweep = g_LastGCStatistics.cntFGC-g_LastGCStatistics.cntCompactFGC; |
328 | fprintf(logFile, "FGC Sweeping %d (%d) Compacting %d (%d)\n" , |
329 | _cntSweep - _cntLastSweep, _cntSweep, |
330 | cntCompactFGC - g_LastGCStatistics.cntCompactFGC, cntCompactFGC); |
331 | |
332 | #ifdef TRACE_GC |
333 | // GC reasons... |
334 | for (int reason=(int)reason_alloc_soh; reason <= (int)reason_gcstress; ++reason) |
335 | { |
336 | if (cntReasons[reason] != 0) |
337 | fprintf(logFile, "%s %d (%d). " , str_gc_reasons[reason], |
338 | cntReasons[reason]-g_LastGCStatistics.cntReasons[reason], cntReasons[reason]); |
339 | } |
340 | #endif // TRACE_GC |
341 | fprintf(logFile, "\n\n" ); |
342 | |
343 | // flush the log file... |
344 | fflush(logFile); |
345 | } |
346 | |
347 | g_LastGCStatistics = *this; |
348 | |
349 | ngc.Reset(); |
350 | fgc.Reset(); |
351 | bgc.Reset(); |
352 | } |
353 | |
354 | #endif // GC_STATS |
355 | |
356 | inline |
357 | size_t round_up_power2 (size_t size) |
358 | { |
359 | // Get the 0-based index of the most-significant bit in size-1. |
360 | // If the call failed (because size-1 is zero), size must be 1, |
361 | // so return 1 (because 1 rounds up to itself). |
362 | DWORD highest_set_bit_index; |
363 | if (0 == |
364 | #ifdef BIT64 |
365 | BitScanReverse64( |
366 | #else |
367 | BitScanReverse( |
368 | #endif |
369 | &highest_set_bit_index, size - 1)) { return 1; } |
370 | |
371 | // The size == 0 case (which would have overflowed to SIZE_MAX when decremented) |
372 | // is handled below by relying on the fact that highest_set_bit_index is the maximum value |
373 | // (31 or 63, depending on sizeof(size_t)) and left-shifting a value >= 2 by that |
374 | // number of bits shifts in zeros from the right, resulting in an output of zero. |
375 | return static_cast<size_t>(2) << highest_set_bit_index; |
376 | } |
377 | |
378 | inline |
379 | size_t round_down_power2 (size_t size) |
380 | { |
381 | // Get the 0-based index of the most-significant bit in size. |
382 | // If the call failed, size must be zero so return zero. |
383 | DWORD highest_set_bit_index; |
384 | if (0 == |
385 | #ifdef BIT64 |
386 | BitScanReverse64( |
387 | #else |
388 | BitScanReverse( |
389 | #endif |
390 | &highest_set_bit_index, size)) { return 0; } |
391 | |
392 | // Left-shift 1 by highest_set_bit_index to get back a value containing only |
393 | // the most-significant set bit of size, i.e. size rounded down |
394 | // to the next power-of-two value. |
395 | return static_cast<size_t>(1) << highest_set_bit_index; |
396 | } |
397 | |
398 | // Get the 0-based index of the most-significant bit in the value. |
399 | // Returns -1 if the input value is zero (i.e. has no set bits). |
400 | inline |
401 | int index_of_highest_set_bit (size_t value) |
402 | { |
403 | // Get the 0-based index of the most-significant bit in the value. |
404 | // If the call failed (because value is zero), return -1. |
405 | DWORD highest_set_bit_index; |
406 | return (0 == |
407 | #ifdef BIT64 |
408 | BitScanReverse64( |
409 | #else |
410 | BitScanReverse( |
411 | #endif |
412 | &highest_set_bit_index, value)) ? -1 : static_cast<int>(highest_set_bit_index); |
413 | } |
414 | |
415 | inline |
416 | int relative_index_power2_plug (size_t power2) |
417 | { |
418 | int index = index_of_highest_set_bit (power2); |
419 | assert (index <= MAX_INDEX_POWER2); |
420 | |
421 | return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2)); |
422 | } |
423 | |
424 | inline |
425 | int relative_index_power2_free_space (size_t power2) |
426 | { |
427 | int index = index_of_highest_set_bit (power2); |
428 | assert (index <= MAX_INDEX_POWER2); |
429 | |
430 | return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2)); |
431 | } |
432 | |
433 | #ifdef BACKGROUND_GC |
434 | uint32_t bgc_alloc_spin_count = 140; |
435 | uint32_t bgc_alloc_spin_count_loh = 16; |
436 | uint32_t bgc_alloc_spin = 2; |
437 | |
438 | |
439 | inline |
440 | void c_write (uint32_t& place, uint32_t value) |
441 | { |
442 | Interlocked::Exchange (&place, value); |
443 | //place = value; |
444 | } |
445 | |
446 | #ifndef DACCESS_COMPILE |
447 | // If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC. |
448 | const size_t bgc_min_per_heap = 4*1024*1024; |
449 | |
450 | int gc_heap::gchist_index = 0; |
451 | gc_mechanisms_store gc_heap::gchist[max_history_count]; |
452 | |
453 | #ifndef MULTIPLE_HEAPS |
454 | size_t gc_heap::total_promoted_bytes = 0; |
455 | VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process; |
456 | int gc_heap::gchist_index_per_heap = 0; |
457 | gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count]; |
458 | #endif //MULTIPLE_HEAPS |
459 | |
460 | void gc_heap::add_to_history_per_heap() |
461 | { |
462 | #ifdef GC_HISTORY |
463 | gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap]; |
464 | current_hist->gc_index = settings.gc_index; |
465 | current_hist->current_bgc_state = current_bgc_state; |
466 | size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0)); |
467 | current_hist->gc_time_ms = (uint32_t)elapsed; |
468 | current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes); |
469 | current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1)); |
470 | current_hist->gen0_start = generation_allocation_start (generation_of (0)); |
471 | current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment); |
472 | #ifdef BACKGROUND_GC |
473 | current_hist->bgc_lowest = background_saved_lowest_address; |
474 | current_hist->bgc_highest = background_saved_highest_address; |
475 | #endif //BACKGROUND_GC |
476 | current_hist->fgc_lowest = lowest_address; |
477 | current_hist->fgc_highest = highest_address; |
478 | current_hist->g_lowest = g_gc_lowest_address; |
479 | current_hist->g_highest = g_gc_highest_address; |
480 | |
481 | gchist_index_per_heap++; |
482 | if (gchist_index_per_heap == max_history_count) |
483 | { |
484 | gchist_index_per_heap = 0; |
485 | } |
486 | #endif //GC_HISTORY |
487 | } |
488 | |
489 | void gc_heap::add_to_history() |
490 | { |
491 | #ifdef GC_HISTORY |
492 | gc_mechanisms_store* current_settings = &gchist[gchist_index]; |
493 | current_settings->store (&settings); |
494 | |
495 | gchist_index++; |
496 | if (gchist_index == max_history_count) |
497 | { |
498 | gchist_index = 0; |
499 | } |
500 | #endif //GC_HISTORY |
501 | } |
502 | |
503 | #endif //DACCESS_COMPILE |
504 | #endif //BACKGROUND_GC |
505 | |
506 | #if defined(TRACE_GC) && !defined(DACCESS_COMPILE) |
507 | BOOL gc_log_on = TRUE; |
508 | FILE* gc_log = NULL; |
509 | size_t gc_log_file_size = 0; |
510 | |
511 | size_t gc_buffer_index = 0; |
512 | size_t max_gc_buffers = 0; |
513 | |
514 | static CLRCriticalSection gc_log_lock; |
515 | |
516 | // we keep this much in a buffer and only flush when the buffer is full |
517 | #define gc_log_buffer_size (1024*1024) |
518 | uint8_t* gc_log_buffer = 0; |
519 | size_t gc_log_buffer_offset = 0; |
520 | |
521 | void log_va_msg(const char *fmt, va_list args) |
522 | { |
523 | gc_log_lock.Enter(); |
524 | |
525 | const int BUFFERSIZE = 512; |
526 | static char rgchBuffer[BUFFERSIZE]; |
527 | char * pBuffer = &rgchBuffer[0]; |
528 | |
529 | pBuffer[0] = '\n'; |
530 | int buffer_start = 1; |
531 | int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]" , (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()); |
532 | buffer_start += pid_len; |
533 | memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start); |
534 | int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args); |
535 | if (msg_len == -1) |
536 | { |
537 | msg_len = BUFFERSIZE - buffer_start; |
538 | } |
539 | |
540 | msg_len += buffer_start; |
541 | |
542 | if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12)) |
543 | { |
544 | char index_str[8]; |
545 | memset (index_str, '-', 8); |
546 | sprintf_s (index_str, _countof(index_str), "%d" , (int)gc_buffer_index); |
547 | gc_log_buffer[gc_log_buffer_offset] = '\n'; |
548 | memcpy (gc_log_buffer + (gc_log_buffer_offset + 1), index_str, 8); |
549 | |
550 | gc_buffer_index++; |
551 | if (gc_buffer_index > max_gc_buffers) |
552 | { |
553 | fseek (gc_log, 0, SEEK_SET); |
554 | gc_buffer_index = 0; |
555 | } |
556 | fwrite(gc_log_buffer, gc_log_buffer_size, 1, gc_log); |
557 | fflush(gc_log); |
558 | memset (gc_log_buffer, '*', gc_log_buffer_size); |
559 | gc_log_buffer_offset = 0; |
560 | } |
561 | |
562 | memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len); |
563 | gc_log_buffer_offset += msg_len; |
564 | |
565 | gc_log_lock.Leave(); |
566 | } |
567 | |
568 | void GCLog (const char *fmt, ... ) |
569 | { |
570 | if (gc_log_on && (gc_log != NULL)) |
571 | { |
572 | va_list args; |
573 | va_start(args, fmt); |
574 | log_va_msg (fmt, args); |
575 | va_end(args); |
576 | } |
577 | } |
578 | #endif // TRACE_GC && !DACCESS_COMPILE |
579 | |
580 | #if defined(GC_CONFIG_DRIVEN) && !defined(DACCESS_COMPILE) |
581 | |
582 | BOOL gc_config_log_on = FALSE; |
583 | FILE* gc_config_log = NULL; |
584 | |
585 | // we keep this much in a buffer and only flush when the buffer is full |
586 | #define gc_config_log_buffer_size (1*1024) // TEMP |
587 | uint8_t* gc_config_log_buffer = 0; |
588 | size_t gc_config_log_buffer_offset = 0; |
589 | |
590 | // For config since we log so little we keep the whole history. Also it's only |
591 | // ever logged by one thread so no need to synchronize. |
592 | void log_va_msg_config(const char *fmt, va_list args) |
593 | { |
594 | const int BUFFERSIZE = 256; |
595 | static char rgchBuffer[BUFFERSIZE]; |
596 | char * pBuffer = &rgchBuffer[0]; |
597 | |
598 | pBuffer[0] = '\n'; |
599 | int buffer_start = 1; |
600 | int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args ); |
601 | assert (msg_len != -1); |
602 | msg_len += buffer_start; |
603 | |
604 | if ((gc_config_log_buffer_offset + msg_len) > gc_config_log_buffer_size) |
605 | { |
606 | fwrite(gc_config_log_buffer, gc_config_log_buffer_offset, 1, gc_config_log); |
607 | fflush(gc_config_log); |
608 | gc_config_log_buffer_offset = 0; |
609 | } |
610 | |
611 | memcpy (gc_config_log_buffer + gc_config_log_buffer_offset, pBuffer, msg_len); |
612 | gc_config_log_buffer_offset += msg_len; |
613 | } |
614 | |
615 | void GCLogConfig (const char *fmt, ... ) |
616 | { |
617 | if (gc_config_log_on && (gc_config_log != NULL)) |
618 | { |
619 | va_list args; |
620 | va_start( args, fmt ); |
621 | log_va_msg_config (fmt, args); |
622 | } |
623 | } |
624 | #endif // GC_CONFIG_DRIVEN && !DACCESS_COMPILE |
625 | |
626 | #ifdef SYNCHRONIZATION_STATS |
627 | |
628 | // Number of GCs have we done since we last logged. |
629 | static unsigned int gc_count_during_log; |
630 | // In ms. This is how often we print out stats. |
631 | static const unsigned int log_interval = 5000; |
632 | // Time (in ms) when we start a new log interval. |
633 | static unsigned int log_start_tick; |
634 | static unsigned int gc_lock_contended; |
635 | static int64_t log_start_hires; |
636 | // Cycles accumulated in SuspendEE during log_interval. |
637 | static uint64_t suspend_ee_during_log; |
638 | // Cycles accumulated in RestartEE during log_interval. |
639 | static uint64_t restart_ee_during_log; |
640 | static uint64_t gc_during_log; |
641 | |
642 | #endif //SYNCHRONIZATION_STATS |
643 | |
644 | void |
645 | init_sync_log_stats() |
646 | { |
647 | #ifdef SYNCHRONIZATION_STATS |
648 | if (gc_count_during_log == 0) |
649 | { |
650 | gc_heap::init_sync_stats(); |
651 | suspend_ee_during_log = 0; |
652 | restart_ee_during_log = 0; |
653 | gc_during_log = 0; |
654 | gc_lock_contended = 0; |
655 | |
656 | log_start_tick = GCToOSInterface::GetLowPrecisionTimeStamp(); |
657 | log_start_hires = GCToOSInterface::QueryPerformanceCounter(); |
658 | } |
659 | gc_count_during_log++; |
660 | #endif //SYNCHRONIZATION_STATS |
661 | } |
662 | |
663 | void |
664 | process_sync_log_stats() |
665 | { |
666 | #ifdef SYNCHRONIZATION_STATS |
667 | |
668 | unsigned int log_elapsed = GCToOSInterface::GetLowPrecisionTimeStamp() - log_start_tick; |
669 | |
670 | if (log_elapsed > log_interval) |
671 | { |
672 | uint64_t total = GCToOSInterface::QueryPerformanceCounter() - log_start_hires; |
673 | // Print out the cycles we spent on average in each suspend and restart. |
674 | printf("\n_________________________________________________________________________________\n" |
675 | "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n" |
676 | "SuspendEE: %8u; RestartEE: %8u GC %.3f%%\n" , |
677 | log_interval / 1000, |
678 | gc_count_during_log, |
679 | gc_lock_contended, |
680 | (unsigned int)(gc_during_log / gc_count_during_log), |
681 | (unsigned int)(suspend_ee_during_log / gc_count_during_log), |
682 | (unsigned int)(restart_ee_during_log / gc_count_during_log), |
683 | (double)(100.0f * gc_during_log / total)); |
684 | gc_heap::print_sync_stats(gc_count_during_log); |
685 | |
686 | gc_count_during_log = 0; |
687 | } |
688 | #endif //SYNCHRONIZATION_STATS |
689 | } |
690 | |
691 | #ifdef MULTIPLE_HEAPS |
692 | |
693 | enum gc_join_stage |
694 | { |
695 | gc_join_init_cpu_mapping = 0, |
696 | gc_join_done = 1, |
697 | gc_join_generation_determined = 2, |
698 | gc_join_begin_mark_phase = 3, |
699 | gc_join_scan_dependent_handles = 4, |
700 | gc_join_rescan_dependent_handles = 5, |
701 | gc_join_scan_sizedref_done = 6, |
702 | gc_join_null_dead_short_weak = 7, |
703 | gc_join_scan_finalization = 8, |
704 | gc_join_null_dead_long_weak = 9, |
705 | gc_join_null_dead_syncblk = 10, |
706 | gc_join_decide_on_compaction = 11, |
707 | gc_join_rearrange_segs_compaction = 12, |
708 | gc_join_adjust_handle_age_compact = 13, |
709 | gc_join_adjust_handle_age_sweep = 14, |
710 | gc_join_begin_relocate_phase = 15, |
711 | gc_join_relocate_phase_done = 16, |
712 | gc_join_verify_objects_done = 17, |
713 | gc_join_start_bgc = 18, |
714 | gc_join_restart_ee = 19, |
715 | gc_join_concurrent_overflow = 20, |
716 | gc_join_suspend_ee = 21, |
717 | gc_join_bgc_after_ephemeral = 22, |
718 | gc_join_allow_fgc = 23, |
719 | gc_join_bgc_sweep = 24, |
720 | gc_join_suspend_ee_verify = 25, |
721 | gc_join_restart_ee_verify = 26, |
722 | gc_join_set_state_free = 27, |
723 | gc_r_join_update_card_bundle = 28, |
724 | gc_join_after_absorb = 29, |
725 | gc_join_verify_copy_table = 30, |
726 | gc_join_after_reset = 31, |
727 | gc_join_after_ephemeral_sweep = 32, |
728 | gc_join_after_profiler_heap_walk = 33, |
729 | gc_join_minimal_gc = 34, |
730 | gc_join_after_commit_soh_no_gc = 35, |
731 | gc_join_expand_loh_no_gc = 36, |
732 | gc_join_final_no_gc = 37, |
733 | gc_join_disable_software_write_watch = 38, |
734 | gc_join_max = 39 |
735 | }; |
736 | |
737 | enum gc_join_flavor |
738 | { |
739 | join_flavor_server_gc = 0, |
740 | join_flavor_bgc = 1 |
741 | }; |
742 | |
743 | #define first_thread_arrived 2 |
744 | #pragma warning(push) |
745 | #pragma warning(disable:4324) // don't complain if DECLSPEC_ALIGN actually pads |
746 | struct DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) join_structure |
747 | { |
748 | // Shared non volatile keep on separate line to prevent eviction |
749 | int n_threads; |
750 | |
751 | // Keep polling/wait structures on separate line write once per join |
752 | DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) |
753 | GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived. |
754 | Volatile<int> lock_color; |
755 | VOLATILE(BOOL) wait_done; |
756 | VOLATILE(BOOL) joined_p; |
757 | |
758 | // Keep volatile counted locks on separate cache line write many per join |
759 | DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) |
760 | VOLATILE(int32_t) join_lock; |
761 | VOLATILE(int32_t) r_join_lock; |
762 | |
763 | }; |
764 | #pragma warning(pop) |
765 | |
766 | enum join_type |
767 | { |
768 | type_last_join = 0, |
769 | type_join = 1, |
770 | type_restart = 2, |
771 | type_first_r_join = 3, |
772 | type_r_join = 4 |
773 | }; |
774 | |
775 | enum join_time |
776 | { |
777 | time_start = 0, |
778 | time_end = 1 |
779 | }; |
780 | |
781 | enum join_heap_index |
782 | { |
783 | join_heap_restart = 100, |
784 | join_heap_r_restart = 200 |
785 | }; |
786 | |
787 | struct join_event |
788 | { |
789 | uint32_t heap; |
790 | join_time time; |
791 | join_type type; |
792 | }; |
793 | |
794 | class t_join |
795 | { |
796 | join_structure join_struct; |
797 | |
798 | int id; |
799 | gc_join_flavor flavor; |
800 | |
801 | #ifdef JOIN_STATS |
802 | uint64_t start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq; |
803 | // remember join id and last thread to arrive so restart can use these |
804 | int thd; |
805 | // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval |
806 | uint32_t start_tick; |
807 | // counters for joins, in 1000's of clock cycles |
808 | uint64_t elapsed_total[gc_join_max], wake_total[gc_join_max], seq_loss_total[gc_join_max], par_loss_total[gc_join_max], in_join_total[gc_join_max]; |
809 | #endif //JOIN_STATS |
810 | |
811 | public: |
812 | BOOL init (int n_th, gc_join_flavor f) |
813 | { |
814 | dprintf (JOIN_LOG, ("Initializing join structure" )); |
815 | join_struct.n_threads = n_th; |
816 | join_struct.lock_color = 0; |
817 | for (int i = 0; i < 3; i++) |
818 | { |
819 | if (!join_struct.joined_event[i].IsValid()) |
820 | { |
821 | join_struct.joined_p = FALSE; |
822 | dprintf (JOIN_LOG, ("Creating join event %d" , i)); |
823 | // TODO - changing this to a non OS event |
824 | // because this is also used by BGC threads which are |
825 | // managed threads and WaitEx does not allow you to wait |
826 | // for an OS event on a managed thread. |
827 | // But we are not sure if this plays well in the hosting |
828 | // environment. |
829 | //join_struct.joined_event[i].CreateOSManualEventNoThrow(FALSE); |
830 | if (!join_struct.joined_event[i].CreateManualEventNoThrow(FALSE)) |
831 | return FALSE; |
832 | } |
833 | } |
834 | join_struct.join_lock = join_struct.n_threads; |
835 | join_struct.r_join_lock = join_struct.n_threads; |
836 | join_struct.wait_done = FALSE; |
837 | flavor = f; |
838 | |
839 | #ifdef JOIN_STATS |
840 | start_tick = GCToOSInterface::GetLowPrecisionTimeStamp(); |
841 | #endif //JOIN_STATS |
842 | |
843 | return TRUE; |
844 | } |
845 | |
846 | void destroy () |
847 | { |
848 | dprintf (JOIN_LOG, ("Destroying join structure" )); |
849 | for (int i = 0; i < 3; i++) |
850 | { |
851 | if (join_struct.joined_event[i].IsValid()) |
852 | join_struct.joined_event[i].CloseEvent(); |
853 | } |
854 | } |
855 | |
856 | inline void fire_event (int heap, join_time time, join_type type, int join_id) |
857 | { |
858 | FIRE_EVENT(GCJoin_V2, heap, time, type, join_id); |
859 | } |
860 | |
861 | void join (gc_heap* gch, int join_id) |
862 | { |
863 | #ifdef JOIN_STATS |
864 | // parallel execution ends here |
865 | end[gch->heap_number] = get_ts(); |
866 | #endif //JOIN_STATS |
867 | |
868 | assert (!join_struct.joined_p); |
869 | int color = join_struct.lock_color.LoadWithoutBarrier(); |
870 | |
871 | if (Interlocked::Decrement(&join_struct.join_lock) != 0) |
872 | { |
873 | dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d" , |
874 | flavor, join_id, (int32_t)(join_struct.join_lock))); |
875 | |
876 | fire_event (gch->heap_number, time_start, type_join, join_id); |
877 | |
878 | //busy wait around the color |
879 | if (color == join_struct.lock_color.LoadWithoutBarrier()) |
880 | { |
881 | respin: |
882 | int spin_count = 128 * yp_spin_count_unit; |
883 | for (int j = 0; j < spin_count; j++) |
884 | { |
885 | if (color != join_struct.lock_color.LoadWithoutBarrier()) |
886 | { |
887 | break; |
888 | } |
889 | YieldProcessor(); // indicate to the processor that we are spinning |
890 | } |
891 | |
892 | // we've spun, and if color still hasn't changed, fall into hard wait |
893 | if (color == join_struct.lock_color.LoadWithoutBarrier()) |
894 | { |
895 | dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d" , |
896 | flavor, join_id, color, (int32_t)(join_struct.join_lock))); |
897 | |
898 | //Thread* current_thread = GCToEEInterface::GetThread(); |
899 | //BOOL cooperative_mode = gc_heap::enable_preemptive (); |
900 | uint32_t dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE); |
901 | //gc_heap::disable_preemptive (cooperative_mode); |
902 | |
903 | if (dwJoinWait != WAIT_OBJECT_0) |
904 | { |
905 | STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix" , dwJoinWait); |
906 | FATAL_GC_ERROR (); |
907 | } |
908 | } |
909 | |
910 | // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent() |
911 | if (color == join_struct.lock_color.LoadWithoutBarrier()) |
912 | { |
913 | goto respin; |
914 | } |
915 | |
916 | dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d" , |
917 | flavor, join_id, (int32_t)(join_struct.join_lock))); |
918 | } |
919 | |
920 | fire_event (gch->heap_number, time_end, type_join, join_id); |
921 | |
922 | #ifdef JOIN_STATS |
923 | // parallel execution starts here |
924 | start[gch->heap_number] = get_ts(); |
925 | Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])); |
926 | #endif //JOIN_STATS |
927 | } |
928 | else |
929 | { |
930 | fire_event (gch->heap_number, time_start, type_last_join, join_id); |
931 | |
932 | join_struct.joined_p = TRUE; |
933 | dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id" , flavor, join_id)); |
934 | join_struct.joined_event[!color].Reset(); |
935 | id = join_id; |
936 | // this one is alone so it can proceed |
937 | #ifdef JOIN_STATS |
938 | // remember the join id, the last thread arriving, the start of the sequential phase, |
939 | // and keep track of the cycles spent waiting in the join |
940 | thd = gch->heap_number; |
941 | start_seq = get_ts(); |
942 | Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number])); |
943 | #endif //JOIN_STATS |
944 | } |
945 | } |
946 | |
947 | // Reverse join - first thread gets here does the work; other threads will only proceed |
948 | // after the work is done. |
949 | // Note that you cannot call this twice in a row on the same thread. Plus there's no |
950 | // need to call it twice in row - you should just merge the work. |
951 | BOOL r_join (gc_heap* gch, int join_id) |
952 | { |
953 | |
954 | if (join_struct.n_threads == 1) |
955 | { |
956 | return TRUE; |
957 | } |
958 | |
959 | if (Interlocked::CompareExchange(&join_struct.r_join_lock, 0, join_struct.n_threads) == 0) |
960 | { |
961 | if (!join_struct.wait_done) |
962 | { |
963 | dprintf (JOIN_LOG, ("r_join() Waiting..." )); |
964 | |
965 | fire_event (gch->heap_number, time_start, type_join, join_id); |
966 | |
967 | //busy wait around the color |
968 | if (!join_struct.wait_done) |
969 | { |
970 | respin: |
971 | int spin_count = 256 * yp_spin_count_unit; |
972 | for (int j = 0; j < spin_count; j++) |
973 | { |
974 | if (join_struct.wait_done) |
975 | { |
976 | break; |
977 | } |
978 | YieldProcessor(); // indicate to the processor that we are spinning |
979 | } |
980 | |
981 | // we've spun, and if color still hasn't changed, fall into hard wait |
982 | if (!join_struct.wait_done) |
983 | { |
984 | dprintf (JOIN_LOG, ("Join() hard wait on reset event %d" , first_thread_arrived)); |
985 | uint32_t dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE); |
986 | if (dwJoinWait != WAIT_OBJECT_0) |
987 | { |
988 | STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix" , dwJoinWait); |
989 | FATAL_GC_ERROR (); |
990 | } |
991 | } |
992 | |
993 | // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent() |
994 | if (!join_struct.wait_done) |
995 | { |
996 | goto respin; |
997 | } |
998 | |
999 | dprintf (JOIN_LOG, ("r_join() done" )); |
1000 | } |
1001 | |
1002 | fire_event (gch->heap_number, time_end, type_join, join_id); |
1003 | } |
1004 | |
1005 | return FALSE; |
1006 | } |
1007 | else |
1008 | { |
1009 | fire_event (gch->heap_number, time_start, type_first_r_join, join_id); |
1010 | return TRUE; |
1011 | } |
1012 | } |
1013 | |
1014 | #ifdef JOIN_STATS |
1015 | uint64_t get_ts() |
1016 | { |
1017 | return GCToOSInterface::QueryPerformanceCounter(); |
1018 | } |
1019 | |
1020 | void start_ts (gc_heap* gch) |
1021 | { |
1022 | // parallel execution ends here |
1023 | start[gch->heap_number] = get_ts(); |
1024 | } |
1025 | #endif //JOIN_STATS |
1026 | |
1027 | void restart() |
1028 | { |
1029 | #ifdef JOIN_STATS |
1030 | uint64_t elapsed_seq = get_ts() - start_seq; |
1031 | uint64_t max = 0, sum = 0, wake = 0; |
1032 | uint64_t min_ts = start[0]; |
1033 | for (int i = 1; i < join_struct.n_threads; i++) |
1034 | { |
1035 | if(min_ts > start[i]) min_ts = start[i]; |
1036 | } |
1037 | |
1038 | for (int i = 0; i < join_struct.n_threads; i++) |
1039 | { |
1040 | uint64_t wake_delay = start[i] - min_ts; |
1041 | uint64_t elapsed = end[i] - start[i]; |
1042 | if (max < elapsed) |
1043 | max = elapsed; |
1044 | sum += elapsed; |
1045 | wake += wake_delay; |
1046 | } |
1047 | uint64_t seq_loss = (join_struct.n_threads - 1)*elapsed_seq; |
1048 | uint64_t par_loss = join_struct.n_threads*max - sum; |
1049 | double efficiency = 0.0; |
1050 | if (max > 0) |
1051 | efficiency = sum*100.0/(join_struct.n_threads*max); |
1052 | |
1053 | const double ts_scale = 1e-6; |
1054 | |
1055 | // enable this printf to get statistics on each individual join as it occurs |
1056 | // printf("join #%3d seq_loss = %5g par_loss = %5g efficiency = %3.0f%%\n", join_id, ts_scale*seq_loss, ts_scale*par_loss, efficiency); |
1057 | |
1058 | elapsed_total[id] += sum; |
1059 | wake_total[id] += wake; |
1060 | seq_loss_total[id] += seq_loss; |
1061 | par_loss_total[id] += par_loss; |
1062 | |
1063 | // every 10 seconds, print a summary of the time spent in each type of join |
1064 | if (GCToOSInterface::GetLowPrecisionTimeStamp() - start_tick > 10*1000) |
1065 | { |
1066 | printf("**** summary *****\n" ); |
1067 | for (int i = 0; i < 16; i++) |
1068 | { |
1069 | printf("join #%3d elapsed_total = %8g wake_loss = %8g seq_loss = %8g par_loss = %8g in_join_total = %8g\n" , |
1070 | i, |
1071 | ts_scale*elapsed_total[i], |
1072 | ts_scale*wake_total[i], |
1073 | ts_scale*seq_loss_total[i], |
1074 | ts_scale*par_loss_total[i], |
1075 | ts_scale*in_join_total[i]); |
1076 | elapsed_total[i] = wake_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0; |
1077 | } |
1078 | start_tick = GCToOSInterface::GetLowPrecisionTimeStamp(); |
1079 | } |
1080 | #endif //JOIN_STATS |
1081 | |
1082 | fire_event (join_heap_restart, time_start, type_restart, -1); |
1083 | assert (join_struct.joined_p); |
1084 | join_struct.joined_p = FALSE; |
1085 | join_struct.join_lock = join_struct.n_threads; |
1086 | dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d" , flavor, id, (int32_t)(join_struct.join_lock))); |
1087 | // printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start); |
1088 | int color = join_struct.lock_color.LoadWithoutBarrier(); |
1089 | join_struct.lock_color = !color; |
1090 | join_struct.joined_event[color].Set(); |
1091 | |
1092 | // printf("Set joined_event %d\n", !join_struct.lock_color); |
1093 | |
1094 | fire_event (join_heap_restart, time_end, type_restart, -1); |
1095 | |
1096 | #ifdef JOIN_STATS |
1097 | start[thd] = get_ts(); |
1098 | #endif //JOIN_STATS |
1099 | } |
1100 | |
1101 | BOOL joined() |
1102 | { |
1103 | dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d" , flavor, id, (int32_t)(join_struct.join_lock))); |
1104 | return join_struct.joined_p; |
1105 | } |
1106 | |
1107 | void r_restart() |
1108 | { |
1109 | if (join_struct.n_threads != 1) |
1110 | { |
1111 | fire_event (join_heap_r_restart, time_start, type_restart, -1); |
1112 | join_struct.wait_done = TRUE; |
1113 | join_struct.joined_event[first_thread_arrived].Set(); |
1114 | fire_event (join_heap_r_restart, time_end, type_restart, -1); |
1115 | } |
1116 | } |
1117 | |
1118 | void r_init() |
1119 | { |
1120 | if (join_struct.n_threads != 1) |
1121 | { |
1122 | join_struct.r_join_lock = join_struct.n_threads; |
1123 | join_struct.wait_done = FALSE; |
1124 | join_struct.joined_event[first_thread_arrived].Reset(); |
1125 | } |
1126 | } |
1127 | }; |
1128 | |
1129 | t_join gc_t_join; |
1130 | |
1131 | #ifdef BACKGROUND_GC |
1132 | t_join bgc_t_join; |
1133 | #endif //BACKGROUND_GC |
1134 | |
1135 | #endif //MULTIPLE_HEAPS |
1136 | |
1137 | #define spin_and_switch(count_to_spin, expr) \ |
1138 | { \ |
1139 | for (int j = 0; j < count_to_spin; j++) \ |
1140 | { \ |
1141 | if (expr) \ |
1142 | { \ |
1143 | break;\ |
1144 | } \ |
1145 | YieldProcessor(); \ |
1146 | } \ |
1147 | if (!(expr)) \ |
1148 | { \ |
1149 | GCToOSInterface::YieldThread(0); \ |
1150 | } \ |
1151 | } |
1152 | |
1153 | #ifndef DACCESS_COMPILE |
1154 | #ifdef BACKGROUND_GC |
1155 | |
1156 | #define max_pending_allocs 64 |
1157 | |
1158 | class exclusive_sync |
1159 | { |
1160 | // TODO - verify that this is the right syntax for Volatile. |
1161 | VOLATILE(uint8_t*) rwp_object; |
1162 | VOLATILE(int32_t) needs_checking; |
1163 | |
1164 | int spin_count; |
1165 | |
1166 | uint8_t cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (int32_t)]; |
1167 | |
1168 | // TODO - perhaps each object should be on its own cache line... |
1169 | VOLATILE(uint8_t*) alloc_objects[max_pending_allocs]; |
1170 | |
1171 | int find_free_index () |
1172 | { |
1173 | for (int i = 0; i < max_pending_allocs; i++) |
1174 | { |
1175 | if (alloc_objects [i] == (uint8_t*)0) |
1176 | { |
1177 | return i; |
1178 | } |
1179 | } |
1180 | |
1181 | return -1; |
1182 | } |
1183 | |
1184 | public: |
1185 | void init() |
1186 | { |
1187 | spin_count = 32 * (g_num_processors - 1); |
1188 | rwp_object = 0; |
1189 | needs_checking = 0; |
1190 | for (int i = 0; i < max_pending_allocs; i++) |
1191 | { |
1192 | alloc_objects [i] = (uint8_t*)0; |
1193 | } |
1194 | } |
1195 | |
1196 | void check() |
1197 | { |
1198 | for (int i = 0; i < max_pending_allocs; i++) |
1199 | { |
1200 | if (alloc_objects [i] != (uint8_t*)0) |
1201 | { |
1202 | GCToOSInterface::DebugBreak(); |
1203 | } |
1204 | } |
1205 | } |
1206 | |
1207 | void bgc_mark_set (uint8_t* obj) |
1208 | { |
1209 | dprintf (3, ("cm: probing %Ix" , obj)); |
1210 | retry: |
1211 | if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0) |
1212 | { |
1213 | // If we spend too much time spending all the allocs, |
1214 | // consider adding a high water mark and scan up |
1215 | // to that; we'll need to interlock in done when |
1216 | // we update the high watermark. |
1217 | for (int i = 0; i < max_pending_allocs; i++) |
1218 | { |
1219 | if (obj == alloc_objects[i]) |
1220 | { |
1221 | needs_checking = 0; |
1222 | dprintf (3, ("cm: will spin" )); |
1223 | spin_and_switch (spin_count, (obj != alloc_objects[i])); |
1224 | goto retry; |
1225 | } |
1226 | } |
1227 | |
1228 | rwp_object = obj; |
1229 | needs_checking = 0; |
1230 | dprintf (3, ("cm: set %Ix" , obj)); |
1231 | return; |
1232 | } |
1233 | else |
1234 | { |
1235 | spin_and_switch (spin_count, (needs_checking == 0)); |
1236 | goto retry; |
1237 | } |
1238 | } |
1239 | |
1240 | int loh_alloc_set (uint8_t* obj) |
1241 | { |
1242 | if (!gc_heap::cm_in_progress) |
1243 | { |
1244 | return -1; |
1245 | } |
1246 | |
1247 | retry: |
1248 | dprintf (3, ("loh alloc: probing %Ix" , obj)); |
1249 | |
1250 | if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0) |
1251 | { |
1252 | if (obj == rwp_object) |
1253 | { |
1254 | needs_checking = 0; |
1255 | spin_and_switch (spin_count, (obj != rwp_object)); |
1256 | goto retry; |
1257 | } |
1258 | else |
1259 | { |
1260 | int cookie = find_free_index(); |
1261 | |
1262 | if (cookie != -1) |
1263 | { |
1264 | alloc_objects[cookie] = obj; |
1265 | needs_checking = 0; |
1266 | //if (cookie >= 4) |
1267 | //{ |
1268 | // GCToOSInterface::DebugBreak(); |
1269 | //} |
1270 | |
1271 | dprintf (3, ("loh alloc: set %Ix at %d" , obj, cookie)); |
1272 | return cookie; |
1273 | } |
1274 | else |
1275 | { |
1276 | needs_checking = 0; |
1277 | dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index" , obj)); |
1278 | spin_and_switch (spin_count, (find_free_index () != -1)); |
1279 | goto retry; |
1280 | } |
1281 | } |
1282 | } |
1283 | else |
1284 | { |
1285 | dprintf (3, ("loh alloc: will spin on checking %Ix" , obj)); |
1286 | spin_and_switch (spin_count, (needs_checking == 0)); |
1287 | goto retry; |
1288 | } |
1289 | } |
1290 | |
1291 | void bgc_mark_done () |
1292 | { |
1293 | dprintf (3, ("cm: release lock on %Ix" , (uint8_t *)rwp_object)); |
1294 | rwp_object = 0; |
1295 | } |
1296 | |
1297 | void loh_alloc_done_with_index (int index) |
1298 | { |
1299 | dprintf (3, ("loh alloc: release lock on %Ix based on %d" , (uint8_t *)alloc_objects[index], index)); |
1300 | assert ((index >= 0) && (index < max_pending_allocs)); |
1301 | alloc_objects[index] = (uint8_t*)0; |
1302 | } |
1303 | |
1304 | void loh_alloc_done (uint8_t* obj) |
1305 | { |
1306 | #ifdef BACKGROUND_GC |
1307 | if (!gc_heap::cm_in_progress) |
1308 | { |
1309 | return; |
1310 | } |
1311 | |
1312 | for (int i = 0; i < max_pending_allocs; i++) |
1313 | { |
1314 | if (alloc_objects [i] == obj) |
1315 | { |
1316 | dprintf (3, ("loh alloc: release lock on %Ix at %d" , (uint8_t *)alloc_objects[i], i)); |
1317 | alloc_objects[i] = (uint8_t*)0; |
1318 | return; |
1319 | } |
1320 | } |
1321 | #endif //BACKGROUND_GC |
1322 | } |
1323 | }; |
1324 | |
1325 | // Note that this class was written assuming just synchronization between |
1326 | // one background GC thread and multiple user threads that might request |
1327 | // an FGC - it does not take into account what kind of locks the multiple |
1328 | // user threads might be holding at the time (eg, there could only be one |
1329 | // user thread requesting an FGC because it needs to take gc_lock first) |
1330 | // so you'll see checks that may not be necessary if you take those conditions |
1331 | // into consideration. |
1332 | // |
1333 | // With the introduction of Server Background GC we no longer use this |
1334 | // class to do synchronization between FGCs and BGC. |
1335 | class recursive_gc_sync |
1336 | { |
1337 | static VOLATILE(int32_t) foreground_request_count;//initial state 0 |
1338 | static VOLATILE(BOOL) gc_background_running; //initial state FALSE |
1339 | static VOLATILE(int32_t) foreground_count; // initial state 0; |
1340 | static VOLATILE(uint32_t) foreground_gate; // initial state FALSE; |
1341 | static GCEvent foreground_complete;//Auto Reset |
1342 | static GCEvent foreground_allowed;//Auto Reset |
1343 | public: |
1344 | static void begin_background(); |
1345 | static void end_background(); |
1346 | static void begin_foreground(); |
1347 | static void end_foreground(); |
1348 | BOOL allow_foreground (); |
1349 | static BOOL init(); |
1350 | static void shutdown(); |
1351 | static BOOL background_running_p() {return gc_background_running;} |
1352 | }; |
1353 | |
1354 | VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial state 0 |
1355 | VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0; |
1356 | VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE |
1357 | VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0; |
1358 | GCEvent recursive_gc_sync::foreground_complete;//Auto Reset |
1359 | GCEvent recursive_gc_sync::foreground_allowed;//Manual Reset |
1360 | |
1361 | BOOL recursive_gc_sync::init () |
1362 | { |
1363 | foreground_request_count = 0; |
1364 | foreground_count = 0; |
1365 | gc_background_running = FALSE; |
1366 | foreground_gate = 0; |
1367 | |
1368 | if (!foreground_complete.CreateOSAutoEventNoThrow(FALSE)) |
1369 | { |
1370 | goto error; |
1371 | } |
1372 | if (!foreground_allowed.CreateManualEventNoThrow(FALSE)) |
1373 | { |
1374 | goto error; |
1375 | } |
1376 | return TRUE; |
1377 | |
1378 | error: |
1379 | shutdown(); |
1380 | return FALSE; |
1381 | |
1382 | } |
1383 | |
1384 | void recursive_gc_sync::shutdown() |
1385 | { |
1386 | if (foreground_complete.IsValid()) |
1387 | foreground_complete.CloseEvent(); |
1388 | if (foreground_allowed.IsValid()) |
1389 | foreground_allowed.CloseEvent(); |
1390 | } |
1391 | |
1392 | void recursive_gc_sync::begin_background() |
1393 | { |
1394 | dprintf (2, ("begin background" )); |
1395 | foreground_request_count = 1; |
1396 | foreground_count = 1; |
1397 | foreground_allowed.Reset(); |
1398 | gc_background_running = TRUE; |
1399 | } |
1400 | void recursive_gc_sync::end_background() |
1401 | { |
1402 | dprintf (2, ("end background" )); |
1403 | gc_background_running = FALSE; |
1404 | foreground_gate = 1; |
1405 | foreground_allowed.Set(); |
1406 | } |
1407 | |
1408 | void recursive_gc_sync::begin_foreground() |
1409 | { |
1410 | dprintf (2, ("begin_foreground" )); |
1411 | |
1412 | bool cooperative_mode = false; |
1413 | if (gc_background_running) |
1414 | { |
1415 | gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc); |
1416 | gc_heap::alloc_wait_event_p = TRUE; |
1417 | |
1418 | try_again_top: |
1419 | |
1420 | Interlocked::Increment (&foreground_request_count); |
1421 | |
1422 | try_again_no_inc: |
1423 | dprintf(2, ("Waiting sync gc point" )); |
1424 | assert (foreground_allowed.IsValid()); |
1425 | assert (foreground_complete.IsValid()); |
1426 | |
1427 | cooperative_mode = gc_heap::enable_preemptive (); |
1428 | |
1429 | foreground_allowed.Wait(INFINITE, FALSE); |
1430 | |
1431 | dprintf(2, ("Waiting sync gc point is done" )); |
1432 | |
1433 | gc_heap::disable_preemptive (cooperative_mode); |
1434 | |
1435 | if (foreground_gate) |
1436 | { |
1437 | Interlocked::Increment (&foreground_count); |
1438 | dprintf (2, ("foreground_count: %d" , (int32_t)foreground_count)); |
1439 | if (foreground_gate) |
1440 | { |
1441 | gc_heap::settings.concurrent = FALSE; |
1442 | return; |
1443 | } |
1444 | else |
1445 | { |
1446 | end_foreground(); |
1447 | goto try_again_top; |
1448 | } |
1449 | } |
1450 | else |
1451 | { |
1452 | goto try_again_no_inc; |
1453 | } |
1454 | } |
1455 | } |
1456 | |
1457 | void recursive_gc_sync::end_foreground() |
1458 | { |
1459 | dprintf (2, ("end_foreground" )); |
1460 | if (gc_background_running) |
1461 | { |
1462 | Interlocked::Decrement (&foreground_request_count); |
1463 | dprintf (2, ("foreground_count before decrement: %d" , (int32_t)foreground_count)); |
1464 | if (Interlocked::Decrement (&foreground_count) == 0) |
1465 | { |
1466 | //c_write ((BOOL*)&foreground_gate, 0); |
1467 | // TODO - couldn't make the syntax work with Volatile<T> |
1468 | foreground_gate = 0; |
1469 | if (foreground_count == 0) |
1470 | { |
1471 | foreground_allowed.Reset (); |
1472 | dprintf(2, ("setting foreground complete event" )); |
1473 | foreground_complete.Set(); |
1474 | } |
1475 | } |
1476 | } |
1477 | } |
1478 | |
1479 | inline |
1480 | BOOL recursive_gc_sync::allow_foreground() |
1481 | { |
1482 | assert (gc_heap::settings.concurrent); |
1483 | dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d" , |
1484 | (int32_t)foreground_request_count, (int32_t)foreground_count)); |
1485 | |
1486 | BOOL did_fgc = FALSE; |
1487 | |
1488 | //if we have suspended the EE, just return because |
1489 | //some thread could be waiting on this to proceed. |
1490 | if (!GCHeap::GcInProgress) |
1491 | { |
1492 | //TODO BACKGROUND_GC This is to stress the concurrency between |
1493 | //background and foreground |
1494 | // gc_heap::disallow_new_allocation (0); |
1495 | |
1496 | //GCToOSInterface::YieldThread(0); |
1497 | |
1498 | //END of TODO |
1499 | if (foreground_request_count != 0) |
1500 | { |
1501 | //foreground wants to run |
1502 | //save the important settings |
1503 | //TODO BACKGROUND_GC be more selective about the important settings. |
1504 | gc_mechanisms saved_settings = gc_heap::settings; |
1505 | do |
1506 | { |
1507 | did_fgc = TRUE; |
1508 | //c_write ((BOOL*)&foreground_gate, 1); |
1509 | // TODO - couldn't make the syntax work with Volatile<T> |
1510 | foreground_gate = 1; |
1511 | foreground_allowed.Set (); |
1512 | foreground_complete.Wait (INFINITE, FALSE); |
1513 | }while (/*foreground_request_count ||*/ foreground_gate); |
1514 | |
1515 | assert (!foreground_gate); |
1516 | |
1517 | //restore the important settings |
1518 | gc_heap::settings = saved_settings; |
1519 | GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation; |
1520 | //the background GC shouldn't be using gc_high and gc_low |
1521 | //gc_low = lowest_address; |
1522 | //gc_high = highest_address; |
1523 | } |
1524 | |
1525 | //TODO BACKGROUND_GC This is to stress the concurrency between |
1526 | //background and foreground |
1527 | // gc_heap::allow_new_allocation (0); |
1528 | //END of TODO |
1529 | } |
1530 | |
1531 | dprintf (100, ("leave allow_foreground" )); |
1532 | assert (gc_heap::settings.concurrent); |
1533 | return did_fgc; |
1534 | } |
1535 | |
1536 | #endif //BACKGROUND_GC |
1537 | #endif //DACCESS_COMPILE |
1538 | |
1539 | |
1540 | #if defined(COUNT_CYCLES) |
1541 | #ifdef _MSC_VER |
1542 | #pragma warning(disable:4035) |
1543 | #endif //_MSC_VER |
1544 | |
1545 | static |
1546 | unsigned GetCycleCount32() // enough for about 40 seconds |
1547 | { |
1548 | __asm push EDX |
1549 | __asm _emit 0x0F |
1550 | __asm _emit 0x31 |
1551 | __asm pop EDX |
1552 | }; |
1553 | |
1554 | #pragma warning(default:4035) |
1555 | |
1556 | #endif //COUNT_CYCLES |
1557 | |
1558 | #ifdef TIME_GC |
1559 | int mark_time, plan_time, sweep_time, reloc_time, compact_time; |
1560 | #endif //TIME_GC |
1561 | |
1562 | #ifndef MULTIPLE_HEAPS |
1563 | |
1564 | #endif // MULTIPLE_HEAPS |
1565 | |
1566 | void reset_memory (uint8_t* o, size_t sizeo); |
1567 | |
1568 | #ifdef WRITE_WATCH |
1569 | |
1570 | #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
1571 | static bool virtual_alloc_hardware_write_watch = false; |
1572 | #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
1573 | |
1574 | static bool hardware_write_watch_capability = false; |
1575 | |
1576 | #ifndef DACCESS_COMPILE |
1577 | |
1578 | //check if the write watch APIs are supported. |
1579 | |
1580 | void hardware_write_watch_api_supported() |
1581 | { |
1582 | if (GCToOSInterface::SupportsWriteWatch()) |
1583 | { |
1584 | hardware_write_watch_capability = true; |
1585 | dprintf (2, ("WriteWatch supported" )); |
1586 | } |
1587 | else |
1588 | { |
1589 | dprintf (2,("WriteWatch not supported" )); |
1590 | } |
1591 | } |
1592 | |
1593 | #endif //!DACCESS_COMPILE |
1594 | |
1595 | inline bool can_use_hardware_write_watch() |
1596 | { |
1597 | return hardware_write_watch_capability; |
1598 | } |
1599 | |
1600 | inline bool can_use_write_watch_for_gc_heap() |
1601 | { |
1602 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
1603 | return true; |
1604 | #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
1605 | return can_use_hardware_write_watch(); |
1606 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
1607 | } |
1608 | |
1609 | inline bool can_use_write_watch_for_card_table() |
1610 | { |
1611 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
1612 | return true; |
1613 | #else |
1614 | return can_use_hardware_write_watch(); |
1615 | #endif |
1616 | } |
1617 | |
1618 | #else |
1619 | #define mem_reserve (MEM_RESERVE) |
1620 | #endif //WRITE_WATCH |
1621 | |
1622 | //check if the low memory notification is supported |
1623 | |
1624 | #ifndef DACCESS_COMPILE |
1625 | |
1626 | void WaitLongerNoInstru (int i) |
1627 | { |
1628 | // every 8th attempt: |
1629 | bool bToggleGC = GCToEEInterface::EnablePreemptiveGC(); |
1630 | |
1631 | // if we're waiting for gc to finish, we should block immediately |
1632 | if (g_fSuspensionPending == 0) |
1633 | { |
1634 | if (g_num_processors > 1) |
1635 | { |
1636 | YieldProcessor(); // indicate to the processor that we are spining |
1637 | if (i & 0x01f) |
1638 | GCToOSInterface::YieldThread (0); |
1639 | else |
1640 | GCToOSInterface::Sleep (5); |
1641 | } |
1642 | else |
1643 | GCToOSInterface::Sleep (5); |
1644 | } |
1645 | |
1646 | // If CLR is hosted, a thread may reach here while it is in preemptive GC mode, |
1647 | // or it has no Thread object, in order to force a task to yield, or to triger a GC. |
1648 | // It is important that the thread is going to wait for GC. Otherwise the thread |
1649 | // is in a tight loop. If the thread has high priority, the perf is going to be very BAD. |
1650 | if (bToggleGC) |
1651 | { |
1652 | #ifdef _DEBUG |
1653 | // In debug builds, all enter_spin_lock operations go through this code. If a GC has |
1654 | // started, it is important to block until the GC thread calls set_gc_done (since it is |
1655 | // guaranteed to have cleared g_TrapReturningThreads by this point). This avoids livelock |
1656 | // conditions which can otherwise occur if threads are allowed to spin in this function |
1657 | // (and therefore starve the GC thread) between the point when the GC thread sets the |
1658 | // WaitForGC event and the point when the GC thread clears g_TrapReturningThreads. |
1659 | if (gc_heap::gc_started) |
1660 | { |
1661 | gc_heap::wait_for_gc_done(); |
1662 | } |
1663 | #endif // _DEBUG |
1664 | GCToEEInterface::DisablePreemptiveGC(); |
1665 | } |
1666 | else if (g_fSuspensionPending > 0) |
1667 | { |
1668 | g_theGCHeap->WaitUntilGCComplete(); |
1669 | } |
1670 | } |
1671 | |
1672 | inline |
1673 | static void safe_switch_to_thread() |
1674 | { |
1675 | bool cooperative_mode = gc_heap::enable_preemptive(); |
1676 | |
1677 | GCToOSInterface::YieldThread(0); |
1678 | |
1679 | gc_heap::disable_preemptive(cooperative_mode); |
1680 | } |
1681 | |
1682 | // |
1683 | // We need the following methods to have volatile arguments, so that they can accept |
1684 | // raw pointers in addition to the results of the & operator on Volatile<T>. |
1685 | // |
1686 | inline |
1687 | static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock) |
1688 | { |
1689 | retry: |
1690 | |
1691 | if (Interlocked::CompareExchange(lock, 0, -1) >= 0) |
1692 | { |
1693 | unsigned int i = 0; |
1694 | while (VolatileLoad(lock) >= 0) |
1695 | { |
1696 | if ((++i & 7) && !IsGCInProgress()) |
1697 | { |
1698 | if (g_num_processors > 1) |
1699 | { |
1700 | #ifndef MULTIPLE_HEAPS |
1701 | int spin_count = 32 * yp_spin_count_unit; |
1702 | #else //!MULTIPLE_HEAPS |
1703 | int spin_count = yp_spin_count_unit; |
1704 | #endif //!MULTIPLE_HEAPS |
1705 | for (int j = 0; j < spin_count; j++) |
1706 | { |
1707 | if (VolatileLoad(lock) < 0 || IsGCInProgress()) |
1708 | break; |
1709 | YieldProcessor(); // indicate to the processor that we are spining |
1710 | } |
1711 | if (VolatileLoad(lock) >= 0 && !IsGCInProgress()) |
1712 | { |
1713 | safe_switch_to_thread(); |
1714 | } |
1715 | } |
1716 | else |
1717 | { |
1718 | safe_switch_to_thread(); |
1719 | } |
1720 | } |
1721 | else |
1722 | { |
1723 | WaitLongerNoInstru(i); |
1724 | } |
1725 | } |
1726 | goto retry; |
1727 | } |
1728 | } |
1729 | |
1730 | inline |
1731 | static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) int32_t* lock) |
1732 | { |
1733 | return (Interlocked::CompareExchange(&*lock, 0, -1) < 0); |
1734 | } |
1735 | |
1736 | inline |
1737 | static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock) |
1738 | { |
1739 | VolatileStore<int32_t>((int32_t*)lock, -1); |
1740 | } |
1741 | |
1742 | #ifdef _DEBUG |
1743 | |
1744 | inline |
1745 | static void enter_spin_lock(GCSpinLock *pSpinLock) |
1746 | { |
1747 | enter_spin_lock_noinstru(&pSpinLock->lock); |
1748 | assert (pSpinLock->holding_thread == (Thread*)-1); |
1749 | pSpinLock->holding_thread = GCToEEInterface::GetThread(); |
1750 | } |
1751 | |
1752 | inline |
1753 | static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock) |
1754 | { |
1755 | BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock); |
1756 | if (ret) |
1757 | pSpinLock->holding_thread = GCToEEInterface::GetThread(); |
1758 | return ret; |
1759 | } |
1760 | |
1761 | inline |
1762 | static void leave_spin_lock(GCSpinLock *pSpinLock) |
1763 | { |
1764 | bool gc_thread_p = GCToEEInterface::WasCurrentThreadCreatedByGC(); |
1765 | // _ASSERTE((pSpinLock->holding_thread == GCToEEInterface::GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p); |
1766 | pSpinLock->released_by_gc_p = gc_thread_p; |
1767 | pSpinLock->holding_thread = (Thread*) -1; |
1768 | if (pSpinLock->lock != -1) |
1769 | leave_spin_lock_noinstru(&pSpinLock->lock); |
1770 | } |
1771 | |
1772 | #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \ |
1773 | _ASSERTE((pSpinLock)->holding_thread == GCToEEInterface::GetThread()); |
1774 | |
1775 | #define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \ |
1776 | _ASSERTE((pSpinLock)->holding_thread != GCToEEInterface::GetThread()); |
1777 | |
1778 | #else //_DEBUG |
1779 | |
1780 | //In the concurrent version, the Enable/DisablePreemptiveGC is optional because |
1781 | //the gc thread call WaitLonger. |
1782 | void WaitLonger (int i |
1783 | #ifdef SYNCHRONIZATION_STATS |
1784 | , GCSpinLock* spin_lock |
1785 | #endif //SYNCHRONIZATION_STATS |
1786 | ) |
1787 | { |
1788 | #ifdef SYNCHRONIZATION_STATS |
1789 | (spin_lock->num_wait_longer)++; |
1790 | #endif //SYNCHRONIZATION_STATS |
1791 | |
1792 | // every 8th attempt: |
1793 | bool bToggleGC = GCToEEInterface::EnablePreemptiveGC(); |
1794 | assert (bToggleGC); |
1795 | |
1796 | // if we're waiting for gc to finish, we should block immediately |
1797 | if (!gc_heap::gc_started) |
1798 | { |
1799 | #ifdef SYNCHRONIZATION_STATS |
1800 | (spin_lock->num_switch_thread_w)++; |
1801 | #endif //SYNCHRONIZATION_STATS |
1802 | if (g_num_processors > 1) |
1803 | { |
1804 | YieldProcessor(); // indicate to the processor that we are spining |
1805 | if (i & 0x01f) |
1806 | GCToOSInterface::YieldThread (0); |
1807 | else |
1808 | GCToOSInterface::Sleep (5); |
1809 | } |
1810 | else |
1811 | GCToOSInterface::Sleep (5); |
1812 | } |
1813 | |
1814 | // If CLR is hosted, a thread may reach here while it is in preemptive GC mode, |
1815 | // or it has no Thread object, in order to force a task to yield, or to triger a GC. |
1816 | // It is important that the thread is going to wait for GC. Otherwise the thread |
1817 | // is in a tight loop. If the thread has high priority, the perf is going to be very BAD. |
1818 | if (gc_heap::gc_started) |
1819 | { |
1820 | gc_heap::wait_for_gc_done(); |
1821 | } |
1822 | |
1823 | if (bToggleGC) |
1824 | { |
1825 | #ifdef SYNCHRONIZATION_STATS |
1826 | (spin_lock->num_disable_preemptive_w)++; |
1827 | #endif //SYNCHRONIZATION_STATS |
1828 | GCToEEInterface::DisablePreemptiveGC(); |
1829 | } |
1830 | } |
1831 | |
1832 | inline |
1833 | static void enter_spin_lock (GCSpinLock* spin_lock) |
1834 | { |
1835 | retry: |
1836 | |
1837 | if (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) >= 0) |
1838 | { |
1839 | unsigned int i = 0; |
1840 | while (spin_lock->lock >= 0) |
1841 | { |
1842 | if ((++i & 7) && !gc_heap::gc_started) |
1843 | { |
1844 | if (g_num_processors > 1) |
1845 | { |
1846 | #ifndef MULTIPLE_HEAPS |
1847 | int spin_count = 32 * yp_spin_count_unit; |
1848 | #else //!MULTIPLE_HEAPS |
1849 | int spin_count = yp_spin_count_unit; |
1850 | #endif //!MULTIPLE_HEAPS |
1851 | for (int j = 0; j < spin_count; j++) |
1852 | { |
1853 | if (spin_lock->lock < 0 || gc_heap::gc_started) |
1854 | break; |
1855 | YieldProcessor(); // indicate to the processor that we are spining |
1856 | } |
1857 | if (spin_lock->lock >= 0 && !gc_heap::gc_started) |
1858 | { |
1859 | #ifdef SYNCHRONIZATION_STATS |
1860 | (spin_lock->num_switch_thread)++; |
1861 | #endif //SYNCHRONIZATION_STATS |
1862 | bool cooperative_mode = gc_heap::enable_preemptive (); |
1863 | |
1864 | GCToOSInterface::YieldThread(0); |
1865 | |
1866 | gc_heap::disable_preemptive (cooperative_mode); |
1867 | } |
1868 | } |
1869 | else |
1870 | GCToOSInterface::YieldThread(0); |
1871 | } |
1872 | else |
1873 | { |
1874 | WaitLonger(i |
1875 | #ifdef SYNCHRONIZATION_STATS |
1876 | , spin_lock |
1877 | #endif //SYNCHRONIZATION_STATS |
1878 | ); |
1879 | } |
1880 | } |
1881 | goto retry; |
1882 | } |
1883 | } |
1884 | |
1885 | inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock) |
1886 | { |
1887 | return (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) < 0); |
1888 | } |
1889 | |
1890 | inline |
1891 | static void leave_spin_lock (GCSpinLock * spin_lock) |
1892 | { |
1893 | spin_lock->lock = -1; |
1894 | } |
1895 | |
1896 | #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) |
1897 | |
1898 | #endif //_DEBUG |
1899 | |
1900 | bool gc_heap::enable_preemptive () |
1901 | { |
1902 | return GCToEEInterface::EnablePreemptiveGC(); |
1903 | } |
1904 | |
1905 | void gc_heap::disable_preemptive (bool restore_cooperative) |
1906 | { |
1907 | if (restore_cooperative) |
1908 | { |
1909 | GCToEEInterface::DisablePreemptiveGC(); |
1910 | } |
1911 | } |
1912 | |
1913 | #endif // !DACCESS_COMPILE |
1914 | |
1915 | typedef void ** PTR_PTR; |
1916 | //This function clears a piece of memory |
1917 | // size has to be Dword aligned |
1918 | |
1919 | inline |
1920 | void memclr ( uint8_t* mem, size_t size) |
1921 | { |
1922 | dprintf (3, ("MEMCLR: %Ix, %d" , mem, size)); |
1923 | assert ((size & (sizeof(PTR_PTR)-1)) == 0); |
1924 | assert (sizeof(PTR_PTR) == DATA_ALIGNMENT); |
1925 | |
1926 | #if 0 |
1927 | // The compiler will recognize this pattern and replace it with memset call. We can as well just call |
1928 | // memset directly to make it obvious what's going on. |
1929 | PTR_PTR m = (PTR_PTR) mem; |
1930 | for (size_t i = 0; i < size / sizeof(PTR_PTR); i++) |
1931 | *(m++) = 0; |
1932 | #endif |
1933 | |
1934 | memset (mem, 0, size); |
1935 | } |
1936 | |
1937 | void memcopy (uint8_t* dmem, uint8_t* smem, size_t size) |
1938 | { |
1939 | const size_t sz4ptr = sizeof(PTR_PTR)*4; |
1940 | const size_t sz2ptr = sizeof(PTR_PTR)*2; |
1941 | const size_t sz1ptr = sizeof(PTR_PTR)*1; |
1942 | |
1943 | // size must be a multiple of the pointer size |
1944 | assert ((size & (sizeof (PTR_PTR)-1)) == 0); |
1945 | assert (sizeof(PTR_PTR) == DATA_ALIGNMENT); |
1946 | |
1947 | // copy in groups of four pointer sized things at a time |
1948 | if (size >= sz4ptr) |
1949 | { |
1950 | do |
1951 | { |
1952 | ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0]; |
1953 | ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1]; |
1954 | ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2]; |
1955 | ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3]; |
1956 | dmem += sz4ptr; |
1957 | smem += sz4ptr; |
1958 | } |
1959 | while ((size -= sz4ptr) >= sz4ptr); |
1960 | } |
1961 | |
1962 | // still two pointer sized things or more left to copy? |
1963 | if (size & sz2ptr) |
1964 | { |
1965 | ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0]; |
1966 | ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1]; |
1967 | dmem += sz2ptr; |
1968 | smem += sz2ptr; |
1969 | } |
1970 | |
1971 | // still one pointer sized thing left to copy? |
1972 | if (size & sz1ptr) |
1973 | { |
1974 | ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0]; |
1975 | // dmem += sz1ptr; |
1976 | // smem += sz1ptr; |
1977 | } |
1978 | |
1979 | } |
1980 | |
1981 | inline |
1982 | ptrdiff_t round_down (ptrdiff_t add, int pitch) |
1983 | { |
1984 | return ((add / pitch) * pitch); |
1985 | } |
1986 | |
1987 | #if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT) |
1988 | // FEATURE_STRUCTALIGN allows the compiler to dictate the alignment, |
1989 | // i.e, if a larger alignment matters or is beneficial, the compiler |
1990 | // generated info tells us so. RESPECT_LARGE_ALIGNMENT is just the |
1991 | // converse - it's a heuristic for the GC to use a larger alignment. |
1992 | #error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT |
1993 | #endif |
1994 | |
1995 | #if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION) |
1996 | #error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive |
1997 | #endif |
1998 | |
1999 | #if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE) |
2000 | #error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined |
2001 | #endif |
2002 | |
2003 | inline |
2004 | BOOL same_large_alignment_p (uint8_t* p1, uint8_t* p2) |
2005 | { |
2006 | #ifdef RESPECT_LARGE_ALIGNMENT |
2007 | return ((((size_t)p1 ^ (size_t)p2) & 7) == 0); |
2008 | #else |
2009 | UNREFERENCED_PARAMETER(p1); |
2010 | UNREFERENCED_PARAMETER(p2); |
2011 | return TRUE; |
2012 | #endif //RESPECT_LARGE_ALIGNMENT |
2013 | } |
2014 | |
2015 | inline |
2016 | size_t switch_alignment_size (BOOL already_padded_p) |
2017 | { |
2018 | if (already_padded_p) |
2019 | return DATA_ALIGNMENT; |
2020 | else |
2021 | return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT)); |
2022 | } |
2023 | |
2024 | |
2025 | #ifdef FEATURE_STRUCTALIGN |
2026 | void set_node_aligninfo (uint8_t *node, int requiredAlignment, ptrdiff_t pad); |
2027 | void clear_node_aligninfo (uint8_t *node); |
2028 | #else // FEATURE_STRUCTALIGN |
2029 | #define node_realigned(node) (((plug_and_reloc*)(node))[-1].reloc & 1) |
2030 | void set_node_realigned (uint8_t* node); |
2031 | void clear_node_realigned(uint8_t* node); |
2032 | #endif // FEATURE_STRUCTALIGN |
2033 | |
2034 | inline |
2035 | size_t AlignQword (size_t nbytes) |
2036 | { |
2037 | #ifdef FEATURE_STRUCTALIGN |
2038 | // This function is used to align everything on the large object |
2039 | // heap to an 8-byte boundary, to reduce the number of unaligned |
2040 | // accesses to (say) arrays of doubles. With FEATURE_STRUCTALIGN, |
2041 | // the compiler dictates the optimal alignment instead of having |
2042 | // a heuristic in the GC. |
2043 | return Align (nbytes); |
2044 | #else // FEATURE_STRUCTALIGN |
2045 | return (nbytes + 7) & ~7; |
2046 | #endif // FEATURE_STRUCTALIGN |
2047 | } |
2048 | |
2049 | inline |
2050 | BOOL Aligned (size_t n) |
2051 | { |
2052 | return (n & ALIGNCONST) == 0; |
2053 | } |
2054 | |
2055 | #define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *)) |
2056 | |
2057 | #ifdef FEATURE_STRUCTALIGN |
2058 | #define MAX_STRUCTALIGN OS_PAGE_SIZE |
2059 | #else // FEATURE_STRUCTALIGN |
2060 | #define MAX_STRUCTALIGN 0 |
2061 | #endif // FEATURE_STRUCTALIGN |
2062 | |
2063 | #ifdef FEATURE_STRUCTALIGN |
2064 | inline |
2065 | ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment) |
2066 | { |
2067 | // The resulting alignpad must be either 0 or at least min_obj_size. |
2068 | // Note that by computing the following difference on unsigned types, |
2069 | // we can do the range check 0 < alignpad < min_obj_size with a |
2070 | // single conditional branch. |
2071 | if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT) |
2072 | { |
2073 | return requiredAlignment; |
2074 | } |
2075 | return 0; |
2076 | } |
2077 | |
2078 | inline |
2079 | uint8_t* StructAlign (uint8_t* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET) |
2080 | { |
2081 | // required alignment must be a power of two |
2082 | _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0); |
2083 | _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0); |
2084 | _ASSERTE(requiredAlignment >= sizeof(void *)); |
2085 | _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN); |
2086 | |
2087 | // When this method is invoked for individual objects (i.e., alignmentOffset |
2088 | // is just the size of the PostHeader), what needs to be aligned when |
2089 | // we're done is the pointer to the payload of the object (which means |
2090 | // the actual resulting object pointer is typically not aligned). |
2091 | |
2092 | uint8_t* result = (uint8_t*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset; |
2093 | ptrdiff_t alignpad = result - origPtr; |
2094 | |
2095 | return result + AdjustmentForMinPadSize (alignpad, requiredAlignment); |
2096 | } |
2097 | |
2098 | inline |
2099 | ptrdiff_t ComputeStructAlignPad (uint8_t* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET) |
2100 | { |
2101 | return StructAlign (plug, requiredAlignment, alignmentOffset) - plug; |
2102 | } |
2103 | |
2104 | BOOL IsStructAligned (uint8_t *ptr, int requiredAlignment) |
2105 | { |
2106 | return StructAlign (ptr, requiredAlignment) == ptr; |
2107 | } |
2108 | |
2109 | inline |
2110 | ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment) |
2111 | { |
2112 | if (requiredAlignment == DATA_ALIGNMENT) |
2113 | return 0; |
2114 | // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the |
2115 | // alignment padding object), the worst-case alignment padding is correspondingly larger |
2116 | // than the required alignment. |
2117 | return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT; |
2118 | } |
2119 | |
2120 | inline |
2121 | ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment) |
2122 | { |
2123 | if (requiredAlignment <= get_alignment_constant (TRUE)+1) |
2124 | return 0; |
2125 | // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space |
2126 | // for padding before the actual object, it also leaves space for filling a gap after the |
2127 | // actual object. This is needed on the large object heap, as the outer allocation functions |
2128 | // don't operate on an allocation context (which would have left space for the final gap). |
2129 | return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT; |
2130 | } |
2131 | |
2132 | uint8_t* gc_heap::pad_for_alignment (uint8_t* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext) |
2133 | { |
2134 | uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment); |
2135 | if (alignedPtr != newAlloc) { |
2136 | make_unused_array (newAlloc, alignedPtr - newAlloc); |
2137 | } |
2138 | acontext->alloc_ptr = alignedPtr + Align (size); |
2139 | return alignedPtr; |
2140 | } |
2141 | |
2142 | uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignment, size_t size) |
2143 | { |
2144 | uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment); |
2145 | if (alignedPtr != newAlloc) { |
2146 | make_unused_array (newAlloc, alignedPtr - newAlloc); |
2147 | } |
2148 | if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) { |
2149 | make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr); |
2150 | } |
2151 | return alignedPtr; |
2152 | } |
2153 | #else // FEATURE_STRUCTALIGN |
2154 | #define ComputeMaxStructAlignPad(requiredAlignment) 0 |
2155 | #define ComputeMaxStructAlignPadLarge(requiredAlignment) 0 |
2156 | #endif // FEATURE_STRUCTALIGN |
2157 | |
2158 | //CLR_SIZE is the max amount of bytes from gen0 that is set to 0 in one chunk |
2159 | #ifdef SERVER_GC |
2160 | #define CLR_SIZE ((size_t)(8*1024)) |
2161 | #else //SERVER_GC |
2162 | #define CLR_SIZE ((size_t)(8*1024)) |
2163 | #endif //SERVER_GC |
2164 | |
2165 | #define END_SPACE_AFTER_GC (loh_size_threshold + MAX_STRUCTALIGN) |
2166 | |
2167 | #ifdef BACKGROUND_GC |
2168 | #define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE) |
2169 | #else |
2170 | #define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE) |
2171 | #endif //BACKGROUND_GC |
2172 | |
2173 | #ifdef SERVER_GC |
2174 | |
2175 | #ifdef BIT64 |
2176 | |
2177 | #define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024)) |
2178 | #define LHEAP_ALLOC ((size_t)(1024*1024*256)) |
2179 | |
2180 | #else |
2181 | |
2182 | #define INITIAL_ALLOC ((size_t)(1024*1024*64)) |
2183 | #define LHEAP_ALLOC ((size_t)(1024*1024*32)) |
2184 | |
2185 | #endif // BIT64 |
2186 | |
2187 | #else //SERVER_GC |
2188 | |
2189 | #ifdef BIT64 |
2190 | |
2191 | #define INITIAL_ALLOC ((size_t)(1024*1024*256)) |
2192 | #define LHEAP_ALLOC ((size_t)(1024*1024*128)) |
2193 | |
2194 | #else |
2195 | |
2196 | #define INITIAL_ALLOC ((size_t)(1024*1024*16)) |
2197 | #define LHEAP_ALLOC ((size_t)(1024*1024*16)) |
2198 | |
2199 | #endif // BIT64 |
2200 | |
2201 | #endif //SERVER_GC |
2202 | |
2203 | //amount in bytes of the etw allocation tick |
2204 | const size_t etw_allocation_tick = 100*1024; |
2205 | |
2206 | const size_t low_latency_alloc = 256*1024; |
2207 | |
2208 | const size_t fgn_check_quantum = 2*1024*1024; |
2209 | |
2210 | #ifdef MH_SC_MARK |
2211 | const int max_snoop_level = 128; |
2212 | #endif //MH_SC_MARK |
2213 | |
2214 | |
2215 | #ifdef CARD_BUNDLE |
2216 | //threshold of heap size to turn on card bundles. |
2217 | #define SH_TH_CARD_BUNDLE (40*1024*1024) |
2218 | #define MH_TH_CARD_BUNDLE (180*1024*1024) |
2219 | #endif //CARD_BUNDLE |
2220 | |
2221 | #define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000 |
2222 | |
2223 | inline |
2224 | size_t align_on_page (size_t add) |
2225 | { |
2226 | return ((add + OS_PAGE_SIZE - 1) & ~((size_t)OS_PAGE_SIZE - 1)); |
2227 | } |
2228 | |
2229 | inline |
2230 | uint8_t* align_on_page (uint8_t* add) |
2231 | { |
2232 | return (uint8_t*)align_on_page ((size_t) add); |
2233 | } |
2234 | |
2235 | inline |
2236 | size_t align_lower_page (size_t add) |
2237 | { |
2238 | return (add & ~((size_t)OS_PAGE_SIZE - 1)); |
2239 | } |
2240 | |
2241 | inline |
2242 | uint8_t* align_lower_page (uint8_t* add) |
2243 | { |
2244 | return (uint8_t*)align_lower_page ((size_t)add); |
2245 | } |
2246 | |
2247 | inline |
2248 | size_t align_write_watch_lower_page (size_t add) |
2249 | { |
2250 | return (add & ~(WRITE_WATCH_UNIT_SIZE - 1)); |
2251 | } |
2252 | |
2253 | inline |
2254 | uint8_t* align_write_watch_lower_page (uint8_t* add) |
2255 | { |
2256 | return (uint8_t*)align_lower_page ((size_t)add); |
2257 | } |
2258 | |
2259 | |
2260 | inline |
2261 | BOOL power_of_two_p (size_t integer) |
2262 | { |
2263 | return !(integer & (integer-1)); |
2264 | } |
2265 | |
2266 | inline |
2267 | BOOL oddp (size_t integer) |
2268 | { |
2269 | return (integer & 1) != 0; |
2270 | } |
2271 | |
2272 | // we only ever use this for WORDs. |
2273 | size_t logcount (size_t word) |
2274 | { |
2275 | //counts the number of high bits in a 16 bit word. |
2276 | assert (word < 0x10000); |
2277 | size_t count; |
2278 | count = (word & 0x5555) + ( (word >> 1 ) & 0x5555); |
2279 | count = (count & 0x3333) + ( (count >> 2) & 0x3333); |
2280 | count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F); |
2281 | count = (count & 0x00FF) + ( (count >> 8) & 0x00FF); |
2282 | return count; |
2283 | } |
2284 | |
2285 | #ifndef DACCESS_COMPILE |
2286 | |
2287 | void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check) |
2288 | { |
2289 | WriteBarrierParameters args = {}; |
2290 | args.operation = WriteBarrierOp::StompResize; |
2291 | args.is_runtime_suspended = is_runtime_suspended; |
2292 | args.requires_upper_bounds_check = requires_upper_bounds_check; |
2293 | |
2294 | args.card_table = g_gc_card_table; |
2295 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
2296 | args.card_bundle_table = g_gc_card_bundle_table; |
2297 | #endif |
2298 | |
2299 | args.lowest_address = g_gc_lowest_address; |
2300 | args.highest_address = g_gc_highest_address; |
2301 | |
2302 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
2303 | if (SoftwareWriteWatch::IsEnabledForGCHeap()) |
2304 | { |
2305 | args.write_watch_table = g_gc_sw_ww_table; |
2306 | } |
2307 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
2308 | |
2309 | GCToEEInterface::StompWriteBarrier(&args); |
2310 | } |
2311 | |
2312 | void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high) |
2313 | { |
2314 | WriteBarrierParameters args = {}; |
2315 | args.operation = WriteBarrierOp::StompEphemeral; |
2316 | args.is_runtime_suspended = true; |
2317 | args.ephemeral_low = ephemeral_low; |
2318 | args.ephemeral_high = ephemeral_high; |
2319 | GCToEEInterface::StompWriteBarrier(&args); |
2320 | } |
2321 | |
2322 | void stomp_write_barrier_initialize(uint8_t* ephemeral_low, uint8_t* ephemeral_high) |
2323 | { |
2324 | WriteBarrierParameters args = {}; |
2325 | args.operation = WriteBarrierOp::Initialize; |
2326 | args.is_runtime_suspended = true; |
2327 | args.requires_upper_bounds_check = false; |
2328 | args.card_table = g_gc_card_table; |
2329 | |
2330 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
2331 | args.card_bundle_table = g_gc_card_bundle_table; |
2332 | #endif |
2333 | |
2334 | args.lowest_address = g_gc_lowest_address; |
2335 | args.highest_address = g_gc_highest_address; |
2336 | args.ephemeral_low = ephemeral_low; |
2337 | args.ephemeral_high = ephemeral_high; |
2338 | GCToEEInterface::StompWriteBarrier(&args); |
2339 | } |
2340 | |
2341 | #endif // DACCESS_COMPILE |
2342 | |
2343 | //extract the low bits [0,low[ of a uint32_t |
2344 | #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1)) |
2345 | //extract the high bits [high, 32] of a uint32_t |
2346 | #define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1)) |
2347 | |
2348 | // Things we need to manually initialize: |
2349 | // gen0 min_size - based on cache |
2350 | // gen0/1 max_size - based on segment size |
2351 | static static_data static_data_table[latency_level_last - latency_level_first + 1][NUMBERGENERATIONS] = |
2352 | { |
2353 | // latency_level_memory_footprint |
2354 | { |
2355 | // gen0 |
2356 | {0, 0, 40000, 0.5f, 9.0f, 20.0f, 1000, 1}, |
2357 | // gen1 |
2358 | {163840, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10}, |
2359 | // gen2 |
2360 | {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100}, |
2361 | // gen3 |
2362 | {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0} |
2363 | }, |
2364 | |
2365 | // latency_level_balanced |
2366 | { |
2367 | // gen0 |
2368 | {0, 0, 40000, 0.5f, |
2369 | #ifdef MULTIPLE_HEAPS |
2370 | 20.0f, 40.0f, |
2371 | #else |
2372 | 9.0f, 20.0f, |
2373 | #endif //MULTIPLE_HEAPS |
2374 | 1000, 1}, |
2375 | // gen1 |
2376 | {9*32*1024, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10}, |
2377 | // gen2 |
2378 | {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100}, |
2379 | // gen3 |
2380 | {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0} |
2381 | }, |
2382 | }; |
2383 | |
2384 | class mark; |
2385 | class generation; |
2386 | class heap_segment; |
2387 | class CObjectHeader; |
2388 | class dynamic_data; |
2389 | class l_heap; |
2390 | class sorted_table; |
2391 | class c_synchronize; |
2392 | |
2393 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
2394 | #ifndef DACCESS_COMPILE |
2395 | static |
2396 | HRESULT AllocateCFinalize(CFinalize **pCFinalize); |
2397 | #endif //!DACCESS_COMPILE |
2398 | #endif // FEATURE_PREMORTEM_FINALIZATION |
2399 | |
2400 | uint8_t* tree_search (uint8_t* tree, uint8_t* old_address); |
2401 | |
2402 | |
2403 | #ifdef USE_INTROSORT |
2404 | #define _sort introsort::sort |
2405 | #else //USE_INTROSORT |
2406 | #define _sort qsort1 |
2407 | void qsort1(uint8_t** low, uint8_t** high, unsigned int depth); |
2408 | #endif //USE_INTROSORT |
2409 | |
2410 | void* virtual_alloc (size_t size); |
2411 | void virtual_free (void* add, size_t size); |
2412 | |
2413 | /* per heap static initialization */ |
2414 | #ifdef MARK_ARRAY |
2415 | #ifndef MULTIPLE_HEAPS |
2416 | uint32_t* gc_heap::mark_array; |
2417 | #endif //MULTIPLE_HEAPS |
2418 | #endif //MARK_ARRAY |
2419 | |
2420 | #ifdef MARK_LIST |
2421 | uint8_t** gc_heap::g_mark_list; |
2422 | |
2423 | #ifdef PARALLEL_MARK_LIST_SORT |
2424 | uint8_t** gc_heap::g_mark_list_copy; |
2425 | #endif //PARALLEL_MARK_LIST_SORT |
2426 | |
2427 | size_t gc_heap::mark_list_size; |
2428 | #endif //MARK_LIST |
2429 | |
2430 | #ifdef SEG_MAPPING_TABLE |
2431 | seg_mapping* seg_mapping_table; |
2432 | #endif //SEG_MAPPING_TABLE |
2433 | |
2434 | #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE) |
2435 | sorted_table* gc_heap::seg_table; |
2436 | #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE |
2437 | |
2438 | #ifdef MULTIPLE_HEAPS |
2439 | GCEvent gc_heap::ee_suspend_event; |
2440 | size_t gc_heap::min_balance_threshold = 0; |
2441 | #endif //MULTIPLE_HEAPS |
2442 | |
2443 | VOLATILE(BOOL) gc_heap::gc_started; |
2444 | |
2445 | #ifdef MULTIPLE_HEAPS |
2446 | |
2447 | GCEvent gc_heap::gc_start_event; |
2448 | bool gc_heap::gc_thread_no_affinitize_p = false; |
2449 | uintptr_t process_mask = 0; |
2450 | |
2451 | int gc_heap::n_heaps; |
2452 | |
2453 | gc_heap** gc_heap::g_heaps; |
2454 | |
2455 | size_t* gc_heap::g_promoted; |
2456 | |
2457 | #ifdef MH_SC_MARK |
2458 | int* gc_heap::g_mark_stack_busy; |
2459 | #endif //MH_SC_MARK |
2460 | |
2461 | |
2462 | #ifdef BACKGROUND_GC |
2463 | size_t* gc_heap::g_bpromoted; |
2464 | #endif //BACKGROUND_GC |
2465 | |
2466 | #else //MULTIPLE_HEAPS |
2467 | |
2468 | size_t gc_heap::g_promoted; |
2469 | |
2470 | #ifdef BACKGROUND_GC |
2471 | size_t gc_heap::g_bpromoted; |
2472 | #endif //BACKGROUND_GC |
2473 | |
2474 | #endif //MULTIPLE_HEAPS |
2475 | |
2476 | size_t gc_heap::reserved_memory = 0; |
2477 | size_t gc_heap::reserved_memory_limit = 0; |
2478 | BOOL gc_heap::g_low_memory_status; |
2479 | |
2480 | #ifndef DACCESS_COMPILE |
2481 | static gc_reason gc_trigger_reason = reason_empty; |
2482 | #endif //DACCESS_COMPILE |
2483 | |
2484 | gc_latency_level gc_heap::latency_level = latency_level_default; |
2485 | |
2486 | gc_mechanisms gc_heap::settings; |
2487 | |
2488 | gc_history_global gc_heap::gc_data_global; |
2489 | |
2490 | size_t gc_heap::gc_last_ephemeral_decommit_time = 0; |
2491 | |
2492 | size_t gc_heap::gc_gen0_desired_high; |
2493 | |
2494 | #ifdef SHORT_PLUGS |
2495 | double gc_heap::short_plugs_pad_ratio = 0; |
2496 | #endif //SHORT_PLUGS |
2497 | |
2498 | #if defined(BIT64) |
2499 | #define MAX_ALLOWED_MEM_LOAD 85 |
2500 | |
2501 | // consider putting this in dynamic data - |
2502 | // we may want different values for workstation |
2503 | // and server GC. |
2504 | #define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024) |
2505 | |
2506 | size_t gc_heap::youngest_gen_desired_th; |
2507 | #endif //BIT64 |
2508 | |
2509 | uint32_t gc_heap::last_gc_memory_load = 0; |
2510 | |
2511 | size_t gc_heap::last_gc_heap_size = 0; |
2512 | |
2513 | size_t gc_heap::last_gc_fragmentation = 0; |
2514 | |
2515 | uint64_t gc_heap::mem_one_percent = 0; |
2516 | |
2517 | uint32_t gc_heap::high_memory_load_th = 0; |
2518 | |
2519 | uint32_t gc_heap::m_high_memory_load_th; |
2520 | |
2521 | uint32_t gc_heap::v_high_memory_load_th; |
2522 | |
2523 | uint64_t gc_heap::total_physical_mem = 0; |
2524 | |
2525 | uint64_t gc_heap::entry_available_physical_mem = 0; |
2526 | |
2527 | #ifdef BACKGROUND_GC |
2528 | GCEvent gc_heap::bgc_start_event; |
2529 | |
2530 | gc_mechanisms gc_heap::saved_bgc_settings; |
2531 | |
2532 | GCEvent gc_heap::background_gc_done_event; |
2533 | |
2534 | GCEvent gc_heap::ee_proceed_event; |
2535 | |
2536 | bool gc_heap::gc_can_use_concurrent = false; |
2537 | |
2538 | bool gc_heap::temp_disable_concurrent_p = false; |
2539 | |
2540 | uint32_t gc_heap::cm_in_progress = FALSE; |
2541 | |
2542 | BOOL gc_heap::dont_restart_ee_p = FALSE; |
2543 | |
2544 | BOOL gc_heap::keep_bgc_threads_p = FALSE; |
2545 | |
2546 | GCEvent gc_heap::bgc_threads_sync_event; |
2547 | |
2548 | BOOL gc_heap::do_ephemeral_gc_p = FALSE; |
2549 | |
2550 | BOOL gc_heap::do_concurrent_p = FALSE; |
2551 | |
2552 | size_t gc_heap::ephemeral_fgc_counts[max_generation]; |
2553 | |
2554 | BOOL gc_heap::alloc_wait_event_p = FALSE; |
2555 | |
2556 | VOLATILE(c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free; |
2557 | |
2558 | #endif //BACKGROUND_GC |
2559 | |
2560 | #ifndef MULTIPLE_HEAPS |
2561 | #ifdef SPINLOCK_HISTORY |
2562 | int gc_heap::spinlock_info_index = 0; |
2563 | spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8]; |
2564 | #endif //SPINLOCK_HISTORY |
2565 | |
2566 | size_t gc_heap::fgn_last_alloc = 0; |
2567 | |
2568 | int gc_heap::generation_skip_ratio = 100; |
2569 | |
2570 | uint64_t gc_heap::loh_alloc_since_cg = 0; |
2571 | |
2572 | BOOL gc_heap::elevation_requested = FALSE; |
2573 | |
2574 | BOOL gc_heap::last_gc_before_oom = FALSE; |
2575 | |
2576 | BOOL gc_heap::sufficient_gen0_space_p = FALSE; |
2577 | |
2578 | #ifdef BACKGROUND_GC |
2579 | uint8_t* gc_heap::background_saved_lowest_address = 0; |
2580 | uint8_t* gc_heap::background_saved_highest_address = 0; |
2581 | uint8_t* gc_heap::next_sweep_obj = 0; |
2582 | uint8_t* gc_heap::current_sweep_pos = 0; |
2583 | exclusive_sync* gc_heap::bgc_alloc_lock; |
2584 | #endif //BACKGROUND_GC |
2585 | |
2586 | oom_history gc_heap::oom_info; |
2587 | |
2588 | fgm_history gc_heap::fgm_result; |
2589 | |
2590 | BOOL gc_heap::ro_segments_in_range; |
2591 | |
2592 | size_t gc_heap::gen0_big_free_spaces = 0; |
2593 | |
2594 | uint8_t* gc_heap::ephemeral_low; |
2595 | |
2596 | uint8_t* gc_heap::ephemeral_high; |
2597 | |
2598 | uint8_t* gc_heap::lowest_address; |
2599 | |
2600 | uint8_t* gc_heap::highest_address; |
2601 | |
2602 | BOOL gc_heap::ephemeral_promotion; |
2603 | |
2604 | uint8_t* gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1]; |
2605 | size_t gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1]; |
2606 | |
2607 | short* gc_heap::brick_table; |
2608 | |
2609 | uint32_t* gc_heap::card_table; |
2610 | |
2611 | #ifdef CARD_BUNDLE |
2612 | uint32_t* gc_heap::card_bundle_table; |
2613 | #endif //CARD_BUNDLE |
2614 | |
2615 | uint8_t* gc_heap::gc_low; |
2616 | |
2617 | uint8_t* gc_heap::gc_high; |
2618 | |
2619 | uint8_t* gc_heap::demotion_low; |
2620 | |
2621 | uint8_t* gc_heap::demotion_high; |
2622 | |
2623 | BOOL gc_heap::demote_gen1_p = TRUE; |
2624 | |
2625 | uint8_t* gc_heap::last_gen1_pin_end; |
2626 | |
2627 | gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons; |
2628 | |
2629 | size_t gc_heap::etw_allocation_running_amount[2]; |
2630 | |
2631 | int gc_heap::gc_policy = 0; |
2632 | |
2633 | size_t gc_heap::allocation_running_time; |
2634 | |
2635 | size_t gc_heap::allocation_running_amount; |
2636 | |
2637 | heap_segment* gc_heap::ephemeral_heap_segment = 0; |
2638 | |
2639 | BOOL gc_heap::blocking_collection = FALSE; |
2640 | |
2641 | heap_segment* gc_heap::freeable_large_heap_segment = 0; |
2642 | |
2643 | size_t gc_heap::time_bgc_last = 0; |
2644 | |
2645 | size_t gc_heap::mark_stack_tos = 0; |
2646 | |
2647 | size_t gc_heap::mark_stack_bos = 0; |
2648 | |
2649 | size_t gc_heap::mark_stack_array_length = 0; |
2650 | |
2651 | mark* gc_heap::mark_stack_array = 0; |
2652 | |
2653 | #if defined (_DEBUG) && defined (VERIFY_HEAP) |
2654 | BOOL gc_heap::verify_pinned_queue_p = FALSE; |
2655 | #endif // defined (_DEBUG) && defined (VERIFY_HEAP) |
2656 | |
2657 | uint8_t* gc_heap::oldest_pinned_plug = 0; |
2658 | |
2659 | #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE) |
2660 | size_t gc_heap::num_pinned_objects = 0; |
2661 | #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE |
2662 | |
2663 | #ifdef FEATURE_LOH_COMPACTION |
2664 | size_t gc_heap::loh_pinned_queue_tos = 0; |
2665 | |
2666 | size_t gc_heap::loh_pinned_queue_bos = 0; |
2667 | |
2668 | size_t gc_heap::loh_pinned_queue_length = 0; |
2669 | |
2670 | mark* gc_heap::loh_pinned_queue = 0; |
2671 | |
2672 | BOOL gc_heap::loh_compacted_p = FALSE; |
2673 | #endif //FEATURE_LOH_COMPACTION |
2674 | |
2675 | #ifdef BACKGROUND_GC |
2676 | |
2677 | EEThreadId gc_heap::bgc_thread_id; |
2678 | |
2679 | uint8_t* gc_heap::background_written_addresses [array_size+2]; |
2680 | |
2681 | heap_segment* gc_heap::freeable_small_heap_segment = 0; |
2682 | |
2683 | size_t gc_heap::bgc_overflow_count = 0; |
2684 | |
2685 | size_t gc_heap::bgc_begin_loh_size = 0; |
2686 | size_t gc_heap::end_loh_size = 0; |
2687 | |
2688 | uint32_t gc_heap::bgc_alloc_spin_loh = 0; |
2689 | |
2690 | size_t gc_heap::bgc_loh_size_increased = 0; |
2691 | |
2692 | size_t gc_heap::bgc_loh_allocated_in_free = 0; |
2693 | |
2694 | size_t gc_heap::background_soh_alloc_count = 0; |
2695 | |
2696 | size_t gc_heap::background_loh_alloc_count = 0; |
2697 | |
2698 | uint8_t** gc_heap::background_mark_stack_tos = 0; |
2699 | |
2700 | uint8_t** gc_heap::background_mark_stack_array = 0; |
2701 | |
2702 | size_t gc_heap::background_mark_stack_array_length = 0; |
2703 | |
2704 | uint8_t* gc_heap::background_min_overflow_address =0; |
2705 | |
2706 | uint8_t* gc_heap::background_max_overflow_address =0; |
2707 | |
2708 | BOOL gc_heap::processed_soh_overflow_p = FALSE; |
2709 | |
2710 | uint8_t* gc_heap::background_min_soh_overflow_address =0; |
2711 | |
2712 | uint8_t* gc_heap::background_max_soh_overflow_address =0; |
2713 | |
2714 | heap_segment* gc_heap::saved_sweep_ephemeral_seg = 0; |
2715 | |
2716 | uint8_t* gc_heap::saved_sweep_ephemeral_start = 0; |
2717 | |
2718 | heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0; |
2719 | |
2720 | Thread* gc_heap::bgc_thread = 0; |
2721 | |
2722 | BOOL gc_heap::expanded_in_fgc = FALSE; |
2723 | |
2724 | uint8_t** gc_heap::c_mark_list = 0; |
2725 | |
2726 | size_t gc_heap::c_mark_list_length = 0; |
2727 | |
2728 | size_t gc_heap::c_mark_list_index = 0; |
2729 | |
2730 | gc_history_per_heap gc_heap::bgc_data_per_heap; |
2731 | |
2732 | BOOL gc_heap::bgc_thread_running; |
2733 | |
2734 | CLRCriticalSection gc_heap::bgc_threads_timeout_cs; |
2735 | |
2736 | GCEvent gc_heap::gc_lh_block_event; |
2737 | |
2738 | #endif //BACKGROUND_GC |
2739 | |
2740 | #ifdef MARK_LIST |
2741 | uint8_t** gc_heap::mark_list; |
2742 | uint8_t** gc_heap::mark_list_index; |
2743 | uint8_t** gc_heap::mark_list_end; |
2744 | #endif //MARK_LIST |
2745 | |
2746 | #ifdef SNOOP_STATS |
2747 | snoop_stats_data gc_heap::snoop_stat; |
2748 | #endif //SNOOP_STATS |
2749 | |
2750 | uint8_t* gc_heap::min_overflow_address = MAX_PTR; |
2751 | |
2752 | uint8_t* gc_heap::max_overflow_address = 0; |
2753 | |
2754 | uint8_t* gc_heap::shigh = 0; |
2755 | |
2756 | uint8_t* gc_heap::slow = MAX_PTR; |
2757 | |
2758 | size_t gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS]; |
2759 | |
2760 | size_t gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS]; |
2761 | |
2762 | size_t gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS]; |
2763 | |
2764 | size_t gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS]; |
2765 | |
2766 | BOOL gc_heap::ordered_plug_indices_init = FALSE; |
2767 | |
2768 | BOOL gc_heap::use_bestfit = FALSE; |
2769 | |
2770 | uint8_t* gc_heap::bestfit_first_pin = 0; |
2771 | |
2772 | BOOL gc_heap::commit_end_of_seg = FALSE; |
2773 | |
2774 | size_t gc_heap::max_free_space_items = 0; |
2775 | |
2776 | size_t gc_heap::free_space_buckets = 0; |
2777 | |
2778 | size_t gc_heap::free_space_items = 0; |
2779 | |
2780 | int gc_heap::trimmed_free_space_index = 0; |
2781 | |
2782 | size_t gc_heap::total_ephemeral_plugs = 0; |
2783 | |
2784 | seg_free_spaces* gc_heap::bestfit_seg = 0; |
2785 | |
2786 | size_t gc_heap::total_ephemeral_size = 0; |
2787 | |
2788 | #ifdef HEAP_ANALYZE |
2789 | |
2790 | size_t gc_heap::internal_root_array_length = initial_internal_roots; |
2791 | |
2792 | uint8_t** gc_heap::internal_root_array = 0; |
2793 | |
2794 | size_t gc_heap::internal_root_array_index = 0; |
2795 | |
2796 | BOOL gc_heap::heap_analyze_success = TRUE; |
2797 | |
2798 | uint8_t* gc_heap::current_obj = 0; |
2799 | size_t gc_heap::current_obj_size = 0; |
2800 | |
2801 | #endif //HEAP_ANALYZE |
2802 | |
2803 | #ifdef GC_CONFIG_DRIVEN |
2804 | size_t gc_heap::interesting_data_per_gc[max_idp_count]; |
2805 | //size_t gc_heap::interesting_data_per_heap[max_idp_count]; |
2806 | //size_t gc_heap::interesting_mechanisms_per_heap[max_im_count]; |
2807 | #endif //GC_CONFIG_DRIVEN |
2808 | #endif //MULTIPLE_HEAPS |
2809 | |
2810 | no_gc_region_info gc_heap::current_no_gc_region_info; |
2811 | BOOL gc_heap::proceed_with_gc_p = FALSE; |
2812 | GCSpinLock gc_heap::gc_lock; |
2813 | |
2814 | size_t gc_heap::eph_gen_starts_size = 0; |
2815 | heap_segment* gc_heap::segment_standby_list; |
2816 | size_t gc_heap::last_gc_index = 0; |
2817 | #ifdef SEG_MAPPING_TABLE |
2818 | size_t gc_heap::min_segment_size = 0; |
2819 | size_t gc_heap::min_segment_size_shr = 0; |
2820 | #endif //SEG_MAPPING_TABLE |
2821 | size_t gc_heap::soh_segment_size = 0; |
2822 | size_t gc_heap::min_loh_segment_size = 0; |
2823 | size_t gc_heap::segment_info_size = 0; |
2824 | |
2825 | #ifdef GC_CONFIG_DRIVEN |
2826 | size_t gc_heap::time_init = 0; |
2827 | size_t gc_heap::time_since_init = 0; |
2828 | size_t gc_heap::compact_or_sweep_gcs[2]; |
2829 | #endif //GC_CONFIG_DRIVEN |
2830 | |
2831 | #ifdef FEATURE_LOH_COMPACTION |
2832 | BOOL gc_heap::loh_compaction_always_p = FALSE; |
2833 | gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default; |
2834 | int gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY; |
2835 | |
2836 | #endif //FEATURE_LOH_COMPACTION |
2837 | |
2838 | GCEvent gc_heap::full_gc_approach_event; |
2839 | |
2840 | GCEvent gc_heap::full_gc_end_event; |
2841 | |
2842 | uint32_t gc_heap::fgn_maxgen_percent = 0; |
2843 | |
2844 | uint32_t gc_heap::fgn_loh_percent = 0; |
2845 | |
2846 | #ifdef BACKGROUND_GC |
2847 | BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE; |
2848 | #endif //BACKGROUND_GC |
2849 | |
2850 | VOLATILE(bool) gc_heap::full_gc_approach_event_set; |
2851 | |
2852 | size_t gc_heap::full_gc_counts[gc_type_max]; |
2853 | |
2854 | bool gc_heap::maxgen_size_inc_p = false; |
2855 | |
2856 | BOOL gc_heap::should_expand_in_full_gc = FALSE; |
2857 | |
2858 | // Provisional mode related stuff. |
2859 | bool gc_heap::provisional_mode_triggered = false; |
2860 | bool gc_heap::pm_trigger_full_gc = false; |
2861 | size_t gc_heap::provisional_triggered_gc_count = 0; |
2862 | size_t gc_heap::provisional_off_gc_count = 0; |
2863 | size_t gc_heap::num_provisional_triggered = 0; |
2864 | bool gc_heap::pm_stress_on = false; |
2865 | |
2866 | #ifdef HEAP_ANALYZE |
2867 | BOOL gc_heap::heap_analyze_enabled = FALSE; |
2868 | #endif //HEAP_ANALYZE |
2869 | |
2870 | #ifndef MULTIPLE_HEAPS |
2871 | |
2872 | alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1]; |
2873 | alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1]; |
2874 | |
2875 | dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1]; |
2876 | gc_history_per_heap gc_heap::gc_data_per_heap; |
2877 | size_t gc_heap::maxgen_pinned_compact_before_advance = 0; |
2878 | |
2879 | uint8_t* gc_heap::alloc_allocated = 0; |
2880 | |
2881 | size_t gc_heap::allocation_quantum = CLR_SIZE; |
2882 | |
2883 | GCSpinLock gc_heap::more_space_lock_soh; |
2884 | GCSpinLock gc_heap::more_space_lock_loh; |
2885 | VOLATILE(int32_t) gc_heap::loh_alloc_thread_count = 0; |
2886 | |
2887 | #ifdef SYNCHRONIZATION_STATS |
2888 | unsigned int gc_heap::good_suspension = 0; |
2889 | unsigned int gc_heap::bad_suspension = 0; |
2890 | uint64_t gc_heap::total_msl_acquire = 0; |
2891 | unsigned int gc_heap::num_msl_acquired = 0; |
2892 | unsigned int gc_heap::num_high_msl_acquire = 0; |
2893 | unsigned int gc_heap::num_low_msl_acquire = 0; |
2894 | #endif //SYNCHRONIZATION_STATS |
2895 | |
2896 | size_t gc_heap::alloc_contexts_used = 0; |
2897 | size_t gc_heap::soh_allocation_no_gc = 0; |
2898 | size_t gc_heap::loh_allocation_no_gc = 0; |
2899 | bool gc_heap::no_gc_oom_p = false; |
2900 | heap_segment* gc_heap::saved_loh_segment_no_gc = 0; |
2901 | |
2902 | #endif //MULTIPLE_HEAPS |
2903 | |
2904 | #ifndef MULTIPLE_HEAPS |
2905 | |
2906 | BOOL gc_heap::gen0_bricks_cleared = FALSE; |
2907 | |
2908 | #ifdef FFIND_OBJECT |
2909 | int gc_heap::gen0_must_clear_bricks = 0; |
2910 | #endif //FFIND_OBJECT |
2911 | |
2912 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
2913 | CFinalize* gc_heap::finalize_queue = 0; |
2914 | #endif // FEATURE_PREMORTEM_FINALIZATION |
2915 | |
2916 | generation gc_heap::generation_table [NUMBERGENERATIONS + 1]; |
2917 | |
2918 | size_t gc_heap::interesting_data_per_heap[max_idp_count]; |
2919 | |
2920 | size_t gc_heap::compact_reasons_per_heap[max_compact_reasons_count]; |
2921 | |
2922 | size_t gc_heap::expand_mechanisms_per_heap[max_expand_mechanisms_count]; |
2923 | |
2924 | size_t gc_heap::interesting_mechanism_bits_per_heap[max_gc_mechanism_bits_count]; |
2925 | |
2926 | #endif // MULTIPLE_HEAPS |
2927 | |
2928 | /* end of per heap static initialization */ |
2929 | |
2930 | /* end of static initialization */ |
2931 | |
2932 | #ifndef DACCESS_COMPILE |
2933 | |
2934 | void gen_to_condemn_tuning::print (int heap_num) |
2935 | { |
2936 | #ifdef DT_LOG |
2937 | dprintf (DT_LOG_0, ("condemned reasons (%d %d)" , condemn_reasons_gen, condemn_reasons_condition)); |
2938 | dprintf (DT_LOG_0, ("%s" , record_condemn_reasons_gen_header)); |
2939 | gc_condemn_reason_gen r_gen; |
2940 | for (int i = 0; i < gcrg_max; i++) |
2941 | { |
2942 | r_gen = (gc_condemn_reason_gen)(i); |
2943 | str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen)); |
2944 | } |
2945 | dprintf (DT_LOG_0, ("[%2d]%s" , heap_num, str_reasons_gen)); |
2946 | |
2947 | dprintf (DT_LOG_0, ("%s" , record_condemn_reasons_condition_header)); |
2948 | gc_condemn_reason_condition r_condition; |
2949 | for (int i = 0; i < gcrc_max; i++) |
2950 | { |
2951 | r_condition = (gc_condemn_reason_condition)(i); |
2952 | str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition)); |
2953 | } |
2954 | |
2955 | dprintf (DT_LOG_0, ("[%2d]%s" , heap_num, str_reasons_condition)); |
2956 | #else |
2957 | UNREFERENCED_PARAMETER(heap_num); |
2958 | #endif //DT_LOG |
2959 | } |
2960 | |
2961 | void gc_generation_data::print (int heap_num, int gen_num) |
2962 | { |
2963 | #if defined(SIMPLE_DPRINTF) && defined(DT_LOG) |
2964 | dprintf (DT_LOG_0, ("[%2d]gen%d beg %Id fl %Id fo %Id end %Id fl %Id fo %Id in %Id p %Id np %Id alloc %Id" , |
2965 | heap_num, gen_num, |
2966 | size_before, |
2967 | free_list_space_before, free_obj_space_before, |
2968 | size_after, |
2969 | free_list_space_after, free_obj_space_after, |
2970 | in, pinned_surv, npinned_surv, |
2971 | new_allocation)); |
2972 | #else |
2973 | UNREFERENCED_PARAMETER(heap_num); |
2974 | UNREFERENCED_PARAMETER(gen_num); |
2975 | #endif //SIMPLE_DPRINTF && DT_LOG |
2976 | } |
2977 | |
2978 | void gc_history_per_heap::set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value) |
2979 | { |
2980 | uint32_t* mechanism = &mechanisms[mechanism_per_heap]; |
2981 | *mechanism = 0; |
2982 | *mechanism |= mechanism_mask; |
2983 | *mechanism |= (1 << value); |
2984 | |
2985 | #ifdef DT_LOG |
2986 | gc_mechanism_descr* descr = &gc_mechanisms_descr[mechanism_per_heap]; |
2987 | dprintf (DT_LOG_0, ("setting %s: %s" , |
2988 | descr->name, |
2989 | (descr->descr)[value])); |
2990 | #endif //DT_LOG |
2991 | } |
2992 | |
2993 | void gc_history_per_heap::print() |
2994 | { |
2995 | #if defined(SIMPLE_DPRINTF) && defined(DT_LOG) |
2996 | for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++) |
2997 | { |
2998 | gen_data[i].print (heap_index, i); |
2999 | } |
3000 | |
3001 | dprintf (DT_LOG_0, ("fla %Id flr %Id esa %Id ca %Id pa %Id paa %Id, rfle %d, ec %Id" , |
3002 | maxgen_size_info.free_list_allocated, |
3003 | maxgen_size_info.free_list_rejected, |
3004 | maxgen_size_info.end_seg_allocated, |
3005 | maxgen_size_info.condemned_allocated, |
3006 | maxgen_size_info.pinned_allocated, |
3007 | maxgen_size_info.pinned_allocated_advance, |
3008 | maxgen_size_info.running_free_list_efficiency, |
3009 | extra_gen0_committed)); |
3010 | |
3011 | int mechanism = 0; |
3012 | gc_mechanism_descr* descr = 0; |
3013 | |
3014 | for (int i = 0; i < max_mechanism_per_heap; i++) |
3015 | { |
3016 | mechanism = get_mechanism ((gc_mechanism_per_heap)i); |
3017 | |
3018 | if (mechanism >= 0) |
3019 | { |
3020 | descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i]; |
3021 | dprintf (DT_LOG_0, ("[%2d]%s%s" , |
3022 | heap_index, |
3023 | descr->name, |
3024 | (descr->descr)[mechanism])); |
3025 | } |
3026 | } |
3027 | #endif //SIMPLE_DPRINTF && DT_LOG |
3028 | } |
3029 | |
3030 | void gc_history_global::print() |
3031 | { |
3032 | #ifdef DT_LOG |
3033 | char str_settings[64]; |
3034 | memset (str_settings, '|', sizeof (char) * 64); |
3035 | str_settings[max_global_mechanisms_count*2] = 0; |
3036 | |
3037 | for (int i = 0; i < max_global_mechanisms_count; i++) |
3038 | { |
3039 | str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N'); |
3040 | } |
3041 | |
3042 | dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|e|" )); |
3043 | |
3044 | dprintf (DT_LOG_0, ("%4d|%s" , num_heaps, str_settings)); |
3045 | dprintf (DT_LOG_0, ("Condemned gen%d(reason: %s; mode: %s), youngest budget %Id(%d), memload %d" , |
3046 | condemned_generation, |
3047 | str_gc_reasons[reason], |
3048 | str_gc_pause_modes[pause_mode], |
3049 | final_youngest_desired, |
3050 | gen0_reduction_count, |
3051 | mem_pressure)); |
3052 | #endif //DT_LOG |
3053 | } |
3054 | |
3055 | void gc_heap::fire_per_heap_hist_event (gc_history_per_heap* current_gc_data_per_heap, int heap_num) |
3056 | { |
3057 | maxgen_size_increase* maxgen_size_info = &(current_gc_data_per_heap->maxgen_size_info); |
3058 | FIRE_EVENT(GCPerHeapHistory_V3, |
3059 | (void *)(maxgen_size_info->free_list_allocated), |
3060 | (void *)(maxgen_size_info->free_list_rejected), |
3061 | (void *)(maxgen_size_info->end_seg_allocated), |
3062 | (void *)(maxgen_size_info->condemned_allocated), |
3063 | (void *)(maxgen_size_info->pinned_allocated), |
3064 | (void *)(maxgen_size_info->pinned_allocated_advance), |
3065 | maxgen_size_info->running_free_list_efficiency, |
3066 | current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons0(), |
3067 | current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons1(), |
3068 | current_gc_data_per_heap->mechanisms[gc_heap_compact], |
3069 | current_gc_data_per_heap->mechanisms[gc_heap_expand], |
3070 | current_gc_data_per_heap->heap_index, |
3071 | (void *)(current_gc_data_per_heap->extra_gen0_committed), |
3072 | (max_generation + 2), |
3073 | (uint32_t)(sizeof (gc_generation_data)), |
3074 | (void *)&(current_gc_data_per_heap->gen_data[0])); |
3075 | |
3076 | current_gc_data_per_heap->print(); |
3077 | current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_num); |
3078 | } |
3079 | |
3080 | void gc_heap::fire_pevents() |
3081 | { |
3082 | settings.record (&gc_data_global); |
3083 | gc_data_global.print(); |
3084 | |
3085 | FIRE_EVENT(GCGlobalHeapHistory_V2, |
3086 | gc_data_global.final_youngest_desired, |
3087 | gc_data_global.num_heaps, |
3088 | gc_data_global.condemned_generation, |
3089 | gc_data_global.gen0_reduction_count, |
3090 | gc_data_global.reason, |
3091 | gc_data_global.global_mechanims_p, |
3092 | gc_data_global.pause_mode, |
3093 | gc_data_global.mem_pressure); |
3094 | |
3095 | #ifdef MULTIPLE_HEAPS |
3096 | for (int i = 0; i < gc_heap::n_heaps; i++) |
3097 | { |
3098 | gc_heap* hp = gc_heap::g_heaps[i]; |
3099 | gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap(); |
3100 | fire_per_heap_hist_event (current_gc_data_per_heap, hp->heap_number); |
3101 | } |
3102 | #else |
3103 | gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); |
3104 | fire_per_heap_hist_event (current_gc_data_per_heap, heap_number); |
3105 | #endif |
3106 | } |
3107 | |
3108 | inline BOOL |
3109 | gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp) |
3110 | { |
3111 | BOOL ret = FALSE; |
3112 | |
3113 | switch (tp) |
3114 | { |
3115 | case tuning_deciding_condemned_gen: |
3116 | case tuning_deciding_compaction: |
3117 | case tuning_deciding_expansion: |
3118 | case tuning_deciding_full_gc: |
3119 | { |
3120 | ret = (!ephemeral_gen_fit_p (tp)); |
3121 | break; |
3122 | } |
3123 | case tuning_deciding_promote_ephemeral: |
3124 | { |
3125 | size_t new_gen0size = approximate_new_allocation(); |
3126 | ptrdiff_t plan_ephemeral_size = total_ephemeral_size; |
3127 | |
3128 | dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id" , |
3129 | heap_number, plan_ephemeral_size, new_gen0size)); |
3130 | // If we were in no_gc_region we could have allocated a larger than normal segment, |
3131 | // and the next seg we allocate will be a normal sized seg so if we can't fit the new |
3132 | // ephemeral generations there, do an ephemeral promotion. |
3133 | ret = ((soh_segment_size - segment_info_size) < (plan_ephemeral_size + new_gen0size)); |
3134 | break; |
3135 | } |
3136 | default: |
3137 | break; |
3138 | } |
3139 | |
3140 | return ret; |
3141 | } |
3142 | |
3143 | BOOL |
3144 | gc_heap::dt_high_frag_p (gc_tuning_point tp, |
3145 | int gen_number, |
3146 | BOOL elevate_p) |
3147 | { |
3148 | BOOL ret = FALSE; |
3149 | |
3150 | switch (tp) |
3151 | { |
3152 | case tuning_deciding_condemned_gen: |
3153 | { |
3154 | dynamic_data* dd = dynamic_data_of (gen_number); |
3155 | float fragmentation_burden = 0; |
3156 | |
3157 | if (elevate_p) |
3158 | { |
3159 | ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd)); |
3160 | dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id" , |
3161 | heap_number, dd_fragmentation (dd), dd_max_size(dd))); |
3162 | } |
3163 | else |
3164 | { |
3165 | #ifndef MULTIPLE_HEAPS |
3166 | if (gen_number == max_generation) |
3167 | { |
3168 | float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation); |
3169 | if (frag_ratio > 0.65) |
3170 | { |
3171 | dprintf (GTC_LOG, ("g2 FR: %d%%" , (int)(frag_ratio*100))); |
3172 | return TRUE; |
3173 | } |
3174 | } |
3175 | #endif //!MULTIPLE_HEAPS |
3176 | size_t fr = generation_unusable_fragmentation (generation_of (gen_number)); |
3177 | ret = (fr > dd_fragmentation_limit(dd)); |
3178 | if (ret) |
3179 | { |
3180 | fragmentation_burden = (float)fr / generation_size (gen_number); |
3181 | ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd)); |
3182 | } |
3183 | dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d" , |
3184 | heap_number, gen_number, dd_fragmentation (dd), |
3185 | (int)(100*generation_allocator_efficiency (generation_of (gen_number))), |
3186 | fr, (int)(fragmentation_burden*100))); |
3187 | } |
3188 | break; |
3189 | } |
3190 | default: |
3191 | break; |
3192 | } |
3193 | |
3194 | return ret; |
3195 | } |
3196 | |
3197 | inline BOOL |
3198 | gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number) |
3199 | { |
3200 | BOOL ret = FALSE; |
3201 | |
3202 | switch (tp) |
3203 | { |
3204 | case tuning_deciding_condemned_gen: |
3205 | { |
3206 | if (gen_number == max_generation) |
3207 | { |
3208 | dynamic_data* dd = dynamic_data_of (gen_number); |
3209 | size_t maxgen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd)); |
3210 | size_t maxgen_total_size = maxgen_allocated + dd_current_size (dd); |
3211 | size_t est_maxgen_surv = (size_t)((float) (maxgen_total_size) * dd_surv (dd)); |
3212 | size_t est_maxgen_free = maxgen_total_size - est_maxgen_surv + dd_fragmentation (dd); |
3213 | |
3214 | dprintf (GTC_LOG, ("h%d: Total gen2 size: %Id, est gen2 dead space: %Id (s: %d, allocated: %Id), frag: %Id" , |
3215 | heap_number, |
3216 | maxgen_total_size, |
3217 | est_maxgen_free, |
3218 | (int)(dd_surv (dd) * 100), |
3219 | maxgen_allocated, |
3220 | dd_fragmentation (dd))); |
3221 | |
3222 | uint32_t num_heaps = 1; |
3223 | |
3224 | #ifdef MULTIPLE_HEAPS |
3225 | num_heaps = gc_heap::n_heaps; |
3226 | #endif //MULTIPLE_HEAPS |
3227 | |
3228 | size_t min_frag_th = min_reclaim_fragmentation_threshold (num_heaps); |
3229 | dprintf (GTC_LOG, ("h%d, min frag is %Id" , heap_number, min_frag_th)); |
3230 | ret = (est_maxgen_free >= min_frag_th); |
3231 | } |
3232 | else |
3233 | { |
3234 | assert (0); |
3235 | } |
3236 | break; |
3237 | } |
3238 | |
3239 | default: |
3240 | break; |
3241 | } |
3242 | |
3243 | return ret; |
3244 | } |
3245 | |
3246 | // DTREVIEW: Right now we only estimate gen2 fragmentation. |
3247 | // on 64-bit though we should consider gen1 or even gen0 fragmentatioin as |
3248 | // well |
3249 | inline BOOL |
3250 | gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, uint64_t available_mem) |
3251 | { |
3252 | BOOL ret = FALSE; |
3253 | |
3254 | switch (tp) |
3255 | { |
3256 | case tuning_deciding_condemned_gen: |
3257 | { |
3258 | if (gen_number == max_generation) |
3259 | { |
3260 | dynamic_data* dd = dynamic_data_of (gen_number); |
3261 | float est_frag_ratio = 0; |
3262 | if (dd_current_size (dd) == 0) |
3263 | { |
3264 | est_frag_ratio = 1; |
3265 | } |
3266 | else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0)) |
3267 | { |
3268 | est_frag_ratio = 0; |
3269 | } |
3270 | else |
3271 | { |
3272 | est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd)); |
3273 | } |
3274 | |
3275 | size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio)); |
3276 | dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id" , |
3277 | heap_number, |
3278 | gen_number, |
3279 | dd_current_size (dd), |
3280 | dd_fragmentation (dd), |
3281 | (int)(est_frag_ratio*100), |
3282 | est_frag)); |
3283 | |
3284 | uint32_t num_heaps = 1; |
3285 | |
3286 | #ifdef MULTIPLE_HEAPS |
3287 | num_heaps = gc_heap::n_heaps; |
3288 | #endif //MULTIPLE_HEAPS |
3289 | uint64_t min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps); |
3290 | //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th)); |
3291 | ret = (est_frag >= min_frag_th); |
3292 | } |
3293 | else |
3294 | { |
3295 | assert (0); |
3296 | } |
3297 | break; |
3298 | } |
3299 | |
3300 | default: |
3301 | break; |
3302 | } |
3303 | |
3304 | return ret; |
3305 | } |
3306 | |
3307 | inline BOOL |
3308 | gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp) |
3309 | { |
3310 | BOOL ret = FALSE; |
3311 | |
3312 | switch (tp) |
3313 | { |
3314 | case tuning_deciding_condemned_gen: |
3315 | { |
3316 | /* promote into max-generation if the card table has too many |
3317 | * generation faults besides the n -> 0 |
3318 | */ |
3319 | ret = (generation_skip_ratio < 30); |
3320 | break; |
3321 | } |
3322 | |
3323 | default: |
3324 | break; |
3325 | } |
3326 | |
3327 | return ret; |
3328 | } |
3329 | |
3330 | inline BOOL |
3331 | in_range_for_segment(uint8_t* add, heap_segment* seg) |
3332 | { |
3333 | return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg))); |
3334 | } |
3335 | |
3336 | #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE) |
3337 | // The array we allocate is organized as follows: |
3338 | // 0th element is the address of the last array we allocated. |
3339 | // starting from the 1st element are the segment addresses, that's |
3340 | // what buckets() returns. |
3341 | struct bk |
3342 | { |
3343 | uint8_t* add; |
3344 | size_t val; |
3345 | }; |
3346 | |
3347 | class sorted_table |
3348 | { |
3349 | private: |
3350 | ptrdiff_t size; |
3351 | ptrdiff_t count; |
3352 | bk* slots; |
3353 | bk* buckets() { return (slots + 1); } |
3354 | uint8_t*& last_slot (bk* arr) { return arr[0].add; } |
3355 | bk* old_slots; |
3356 | public: |
3357 | static sorted_table* make_sorted_table (); |
3358 | BOOL insert (uint8_t* add, size_t val);; |
3359 | size_t lookup (uint8_t*& add); |
3360 | void remove (uint8_t* add); |
3361 | void clear (); |
3362 | void delete_sorted_table(); |
3363 | void delete_old_slots(); |
3364 | void enqueue_old_slot(bk* sl); |
3365 | BOOL ensure_space_for_insert(); |
3366 | }; |
3367 | |
3368 | sorted_table* |
3369 | sorted_table::make_sorted_table () |
3370 | { |
3371 | size_t size = 400; |
3372 | |
3373 | // allocate one more bk to store the older slot address. |
3374 | sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)]; |
3375 | if (!res) |
3376 | return 0; |
3377 | res->size = size; |
3378 | res->slots = (bk*)(res + 1); |
3379 | res->old_slots = 0; |
3380 | res->clear(); |
3381 | return res; |
3382 | } |
3383 | |
3384 | void |
3385 | sorted_table::delete_sorted_table() |
3386 | { |
3387 | if (slots != (bk*)(this+1)) |
3388 | { |
3389 | delete slots; |
3390 | } |
3391 | delete_old_slots(); |
3392 | delete this; |
3393 | } |
3394 | void |
3395 | sorted_table::delete_old_slots() |
3396 | { |
3397 | uint8_t* sl = (uint8_t*)old_slots; |
3398 | while (sl) |
3399 | { |
3400 | uint8_t* dsl = sl; |
3401 | sl = last_slot ((bk*)sl); |
3402 | delete dsl; |
3403 | } |
3404 | old_slots = 0; |
3405 | } |
3406 | void |
3407 | sorted_table::enqueue_old_slot(bk* sl) |
3408 | { |
3409 | last_slot (sl) = (uint8_t*)old_slots; |
3410 | old_slots = sl; |
3411 | } |
3412 | |
3413 | inline |
3414 | size_t |
3415 | sorted_table::lookup (uint8_t*& add) |
3416 | { |
3417 | ptrdiff_t high = (count-1); |
3418 | ptrdiff_t low = 0; |
3419 | ptrdiff_t ti; |
3420 | ptrdiff_t mid; |
3421 | bk* buck = buckets(); |
3422 | while (low <= high) |
3423 | { |
3424 | mid = ((low + high)/2); |
3425 | ti = mid; |
3426 | if (buck[ti].add > add) |
3427 | { |
3428 | if ((ti > 0) && (buck[ti-1].add <= add)) |
3429 | { |
3430 | add = buck[ti-1].add; |
3431 | return buck[ti - 1].val; |
3432 | } |
3433 | high = mid - 1; |
3434 | } |
3435 | else |
3436 | { |
3437 | if (buck[ti+1].add > add) |
3438 | { |
3439 | add = buck[ti].add; |
3440 | return buck[ti].val; |
3441 | } |
3442 | low = mid + 1; |
3443 | } |
3444 | } |
3445 | add = 0; |
3446 | return 0; |
3447 | } |
3448 | |
3449 | BOOL |
3450 | sorted_table::ensure_space_for_insert() |
3451 | { |
3452 | if (count == size) |
3453 | { |
3454 | size = (size * 3)/2; |
3455 | assert((size * sizeof (bk)) > 0); |
3456 | bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)]; |
3457 | assert (res); |
3458 | if (!res) |
3459 | return FALSE; |
3460 | |
3461 | last_slot (res) = 0; |
3462 | memcpy (((bk*)res + 1), buckets(), count * sizeof (bk)); |
3463 | bk* last_old_slots = slots; |
3464 | slots = res; |
3465 | if (last_old_slots != (bk*)(this + 1)) |
3466 | enqueue_old_slot (last_old_slots); |
3467 | } |
3468 | return TRUE; |
3469 | } |
3470 | |
3471 | BOOL |
3472 | sorted_table::insert (uint8_t* add, size_t val) |
3473 | { |
3474 | //grow if no more room |
3475 | assert (count < size); |
3476 | |
3477 | //insert sorted |
3478 | ptrdiff_t high = (count-1); |
3479 | ptrdiff_t low = 0; |
3480 | ptrdiff_t ti; |
3481 | ptrdiff_t mid; |
3482 | bk* buck = buckets(); |
3483 | while (low <= high) |
3484 | { |
3485 | mid = ((low + high)/2); |
3486 | ti = mid; |
3487 | if (buck[ti].add > add) |
3488 | { |
3489 | if ((ti == 0) || (buck[ti-1].add <= add)) |
3490 | { |
3491 | // found insertion point |
3492 | for (ptrdiff_t k = count; k > ti;k--) |
3493 | { |
3494 | buck [k] = buck [k-1]; |
3495 | } |
3496 | buck[ti].add = add; |
3497 | buck[ti].val = val; |
3498 | count++; |
3499 | return TRUE; |
3500 | } |
3501 | high = mid - 1; |
3502 | } |
3503 | else |
3504 | { |
3505 | if (buck[ti+1].add > add) |
3506 | { |
3507 | //found the insertion point |
3508 | for (ptrdiff_t k = count; k > ti+1;k--) |
3509 | { |
3510 | buck [k] = buck [k-1]; |
3511 | } |
3512 | buck[ti+1].add = add; |
3513 | buck[ti+1].val = val; |
3514 | count++; |
3515 | return TRUE; |
3516 | } |
3517 | low = mid + 1; |
3518 | } |
3519 | } |
3520 | assert (0); |
3521 | return TRUE; |
3522 | } |
3523 | |
3524 | void |
3525 | sorted_table::remove (uint8_t* add) |
3526 | { |
3527 | ptrdiff_t high = (count-1); |
3528 | ptrdiff_t low = 0; |
3529 | ptrdiff_t ti; |
3530 | ptrdiff_t mid; |
3531 | bk* buck = buckets(); |
3532 | while (low <= high) |
3533 | { |
3534 | mid = ((low + high)/2); |
3535 | ti = mid; |
3536 | if (buck[ti].add > add) |
3537 | { |
3538 | if (buck[ti-1].add <= add) |
3539 | { |
3540 | // found the guy to remove |
3541 | for (ptrdiff_t k = ti; k < count; k++) |
3542 | buck[k-1] = buck[k]; |
3543 | count--; |
3544 | return; |
3545 | } |
3546 | high = mid - 1; |
3547 | } |
3548 | else |
3549 | { |
3550 | if (buck[ti+1].add > add) |
3551 | { |
3552 | // found the guy to remove |
3553 | for (ptrdiff_t k = ti+1; k < count; k++) |
3554 | buck[k-1] = buck[k]; |
3555 | count--; |
3556 | return; |
3557 | } |
3558 | low = mid + 1; |
3559 | } |
3560 | } |
3561 | assert (0); |
3562 | } |
3563 | |
3564 | void |
3565 | sorted_table::clear() |
3566 | { |
3567 | count = 1; |
3568 | buckets()[0].add = MAX_PTR; |
3569 | } |
3570 | #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE |
3571 | |
3572 | #ifdef SEG_MAPPING_TABLE |
3573 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
3574 | inline |
3575 | uint8_t* align_on_segment (uint8_t* add) |
3576 | { |
3577 | return (uint8_t*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1)); |
3578 | } |
3579 | |
3580 | inline |
3581 | uint8_t* align_lower_segment (uint8_t* add) |
3582 | { |
3583 | return (uint8_t*)((size_t)(add) & ~(gc_heap::min_segment_size - 1)); |
3584 | } |
3585 | |
3586 | size_t size_seg_mapping_table_of (uint8_t* from, uint8_t* end) |
3587 | { |
3588 | from = align_lower_segment (from); |
3589 | end = align_on_segment (end); |
3590 | dprintf (1, ("from: %Ix, end: %Ix, size: %Ix" , from, end, sizeof (seg_mapping)*(((size_t)(end - from) >> gc_heap::min_segment_size_shr)))); |
3591 | return sizeof (seg_mapping)*((size_t)(end - from) >> gc_heap::min_segment_size_shr); |
3592 | } |
3593 | |
3594 | // for seg_mapping_table we want it to start from a pointer sized address. |
3595 | inline |
3596 | size_t align_for_seg_mapping_table (size_t size) |
3597 | { |
3598 | return ((size + (sizeof (uint8_t*) - 1)) &~ (sizeof (uint8_t*) - 1)); |
3599 | } |
3600 | |
3601 | inline |
3602 | size_t seg_mapping_word_of (uint8_t* add) |
3603 | { |
3604 | return (size_t)add >> gc_heap::min_segment_size_shr; |
3605 | } |
3606 | #else //GROWABLE_SEG_MAPPING_TABLE |
3607 | BOOL seg_mapping_table_init() |
3608 | { |
3609 | #ifdef BIT64 |
3610 | uint64_t total_address_space = (uint64_t)8*1024*1024*1024*1024; |
3611 | #else |
3612 | uint64_t total_address_space = (uint64_t)4*1024*1024*1024; |
3613 | #endif // BIT64 |
3614 | |
3615 | size_t num_entries = (size_t)(total_address_space >> gc_heap::min_segment_size_shr); |
3616 | seg_mapping_table = new seg_mapping[num_entries]; |
3617 | |
3618 | if (seg_mapping_table) |
3619 | { |
3620 | memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping)); |
3621 | dprintf (1, ("created %d entries for heap mapping (%Id bytes)" , |
3622 | num_entries, (num_entries * sizeof (seg_mapping)))); |
3623 | return TRUE; |
3624 | } |
3625 | else |
3626 | { |
3627 | dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)" , |
3628 | num_entries, (num_entries * sizeof (seg_mapping)))); |
3629 | return FALSE; |
3630 | } |
3631 | } |
3632 | #endif //GROWABLE_SEG_MAPPING_TABLE |
3633 | |
3634 | #ifdef FEATURE_BASICFREEZE |
3635 | inline |
3636 | size_t ro_seg_begin_index (heap_segment* seg) |
3637 | { |
3638 | size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr; |
3639 | begin_index = max (begin_index, (size_t)g_gc_lowest_address >> gc_heap::min_segment_size_shr); |
3640 | return begin_index; |
3641 | } |
3642 | |
3643 | inline |
3644 | size_t ro_seg_end_index (heap_segment* seg) |
3645 | { |
3646 | size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) >> gc_heap::min_segment_size_shr; |
3647 | end_index = min (end_index, (size_t)g_gc_highest_address >> gc_heap::min_segment_size_shr); |
3648 | return end_index; |
3649 | } |
3650 | |
3651 | void seg_mapping_table_add_ro_segment (heap_segment* seg) |
3652 | { |
3653 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
3654 | if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address)) |
3655 | return; |
3656 | #endif //GROWABLE_SEG_MAPPING_TABLE |
3657 | |
3658 | for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++) |
3659 | seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry); |
3660 | } |
3661 | |
3662 | void seg_mapping_table_remove_ro_segment (heap_segment* seg) |
3663 | { |
3664 | UNREFERENCED_PARAMETER(seg); |
3665 | #if 0 |
3666 | // POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves |
3667 | // to be a perf problem, we can search in the current ro segs and see if any lands in this range and only |
3668 | // remove the flag if none lands in this range. |
3669 | #endif //0 |
3670 | } |
3671 | |
3672 | heap_segment* ro_segment_lookup (uint8_t* o) |
3673 | { |
3674 | uint8_t* ro_seg_start = o; |
3675 | heap_segment* seg = (heap_segment*)gc_heap::seg_table->lookup (ro_seg_start); |
3676 | |
3677 | if (ro_seg_start && in_range_for_segment (o, seg)) |
3678 | return seg; |
3679 | else |
3680 | return 0; |
3681 | } |
3682 | |
3683 | #endif //FEATURE_BASICFREEZE |
3684 | |
3685 | void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp) |
3686 | { |
3687 | size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1); |
3688 | size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr; |
3689 | seg_mapping* begin_entry = &seg_mapping_table[begin_index]; |
3690 | size_t end_index = seg_end >> gc_heap::min_segment_size_shr; |
3691 | seg_mapping* end_entry = &seg_mapping_table[end_index]; |
3692 | |
3693 | dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)" , |
3694 | seg, begin_index, heap_segment_reserved (seg), end_index)); |
3695 | |
3696 | dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix" , |
3697 | begin_index, (seg_mapping_table[begin_index].boundary + 1), |
3698 | end_index, (seg_mapping_table[end_index].boundary + 1))); |
3699 | |
3700 | #ifdef MULTIPLE_HEAPS |
3701 | #ifdef SIMPLE_DPRINTF |
3702 | dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)" , |
3703 | begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1), |
3704 | (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1), |
3705 | end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1), |
3706 | (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1))); |
3707 | #endif //SIMPLE_DPRINTF |
3708 | assert (end_entry->boundary == 0); |
3709 | assert (end_entry->h0 == 0); |
3710 | end_entry->h0 = hp; |
3711 | assert (begin_entry->h1 == 0); |
3712 | begin_entry->h1 = hp; |
3713 | #else |
3714 | UNREFERENCED_PARAMETER(hp); |
3715 | #endif //MULTIPLE_HEAPS |
3716 | |
3717 | end_entry->boundary = (uint8_t*)seg_end; |
3718 | |
3719 | dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix" , begin_index, end_index, seg)); |
3720 | assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry)); |
3721 | begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg); |
3722 | end_entry->seg0 = seg; |
3723 | |
3724 | // for every entry inbetween we need to set its heap too. |
3725 | for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++) |
3726 | { |
3727 | assert (seg_mapping_table[entry_index].boundary == 0); |
3728 | #ifdef MULTIPLE_HEAPS |
3729 | assert (seg_mapping_table[entry_index].h0 == 0); |
3730 | seg_mapping_table[entry_index].h1 = hp; |
3731 | #endif //MULTIPLE_HEAPS |
3732 | seg_mapping_table[entry_index].seg1 = seg; |
3733 | } |
3734 | |
3735 | dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix" , |
3736 | begin_index, (seg_mapping_table[begin_index].boundary + 1), |
3737 | end_index, (seg_mapping_table[end_index].boundary + 1))); |
3738 | #if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF) |
3739 | dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)" , |
3740 | begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1), |
3741 | (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1), |
3742 | end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1), |
3743 | (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1))); |
3744 | #endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF |
3745 | } |
3746 | |
3747 | void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg) |
3748 | { |
3749 | size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1); |
3750 | size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr; |
3751 | seg_mapping* begin_entry = &seg_mapping_table[begin_index]; |
3752 | size_t end_index = seg_end >> gc_heap::min_segment_size_shr; |
3753 | seg_mapping* end_entry = &seg_mapping_table[end_index]; |
3754 | dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)" , |
3755 | seg, begin_index, heap_segment_reserved (seg), end_index)); |
3756 | |
3757 | assert (end_entry->boundary == (uint8_t*)seg_end); |
3758 | end_entry->boundary = 0; |
3759 | |
3760 | #ifdef MULTIPLE_HEAPS |
3761 | gc_heap* hp = heap_segment_heap (seg); |
3762 | assert (end_entry->h0 == hp); |
3763 | end_entry->h0 = 0; |
3764 | assert (begin_entry->h1 == hp); |
3765 | begin_entry->h1 = 0; |
3766 | #endif //MULTIPLE_HEAPS |
3767 | |
3768 | assert (begin_entry->seg1 != 0); |
3769 | begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry); |
3770 | end_entry->seg0 = 0; |
3771 | |
3772 | // for every entry inbetween we need to reset its heap too. |
3773 | for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++) |
3774 | { |
3775 | assert (seg_mapping_table[entry_index].boundary == 0); |
3776 | #ifdef MULTIPLE_HEAPS |
3777 | assert (seg_mapping_table[entry_index].h0 == 0); |
3778 | assert (seg_mapping_table[entry_index].h1 == hp); |
3779 | seg_mapping_table[entry_index].h1 = 0; |
3780 | #endif //MULTIPLE_HEAPS |
3781 | seg_mapping_table[entry_index].seg1 = 0; |
3782 | } |
3783 | |
3784 | dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix" , |
3785 | begin_index, (seg_mapping_table[begin_index].boundary + 1), |
3786 | end_index, (seg_mapping_table[end_index].boundary + 1))); |
3787 | #ifdef MULTIPLE_HEAPS |
3788 | dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix" , |
3789 | begin_index, (uint8_t*)(begin_entry->h0), (uint8_t*)(begin_entry->h1), |
3790 | end_index, (uint8_t*)(end_entry->h0), (uint8_t*)(end_entry->h1))); |
3791 | #endif //MULTIPLE_HEAPS |
3792 | } |
3793 | |
3794 | #ifdef MULTIPLE_HEAPS |
3795 | inline |
3796 | gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o) |
3797 | { |
3798 | size_t index = (size_t)o >> gc_heap::min_segment_size_shr; |
3799 | seg_mapping* entry = &seg_mapping_table[index]; |
3800 | |
3801 | gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0); |
3802 | |
3803 | dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix" , |
3804 | o, index, (entry->boundary + 1), |
3805 | (uint8_t*)(entry->h0), (uint8_t*)(entry->seg0), |
3806 | (uint8_t*)(entry->h1), (uint8_t*)(entry->seg1))); |
3807 | |
3808 | #ifdef _DEBUG |
3809 | heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0); |
3810 | #ifdef FEATURE_BASICFREEZE |
3811 | if ((size_t)seg & ro_in_entry) |
3812 | seg = (heap_segment*)((size_t)seg & ~ro_in_entry); |
3813 | #endif //FEATURE_BASICFREEZE |
3814 | |
3815 | if (seg) |
3816 | { |
3817 | if (in_range_for_segment (o, seg)) |
3818 | { |
3819 | dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)" , o, seg, (uint8_t*)heap_segment_allocated (seg))); |
3820 | } |
3821 | else |
3822 | { |
3823 | dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg" , |
3824 | seg, (uint8_t*)heap_segment_allocated (seg), o)); |
3825 | } |
3826 | } |
3827 | else |
3828 | { |
3829 | dprintf (2, ("could not find obj %Ix in any existing segments" , o)); |
3830 | } |
3831 | #endif //_DEBUG |
3832 | |
3833 | return hp; |
3834 | } |
3835 | |
3836 | gc_heap* seg_mapping_table_heap_of (uint8_t* o) |
3837 | { |
3838 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
3839 | if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) |
3840 | return 0; |
3841 | #endif //GROWABLE_SEG_MAPPING_TABLE |
3842 | |
3843 | return seg_mapping_table_heap_of_worker (o); |
3844 | } |
3845 | |
3846 | gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o) |
3847 | { |
3848 | #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE) |
3849 | if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) |
3850 | return 0; |
3851 | #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE |
3852 | |
3853 | return seg_mapping_table_heap_of_worker (o); |
3854 | } |
3855 | #endif //MULTIPLE_HEAPS |
3856 | |
3857 | // Only returns a valid seg if we can actually find o on the seg. |
3858 | heap_segment* seg_mapping_table_segment_of (uint8_t* o) |
3859 | { |
3860 | #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE) |
3861 | if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) |
3862 | #ifdef FEATURE_BASICFREEZE |
3863 | return ro_segment_lookup (o); |
3864 | #else |
3865 | return 0; |
3866 | #endif //FEATURE_BASICFREEZE |
3867 | #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE |
3868 | |
3869 | size_t index = (size_t)o >> gc_heap::min_segment_size_shr; |
3870 | seg_mapping* entry = &seg_mapping_table[index]; |
3871 | |
3872 | dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix" , |
3873 | o, index, (entry->boundary + 1), |
3874 | (uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1))); |
3875 | |
3876 | heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0); |
3877 | #ifdef FEATURE_BASICFREEZE |
3878 | if ((size_t)seg & ro_in_entry) |
3879 | seg = (heap_segment*)((size_t)seg & ~ro_in_entry); |
3880 | #endif //FEATURE_BASICFREEZE |
3881 | |
3882 | if (seg) |
3883 | { |
3884 | if (in_range_for_segment (o, seg)) |
3885 | { |
3886 | dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)" , o, (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg))); |
3887 | } |
3888 | else |
3889 | { |
3890 | dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0" , |
3891 | (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg), o)); |
3892 | seg = 0; |
3893 | } |
3894 | } |
3895 | else |
3896 | { |
3897 | dprintf (2, ("could not find obj %Ix in any existing segments" , o)); |
3898 | } |
3899 | |
3900 | #ifdef FEATURE_BASICFREEZE |
3901 | // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro |
3902 | // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range. I.e., it had an |
3903 | // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression. However, at the moment, grow_brick_card_table does |
3904 | // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest) |
3905 | // range changes. We should probably go ahead and modify grow_brick_card_table and put back the |
3906 | // "&& (size_t)(entry->seg1) & ro_in_entry" here. |
3907 | if (!seg) |
3908 | { |
3909 | seg = ro_segment_lookup (o); |
3910 | if (seg && !in_range_for_segment (o, seg)) |
3911 | seg = 0; |
3912 | } |
3913 | #endif //FEATURE_BASICFREEZE |
3914 | |
3915 | return seg; |
3916 | } |
3917 | #endif //SEG_MAPPING_TABLE |
3918 | |
3919 | size_t gcard_of ( uint8_t*); |
3920 | |
3921 | #define memref(i) *(uint8_t**)(i) |
3922 | |
3923 | //GC Flags |
3924 | #define GC_MARKED (size_t)0x1 |
3925 | #define slot(i, j) ((uint8_t**)(i))[j+1] |
3926 | |
3927 | #define free_object_base_size (plug_skew + sizeof(ArrayBase)) |
3928 | |
3929 | class : public Object |
3930 | { |
3931 | public: |
3932 | |
3933 | #if defined(FEATURE_REDHAWK) || defined(BUILD_AS_STANDALONE) |
3934 | // The GC expects the following methods that are provided by the Object class in the CLR but not provided |
3935 | // by Redhawk's version of Object. |
3936 | uint32_t () |
3937 | { |
3938 | return ((ArrayBase *)this)->GetNumComponents(); |
3939 | } |
3940 | |
3941 | void (BOOL bDeep=TRUE, BOOL = TRUE) |
3942 | { |
3943 | UNREFERENCED_PARAMETER(bVerifyNextHeader); |
3944 | |
3945 | if (this == NULL) |
3946 | return; |
3947 | |
3948 | MethodTable * pMT = GetMethodTable(); |
3949 | |
3950 | _ASSERTE(pMT->SanityCheck()); |
3951 | |
3952 | bool noRangeChecks = |
3953 | (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_RANGE_CHECKS) == GCConfig::HEAPVERIFY_NO_RANGE_CHECKS; |
3954 | |
3955 | BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE; |
3956 | if (!noRangeChecks) |
3957 | { |
3958 | fSmallObjectHeapPtr = g_theGCHeap->IsHeapPointer(this, TRUE); |
3959 | if (!fSmallObjectHeapPtr) |
3960 | fLargeObjectHeapPtr = g_theGCHeap->IsHeapPointer(this); |
3961 | |
3962 | _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr); |
3963 | } |
3964 | |
3965 | #ifdef FEATURE_STRUCTALIGN |
3966 | _ASSERTE(IsStructAligned((uint8_t *)this, GetMethodTable()->GetBaseAlignment())); |
3967 | #endif // FEATURE_STRUCTALIGN |
3968 | |
3969 | #ifdef FEATURE_64BIT_ALIGNMENT |
3970 | if (pMT->RequiresAlign8()) |
3971 | { |
3972 | _ASSERTE((((size_t)this) & 0x7) == (pMT->IsValueType() ? 4U : 0U)); |
3973 | } |
3974 | #endif // FEATURE_64BIT_ALIGNMENT |
3975 | |
3976 | #ifdef VERIFY_HEAP |
3977 | if (bDeep && (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)) |
3978 | g_theGCHeap->ValidateObjectMember(this); |
3979 | #endif |
3980 | if (fSmallObjectHeapPtr) |
3981 | { |
3982 | #ifdef FEATURE_BASICFREEZE |
3983 | _ASSERTE(!g_theGCHeap->IsLargeObject(pMT) || g_theGCHeap->IsInFrozenSegment(this)); |
3984 | #else |
3985 | _ASSERTE(!g_theGCHeap->IsLargeObject(pMT)); |
3986 | #endif |
3987 | } |
3988 | } |
3989 | |
3990 | void (ScanContext *sc, uint32_t flags) |
3991 | { |
3992 | UNREFERENCED_PARAMETER(sc); |
3993 | UNREFERENCED_PARAMETER(flags); |
3994 | |
3995 | Validate(); |
3996 | } |
3997 | |
3998 | void (Object *from, BOOL bDeep) |
3999 | { |
4000 | UNREFERENCED_PARAMETER(from); |
4001 | |
4002 | Validate(bDeep, FALSE); |
4003 | } |
4004 | |
4005 | #endif //FEATURE_REDHAWK || BUILD_AS_STANDALONE |
4006 | |
4007 | ///// |
4008 | // |
4009 | // Header Status Information |
4010 | // |
4011 | |
4012 | MethodTable *() const |
4013 | { |
4014 | return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED)))); |
4015 | } |
4016 | |
4017 | void () |
4018 | { |
4019 | RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED)); |
4020 | } |
4021 | |
4022 | BOOL () const |
4023 | { |
4024 | return !!(((size_t)RawGetMethodTable()) & GC_MARKED); |
4025 | } |
4026 | |
4027 | void () |
4028 | { |
4029 | assert (!(gc_heap::settings.concurrent)); |
4030 | GetHeader()->SetGCBit(); |
4031 | } |
4032 | |
4033 | BOOL () const |
4034 | { |
4035 | return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE); |
4036 | } |
4037 | |
4038 | void () |
4039 | { |
4040 | RawSetMethodTable( GetMethodTable() ); |
4041 | } |
4042 | |
4043 | CGCDesc * () |
4044 | { |
4045 | assert (GetMethodTable()->ContainsPointers()); |
4046 | return CGCDesc::GetCGCDescFromMT(GetMethodTable()); |
4047 | } |
4048 | |
4049 | void (size_t size) |
4050 | { |
4051 | assert (size >= free_object_base_size); |
4052 | |
4053 | assert (g_gc_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size); |
4054 | assert (g_gc_pFreeObjectMethodTable->RawGetComponentSize() == 1); |
4055 | |
4056 | RawSetMethodTable( g_gc_pFreeObjectMethodTable ); |
4057 | |
4058 | size_t* numComponentsPtr = (size_t*) &((uint8_t*) this)[ArrayBase::GetOffsetOfNumComponents()]; |
4059 | *numComponentsPtr = size - free_object_base_size; |
4060 | #ifdef VERIFY_HEAP |
4061 | //This introduces a bug in the free list management. |
4062 | //((void**) this)[-1] = 0; // clear the sync block, |
4063 | assert (*numComponentsPtr >= 0); |
4064 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
4065 | memset (((uint8_t*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr); |
4066 | #endif //VERIFY_HEAP |
4067 | } |
4068 | |
4069 | void () |
4070 | { |
4071 | size_t size = free_object_base_size - plug_skew; |
4072 | |
4073 | // since we only need to clear 2 ptr size, we do it manually |
4074 | PTR_PTR m = (PTR_PTR) this; |
4075 | for (size_t i = 0; i < size / sizeof(PTR_PTR); i++) |
4076 | *(m++) = 0; |
4077 | } |
4078 | |
4079 | BOOL () const |
4080 | { |
4081 | return (GetMethodTable() == g_gc_pFreeObjectMethodTable); |
4082 | } |
4083 | |
4084 | #ifdef FEATURE_STRUCTALIGN |
4085 | int GetRequiredAlignment () const |
4086 | { |
4087 | return GetMethodTable()->GetRequiredAlignment(); |
4088 | } |
4089 | #endif // FEATURE_STRUCTALIGN |
4090 | |
4091 | BOOL () const |
4092 | { |
4093 | return GetMethodTable()->ContainsPointers(); |
4094 | } |
4095 | |
4096 | #ifdef COLLECTIBLE_CLASS |
4097 | BOOL () const |
4098 | { |
4099 | return GetMethodTable()->Collectible(); |
4100 | } |
4101 | |
4102 | FORCEINLINE BOOL () const |
4103 | { |
4104 | MethodTable *pMethodTable = GetMethodTable(); |
4105 | return (pMethodTable->ContainsPointers() || pMethodTable->Collectible()); |
4106 | } |
4107 | #endif //COLLECTIBLE_CLASS |
4108 | |
4109 | Object* () const |
4110 | { |
4111 | return (Object*) this; |
4112 | } |
4113 | }; |
4114 | |
4115 | #define (i) ((CObjectHeader*)(i)) |
4116 | |
4117 | #define free_list_slot(x) ((uint8_t**)(x))[2] |
4118 | #define free_list_undo(x) ((uint8_t**)(x))[-1] |
4119 | #define UNDO_EMPTY ((uint8_t*)1) |
4120 | |
4121 | #ifdef SHORT_PLUGS |
4122 | inline |
4123 | void set_plug_padded (uint8_t* node) |
4124 | { |
4125 | header(node)->SetMarked(); |
4126 | } |
4127 | inline |
4128 | void clear_plug_padded (uint8_t* node) |
4129 | { |
4130 | header(node)->ClearMarked(); |
4131 | } |
4132 | inline |
4133 | BOOL is_plug_padded (uint8_t* node) |
4134 | { |
4135 | return header(node)->IsMarked(); |
4136 | } |
4137 | #else //SHORT_PLUGS |
4138 | inline void set_plug_padded (uint8_t* node){} |
4139 | inline void clear_plug_padded (uint8_t* node){} |
4140 | inline |
4141 | BOOL is_plug_padded (uint8_t* node){return FALSE;} |
4142 | #endif //SHORT_PLUGS |
4143 | |
4144 | |
4145 | inline size_t unused_array_size(uint8_t * p) |
4146 | { |
4147 | assert(((CObjectHeader*)p)->IsFree()); |
4148 | |
4149 | size_t* numComponentsPtr = (size_t*)(p + ArrayBase::GetOffsetOfNumComponents()); |
4150 | return free_object_base_size + *numComponentsPtr; |
4151 | } |
4152 | |
4153 | heap_segment* heap_segment_rw (heap_segment* ns) |
4154 | { |
4155 | if ((ns == 0) || !heap_segment_read_only_p (ns)) |
4156 | { |
4157 | return ns; |
4158 | } |
4159 | else |
4160 | { |
4161 | do |
4162 | { |
4163 | ns = heap_segment_next (ns); |
4164 | } while ((ns != 0) && heap_segment_read_only_p (ns)); |
4165 | return ns; |
4166 | } |
4167 | } |
4168 | |
4169 | //returns the next non ro segment. |
4170 | heap_segment* heap_segment_next_rw (heap_segment* seg) |
4171 | { |
4172 | heap_segment* ns = heap_segment_next (seg); |
4173 | return heap_segment_rw (ns); |
4174 | } |
4175 | |
4176 | // returns the segment before seg. |
4177 | heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg) |
4178 | { |
4179 | assert (begin != 0); |
4180 | heap_segment* prev = begin; |
4181 | heap_segment* current = heap_segment_next_rw (begin); |
4182 | |
4183 | while (current && current != seg) |
4184 | { |
4185 | prev = current; |
4186 | current = heap_segment_next_rw (current); |
4187 | } |
4188 | |
4189 | if (current == seg) |
4190 | { |
4191 | return prev; |
4192 | } |
4193 | else |
4194 | { |
4195 | return 0; |
4196 | } |
4197 | } |
4198 | |
4199 | // returns the segment before seg. |
4200 | heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg) |
4201 | { |
4202 | assert (begin != 0); |
4203 | heap_segment* prev = begin; |
4204 | heap_segment* current = heap_segment_next (begin); |
4205 | |
4206 | while (current && current != seg) |
4207 | { |
4208 | prev = current; |
4209 | current = heap_segment_next (current); |
4210 | } |
4211 | |
4212 | if (current == seg) |
4213 | { |
4214 | return prev; |
4215 | } |
4216 | else |
4217 | { |
4218 | return 0; |
4219 | } |
4220 | } |
4221 | |
4222 | heap_segment* heap_segment_in_range (heap_segment* ns) |
4223 | { |
4224 | if ((ns == 0) || heap_segment_in_range_p (ns)) |
4225 | { |
4226 | return ns; |
4227 | } |
4228 | else |
4229 | { |
4230 | do |
4231 | { |
4232 | ns = heap_segment_next (ns); |
4233 | } while ((ns != 0) && !heap_segment_in_range_p (ns)); |
4234 | return ns; |
4235 | } |
4236 | } |
4237 | |
4238 | heap_segment* heap_segment_next_in_range (heap_segment* seg) |
4239 | { |
4240 | heap_segment* ns = heap_segment_next (seg); |
4241 | return heap_segment_in_range (ns); |
4242 | } |
4243 | |
4244 | typedef struct |
4245 | { |
4246 | uint8_t* memory_base; |
4247 | } imemory_data; |
4248 | |
4249 | typedef struct |
4250 | { |
4251 | imemory_data *initial_memory; |
4252 | imemory_data *initial_normal_heap; // points into initial_memory_array |
4253 | imemory_data *initial_large_heap; // points into initial_memory_array |
4254 | |
4255 | size_t block_size_normal; |
4256 | size_t block_size_large; |
4257 | |
4258 | size_t block_count; // # of blocks in each |
4259 | size_t current_block_normal; |
4260 | size_t current_block_large; |
4261 | |
4262 | enum |
4263 | { |
4264 | ALLATONCE = 1, |
4265 | TWO_STAGE, |
4266 | EACH_BLOCK |
4267 | }; |
4268 | |
4269 | size_t allocation_pattern; |
4270 | } initial_memory_details; |
4271 | |
4272 | initial_memory_details memory_details; |
4273 | |
4274 | BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps) |
4275 | { |
4276 | BOOL reserve_success = FALSE; |
4277 | |
4278 | // should only be called once |
4279 | assert (memory_details.initial_memory == 0); |
4280 | |
4281 | memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2]; |
4282 | if (memory_details.initial_memory == 0) |
4283 | { |
4284 | dprintf (2, ("failed to reserve %Id bytes for imemory_data" , num_heaps*2*sizeof(imemory_data))); |
4285 | return FALSE; |
4286 | } |
4287 | |
4288 | memory_details.initial_normal_heap = memory_details.initial_memory; |
4289 | memory_details.initial_large_heap = memory_details.initial_memory + num_heaps; |
4290 | memory_details.block_size_normal = normal_size; |
4291 | memory_details.block_size_large = large_size; |
4292 | memory_details.block_count = num_heaps; |
4293 | |
4294 | memory_details.current_block_normal = 0; |
4295 | memory_details.current_block_large = 0; |
4296 | |
4297 | g_gc_lowest_address = MAX_PTR; |
4298 | g_gc_highest_address = 0; |
4299 | |
4300 | if (((size_t)MAX_PTR - large_size) < normal_size) |
4301 | { |
4302 | // we are already overflowing with just one heap. |
4303 | dprintf (2, ("0x%Ix + 0x%Ix already overflow" , normal_size, large_size)); |
4304 | return FALSE; |
4305 | } |
4306 | |
4307 | if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size)) |
4308 | { |
4309 | dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow" , normal_size, large_size, memory_details.block_count)); |
4310 | return FALSE; |
4311 | } |
4312 | |
4313 | size_t requestedMemory = memory_details.block_count * (normal_size + large_size); |
4314 | |
4315 | uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory); |
4316 | if (allatonce_block) |
4317 | { |
4318 | g_gc_lowest_address = allatonce_block; |
4319 | g_gc_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size)); |
4320 | memory_details.allocation_pattern = initial_memory_details::ALLATONCE; |
4321 | |
4322 | for(size_t i = 0; i < memory_details.block_count; i++) |
4323 | { |
4324 | memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size); |
4325 | memory_details.initial_large_heap[i].memory_base = allatonce_block + |
4326 | (memory_details.block_count*normal_size) + (i*large_size); |
4327 | reserve_success = TRUE; |
4328 | } |
4329 | } |
4330 | else |
4331 | { |
4332 | // try to allocate 2 blocks |
4333 | uint8_t* b1 = 0; |
4334 | uint8_t* b2 = 0; |
4335 | b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size); |
4336 | if (b1) |
4337 | { |
4338 | b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size); |
4339 | if (b2) |
4340 | { |
4341 | memory_details.allocation_pattern = initial_memory_details::TWO_STAGE; |
4342 | g_gc_lowest_address = min(b1,b2); |
4343 | g_gc_highest_address = max(b1 + memory_details.block_count*normal_size, |
4344 | b2 + memory_details.block_count*large_size); |
4345 | for(size_t i = 0; i < memory_details.block_count; i++) |
4346 | { |
4347 | memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size); |
4348 | memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size); |
4349 | reserve_success = TRUE; |
4350 | } |
4351 | } |
4352 | else |
4353 | { |
4354 | // b2 allocation failed, we'll go on to try allocating each block. |
4355 | // We could preserve the b1 alloc, but code complexity increases |
4356 | virtual_free (b1, memory_details.block_count * normal_size); |
4357 | } |
4358 | } |
4359 | |
4360 | if ((b2==NULL) && ( memory_details.block_count > 1)) |
4361 | { |
4362 | memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK; |
4363 | |
4364 | imemory_data *current_block = memory_details.initial_memory; |
4365 | for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++) |
4366 | { |
4367 | size_t block_size = ((i < memory_details.block_count) ? |
4368 | memory_details.block_size_normal : |
4369 | memory_details.block_size_large); |
4370 | current_block->memory_base = |
4371 | (uint8_t*)virtual_alloc (block_size); |
4372 | if (current_block->memory_base == 0) |
4373 | { |
4374 | // Free the blocks that we've allocated so far |
4375 | current_block = memory_details.initial_memory; |
4376 | for(size_t j = 0; j < i; j++, current_block++){ |
4377 | if (current_block->memory_base != 0){ |
4378 | block_size = ((j < memory_details.block_count) ? |
4379 | memory_details.block_size_normal : |
4380 | memory_details.block_size_large); |
4381 | virtual_free (current_block->memory_base , block_size); |
4382 | } |
4383 | } |
4384 | reserve_success = FALSE; |
4385 | break; |
4386 | } |
4387 | else |
4388 | { |
4389 | if (current_block->memory_base < g_gc_lowest_address) |
4390 | g_gc_lowest_address = current_block->memory_base; |
4391 | if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address) |
4392 | g_gc_highest_address = (current_block->memory_base + block_size); |
4393 | } |
4394 | reserve_success = TRUE; |
4395 | } |
4396 | } |
4397 | } |
4398 | |
4399 | return reserve_success; |
4400 | } |
4401 | |
4402 | void destroy_initial_memory() |
4403 | { |
4404 | if (memory_details.initial_memory != NULL) |
4405 | { |
4406 | if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE) |
4407 | { |
4408 | virtual_free(memory_details.initial_memory[0].memory_base, |
4409 | memory_details.block_count*(memory_details.block_size_normal + |
4410 | memory_details.block_size_large)); |
4411 | } |
4412 | else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE) |
4413 | { |
4414 | virtual_free (memory_details.initial_normal_heap[0].memory_base, |
4415 | memory_details.block_count*memory_details.block_size_normal); |
4416 | |
4417 | virtual_free (memory_details.initial_large_heap[0].memory_base, |
4418 | memory_details.block_count*memory_details.block_size_large); |
4419 | } |
4420 | else |
4421 | { |
4422 | assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK); |
4423 | imemory_data *current_block = memory_details.initial_memory; |
4424 | for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++) |
4425 | { |
4426 | size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal : |
4427 | memory_details.block_size_large; |
4428 | if (current_block->memory_base != NULL) |
4429 | { |
4430 | virtual_free (current_block->memory_base, block_size); |
4431 | } |
4432 | } |
4433 | } |
4434 | |
4435 | delete [] memory_details.initial_memory; |
4436 | memory_details.initial_memory = NULL; |
4437 | memory_details.initial_normal_heap = NULL; |
4438 | memory_details.initial_large_heap = NULL; |
4439 | } |
4440 | } |
4441 | |
4442 | void* next_initial_memory (size_t size) |
4443 | { |
4444 | assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large)); |
4445 | void *res = NULL; |
4446 | |
4447 | if ((size != memory_details.block_size_normal) || |
4448 | ((memory_details.current_block_normal == memory_details.block_count) && |
4449 | (memory_details.block_size_normal == memory_details.block_size_large))) |
4450 | { |
4451 | // If the block sizes are the same, flow block requests from normal to large |
4452 | assert (memory_details.current_block_large < memory_details.block_count); |
4453 | assert (memory_details.initial_large_heap != 0); |
4454 | |
4455 | res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base; |
4456 | memory_details.current_block_large++; |
4457 | } |
4458 | else |
4459 | { |
4460 | assert (memory_details.current_block_normal < memory_details.block_count); |
4461 | assert (memory_details.initial_normal_heap != NULL); |
4462 | |
4463 | res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base; |
4464 | memory_details.current_block_normal++; |
4465 | } |
4466 | |
4467 | return res; |
4468 | } |
4469 | |
4470 | heap_segment* get_initial_segment (size_t size, int h_number) |
4471 | { |
4472 | void* mem = next_initial_memory (size); |
4473 | heap_segment* res = gc_heap::make_heap_segment ((uint8_t*)mem, size , h_number); |
4474 | |
4475 | return res; |
4476 | } |
4477 | |
4478 | void* virtual_alloc (size_t size) |
4479 | { |
4480 | size_t requested_size = size; |
4481 | |
4482 | if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size) |
4483 | { |
4484 | gc_heap::reserved_memory_limit = |
4485 | GCScan::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size); |
4486 | if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size) |
4487 | { |
4488 | return 0; |
4489 | } |
4490 | } |
4491 | |
4492 | uint32_t flags = VirtualReserveFlags::None; |
4493 | #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
4494 | if (virtual_alloc_hardware_write_watch) |
4495 | { |
4496 | flags = VirtualReserveFlags::WriteWatch; |
4497 | } |
4498 | #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
4499 | void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags); |
4500 | void *aligned_mem = prgmem; |
4501 | |
4502 | // We don't want (prgmem + size) to be right at the end of the address space |
4503 | // because we'd have to worry about that everytime we do (address + size). |
4504 | // We also want to make sure that we leave loh_size_threshold at the end |
4505 | // so we allocate a small object we don't need to worry about overflow there |
4506 | // when we do alloc_ptr+size. |
4507 | if (prgmem) |
4508 | { |
4509 | uint8_t* end_mem = (uint8_t*)prgmem + requested_size; |
4510 | |
4511 | if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC)) |
4512 | { |
4513 | GCToOSInterface::VirtualRelease (prgmem, requested_size); |
4514 | dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding" , |
4515 | requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size))); |
4516 | prgmem = 0; |
4517 | aligned_mem = 0; |
4518 | } |
4519 | } |
4520 | |
4521 | if (prgmem) |
4522 | { |
4523 | gc_heap::reserved_memory += requested_size; |
4524 | } |
4525 | |
4526 | dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[" , |
4527 | requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size))); |
4528 | |
4529 | return aligned_mem; |
4530 | } |
4531 | |
4532 | void virtual_free (void* add, size_t size) |
4533 | { |
4534 | GCToOSInterface::VirtualRelease (add, size); |
4535 | gc_heap::reserved_memory -= size; |
4536 | dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[" , |
4537 | size, (size_t)add, (size_t)((uint8_t*)add+size))); |
4538 | } |
4539 | |
4540 | static size_t get_valid_segment_size (BOOL large_seg=FALSE) |
4541 | { |
4542 | size_t seg_size, initial_seg_size; |
4543 | |
4544 | if (!large_seg) |
4545 | { |
4546 | initial_seg_size = INITIAL_ALLOC; |
4547 | seg_size = static_cast<size_t>(GCConfig::GetSegmentSize()); |
4548 | } |
4549 | else |
4550 | { |
4551 | initial_seg_size = LHEAP_ALLOC; |
4552 | seg_size = static_cast<size_t>(GCConfig::GetSegmentSize()) / 2; |
4553 | } |
4554 | |
4555 | #ifdef MULTIPLE_HEAPS |
4556 | #ifdef BIT64 |
4557 | if (!large_seg) |
4558 | #endif // BIT64 |
4559 | { |
4560 | if (g_num_processors > 4) |
4561 | initial_seg_size /= 2; |
4562 | if (g_num_processors > 8) |
4563 | initial_seg_size /= 2; |
4564 | } |
4565 | #endif //MULTIPLE_HEAPS |
4566 | |
4567 | // if seg_size is small but not 0 (0 is default if config not set) |
4568 | // then set the segment to the minimum size |
4569 | if (!g_theGCHeap->IsValidSegmentSize(seg_size)) |
4570 | { |
4571 | // if requested size is between 1 byte and 4MB, use min |
4572 | if ((seg_size >> 1) && !(seg_size >> 22)) |
4573 | seg_size = 1024*1024*4; |
4574 | else |
4575 | seg_size = initial_seg_size; |
4576 | } |
4577 | |
4578 | #ifdef SEG_MAPPING_TABLE |
4579 | #ifdef BIT64 |
4580 | seg_size = round_up_power2 (seg_size); |
4581 | #else |
4582 | seg_size = round_down_power2 (seg_size); |
4583 | #endif // BIT64 |
4584 | #endif //SEG_MAPPING_TABLE |
4585 | |
4586 | return (seg_size); |
4587 | } |
4588 | |
4589 | void |
4590 | gc_heap::compute_new_ephemeral_size() |
4591 | { |
4592 | int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0); |
4593 | size_t padding_size = 0; |
4594 | |
4595 | for (int i = 0; i <= eph_gen_max; i++) |
4596 | { |
4597 | dynamic_data* dd = dynamic_data_of (i); |
4598 | total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd)); |
4599 | #ifdef RESPECT_LARGE_ALIGNMENT |
4600 | total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE); |
4601 | #endif //RESPECT_LARGE_ALIGNMENT |
4602 | #ifdef FEATURE_STRUCTALIGN |
4603 | total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN; |
4604 | #endif //FEATURE_STRUCTALIGN |
4605 | |
4606 | #ifdef SHORT_PLUGS |
4607 | padding_size += dd_padding_size (dd); |
4608 | #endif //SHORT_PLUGS |
4609 | } |
4610 | |
4611 | total_ephemeral_size += eph_gen_starts_size; |
4612 | |
4613 | #ifdef RESPECT_LARGE_ALIGNMENT |
4614 | size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) - |
4615 | generation_plan_allocation_start (generation_of (max_generation-1)); |
4616 | total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size); |
4617 | #endif //RESPECT_LARGE_ALIGNMENT |
4618 | |
4619 | #ifdef SHORT_PLUGS |
4620 | total_ephemeral_size = Align ((size_t)((double)total_ephemeral_size * short_plugs_pad_ratio) + 1); |
4621 | total_ephemeral_size += Align (DESIRED_PLUG_LENGTH); |
4622 | #endif //SHORT_PLUGS |
4623 | |
4624 | dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)" , |
4625 | total_ephemeral_size, |
4626 | padding_size, (total_ephemeral_size - padding_size))); |
4627 | } |
4628 | |
4629 | #ifdef _MSC_VER |
4630 | #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function. |
4631 | #endif // _MSC_VER |
4632 | |
4633 | heap_segment* |
4634 | gc_heap::soh_get_segment_to_expand() |
4635 | { |
4636 | size_t size = soh_segment_size; |
4637 | |
4638 | ordered_plug_indices_init = FALSE; |
4639 | use_bestfit = FALSE; |
4640 | |
4641 | //compute the size of the new ephemeral heap segment. |
4642 | compute_new_ephemeral_size(); |
4643 | |
4644 | if ((settings.pause_mode != pause_low_latency) && |
4645 | (settings.pause_mode != pause_no_gc) |
4646 | #ifdef BACKGROUND_GC |
4647 | && (!recursive_gc_sync::background_running_p()) |
4648 | #endif //BACKGROUND_GC |
4649 | ) |
4650 | { |
4651 | allocator* gen_alloc = ((settings.condemned_generation == max_generation) ? 0 : |
4652 | generation_allocator (generation_of (max_generation))); |
4653 | dprintf (2, ("(gen%d)soh_get_segment_to_expand" , settings.condemned_generation)); |
4654 | |
4655 | // try to find one in the gen 2 segment list, search backwards because the first segments |
4656 | // tend to be more compact than the later ones. |
4657 | heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
4658 | |
4659 | PREFIX_ASSUME(fseg != NULL); |
4660 | |
4661 | #ifdef SEG_REUSE_STATS |
4662 | int try_reuse = 0; |
4663 | #endif //SEG_REUSE_STATS |
4664 | |
4665 | heap_segment* seg = ephemeral_heap_segment; |
4666 | while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg)) |
4667 | { |
4668 | #ifdef SEG_REUSE_STATS |
4669 | try_reuse++; |
4670 | #endif //SEG_REUSE_STATS |
4671 | |
4672 | if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc)) |
4673 | { |
4674 | get_gc_data_per_heap()->set_mechanism (gc_heap_expand, |
4675 | (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal)); |
4676 | if (settings.condemned_generation == max_generation) |
4677 | { |
4678 | if (use_bestfit) |
4679 | { |
4680 | build_ordered_free_spaces (seg); |
4681 | dprintf (GTC_LOG, ("can use best fit" )); |
4682 | } |
4683 | |
4684 | #ifdef SEG_REUSE_STATS |
4685 | dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse" , |
4686 | settings.condemned_generation, try_reuse)); |
4687 | #endif //SEG_REUSE_STATS |
4688 | dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix" , (size_t)seg)); |
4689 | return seg; |
4690 | } |
4691 | else |
4692 | { |
4693 | #ifdef SEG_REUSE_STATS |
4694 | dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning" , |
4695 | settings.condemned_generation, try_reuse)); |
4696 | #endif //SEG_REUSE_STATS |
4697 | dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix" , (size_t)seg)); |
4698 | |
4699 | // If we return 0 here, the allocator will think since we are short on end |
4700 | // of seg we neeed to trigger a full compacting GC. So if sustained low latency |
4701 | // is set we should acquire a new seg instead, that way we wouldn't be short. |
4702 | // The real solution, of course, is to actually implement seg reuse in gen1. |
4703 | if (settings.pause_mode != pause_sustained_low_latency) |
4704 | { |
4705 | dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg" )); |
4706 | get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_next_full_gc); |
4707 | return 0; |
4708 | } |
4709 | } |
4710 | } |
4711 | } |
4712 | } |
4713 | |
4714 | heap_segment* result = get_segment (size, FALSE); |
4715 | |
4716 | if(result) |
4717 | { |
4718 | #ifdef BACKGROUND_GC |
4719 | if (current_c_gc_state == c_gc_state_planning) |
4720 | { |
4721 | // When we expand heap during bgc sweep, we set the seg to be swept so |
4722 | // we'll always look at cards for objects on the new segment. |
4723 | result->flags |= heap_segment_flags_swept; |
4724 | } |
4725 | #endif //BACKGROUND_GC |
4726 | |
4727 | FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(result), |
4728 | (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)), |
4729 | gc_etw_segment_small_object_heap); |
4730 | } |
4731 | |
4732 | get_gc_data_per_heap()->set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory)); |
4733 | |
4734 | if (result == 0) |
4735 | { |
4736 | dprintf (2, ("h%d: failed to allocate a new segment!" , heap_number)); |
4737 | } |
4738 | else |
4739 | { |
4740 | #ifdef MULTIPLE_HEAPS |
4741 | heap_segment_heap (result) = this; |
4742 | #endif //MULTIPLE_HEAPS |
4743 | } |
4744 | |
4745 | dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix" , settings.condemned_generation, result)); |
4746 | return result; |
4747 | } |
4748 | |
4749 | #ifdef _MSC_VER |
4750 | #pragma warning(default:4706) |
4751 | #endif // _MSC_VER |
4752 | |
4753 | //returns 0 in case of allocation failure |
4754 | heap_segment* |
4755 | gc_heap::get_segment (size_t size, BOOL loh_p) |
4756 | { |
4757 | heap_segment* result = 0; |
4758 | |
4759 | if (segment_standby_list != 0) |
4760 | { |
4761 | result = segment_standby_list; |
4762 | heap_segment* last = 0; |
4763 | while (result) |
4764 | { |
4765 | size_t hs = (size_t)(heap_segment_reserved (result) - (uint8_t*)result); |
4766 | if ((hs >= size) && ((hs / 2) < size)) |
4767 | { |
4768 | dprintf (2, ("Hoarded segment %Ix found" , (size_t) result)); |
4769 | if (last) |
4770 | { |
4771 | heap_segment_next (last) = heap_segment_next (result); |
4772 | } |
4773 | else |
4774 | { |
4775 | segment_standby_list = heap_segment_next (result); |
4776 | } |
4777 | break; |
4778 | } |
4779 | else |
4780 | { |
4781 | last = result; |
4782 | result = heap_segment_next (result); |
4783 | } |
4784 | } |
4785 | } |
4786 | |
4787 | if (result) |
4788 | { |
4789 | init_heap_segment (result); |
4790 | #ifdef BACKGROUND_GC |
4791 | if (should_commit_mark_array()) |
4792 | { |
4793 | dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix" , result, mark_array)); |
4794 | if (!commit_mark_array_new_seg (__this, result)) |
4795 | { |
4796 | dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg" )); |
4797 | // If we can't use it we need to thread it back. |
4798 | if (segment_standby_list != 0) |
4799 | { |
4800 | heap_segment_next (result) = segment_standby_list; |
4801 | segment_standby_list = result; |
4802 | } |
4803 | else |
4804 | { |
4805 | segment_standby_list = result; |
4806 | } |
4807 | |
4808 | result = 0; |
4809 | } |
4810 | } |
4811 | #endif //BACKGROUND_GC |
4812 | |
4813 | #ifdef SEG_MAPPING_TABLE |
4814 | if (result) |
4815 | seg_mapping_table_add_segment (result, __this); |
4816 | #endif //SEG_MAPPING_TABLE |
4817 | } |
4818 | |
4819 | if (!result) |
4820 | { |
4821 | #ifndef SEG_MAPPING_TABLE |
4822 | if (!seg_table->ensure_space_for_insert ()) |
4823 | return 0; |
4824 | #endif //SEG_MAPPING_TABLE |
4825 | void* mem = virtual_alloc (size); |
4826 | if (!mem) |
4827 | { |
4828 | fgm_result.set_fgm (fgm_reserve_segment, size, loh_p); |
4829 | return 0; |
4830 | } |
4831 | |
4832 | result = gc_heap::make_heap_segment ((uint8_t*)mem, size, heap_number); |
4833 | |
4834 | if (result) |
4835 | { |
4836 | uint8_t* start; |
4837 | uint8_t* end; |
4838 | if (mem < g_gc_lowest_address) |
4839 | { |
4840 | start = (uint8_t*)mem; |
4841 | } |
4842 | else |
4843 | { |
4844 | start = (uint8_t*)g_gc_lowest_address; |
4845 | } |
4846 | |
4847 | if (((uint8_t*)mem + size) > g_gc_highest_address) |
4848 | { |
4849 | end = (uint8_t*)mem + size; |
4850 | } |
4851 | else |
4852 | { |
4853 | end = (uint8_t*)g_gc_highest_address; |
4854 | } |
4855 | |
4856 | if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0) |
4857 | { |
4858 | virtual_free (mem, size); |
4859 | return 0; |
4860 | } |
4861 | } |
4862 | else |
4863 | { |
4864 | fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p); |
4865 | virtual_free (mem, size); |
4866 | } |
4867 | |
4868 | if (result) |
4869 | { |
4870 | #ifdef SEG_MAPPING_TABLE |
4871 | seg_mapping_table_add_segment (result, __this); |
4872 | #else //SEG_MAPPING_TABLE |
4873 | gc_heap::seg_table->insert ((uint8_t*)result, delta); |
4874 | #endif //SEG_MAPPING_TABLE |
4875 | } |
4876 | } |
4877 | |
4878 | #ifdef BACKGROUND_GC |
4879 | if (result) |
4880 | { |
4881 | ::record_changed_seg ((uint8_t*)result, heap_segment_reserved (result), |
4882 | settings.gc_index, current_bgc_state, |
4883 | seg_added); |
4884 | bgc_verify_mark_array_cleared (result); |
4885 | } |
4886 | #endif //BACKGROUND_GC |
4887 | |
4888 | dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)" , heap_number, result, ((uint8_t*)result + size), size)); |
4889 | return result; |
4890 | } |
4891 | |
4892 | void release_segment (heap_segment* sg) |
4893 | { |
4894 | ptrdiff_t delta = 0; |
4895 | FIRE_EVENT(GCFreeSegment_V1, heap_segment_mem(sg)); |
4896 | virtual_free (sg, (uint8_t*)heap_segment_reserved (sg)-(uint8_t*)sg); |
4897 | } |
4898 | |
4899 | heap_segment* gc_heap::get_segment_for_loh (size_t size |
4900 | #ifdef MULTIPLE_HEAPS |
4901 | , gc_heap* hp |
4902 | #endif //MULTIPLE_HEAPS |
4903 | ) |
4904 | { |
4905 | #ifndef MULTIPLE_HEAPS |
4906 | gc_heap* hp = 0; |
4907 | #endif //MULTIPLE_HEAPS |
4908 | heap_segment* res = hp->get_segment (size, TRUE); |
4909 | if (res != 0) |
4910 | { |
4911 | #ifdef MULTIPLE_HEAPS |
4912 | heap_segment_heap (res) = hp; |
4913 | #endif //MULTIPLE_HEAPS |
4914 | res->flags |= heap_segment_flags_loh; |
4915 | |
4916 | FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), gc_etw_segment_large_object_heap); |
4917 | |
4918 | GCToEEInterface::DiagUpdateGenerationBounds(); |
4919 | |
4920 | #ifdef MULTIPLE_HEAPS |
4921 | hp->thread_loh_segment (res); |
4922 | #else |
4923 | thread_loh_segment (res); |
4924 | #endif //MULTIPLE_HEAPS |
4925 | } |
4926 | |
4927 | return res; |
4928 | } |
4929 | |
4930 | void gc_heap::thread_loh_segment (heap_segment* new_seg) |
4931 | { |
4932 | heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1)); |
4933 | |
4934 | while (heap_segment_next_rw (seg)) |
4935 | seg = heap_segment_next_rw (seg); |
4936 | heap_segment_next (seg) = new_seg; |
4937 | } |
4938 | |
4939 | heap_segment* |
4940 | gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc) |
4941 | { |
4942 | *did_full_compact_gc = FALSE; |
4943 | size_t last_full_compact_gc_count = get_full_compact_gc_count(); |
4944 | |
4945 | //access to get_segment needs to be serialized |
4946 | add_saved_spinlock_info (true, me_release, mt_get_large_seg); |
4947 | leave_spin_lock (&more_space_lock_loh); |
4948 | enter_spin_lock (&gc_heap::gc_lock); |
4949 | dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc" , heap_number)); |
4950 | // if a GC happened between here and before we ask for a segment in |
4951 | // get_large_segment, we need to count that GC. |
4952 | size_t current_full_compact_gc_count = get_full_compact_gc_count(); |
4953 | |
4954 | if (current_full_compact_gc_count > last_full_compact_gc_count) |
4955 | { |
4956 | *did_full_compact_gc = TRUE; |
4957 | } |
4958 | |
4959 | heap_segment* res = get_segment_for_loh (size |
4960 | #ifdef MULTIPLE_HEAPS |
4961 | , this |
4962 | #endif //MULTIPLE_HEAPS |
4963 | ); |
4964 | |
4965 | dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc" , heap_number)); |
4966 | leave_spin_lock (&gc_heap::gc_lock); |
4967 | enter_spin_lock (&more_space_lock_loh); |
4968 | add_saved_spinlock_info (true, me_acquire, mt_get_large_seg); |
4969 | |
4970 | return res; |
4971 | } |
4972 | |
4973 | #if 0 |
4974 | BOOL gc_heap::unprotect_segment (heap_segment* seg) |
4975 | { |
4976 | uint8_t* start = align_lower_page (heap_segment_mem (seg)); |
4977 | ptrdiff_t region_size = heap_segment_allocated (seg) - start; |
4978 | |
4979 | if (region_size != 0 ) |
4980 | { |
4981 | dprintf (3, ("unprotecting segment %Ix:" , (size_t)seg)); |
4982 | |
4983 | BOOL status = GCToOSInterface::VirtualUnprotect (start, region_size); |
4984 | assert (status); |
4985 | return status; |
4986 | } |
4987 | return FALSE; |
4988 | } |
4989 | #endif |
4990 | |
4991 | #ifdef MULTIPLE_HEAPS |
4992 | #ifdef _X86_ |
4993 | #ifdef _MSC_VER |
4994 | #pragma warning(disable:4035) |
4995 | static ptrdiff_t get_cycle_count() |
4996 | { |
4997 | __asm rdtsc |
4998 | } |
4999 | #pragma warning(default:4035) |
5000 | #elif defined(__GNUC__) |
5001 | static ptrdiff_t get_cycle_count() |
5002 | { |
5003 | ptrdiff_t cycles; |
5004 | ptrdiff_t cyclesHi; |
5005 | __asm__ __volatile__ |
5006 | ("rdtsc" :"=a" (cycles), "=d" (cyclesHi)); |
5007 | return cycles; |
5008 | } |
5009 | #else //_MSC_VER |
5010 | #error Unknown compiler |
5011 | #endif //_MSC_VER |
5012 | #elif defined(_TARGET_AMD64_) |
5013 | #ifdef _MSC_VER |
5014 | extern "C" uint64_t __rdtsc(); |
5015 | #pragma intrinsic(__rdtsc) |
5016 | static ptrdiff_t get_cycle_count() |
5017 | { |
5018 | return (ptrdiff_t)__rdtsc(); |
5019 | } |
5020 | #elif defined(__clang__) |
5021 | static ptrdiff_t get_cycle_count() |
5022 | { |
5023 | ptrdiff_t cycles; |
5024 | ptrdiff_t cyclesHi; |
5025 | __asm__ __volatile__ |
5026 | ("rdtsc" :"=a" (cycles), "=d" (cyclesHi)); |
5027 | return (cyclesHi << 32) | cycles; |
5028 | } |
5029 | #else // _MSC_VER |
5030 | extern "C" ptrdiff_t get_cycle_count(void); |
5031 | #endif // _MSC_VER |
5032 | #elif defined(_TARGET_ARM_) |
5033 | static ptrdiff_t get_cycle_count() |
5034 | { |
5035 | // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this |
5036 | // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in |
5037 | // all buffer access times being reported as equal in access_time(). |
5038 | return 0; |
5039 | } |
5040 | #elif defined(_TARGET_ARM64_) |
5041 | static ptrdiff_t get_cycle_count() |
5042 | { |
5043 | // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this |
5044 | // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in |
5045 | // all buffer access times being reported as equal in access_time(). |
5046 | return 0; |
5047 | } |
5048 | #else |
5049 | #error NYI platform: get_cycle_count |
5050 | #endif //_TARGET_X86_ |
5051 | |
5052 | class heap_select |
5053 | { |
5054 | heap_select() {} |
5055 | static uint8_t* sniff_buffer; |
5056 | static unsigned n_sniff_buffers; |
5057 | static unsigned cur_sniff_index; |
5058 | |
5059 | static uint16_t proc_no_to_heap_no[MAX_SUPPORTED_CPUS]; |
5060 | static uint16_t heap_no_to_proc_no[MAX_SUPPORTED_CPUS]; |
5061 | static uint16_t heap_no_to_numa_node[MAX_SUPPORTED_CPUS]; |
5062 | static uint16_t heap_no_to_cpu_group[MAX_SUPPORTED_CPUS]; |
5063 | static uint16_t heap_no_to_group_proc[MAX_SUPPORTED_CPUS]; |
5064 | static uint16_t numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4]; |
5065 | |
5066 | static int access_time(uint8_t *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers) |
5067 | { |
5068 | ptrdiff_t start_cycles = get_cycle_count(); |
5069 | uint8_t sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE]; |
5070 | assert (sniff == 0); |
5071 | ptrdiff_t elapsed_cycles = get_cycle_count() - start_cycles; |
5072 | // add sniff here just to defeat the optimizer |
5073 | elapsed_cycles += sniff; |
5074 | return (int) elapsed_cycles; |
5075 | } |
5076 | |
5077 | public: |
5078 | static BOOL init(int n_heaps) |
5079 | { |
5080 | assert (sniff_buffer == NULL && n_sniff_buffers == 0); |
5081 | if (!GCToOSInterface::CanGetCurrentProcessorNumber()) |
5082 | { |
5083 | n_sniff_buffers = n_heaps*2+1; |
5084 | size_t n_cache_lines = 1 + n_heaps * n_sniff_buffers + 1; |
5085 | size_t sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE; |
5086 | if (sniff_buf_size / HS_CACHE_LINE_SIZE != n_cache_lines) // check for overlow |
5087 | { |
5088 | return FALSE; |
5089 | } |
5090 | |
5091 | sniff_buffer = new (nothrow) uint8_t[sniff_buf_size]; |
5092 | if (sniff_buffer == 0) |
5093 | return FALSE; |
5094 | memset(sniff_buffer, 0, sniff_buf_size*sizeof(uint8_t)); |
5095 | } |
5096 | |
5097 | //can not enable gc numa aware, force all heaps to be in |
5098 | //one numa node by filling the array with all 0s |
5099 | if (!GCToOSInterface::CanEnableGCNumaAware()) |
5100 | memset(heap_no_to_numa_node, 0, sizeof (heap_no_to_numa_node)); |
5101 | |
5102 | return TRUE; |
5103 | } |
5104 | |
5105 | static void init_cpu_mapping(gc_heap * /*heap*/, int heap_number) |
5106 | { |
5107 | if (GCToOSInterface::CanGetCurrentProcessorNumber()) |
5108 | { |
5109 | uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps; |
5110 | // We can safely cast heap_number to a uint16_t 'cause GetCurrentProcessCpuCount |
5111 | // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most |
5112 | // MAX_SUPPORTED_CPUS GC threads. |
5113 | proc_no_to_heap_no[proc_no] = (uint16_t)heap_number; |
5114 | } |
5115 | } |
5116 | |
5117 | static void mark_heap(int heap_number) |
5118 | { |
5119 | if (GCToOSInterface::CanGetCurrentProcessorNumber()) |
5120 | return; |
5121 | |
5122 | for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++) |
5123 | sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1; |
5124 | } |
5125 | |
5126 | static int select_heap(alloc_context* acontext, int /*hint*/) |
5127 | { |
5128 | UNREFERENCED_PARAMETER(acontext); // only referenced by dprintf |
5129 | |
5130 | if (GCToOSInterface::CanGetCurrentProcessorNumber()) |
5131 | return proc_no_to_heap_no[GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps]; |
5132 | |
5133 | unsigned sniff_index = Interlocked::Increment(&cur_sniff_index); |
5134 | sniff_index %= n_sniff_buffers; |
5135 | |
5136 | int best_heap = 0; |
5137 | int best_access_time = 1000*1000*1000; |
5138 | int second_best_access_time = best_access_time; |
5139 | |
5140 | uint8_t *l_sniff_buffer = sniff_buffer; |
5141 | unsigned l_n_sniff_buffers = n_sniff_buffers; |
5142 | for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++) |
5143 | { |
5144 | int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers); |
5145 | if (this_access_time < best_access_time) |
5146 | { |
5147 | second_best_access_time = best_access_time; |
5148 | best_access_time = this_access_time; |
5149 | best_heap = heap_number; |
5150 | } |
5151 | else if (this_access_time < second_best_access_time) |
5152 | { |
5153 | second_best_access_time = this_access_time; |
5154 | } |
5155 | } |
5156 | |
5157 | if (best_access_time*2 < second_best_access_time) |
5158 | { |
5159 | sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1; |
5160 | |
5161 | dprintf (3, ("select_heap yields crisp %d for context %p\n" , best_heap, (void *)acontext)); |
5162 | } |
5163 | else |
5164 | { |
5165 | dprintf (3, ("select_heap yields vague %d for context %p\n" , best_heap, (void *)acontext )); |
5166 | } |
5167 | |
5168 | return best_heap; |
5169 | } |
5170 | |
5171 | static bool can_find_heap_fast() |
5172 | { |
5173 | return GCToOSInterface::CanGetCurrentProcessorNumber(); |
5174 | } |
5175 | |
5176 | static uint16_t find_proc_no_from_heap_no(int heap_number) |
5177 | { |
5178 | return heap_no_to_proc_no[heap_number]; |
5179 | } |
5180 | |
5181 | static void set_proc_no_for_heap(int heap_number, uint16_t proc_no) |
5182 | { |
5183 | heap_no_to_proc_no[heap_number] = proc_no; |
5184 | } |
5185 | |
5186 | static uint16_t find_numa_node_from_heap_no(int heap_number) |
5187 | { |
5188 | return heap_no_to_numa_node[heap_number]; |
5189 | } |
5190 | |
5191 | static void set_numa_node_for_heap(int heap_number, uint16_t numa_node) |
5192 | { |
5193 | heap_no_to_numa_node[heap_number] = numa_node; |
5194 | } |
5195 | |
5196 | static uint16_t find_cpu_group_from_heap_no(int heap_number) |
5197 | { |
5198 | return heap_no_to_cpu_group[heap_number]; |
5199 | } |
5200 | |
5201 | static void set_cpu_group_for_heap(int heap_number, uint16_t group_number) |
5202 | { |
5203 | heap_no_to_cpu_group[heap_number] = group_number; |
5204 | } |
5205 | |
5206 | static uint16_t find_group_proc_from_heap_no(int heap_number) |
5207 | { |
5208 | return heap_no_to_group_proc[heap_number]; |
5209 | } |
5210 | |
5211 | static void set_group_proc_for_heap(int heap_number, uint16_t group_proc) |
5212 | { |
5213 | heap_no_to_group_proc[heap_number] = group_proc; |
5214 | } |
5215 | |
5216 | static void init_numa_node_to_heap_map(int nheaps) |
5217 | { // called right after GCHeap::Init() for each heap is finished |
5218 | // when numa is not enabled, heap_no_to_numa_node[] are all filled |
5219 | // with 0s during initialization, and will be treated as one node |
5220 | numa_node_to_heap_map[0] = 0; |
5221 | int node_index = 1; |
5222 | |
5223 | for (int i=1; i < nheaps; i++) |
5224 | { |
5225 | if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1]) |
5226 | numa_node_to_heap_map[node_index++] = (uint16_t)i; |
5227 | } |
5228 | numa_node_to_heap_map[node_index] = (uint16_t)nheaps; //mark the end with nheaps |
5229 | } |
5230 | |
5231 | static void get_heap_range_for_heap(int hn, int* start, int* end) |
5232 | { // 1-tier/no numa case: heap_no_to_numa_node[] all zeros, |
5233 | // and treated as in one node. thus: start=0, end=n_heaps |
5234 | uint16_t numa_node = heap_no_to_numa_node[hn]; |
5235 | *start = (int)numa_node_to_heap_map[numa_node]; |
5236 | *end = (int)(numa_node_to_heap_map[numa_node+1]); |
5237 | } |
5238 | }; |
5239 | uint8_t* heap_select::sniff_buffer; |
5240 | unsigned heap_select::n_sniff_buffers; |
5241 | unsigned heap_select::cur_sniff_index; |
5242 | uint16_t heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS]; |
5243 | uint16_t heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS]; |
5244 | uint16_t heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS]; |
5245 | uint16_t heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS]; |
5246 | uint16_t heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS]; |
5247 | uint16_t heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4]; |
5248 | |
5249 | BOOL gc_heap::create_thread_support (unsigned number_of_heaps) |
5250 | { |
5251 | BOOL ret = FALSE; |
5252 | if (!gc_start_event.CreateOSManualEventNoThrow (FALSE)) |
5253 | { |
5254 | goto cleanup; |
5255 | } |
5256 | if (!ee_suspend_event.CreateOSAutoEventNoThrow (FALSE)) |
5257 | { |
5258 | goto cleanup; |
5259 | } |
5260 | if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc)) |
5261 | { |
5262 | goto cleanup; |
5263 | } |
5264 | |
5265 | ret = TRUE; |
5266 | |
5267 | cleanup: |
5268 | |
5269 | if (!ret) |
5270 | { |
5271 | destroy_thread_support(); |
5272 | } |
5273 | |
5274 | return ret; |
5275 | } |
5276 | |
5277 | void gc_heap::destroy_thread_support () |
5278 | { |
5279 | if (ee_suspend_event.IsValid()) |
5280 | { |
5281 | ee_suspend_event.CloseEvent(); |
5282 | } |
5283 | if (gc_start_event.IsValid()) |
5284 | { |
5285 | gc_start_event.CloseEvent(); |
5286 | } |
5287 | } |
5288 | |
5289 | void set_thread_group_affinity_for_heap(int heap_number, GCThreadAffinity* affinity) |
5290 | { |
5291 | affinity->Group = GCThreadAffinity::None; |
5292 | affinity->Processor = GCThreadAffinity::None; |
5293 | |
5294 | uint16_t gn, gpn; |
5295 | GCToOSInterface::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn); |
5296 | |
5297 | int bit_number = 0; |
5298 | for (uintptr_t mask = 1; mask !=0; mask <<=1) |
5299 | { |
5300 | if (bit_number == gpn) |
5301 | { |
5302 | dprintf(3, ("using processor group %d, mask %Ix for heap %d\n" , gn, mask, heap_number)); |
5303 | affinity->Processor = gpn; |
5304 | affinity->Group = gn; |
5305 | heap_select::set_cpu_group_for_heap(heap_number, gn); |
5306 | heap_select::set_group_proc_for_heap(heap_number, gpn); |
5307 | if (GCToOSInterface::CanEnableGCNumaAware()) |
5308 | { |
5309 | PROCESSOR_NUMBER proc_no; |
5310 | proc_no.Group = gn; |
5311 | proc_no.Number = (uint8_t)gpn; |
5312 | proc_no.Reserved = 0; |
5313 | |
5314 | uint16_t node_no = 0; |
5315 | if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no)) |
5316 | heap_select::set_numa_node_for_heap(heap_number, node_no); |
5317 | } |
5318 | else |
5319 | { // no numa setting, each cpu group is treated as a node |
5320 | heap_select::set_numa_node_for_heap(heap_number, gn); |
5321 | } |
5322 | return; |
5323 | } |
5324 | bit_number++; |
5325 | } |
5326 | } |
5327 | |
5328 | void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affinity) |
5329 | { |
5330 | affinity->Group = GCThreadAffinity::None; |
5331 | affinity->Processor = GCThreadAffinity::None; |
5332 | |
5333 | uintptr_t pmask = process_mask; |
5334 | int bit_number = 0; |
5335 | uint8_t proc_number = 0; |
5336 | for (uintptr_t mask = 1; mask != 0; mask <<= 1) |
5337 | { |
5338 | if ((mask & pmask) != 0) |
5339 | { |
5340 | if (bit_number == heap_number) |
5341 | { |
5342 | dprintf (3, ("Using processor %d for heap %d" , proc_number, heap_number)); |
5343 | affinity->Processor = proc_number; |
5344 | heap_select::set_proc_no_for_heap(heap_number, proc_number); |
5345 | if (GCToOSInterface::CanEnableGCNumaAware()) |
5346 | { |
5347 | uint16_t node_no = 0; |
5348 | PROCESSOR_NUMBER proc_no; |
5349 | proc_no.Group = 0; |
5350 | proc_no.Number = (uint8_t)proc_number; |
5351 | proc_no.Reserved = 0; |
5352 | if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no)) |
5353 | { |
5354 | heap_select::set_numa_node_for_heap(heap_number, node_no); |
5355 | } |
5356 | } |
5357 | return; |
5358 | } |
5359 | bit_number++; |
5360 | } |
5361 | proc_number++; |
5362 | } |
5363 | } |
5364 | |
5365 | bool gc_heap::create_gc_thread () |
5366 | { |
5367 | dprintf (3, ("Creating gc thread\n" )); |
5368 | return GCToEEInterface::CreateThread(gc_thread_stub, this, false, ".NET Server GC" ); |
5369 | } |
5370 | |
5371 | #ifdef _MSC_VER |
5372 | #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 |
5373 | #endif //_MSC_VER |
5374 | void gc_heap::gc_thread_function () |
5375 | { |
5376 | assert (gc_done_event.IsValid()); |
5377 | assert (gc_start_event.IsValid()); |
5378 | dprintf (3, ("gc thread started" )); |
5379 | |
5380 | heap_select::init_cpu_mapping(this, heap_number); |
5381 | |
5382 | while (1) |
5383 | { |
5384 | assert (!gc_t_join.joined()); |
5385 | |
5386 | if (heap_number == 0) |
5387 | { |
5388 | gc_heap::ee_suspend_event.Wait(INFINITE, FALSE); |
5389 | |
5390 | BEGIN_TIMING(suspend_ee_during_log); |
5391 | GCToEEInterface::SuspendEE(SUSPEND_FOR_GC); |
5392 | END_TIMING(suspend_ee_during_log); |
5393 | |
5394 | proceed_with_gc_p = TRUE; |
5395 | |
5396 | if (!should_proceed_with_gc()) |
5397 | { |
5398 | update_collection_counts_for_no_gc(); |
5399 | proceed_with_gc_p = FALSE; |
5400 | } |
5401 | else |
5402 | { |
5403 | settings.init_mechanisms(); |
5404 | gc_start_event.Set(); |
5405 | } |
5406 | dprintf (3, ("%d gc thread waiting..." , heap_number)); |
5407 | } |
5408 | else |
5409 | { |
5410 | gc_start_event.Wait(INFINITE, FALSE); |
5411 | dprintf (3, ("%d gc thread waiting... Done" , heap_number)); |
5412 | } |
5413 | |
5414 | assert ((heap_number == 0) || proceed_with_gc_p); |
5415 | |
5416 | if (proceed_with_gc_p) |
5417 | { |
5418 | garbage_collect (GCHeap::GcCondemnedGeneration); |
5419 | |
5420 | if (pm_trigger_full_gc) |
5421 | { |
5422 | garbage_collect_pm_full_gc(); |
5423 | } |
5424 | } |
5425 | |
5426 | if (heap_number == 0) |
5427 | { |
5428 | if (proceed_with_gc_p && (!settings.concurrent)) |
5429 | { |
5430 | do_post_gc(); |
5431 | } |
5432 | |
5433 | #ifdef BACKGROUND_GC |
5434 | recover_bgc_settings(); |
5435 | #endif //BACKGROUND_GC |
5436 | |
5437 | #ifdef MULTIPLE_HEAPS |
5438 | for (int i = 0; i < gc_heap::n_heaps; i++) |
5439 | { |
5440 | gc_heap* hp = gc_heap::g_heaps[i]; |
5441 | hp->add_saved_spinlock_info (false, me_release, mt_block_gc); |
5442 | leave_spin_lock(&hp->more_space_lock_soh); |
5443 | } |
5444 | #endif //MULTIPLE_HEAPS |
5445 | |
5446 | gc_heap::gc_started = FALSE; |
5447 | |
5448 | BEGIN_TIMING(restart_ee_during_log); |
5449 | GCToEEInterface::RestartEE(TRUE); |
5450 | END_TIMING(restart_ee_during_log); |
5451 | process_sync_log_stats(); |
5452 | |
5453 | dprintf (SPINLOCK_LOG, ("GC Lgc" )); |
5454 | leave_spin_lock (&gc_heap::gc_lock); |
5455 | |
5456 | gc_heap::internal_gc_done = true; |
5457 | |
5458 | if (proceed_with_gc_p) |
5459 | set_gc_done(); |
5460 | else |
5461 | { |
5462 | // If we didn't actually do a GC, it means we didn't wait up the other threads, |
5463 | // we still need to set the gc_done_event for those threads. |
5464 | for (int i = 0; i < gc_heap::n_heaps; i++) |
5465 | { |
5466 | gc_heap* hp = gc_heap::g_heaps[i]; |
5467 | hp->set_gc_done(); |
5468 | } |
5469 | } |
5470 | } |
5471 | else |
5472 | { |
5473 | int spin_count = 32 * (gc_heap::n_heaps - 1); |
5474 | |
5475 | // wait until RestartEE has progressed to a stage where we can restart user threads |
5476 | while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads()) |
5477 | { |
5478 | spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads())); |
5479 | } |
5480 | set_gc_done(); |
5481 | } |
5482 | } |
5483 | } |
5484 | #ifdef _MSC_VER |
5485 | #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 |
5486 | #endif //_MSC_VER |
5487 | |
5488 | #endif //MULTIPLE_HEAPS |
5489 | |
5490 | bool virtual_alloc_commit_for_heap(void* addr, size_t size, int h_number) |
5491 | { |
5492 | #if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK) |
5493 | // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to |
5494 | // a host. This will need to be added later. |
5495 | #if !defined(FEATURE_CORECLR) && !defined(BUILD_AS_STANDALONE) |
5496 | if (!CLRMemoryHosted()) |
5497 | #endif |
5498 | { |
5499 | if (GCToOSInterface::CanEnableGCNumaAware()) |
5500 | { |
5501 | uint32_t numa_node = heap_select::find_numa_node_from_heap_no(h_number); |
5502 | if (GCToOSInterface::VirtualCommit(addr, size, numa_node)) |
5503 | return true; |
5504 | } |
5505 | } |
5506 | #else |
5507 | UNREFERENCED_PARAMETER(h_number); |
5508 | #endif |
5509 | |
5510 | //numa aware not enabled, or call failed --> fallback to VirtualCommit() |
5511 | return GCToOSInterface::VirtualCommit(addr, size); |
5512 | } |
5513 | |
5514 | #ifndef SEG_MAPPING_TABLE |
5515 | inline |
5516 | heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p) |
5517 | { |
5518 | uint8_t* sadd = add; |
5519 | heap_segment* hs = 0; |
5520 | heap_segment* hs1 = 0; |
5521 | if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address))) |
5522 | { |
5523 | delta = 0; |
5524 | return 0; |
5525 | } |
5526 | //repeat in case there is a concurrent insertion in the table. |
5527 | do |
5528 | { |
5529 | hs = hs1; |
5530 | sadd = add; |
5531 | seg_table->lookup (sadd); |
5532 | hs1 = (heap_segment*)sadd; |
5533 | } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1)); |
5534 | |
5535 | hs = hs1; |
5536 | |
5537 | if ((hs == 0) || |
5538 | (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta))))) |
5539 | delta = 0; |
5540 | return hs; |
5541 | } |
5542 | #endif //SEG_MAPPING_TABLE |
5543 | |
5544 | class mark |
5545 | { |
5546 | public: |
5547 | uint8_t* first; |
5548 | size_t len; |
5549 | |
5550 | // If we want to save space we can have a pool of plug_and_gap's instead of |
5551 | // always having 2 allocated for each pinned plug. |
5552 | gap_reloc_pair saved_pre_plug; |
5553 | // If we decide to not compact, we need to restore the original values. |
5554 | gap_reloc_pair saved_pre_plug_reloc; |
5555 | |
5556 | gap_reloc_pair saved_post_plug; |
5557 | |
5558 | // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke |
5559 | // frames. Also if it's an artificially pinned plug created by us, it can certainly |
5560 | // have references. |
5561 | // We know these cases will be rare so we can optimize this to be only allocated on decommand. |
5562 | gap_reloc_pair saved_post_plug_reloc; |
5563 | |
5564 | // We need to calculate this after we are done with plan phase and before compact |
5565 | // phase because compact phase will change the bricks so relocate_address will no |
5566 | // longer work. |
5567 | uint8_t* saved_pre_plug_info_reloc_start; |
5568 | |
5569 | // We need to save this because we will have no way to calculate it, unlike the |
5570 | // pre plug info start which is right before this plug. |
5571 | uint8_t* saved_post_plug_info_start; |
5572 | |
5573 | #ifdef SHORT_PLUGS |
5574 | uint8_t* allocation_context_start_region; |
5575 | #endif //SHORT_PLUGS |
5576 | |
5577 | // How the bits in these bytes are organized: |
5578 | // MSB --> LSB |
5579 | // 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 |
5580 | // 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. |
5581 | BOOL saved_pre_p; |
5582 | BOOL saved_post_p; |
5583 | |
5584 | #ifdef _DEBUG |
5585 | // We are seeing this is getting corrupted for a PP with a NP after. |
5586 | // Save it when we first set it and make sure it doesn't change. |
5587 | gap_reloc_pair saved_post_plug_debug; |
5588 | #endif //_DEBUG |
5589 | |
5590 | size_t get_max_short_bits() |
5591 | { |
5592 | return (sizeof (gap_reloc_pair) / sizeof (uint8_t*)); |
5593 | } |
5594 | |
5595 | // pre bits |
5596 | size_t get_pre_short_start_bit () |
5597 | { |
5598 | return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*))); |
5599 | } |
5600 | |
5601 | BOOL pre_short_p() |
5602 | { |
5603 | return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1))); |
5604 | } |
5605 | |
5606 | void set_pre_short() |
5607 | { |
5608 | saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1)); |
5609 | } |
5610 | |
5611 | void set_pre_short_bit (size_t bit) |
5612 | { |
5613 | saved_pre_p |= 1 << (get_pre_short_start_bit() + bit); |
5614 | } |
5615 | |
5616 | BOOL pre_short_bit_p (size_t bit) |
5617 | { |
5618 | return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit))); |
5619 | } |
5620 | |
5621 | #ifdef COLLECTIBLE_CLASS |
5622 | void set_pre_short_collectible() |
5623 | { |
5624 | saved_pre_p |= 2; |
5625 | } |
5626 | |
5627 | BOOL pre_short_collectible_p() |
5628 | { |
5629 | return (saved_pre_p & 2); |
5630 | } |
5631 | #endif //COLLECTIBLE_CLASS |
5632 | |
5633 | // post bits |
5634 | size_t get_post_short_start_bit () |
5635 | { |
5636 | return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*))); |
5637 | } |
5638 | |
5639 | BOOL post_short_p() |
5640 | { |
5641 | return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1))); |
5642 | } |
5643 | |
5644 | void set_post_short() |
5645 | { |
5646 | saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1)); |
5647 | } |
5648 | |
5649 | void set_post_short_bit (size_t bit) |
5650 | { |
5651 | saved_post_p |= 1 << (get_post_short_start_bit() + bit); |
5652 | } |
5653 | |
5654 | BOOL post_short_bit_p (size_t bit) |
5655 | { |
5656 | return (saved_post_p & (1 << (get_post_short_start_bit() + bit))); |
5657 | } |
5658 | |
5659 | #ifdef COLLECTIBLE_CLASS |
5660 | void set_post_short_collectible() |
5661 | { |
5662 | saved_post_p |= 2; |
5663 | } |
5664 | |
5665 | BOOL post_short_collectible_p() |
5666 | { |
5667 | return (saved_post_p & 2); |
5668 | } |
5669 | #endif //COLLECTIBLE_CLASS |
5670 | |
5671 | uint8_t* get_plug_address() { return first; } |
5672 | |
5673 | BOOL has_pre_plug_info() { return saved_pre_p; } |
5674 | BOOL has_post_plug_info() { return saved_post_p; } |
5675 | |
5676 | gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; } |
5677 | gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; } |
5678 | void set_pre_plug_info_reloc_start (uint8_t* reloc) { saved_pre_plug_info_reloc_start = reloc; } |
5679 | uint8_t* get_post_plug_info_start() { return saved_post_plug_info_start; } |
5680 | |
5681 | // We need to temporarily recover the shortened plugs for compact phase so we can |
5682 | // copy over the whole plug and their related info (mark bits/cards). But we will |
5683 | // need to set the artificial gap back so compact phase can keep reading the plug info. |
5684 | // We also need to recover the saved info because we'll need to recover it later. |
5685 | // |
5686 | // So we would call swap_p*_plug_and_saved once to recover the object info; then call |
5687 | // it again to recover the artificial gap. |
5688 | void swap_pre_plug_and_saved() |
5689 | { |
5690 | gap_reloc_pair temp; |
5691 | memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp)); |
5692 | memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc)); |
5693 | saved_pre_plug_reloc = temp; |
5694 | } |
5695 | |
5696 | void swap_post_plug_and_saved() |
5697 | { |
5698 | gap_reloc_pair temp; |
5699 | memcpy (&temp, saved_post_plug_info_start, sizeof (temp)); |
5700 | memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc)); |
5701 | saved_post_plug_reloc = temp; |
5702 | } |
5703 | |
5704 | void swap_pre_plug_and_saved_for_profiler() |
5705 | { |
5706 | gap_reloc_pair temp; |
5707 | memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp)); |
5708 | memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug)); |
5709 | saved_pre_plug = temp; |
5710 | } |
5711 | |
5712 | void swap_post_plug_and_saved_for_profiler() |
5713 | { |
5714 | gap_reloc_pair temp; |
5715 | memcpy (&temp, saved_post_plug_info_start, sizeof (temp)); |
5716 | memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug)); |
5717 | saved_post_plug = temp; |
5718 | } |
5719 | |
5720 | // We should think about whether it's really necessary to have to copy back the pre plug |
5721 | // info since it was already copied during compacting plugs. But if a plug doesn't move |
5722 | // by >= 3 ptr size (the size of gap_reloc_pair), it means we'd have to recover pre plug info. |
5723 | void recover_plug_info() |
5724 | { |
5725 | if (saved_pre_p) |
5726 | { |
5727 | if (gc_heap::settings.compaction) |
5728 | { |
5729 | dprintf (3, ("%Ix: REC Pre: %Ix-%Ix" , |
5730 | first, |
5731 | &saved_pre_plug_reloc, |
5732 | saved_pre_plug_info_reloc_start)); |
5733 | memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc)); |
5734 | } |
5735 | else |
5736 | { |
5737 | dprintf (3, ("%Ix: REC Pre: %Ix-%Ix" , |
5738 | first, |
5739 | &saved_pre_plug, |
5740 | (first - sizeof (plug_and_gap)))); |
5741 | memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug)); |
5742 | } |
5743 | } |
5744 | |
5745 | if (saved_post_p) |
5746 | { |
5747 | if (gc_heap::settings.compaction) |
5748 | { |
5749 | dprintf (3, ("%Ix: REC Post: %Ix-%Ix" , |
5750 | first, |
5751 | &saved_post_plug_reloc, |
5752 | saved_post_plug_info_start)); |
5753 | memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc)); |
5754 | } |
5755 | else |
5756 | { |
5757 | dprintf (3, ("%Ix: REC Post: %Ix-%Ix" , |
5758 | first, |
5759 | &saved_post_plug, |
5760 | saved_post_plug_info_start)); |
5761 | memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug)); |
5762 | } |
5763 | } |
5764 | } |
5765 | }; |
5766 | |
5767 | |
5768 | void gc_mechanisms::init_mechanisms() |
5769 | { |
5770 | condemned_generation = 0; |
5771 | promotion = FALSE;//TRUE; |
5772 | compaction = TRUE; |
5773 | #ifdef FEATURE_LOH_COMPACTION |
5774 | loh_compaction = gc_heap::should_compact_loh(); |
5775 | #else |
5776 | loh_compaction = FALSE; |
5777 | #endif //FEATURE_LOH_COMPACTION |
5778 | heap_expansion = FALSE; |
5779 | concurrent = FALSE; |
5780 | demotion = FALSE; |
5781 | elevation_reduced = FALSE; |
5782 | found_finalizers = FALSE; |
5783 | #ifdef BACKGROUND_GC |
5784 | background_p = recursive_gc_sync::background_running_p() != FALSE; |
5785 | allocations_allowed = TRUE; |
5786 | #endif //BACKGROUND_GC |
5787 | |
5788 | entry_memory_load = 0; |
5789 | exit_memory_load = 0; |
5790 | |
5791 | #ifdef STRESS_HEAP |
5792 | stress_induced = FALSE; |
5793 | #endif // STRESS_HEAP |
5794 | } |
5795 | |
5796 | void gc_mechanisms::first_init() |
5797 | { |
5798 | gc_index = 0; |
5799 | gen0_reduction_count = 0; |
5800 | should_lock_elevation = FALSE; |
5801 | elevation_locked_count = 0; |
5802 | reason = reason_empty; |
5803 | #ifdef BACKGROUND_GC |
5804 | pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch; |
5805 | #ifdef _DEBUG |
5806 | int debug_pause_mode = static_cast<int>(GCConfig::GetLatencyMode()); |
5807 | if (debug_pause_mode >= 0) |
5808 | { |
5809 | assert (debug_pause_mode <= pause_sustained_low_latency); |
5810 | pause_mode = (gc_pause_mode)debug_pause_mode; |
5811 | } |
5812 | #endif //_DEBUG |
5813 | #else //BACKGROUND_GC |
5814 | pause_mode = pause_batch; |
5815 | #endif //BACKGROUND_GC |
5816 | |
5817 | init_mechanisms(); |
5818 | } |
5819 | |
5820 | void gc_mechanisms::record (gc_history_global* history) |
5821 | { |
5822 | #ifdef MULTIPLE_HEAPS |
5823 | history->num_heaps = gc_heap::n_heaps; |
5824 | #else |
5825 | history->num_heaps = 1; |
5826 | #endif //MULTIPLE_HEAPS |
5827 | |
5828 | history->condemned_generation = condemned_generation; |
5829 | history->gen0_reduction_count = gen0_reduction_count; |
5830 | history->reason = reason; |
5831 | history->pause_mode = (int)pause_mode; |
5832 | history->mem_pressure = entry_memory_load; |
5833 | history->global_mechanims_p = 0; |
5834 | |
5835 | // start setting the boolean values. |
5836 | if (concurrent) |
5837 | history->set_mechanism_p (global_concurrent); |
5838 | |
5839 | if (compaction) |
5840 | history->set_mechanism_p (global_compaction); |
5841 | |
5842 | if (promotion) |
5843 | history->set_mechanism_p (global_promotion); |
5844 | |
5845 | if (demotion) |
5846 | history->set_mechanism_p (global_demotion); |
5847 | |
5848 | if (card_bundles) |
5849 | history->set_mechanism_p (global_card_bundles); |
5850 | |
5851 | if (elevation_reduced) |
5852 | history->set_mechanism_p (global_elevation); |
5853 | } |
5854 | |
5855 | /********************************** |
5856 | called at the beginning of GC to fix the allocated size to |
5857 | what is really allocated, or to turn the free area into an unused object |
5858 | It needs to be called after all of the other allocation contexts have been |
5859 | fixed since it relies on alloc_allocated. |
5860 | ********************************/ |
5861 | |
5862 | //for_gc_p indicates that the work is being done for GC, |
5863 | //as opposed to concurrent heap verification |
5864 | void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p) |
5865 | { |
5866 | UNREFERENCED_PARAMETER(for_gc_p); |
5867 | |
5868 | // The gen 0 alloc context is never used for allocation in the allocator path. It's |
5869 | // still used in the allocation path during GCs. |
5870 | assert (generation_allocation_pointer (youngest_generation) == nullptr); |
5871 | assert (generation_allocation_limit (youngest_generation) == nullptr); |
5872 | heap_segment_allocated (ephemeral_heap_segment) = alloc_allocated; |
5873 | } |
5874 | |
5875 | void gc_heap::fix_large_allocation_area (BOOL for_gc_p) |
5876 | { |
5877 | UNREFERENCED_PARAMETER(for_gc_p); |
5878 | |
5879 | #ifdef _DEBUG |
5880 | alloc_context* acontext = |
5881 | #endif // _DEBUG |
5882 | generation_alloc_context (large_object_generation); |
5883 | assert (acontext->alloc_ptr == 0); |
5884 | assert (acontext->alloc_limit == 0); |
5885 | #if 0 |
5886 | dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix" , |
5887 | (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit)); |
5888 | fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE)); |
5889 | if (for_gc_p) |
5890 | { |
5891 | acontext->alloc_ptr = 0; |
5892 | acontext->alloc_limit = acontext->alloc_ptr; |
5893 | } |
5894 | #endif //0 |
5895 | } |
5896 | |
5897 | //for_gc_p indicates that the work is being done for GC, |
5898 | //as opposed to concurrent heap verification |
5899 | void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p, |
5900 | int align_const) |
5901 | { |
5902 | dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix" , |
5903 | (size_t)acontext, |
5904 | (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit)); |
5905 | |
5906 | if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) || |
5907 | !for_gc_p) |
5908 | { |
5909 | uint8_t* point = acontext->alloc_ptr; |
5910 | if (point != 0) |
5911 | { |
5912 | size_t size = (acontext->alloc_limit - acontext->alloc_ptr); |
5913 | // the allocation area was from the free list |
5914 | // it was shortened by Align (min_obj_size) to make room for |
5915 | // at least the shortest unused object |
5916 | size += Align (min_obj_size, align_const); |
5917 | assert ((size >= Align (min_obj_size))); |
5918 | |
5919 | dprintf(3,("Making unused area [%Ix, %Ix[" , (size_t)point, |
5920 | (size_t)point + size )); |
5921 | make_unused_array (point, size); |
5922 | |
5923 | if (for_gc_p) |
5924 | { |
5925 | generation_free_obj_space (generation_of (0)) += size; |
5926 | alloc_contexts_used ++; |
5927 | } |
5928 | } |
5929 | } |
5930 | else if (for_gc_p) |
5931 | { |
5932 | alloc_allocated = acontext->alloc_ptr; |
5933 | assert (heap_segment_allocated (ephemeral_heap_segment) <= |
5934 | heap_segment_committed (ephemeral_heap_segment)); |
5935 | alloc_contexts_used ++; |
5936 | } |
5937 | |
5938 | if (for_gc_p) |
5939 | { |
5940 | // We need to update the alloc_bytes to reflect the portion that we have not used |
5941 | acontext->alloc_bytes -= (acontext->alloc_limit - acontext->alloc_ptr); |
5942 | acontext->alloc_ptr = 0; |
5943 | acontext->alloc_limit = acontext->alloc_ptr; |
5944 | } |
5945 | } |
5946 | |
5947 | //used by the heap verification for concurrent gc. |
5948 | //it nulls out the words set by fix_allocation_context for heap_verification |
5949 | void repair_allocation (gc_alloc_context* acontext, void*) |
5950 | { |
5951 | uint8_t* point = acontext->alloc_ptr; |
5952 | |
5953 | if (point != 0) |
5954 | { |
5955 | dprintf (3, ("Clearing [%Ix, %Ix[" , (size_t)acontext->alloc_ptr, |
5956 | (size_t)acontext->alloc_limit+Align(min_obj_size))); |
5957 | memclr (acontext->alloc_ptr - plug_skew, |
5958 | (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size)); |
5959 | } |
5960 | } |
5961 | |
5962 | void void_allocation (gc_alloc_context* acontext, void*) |
5963 | { |
5964 | uint8_t* point = acontext->alloc_ptr; |
5965 | |
5966 | if (point != 0) |
5967 | { |
5968 | dprintf (3, ("Void [%Ix, %Ix[" , (size_t)acontext->alloc_ptr, |
5969 | (size_t)acontext->alloc_limit+Align(min_obj_size))); |
5970 | acontext->alloc_ptr = 0; |
5971 | acontext->alloc_limit = acontext->alloc_ptr; |
5972 | } |
5973 | } |
5974 | |
5975 | void gc_heap::repair_allocation_contexts (BOOL repair_p) |
5976 | { |
5977 | GCToEEInterface::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation, NULL); |
5978 | } |
5979 | |
5980 | struct fix_alloc_context_args |
5981 | { |
5982 | BOOL for_gc_p; |
5983 | void* heap; |
5984 | }; |
5985 | |
5986 | void fix_alloc_context (gc_alloc_context* acontext, void* param) |
5987 | { |
5988 | fix_alloc_context_args* args = (fix_alloc_context_args*)param; |
5989 | g_theGCHeap->FixAllocContext(acontext, (void*)(size_t)(args->for_gc_p), args->heap); |
5990 | } |
5991 | |
5992 | void gc_heap::fix_allocation_contexts (BOOL for_gc_p) |
5993 | { |
5994 | fix_alloc_context_args args; |
5995 | args.for_gc_p = for_gc_p; |
5996 | args.heap = __this; |
5997 | |
5998 | GCToEEInterface::GcEnumAllocContexts(fix_alloc_context, &args); |
5999 | fix_youngest_allocation_area(for_gc_p); |
6000 | fix_large_allocation_area(for_gc_p); |
6001 | } |
6002 | |
6003 | void gc_heap::fix_older_allocation_area (generation* older_gen) |
6004 | { |
6005 | heap_segment* older_gen_seg = generation_allocation_segment (older_gen); |
6006 | if (generation_allocation_limit (older_gen) != |
6007 | heap_segment_plan_allocated (older_gen_seg)) |
6008 | { |
6009 | uint8_t* point = generation_allocation_pointer (older_gen); |
6010 | |
6011 | size_t size = (generation_allocation_limit (older_gen) - |
6012 | generation_allocation_pointer (older_gen)); |
6013 | if (size != 0) |
6014 | { |
6015 | assert ((size >= Align (min_obj_size))); |
6016 | dprintf(3,("Making unused area [%Ix, %Ix[" , (size_t)point, (size_t)point+size)); |
6017 | make_unused_array (point, size); |
6018 | if (size >= min_free_list) |
6019 | { |
6020 | generation_allocator (older_gen)->thread_item_front (point, size); |
6021 | add_gen_free (older_gen->gen_num, size); |
6022 | generation_free_list_space (older_gen) += size; |
6023 | } |
6024 | else |
6025 | { |
6026 | generation_free_obj_space (older_gen) += size; |
6027 | } |
6028 | } |
6029 | } |
6030 | else |
6031 | { |
6032 | assert (older_gen_seg != ephemeral_heap_segment); |
6033 | heap_segment_plan_allocated (older_gen_seg) = |
6034 | generation_allocation_pointer (older_gen); |
6035 | generation_allocation_limit (older_gen) = |
6036 | generation_allocation_pointer (older_gen); |
6037 | } |
6038 | |
6039 | generation_allocation_pointer (older_gen) = 0; |
6040 | generation_allocation_limit (older_gen) = 0; |
6041 | } |
6042 | |
6043 | void gc_heap::set_allocation_heap_segment (generation* gen) |
6044 | { |
6045 | uint8_t* p = generation_allocation_start (gen); |
6046 | assert (p); |
6047 | heap_segment* seg = generation_allocation_segment (gen); |
6048 | if (in_range_for_segment (p, seg)) |
6049 | return; |
6050 | |
6051 | // try ephemeral heap segment in case of heap expansion |
6052 | seg = ephemeral_heap_segment; |
6053 | if (!in_range_for_segment (p, seg)) |
6054 | { |
6055 | seg = heap_segment_rw (generation_start_segment (gen)); |
6056 | |
6057 | PREFIX_ASSUME(seg != NULL); |
6058 | |
6059 | while (!in_range_for_segment (p, seg)) |
6060 | { |
6061 | seg = heap_segment_next_rw (seg); |
6062 | PREFIX_ASSUME(seg != NULL); |
6063 | } |
6064 | } |
6065 | |
6066 | generation_allocation_segment (gen) = seg; |
6067 | } |
6068 | |
6069 | void gc_heap::reset_allocation_pointers (generation* gen, uint8_t* start) |
6070 | { |
6071 | assert (start); |
6072 | assert (Align ((size_t)start) == (size_t)start); |
6073 | generation_allocation_start (gen) = start; |
6074 | generation_allocation_pointer (gen) = 0;//start + Align (min_obj_size); |
6075 | generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen); |
6076 | set_allocation_heap_segment (gen); |
6077 | } |
6078 | |
6079 | #ifdef BACKGROUND_GC |
6080 | //TODO BACKGROUND_GC this is for test only |
6081 | void |
6082 | gc_heap::disallow_new_allocation (int gen_number) |
6083 | { |
6084 | UNREFERENCED_PARAMETER(gen_number); |
6085 | settings.allocations_allowed = FALSE; |
6086 | } |
6087 | void |
6088 | gc_heap::allow_new_allocation (int gen_number) |
6089 | { |
6090 | UNREFERENCED_PARAMETER(gen_number); |
6091 | settings.allocations_allowed = TRUE; |
6092 | } |
6093 | |
6094 | #endif //BACKGROUND_GC |
6095 | |
6096 | bool gc_heap::new_allocation_allowed (int gen_number) |
6097 | { |
6098 | #ifdef BACKGROUND_GC |
6099 | //TODO BACKGROUND_GC this is for test only |
6100 | if (!settings.allocations_allowed) |
6101 | { |
6102 | dprintf (2, ("new allocation not allowed" )); |
6103 | return FALSE; |
6104 | } |
6105 | #endif //BACKGROUND_GC |
6106 | |
6107 | if (dd_new_allocation (dynamic_data_of (gen_number)) < 0) |
6108 | { |
6109 | if (gen_number != 0) |
6110 | { |
6111 | // For LOH we will give it more budget before we try a GC. |
6112 | if (settings.concurrent) |
6113 | { |
6114 | dynamic_data* dd2 = dynamic_data_of (max_generation + 1 ); |
6115 | |
6116 | if (dd_new_allocation (dd2) <= (ptrdiff_t)(-2 * dd_desired_allocation (dd2))) |
6117 | { |
6118 | return TRUE; |
6119 | } |
6120 | } |
6121 | } |
6122 | return FALSE; |
6123 | } |
6124 | #ifndef MULTIPLE_HEAPS |
6125 | else if ((settings.pause_mode != pause_no_gc) && (gen_number == 0)) |
6126 | { |
6127 | dprintf (3, ("evaluating allocation rate" )); |
6128 | dynamic_data* dd0 = dynamic_data_of (0); |
6129 | if ((allocation_running_amount - dd_new_allocation (dd0)) > |
6130 | dd_min_size (dd0)) |
6131 | { |
6132 | uint32_t ctime = GCToOSInterface::GetLowPrecisionTimeStamp(); |
6133 | if ((ctime - allocation_running_time) > 1000) |
6134 | { |
6135 | dprintf (2, (">1s since last gen0 gc" )); |
6136 | return FALSE; |
6137 | } |
6138 | else |
6139 | { |
6140 | allocation_running_amount = dd_new_allocation (dd0); |
6141 | } |
6142 | } |
6143 | } |
6144 | #endif //MULTIPLE_HEAPS |
6145 | return TRUE; |
6146 | } |
6147 | |
6148 | inline |
6149 | ptrdiff_t gc_heap::get_desired_allocation (int gen_number) |
6150 | { |
6151 | return dd_desired_allocation (dynamic_data_of (gen_number)); |
6152 | } |
6153 | |
6154 | inline |
6155 | ptrdiff_t gc_heap::get_new_allocation (int gen_number) |
6156 | { |
6157 | return dd_new_allocation (dynamic_data_of (gen_number)); |
6158 | } |
6159 | |
6160 | //return the amount allocated so far in gen_number |
6161 | inline |
6162 | ptrdiff_t gc_heap::get_allocation (int gen_number) |
6163 | { |
6164 | dynamic_data* dd = dynamic_data_of (gen_number); |
6165 | |
6166 | return dd_desired_allocation (dd) - dd_new_allocation (dd); |
6167 | } |
6168 | |
6169 | inline |
6170 | BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len) |
6171 | { |
6172 | size_t new_size = max (init_len, 2*len); |
6173 | mark* tmp = new (nothrow) mark [new_size]; |
6174 | if (tmp) |
6175 | { |
6176 | memcpy (tmp, m, len * sizeof (mark)); |
6177 | delete m; |
6178 | m = tmp; |
6179 | len = new_size; |
6180 | return TRUE; |
6181 | } |
6182 | else |
6183 | { |
6184 | dprintf (1, ("Failed to allocate %Id bytes for mark stack" , (len * sizeof (mark)))); |
6185 | return FALSE; |
6186 | } |
6187 | } |
6188 | |
6189 | inline |
6190 | uint8_t* pinned_plug (mark* m) |
6191 | { |
6192 | return m->first; |
6193 | } |
6194 | |
6195 | inline |
6196 | size_t& pinned_len (mark* m) |
6197 | { |
6198 | return m->len; |
6199 | } |
6200 | |
6201 | inline |
6202 | void set_new_pin_info (mark* m, uint8_t* pin_free_space_start) |
6203 | { |
6204 | m->len = pinned_plug (m) - pin_free_space_start; |
6205 | #ifdef SHORT_PLUGS |
6206 | m->allocation_context_start_region = pin_free_space_start; |
6207 | #endif //SHORT_PLUGS |
6208 | } |
6209 | |
6210 | #ifdef SHORT_PLUGS |
6211 | inline |
6212 | uint8_t*& pin_allocation_context_start_region (mark* m) |
6213 | { |
6214 | return m->allocation_context_start_region; |
6215 | } |
6216 | |
6217 | uint8_t* get_plug_start_in_saved (uint8_t* old_loc, mark* pinned_plug_entry) |
6218 | { |
6219 | uint8_t* saved_pre_plug_info = (uint8_t*)(pinned_plug_entry->get_pre_plug_reloc_info()); |
6220 | uint8_t* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap))); |
6221 | //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix", |
6222 | // old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved)); |
6223 | dprintf (1, ("EP: %Ix(%Ix), %Ix" , old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved)); |
6224 | return plug_start_in_saved; |
6225 | } |
6226 | |
6227 | inline |
6228 | void set_padding_in_expand (uint8_t* old_loc, |
6229 | BOOL set_padding_on_saved_p, |
6230 | mark* pinned_plug_entry) |
6231 | { |
6232 | if (set_padding_on_saved_p) |
6233 | { |
6234 | set_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry)); |
6235 | } |
6236 | else |
6237 | { |
6238 | set_plug_padded (old_loc); |
6239 | } |
6240 | } |
6241 | |
6242 | inline |
6243 | void clear_padding_in_expand (uint8_t* old_loc, |
6244 | BOOL set_padding_on_saved_p, |
6245 | mark* pinned_plug_entry) |
6246 | { |
6247 | if (set_padding_on_saved_p) |
6248 | { |
6249 | clear_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry)); |
6250 | } |
6251 | else |
6252 | { |
6253 | clear_plug_padded (old_loc); |
6254 | } |
6255 | } |
6256 | #endif //SHORT_PLUGS |
6257 | |
6258 | void gc_heap::reset_pinned_queue() |
6259 | { |
6260 | mark_stack_tos = 0; |
6261 | mark_stack_bos = 0; |
6262 | } |
6263 | |
6264 | void gc_heap::reset_pinned_queue_bos() |
6265 | { |
6266 | mark_stack_bos = 0; |
6267 | } |
6268 | |
6269 | // last_pinned_plug is only for asserting purpose. |
6270 | void gc_heap::merge_with_last_pinned_plug (uint8_t* last_pinned_plug, size_t plug_size) |
6271 | { |
6272 | if (last_pinned_plug) |
6273 | { |
6274 | mark& last_m = mark_stack_array[mark_stack_tos - 1]; |
6275 | assert (last_pinned_plug == last_m.first); |
6276 | if (last_m.saved_post_p) |
6277 | { |
6278 | last_m.saved_post_p = FALSE; |
6279 | dprintf (3, ("setting last plug %Ix post to false" , last_m.first)); |
6280 | // We need to recover what the gap has overwritten. |
6281 | memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair)); |
6282 | } |
6283 | last_m.len += plug_size; |
6284 | dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix" , last_m.first, last_m.len)); |
6285 | } |
6286 | } |
6287 | |
6288 | void gc_heap::set_allocator_next_pin (uint8_t* alloc_pointer, uint8_t*& alloc_limit) |
6289 | { |
6290 | dprintf (3, ("sanp: ptr: %Ix, limit: %Ix" , alloc_pointer, alloc_limit)); |
6291 | dprintf (3, ("oldest %Id: %Ix" , mark_stack_bos, pinned_plug (oldest_pin()))); |
6292 | if (!(pinned_plug_que_empty_p())) |
6293 | { |
6294 | mark* oldest_entry = oldest_pin(); |
6295 | uint8_t* plug = pinned_plug (oldest_entry); |
6296 | if ((plug >= alloc_pointer) && (plug < alloc_limit)) |
6297 | { |
6298 | alloc_limit = pinned_plug (oldest_entry); |
6299 | dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)" , |
6300 | alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer))); |
6301 | } |
6302 | } |
6303 | } |
6304 | |
6305 | void gc_heap::set_allocator_next_pin (generation* gen) |
6306 | { |
6307 | dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix" , gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen))); |
6308 | if (!(pinned_plug_que_empty_p())) |
6309 | { |
6310 | mark* oldest_entry = oldest_pin(); |
6311 | uint8_t* plug = pinned_plug (oldest_entry); |
6312 | if ((plug >= generation_allocation_pointer (gen)) && |
6313 | (plug < generation_allocation_limit (gen))) |
6314 | { |
6315 | generation_allocation_limit (gen) = pinned_plug (oldest_entry); |
6316 | dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)" , |
6317 | gen->gen_num, |
6318 | generation_allocation_pointer (gen), generation_allocation_limit (gen), |
6319 | (generation_allocation_limit (gen) - generation_allocation_pointer (gen)))); |
6320 | } |
6321 | else |
6322 | assert (!((plug < generation_allocation_pointer (gen)) && |
6323 | (plug >= heap_segment_mem (generation_allocation_segment (gen))))); |
6324 | } |
6325 | } |
6326 | |
6327 | // After we set the info, we increase tos. |
6328 | void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, uint8_t* alloc_pointer, uint8_t*& alloc_limit) |
6329 | { |
6330 | UNREFERENCED_PARAMETER(last_pinned_plug); |
6331 | |
6332 | mark& m = mark_stack_array[mark_stack_tos]; |
6333 | assert (m.first == last_pinned_plug); |
6334 | |
6335 | m.len = plug_len; |
6336 | mark_stack_tos++; |
6337 | set_allocator_next_pin (alloc_pointer, alloc_limit); |
6338 | } |
6339 | |
6340 | // After we set the info, we increase tos. |
6341 | void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, generation* gen) |
6342 | { |
6343 | UNREFERENCED_PARAMETER(last_pinned_plug); |
6344 | |
6345 | mark& m = mark_stack_array[mark_stack_tos]; |
6346 | assert (m.first == last_pinned_plug); |
6347 | |
6348 | m.len = plug_len; |
6349 | mark_stack_tos++; |
6350 | assert (gen != 0); |
6351 | // Why are we checking here? gen is never 0. |
6352 | if (gen != 0) |
6353 | { |
6354 | set_allocator_next_pin (gen); |
6355 | } |
6356 | } |
6357 | |
6358 | size_t gc_heap::deque_pinned_plug () |
6359 | { |
6360 | dprintf (3, ("dequed: %Id" , mark_stack_bos)); |
6361 | size_t m = mark_stack_bos; |
6362 | mark_stack_bos++; |
6363 | return m; |
6364 | } |
6365 | |
6366 | inline |
6367 | mark* gc_heap::pinned_plug_of (size_t bos) |
6368 | { |
6369 | return &mark_stack_array [ bos ]; |
6370 | } |
6371 | |
6372 | inline |
6373 | mark* gc_heap::oldest_pin () |
6374 | { |
6375 | return pinned_plug_of (mark_stack_bos); |
6376 | } |
6377 | |
6378 | inline |
6379 | BOOL gc_heap::pinned_plug_que_empty_p () |
6380 | { |
6381 | return (mark_stack_bos == mark_stack_tos); |
6382 | } |
6383 | |
6384 | inline |
6385 | mark* gc_heap::before_oldest_pin() |
6386 | { |
6387 | if (mark_stack_bos >= 1) |
6388 | return pinned_plug_of (mark_stack_bos-1); |
6389 | else |
6390 | return 0; |
6391 | } |
6392 | |
6393 | inline |
6394 | BOOL gc_heap::ephemeral_pointer_p (uint8_t* o) |
6395 | { |
6396 | return ((o >= ephemeral_low) && (o < ephemeral_high)); |
6397 | } |
6398 | |
6399 | #ifdef MH_SC_MARK |
6400 | inline |
6401 | int& gc_heap::mark_stack_busy() |
6402 | { |
6403 | return g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)]; |
6404 | } |
6405 | #endif //MH_SC_MARK |
6406 | |
6407 | void gc_heap::make_mark_stack (mark* arr) |
6408 | { |
6409 | reset_pinned_queue(); |
6410 | mark_stack_array = arr; |
6411 | mark_stack_array_length = MARK_STACK_INITIAL_LENGTH; |
6412 | #ifdef MH_SC_MARK |
6413 | mark_stack_busy() = 0; |
6414 | #endif //MH_SC_MARK |
6415 | } |
6416 | |
6417 | #ifdef BACKGROUND_GC |
6418 | inline |
6419 | size_t& gc_heap::bpromoted_bytes(int thread) |
6420 | { |
6421 | #ifdef MULTIPLE_HEAPS |
6422 | return g_bpromoted [thread*16]; |
6423 | #else //MULTIPLE_HEAPS |
6424 | UNREFERENCED_PARAMETER(thread); |
6425 | return g_bpromoted; |
6426 | #endif //MULTIPLE_HEAPS |
6427 | } |
6428 | |
6429 | void gc_heap::make_background_mark_stack (uint8_t** arr) |
6430 | { |
6431 | background_mark_stack_array = arr; |
6432 | background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH; |
6433 | background_mark_stack_tos = arr; |
6434 | } |
6435 | |
6436 | void gc_heap::make_c_mark_list (uint8_t** arr) |
6437 | { |
6438 | c_mark_list = arr; |
6439 | c_mark_list_index = 0; |
6440 | c_mark_list_length = 1 + (OS_PAGE_SIZE / MIN_OBJECT_SIZE); |
6441 | } |
6442 | #endif //BACKGROUND_GC |
6443 | |
6444 | |
6445 | #ifdef CARD_BUNDLE |
6446 | |
6447 | // The card bundle keeps track of groups of card words. |
6448 | static const size_t card_bundle_word_width = 32; |
6449 | |
6450 | // How do we express the fact that 32 bits (card_word_width) is one uint32_t? |
6451 | static const size_t card_bundle_size = (size_t)(GC_PAGE_SIZE / (sizeof(uint32_t)*card_bundle_word_width)); |
6452 | |
6453 | inline |
6454 | size_t card_bundle_word (size_t cardb) |
6455 | { |
6456 | return cardb / card_bundle_word_width; |
6457 | } |
6458 | |
6459 | inline |
6460 | uint32_t card_bundle_bit (size_t cardb) |
6461 | { |
6462 | return (uint32_t)(cardb % card_bundle_word_width); |
6463 | } |
6464 | |
6465 | size_t align_cardw_on_bundle (size_t cardw) |
6466 | { |
6467 | return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 )); |
6468 | } |
6469 | |
6470 | // Get the card bundle representing a card word |
6471 | size_t cardw_card_bundle (size_t cardw) |
6472 | { |
6473 | return cardw / card_bundle_size; |
6474 | } |
6475 | |
6476 | // Get the first card word in a card bundle |
6477 | size_t card_bundle_cardw (size_t cardb) |
6478 | { |
6479 | return cardb * card_bundle_size; |
6480 | } |
6481 | |
6482 | // Clear the specified card bundle |
6483 | void gc_heap::card_bundle_clear (size_t cardb) |
6484 | { |
6485 | card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb)); |
6486 | dprintf (1,("Cleared card bundle %Ix [%Ix, %Ix[" , cardb, (size_t)card_bundle_cardw (cardb), |
6487 | (size_t)card_bundle_cardw (cardb+1))); |
6488 | } |
6489 | |
6490 | void gc_heap::card_bundle_set (size_t cardb) |
6491 | { |
6492 | if (!card_bundle_set_p (cardb)) |
6493 | { |
6494 | card_bundle_table [card_bundle_word (cardb)] |= (1 << card_bundle_bit (cardb)); |
6495 | } |
6496 | } |
6497 | |
6498 | // Set the card bundle bits between start_cardb and end_cardb |
6499 | void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb) |
6500 | { |
6501 | if (start_cardb == end_cardb) |
6502 | { |
6503 | card_bundle_set(start_cardb); |
6504 | return; |
6505 | } |
6506 | |
6507 | size_t start_word = card_bundle_word (start_cardb); |
6508 | size_t end_word = card_bundle_word (end_cardb); |
6509 | |
6510 | if (start_word < end_word) |
6511 | { |
6512 | // Set the partial words |
6513 | card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb)); |
6514 | |
6515 | if (card_bundle_bit (end_cardb)) |
6516 | card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb)); |
6517 | |
6518 | // Set the full words |
6519 | for (size_t i = start_word + 1; i < end_word; i++) |
6520 | card_bundle_table [i] = ~0u; |
6521 | } |
6522 | else |
6523 | { |
6524 | card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) & |
6525 | lowbits (~0u, card_bundle_bit (end_cardb))); |
6526 | } |
6527 | } |
6528 | |
6529 | // Indicates whether the specified bundle is set. |
6530 | BOOL gc_heap::card_bundle_set_p (size_t cardb) |
6531 | { |
6532 | return (card_bundle_table[card_bundle_word(cardb)] & (1 << card_bundle_bit (cardb))); |
6533 | } |
6534 | |
6535 | // Returns the size (in bytes) of a card bundle representing the region from 'from' to 'end' |
6536 | size_t size_card_bundle_of (uint8_t* from, uint8_t* end) |
6537 | { |
6538 | // Number of heap bytes represented by a card bundle word |
6539 | size_t cbw_span = card_size * card_word_width * card_bundle_size * card_bundle_word_width; |
6540 | |
6541 | // Align the start of the region down |
6542 | from = (uint8_t*)((size_t)from & ~(cbw_span - 1)); |
6543 | |
6544 | // Align the end of the region up |
6545 | end = (uint8_t*)((size_t)(end + (cbw_span - 1)) & ~(cbw_span - 1)); |
6546 | |
6547 | // Make sure they're really aligned |
6548 | assert (((size_t)from & (cbw_span - 1)) == 0); |
6549 | assert (((size_t)end & (cbw_span - 1)) == 0); |
6550 | |
6551 | return ((end - from) / cbw_span) * sizeof (uint32_t); |
6552 | } |
6553 | |
6554 | // Takes a pointer to a card bundle table and an address, and returns a pointer that represents |
6555 | // where a theoretical card bundle table that represents every address (starting from 0) would |
6556 | // start if the bundle word representing the address were to be located at the pointer passed in. |
6557 | // The returned 'translated' pointer makes it convenient/fast to calculate where the card bundle |
6558 | // for a given address is using a simple shift operation on the address. |
6559 | uint32_t* translate_card_bundle_table (uint32_t* cb, uint8_t* lowest_address) |
6560 | { |
6561 | // The number of bytes of heap memory represented by a card bundle word |
6562 | const size_t heap_bytes_for_bundle_word = card_size * card_word_width * card_bundle_size * card_bundle_word_width; |
6563 | |
6564 | // Each card bundle word is 32 bits |
6565 | return (uint32_t*)((uint8_t*)cb - (((size_t)lowest_address / heap_bytes_for_bundle_word) * sizeof (uint32_t))); |
6566 | } |
6567 | |
6568 | void gc_heap::enable_card_bundles () |
6569 | { |
6570 | if (can_use_write_watch_for_card_table() && (!card_bundles_enabled())) |
6571 | { |
6572 | dprintf (1, ("Enabling card bundles" )); |
6573 | |
6574 | // We initially set all of the card bundles |
6575 | card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))), |
6576 | cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address))))); |
6577 | settings.card_bundles = TRUE; |
6578 | } |
6579 | } |
6580 | |
6581 | BOOL gc_heap::card_bundles_enabled () |
6582 | { |
6583 | return settings.card_bundles; |
6584 | } |
6585 | |
6586 | #endif // CARD_BUNDLE |
6587 | |
6588 | #if defined (_TARGET_AMD64_) |
6589 | #define brick_size ((size_t)4096) |
6590 | #else |
6591 | #define brick_size ((size_t)2048) |
6592 | #endif //_TARGET_AMD64_ |
6593 | |
6594 | inline |
6595 | size_t gc_heap::brick_of (uint8_t* add) |
6596 | { |
6597 | return (size_t)(add - lowest_address) / brick_size; |
6598 | } |
6599 | |
6600 | inline |
6601 | uint8_t* gc_heap::brick_address (size_t brick) |
6602 | { |
6603 | return lowest_address + (brick_size * brick); |
6604 | } |
6605 | |
6606 | |
6607 | void gc_heap::clear_brick_table (uint8_t* from, uint8_t* end) |
6608 | { |
6609 | for (size_t i = brick_of (from);i < brick_of (end); i++) |
6610 | brick_table[i] = 0; |
6611 | } |
6612 | |
6613 | //codes for the brick entries: |
6614 | //entry == 0 -> not assigned |
6615 | //entry >0 offset is entry-1 |
6616 | //entry <0 jump back entry bricks |
6617 | |
6618 | |
6619 | inline |
6620 | void gc_heap::set_brick (size_t index, ptrdiff_t val) |
6621 | { |
6622 | if (val < -32767) |
6623 | { |
6624 | val = -32767; |
6625 | } |
6626 | assert (val < 32767); |
6627 | if (val >= 0) |
6628 | brick_table [index] = (short)val+1; |
6629 | else |
6630 | brick_table [index] = (short)val; |
6631 | } |
6632 | |
6633 | inline |
6634 | int gc_heap::get_brick_entry (size_t index) |
6635 | { |
6636 | #ifdef MULTIPLE_HEAPS |
6637 | return VolatileLoadWithoutBarrier(&brick_table [index]); |
6638 | #else |
6639 | return brick_table[index]; |
6640 | #endif |
6641 | } |
6642 | |
6643 | |
6644 | inline |
6645 | uint8_t* align_on_brick (uint8_t* add) |
6646 | { |
6647 | return (uint8_t*)((size_t)(add + brick_size - 1) & ~(brick_size - 1)); |
6648 | } |
6649 | |
6650 | inline |
6651 | uint8_t* align_lower_brick (uint8_t* add) |
6652 | { |
6653 | return (uint8_t*)(((size_t)add) & ~(brick_size - 1)); |
6654 | } |
6655 | |
6656 | size_t size_brick_of (uint8_t* from, uint8_t* end) |
6657 | { |
6658 | assert (((size_t)from & (brick_size-1)) == 0); |
6659 | assert (((size_t)end & (brick_size-1)) == 0); |
6660 | |
6661 | return ((end - from) / brick_size) * sizeof (short); |
6662 | } |
6663 | |
6664 | inline |
6665 | uint8_t* gc_heap::card_address (size_t card) |
6666 | { |
6667 | return (uint8_t*) (card_size * card); |
6668 | } |
6669 | |
6670 | inline |
6671 | size_t gc_heap::card_of ( uint8_t* object) |
6672 | { |
6673 | return (size_t)(object) / card_size; |
6674 | } |
6675 | |
6676 | inline |
6677 | size_t gc_heap::card_to_brick (size_t card) |
6678 | { |
6679 | return brick_of (card_address (card)); |
6680 | } |
6681 | |
6682 | inline |
6683 | uint8_t* align_on_card (uint8_t* add) |
6684 | { |
6685 | return (uint8_t*)((size_t)(add + card_size - 1) & ~(card_size - 1 )); |
6686 | } |
6687 | inline |
6688 | uint8_t* align_on_card_word (uint8_t* add) |
6689 | { |
6690 | return (uint8_t*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1)); |
6691 | } |
6692 | |
6693 | inline |
6694 | uint8_t* align_lower_card (uint8_t* add) |
6695 | { |
6696 | return (uint8_t*)((size_t)add & ~(card_size-1)); |
6697 | } |
6698 | |
6699 | inline |
6700 | void gc_heap::clear_card (size_t card) |
6701 | { |
6702 | card_table [card_word (card)] = |
6703 | (card_table [card_word (card)] & ~(1 << card_bit (card))); |
6704 | dprintf (3,("Cleared card %Ix [%Ix, %Ix[" , card, (size_t)card_address (card), |
6705 | (size_t)card_address (card+1))); |
6706 | } |
6707 | |
6708 | inline |
6709 | void gc_heap::set_card (size_t card) |
6710 | { |
6711 | size_t word = card_word (card); |
6712 | card_table[word] = (card_table [word] | (1 << card_bit (card))); |
6713 | |
6714 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
6715 | // Also set the card bundle that corresponds to the card |
6716 | size_t bundle_to_set = cardw_card_bundle(word); |
6717 | |
6718 | card_bundle_set(bundle_to_set); |
6719 | |
6720 | dprintf (3,("Set card %Ix [%Ix, %Ix[ and bundle %Ix" , card, (size_t)card_address (card), (size_t)card_address (card+1), bundle_to_set)); |
6721 | assert(card_bundle_set_p(bundle_to_set) != 0); |
6722 | #endif |
6723 | } |
6724 | |
6725 | inline |
6726 | BOOL gc_heap::card_set_p (size_t card) |
6727 | { |
6728 | return ( card_table [ card_word (card) ] & (1 << card_bit (card))); |
6729 | } |
6730 | |
6731 | // Returns the number of DWORDs in the card table that cover the |
6732 | // range of addresses [from, end[. |
6733 | size_t count_card_of (uint8_t* from, uint8_t* end) |
6734 | { |
6735 | return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1; |
6736 | } |
6737 | |
6738 | // Returns the number of bytes to allocate for a card table |
6739 | // that covers the range of addresses [from, end[. |
6740 | size_t size_card_of (uint8_t* from, uint8_t* end) |
6741 | { |
6742 | return count_card_of (from, end) * sizeof(uint32_t); |
6743 | } |
6744 | |
6745 | // We don't store seg_mapping_table in card_table_info because there's only always one view. |
6746 | class card_table_info |
6747 | { |
6748 | public: |
6749 | unsigned recount; |
6750 | uint8_t* lowest_address; |
6751 | uint8_t* highest_address; |
6752 | short* brick_table; |
6753 | |
6754 | #ifdef CARD_BUNDLE |
6755 | uint32_t* card_bundle_table; |
6756 | #endif //CARD_BUNDLE |
6757 | |
6758 | // mark_array is always at the end of the data structure because we |
6759 | // want to be able to make one commit call for everything before it. |
6760 | #ifdef MARK_ARRAY |
6761 | uint32_t* mark_array; |
6762 | #endif //MARK_ARRAY |
6763 | |
6764 | size_t size; |
6765 | uint32_t* next_card_table; |
6766 | }; |
6767 | |
6768 | //These are accessors on untranslated cardtable |
6769 | inline |
6770 | unsigned& card_table_refcount (uint32_t* c_table) |
6771 | { |
6772 | return *(unsigned*)((char*)c_table - sizeof (card_table_info)); |
6773 | } |
6774 | |
6775 | inline |
6776 | uint8_t*& card_table_lowest_address (uint32_t* c_table) |
6777 | { |
6778 | return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->lowest_address; |
6779 | } |
6780 | |
6781 | uint32_t* translate_card_table (uint32_t* ct) |
6782 | { |
6783 | return (uint32_t*)((uint8_t*)ct - card_word (gcard_of (card_table_lowest_address (ct))) * sizeof(uint32_t)); |
6784 | } |
6785 | |
6786 | inline |
6787 | uint8_t*& card_table_highest_address (uint32_t* c_table) |
6788 | { |
6789 | return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->highest_address; |
6790 | } |
6791 | |
6792 | inline |
6793 | short*& card_table_brick_table (uint32_t* c_table) |
6794 | { |
6795 | return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->brick_table; |
6796 | } |
6797 | |
6798 | #ifdef CARD_BUNDLE |
6799 | inline |
6800 | uint32_t*& card_table_card_bundle_table (uint32_t* c_table) |
6801 | { |
6802 | return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->card_bundle_table; |
6803 | } |
6804 | #endif //CARD_BUNDLE |
6805 | |
6806 | #ifdef MARK_ARRAY |
6807 | /* Support for mark_array */ |
6808 | |
6809 | inline |
6810 | uint32_t*& card_table_mark_array (uint32_t* c_table) |
6811 | { |
6812 | return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->mark_array; |
6813 | } |
6814 | |
6815 | #ifdef BIT64 |
6816 | #define mark_bit_pitch ((size_t)16) |
6817 | #else |
6818 | #define mark_bit_pitch ((size_t)8) |
6819 | #endif // BIT64 |
6820 | #define mark_word_width ((size_t)32) |
6821 | #define mark_word_size (mark_word_width * mark_bit_pitch) |
6822 | |
6823 | inline |
6824 | uint8_t* align_on_mark_bit (uint8_t* add) |
6825 | { |
6826 | return (uint8_t*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1)); |
6827 | } |
6828 | |
6829 | inline |
6830 | uint8_t* align_lower_mark_bit (uint8_t* add) |
6831 | { |
6832 | return (uint8_t*)((size_t)(add) & ~(mark_bit_pitch - 1)); |
6833 | } |
6834 | |
6835 | inline |
6836 | BOOL is_aligned_on_mark_word (uint8_t* add) |
6837 | { |
6838 | return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1))); |
6839 | } |
6840 | |
6841 | inline |
6842 | uint8_t* align_on_mark_word (uint8_t* add) |
6843 | { |
6844 | return (uint8_t*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1)); |
6845 | } |
6846 | |
6847 | inline |
6848 | uint8_t* align_lower_mark_word (uint8_t* add) |
6849 | { |
6850 | return (uint8_t*)((size_t)(add) & ~(mark_word_size - 1)); |
6851 | } |
6852 | |
6853 | inline |
6854 | size_t mark_bit_of (uint8_t* add) |
6855 | { |
6856 | return ((size_t)add / mark_bit_pitch); |
6857 | } |
6858 | |
6859 | inline |
6860 | unsigned int mark_bit_bit (size_t mark_bit) |
6861 | { |
6862 | return (unsigned int)(mark_bit % mark_word_width); |
6863 | } |
6864 | |
6865 | inline |
6866 | size_t mark_bit_word (size_t mark_bit) |
6867 | { |
6868 | return (mark_bit / mark_word_width); |
6869 | } |
6870 | |
6871 | inline |
6872 | size_t mark_word_of (uint8_t* add) |
6873 | { |
6874 | return ((size_t)add) / mark_word_size; |
6875 | } |
6876 | |
6877 | uint8_t* mark_word_address (size_t wd) |
6878 | { |
6879 | return (uint8_t*)(wd*mark_word_size); |
6880 | } |
6881 | |
6882 | uint8_t* mark_bit_address (size_t mark_bit) |
6883 | { |
6884 | return (uint8_t*)(mark_bit*mark_bit_pitch); |
6885 | } |
6886 | |
6887 | inline |
6888 | size_t mark_bit_bit_of (uint8_t* add) |
6889 | { |
6890 | return (((size_t)add / mark_bit_pitch) % mark_word_width); |
6891 | } |
6892 | |
6893 | inline |
6894 | unsigned int gc_heap::mark_array_marked(uint8_t* add) |
6895 | { |
6896 | return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)); |
6897 | } |
6898 | |
6899 | inline |
6900 | BOOL gc_heap::is_mark_bit_set (uint8_t* add) |
6901 | { |
6902 | return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add))); |
6903 | } |
6904 | |
6905 | inline |
6906 | void gc_heap::mark_array_set_marked (uint8_t* add) |
6907 | { |
6908 | size_t index = mark_word_of (add); |
6909 | uint32_t val = (1 << mark_bit_bit_of (add)); |
6910 | #ifdef MULTIPLE_HEAPS |
6911 | Interlocked::Or (&(mark_array [index]), val); |
6912 | #else |
6913 | mark_array [index] |= val; |
6914 | #endif |
6915 | } |
6916 | |
6917 | inline |
6918 | void gc_heap::mark_array_clear_marked (uint8_t* add) |
6919 | { |
6920 | mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add)); |
6921 | } |
6922 | |
6923 | size_t size_mark_array_of (uint8_t* from, uint8_t* end) |
6924 | { |
6925 | assert (((size_t)from & ((mark_word_size)-1)) == 0); |
6926 | assert (((size_t)end & ((mark_word_size)-1)) == 0); |
6927 | return sizeof (uint32_t)*(((end - from) / mark_word_size)); |
6928 | } |
6929 | |
6930 | //In order to eliminate the lowest_address in the mark array |
6931 | //computations (mark_word_of, etc) mark_array is offset |
6932 | // according to the lowest_address. |
6933 | uint32_t* translate_mark_array (uint32_t* ma) |
6934 | { |
6935 | return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address)); |
6936 | } |
6937 | |
6938 | // from and end must be page aligned addresses. |
6939 | void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end, BOOL check_only/*=TRUE*/ |
6940 | #ifdef FEATURE_BASICFREEZE |
6941 | , BOOL read_only/*=FALSE*/ |
6942 | #endif // FEATURE_BASICFREEZE |
6943 | ) |
6944 | { |
6945 | if(!gc_can_use_concurrent) |
6946 | return; |
6947 | |
6948 | #ifdef FEATURE_BASICFREEZE |
6949 | if (!read_only) |
6950 | #endif // FEATURE_BASICFREEZE |
6951 | { |
6952 | assert (from == align_on_mark_word (from)); |
6953 | } |
6954 | assert (end == align_on_mark_word (end)); |
6955 | |
6956 | #ifdef BACKGROUND_GC |
6957 | uint8_t* current_lowest_address = background_saved_lowest_address; |
6958 | uint8_t* current_highest_address = background_saved_highest_address; |
6959 | #else |
6960 | uint8_t* current_lowest_address = lowest_address; |
6961 | uint8_t* current_highest_address = highest_address; |
6962 | #endif //BACKGROUND_GC |
6963 | |
6964 | //there is a possibility of the addresses to be |
6965 | //outside of the covered range because of a newly allocated |
6966 | //large object segment |
6967 | if ((end <= current_highest_address) && (from >= current_lowest_address)) |
6968 | { |
6969 | size_t beg_word = mark_word_of (align_on_mark_word (from)); |
6970 | MAYBE_UNUSED_VAR(beg_word); |
6971 | //align end word to make sure to cover the address |
6972 | size_t end_word = mark_word_of (align_on_mark_word (end)); |
6973 | MAYBE_UNUSED_VAR(end_word); |
6974 | dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)" , |
6975 | (size_t)mark_word_address (beg_word), |
6976 | (size_t)mark_word_address (end_word), |
6977 | (size_t)from, (size_t)end, |
6978 | (check_only ? "check_only" : "clear" ))); |
6979 | if (!check_only) |
6980 | { |
6981 | uint8_t* op = from; |
6982 | while (op < mark_word_address (beg_word)) |
6983 | { |
6984 | mark_array_clear_marked (op); |
6985 | op += mark_bit_pitch; |
6986 | } |
6987 | |
6988 | memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (uint32_t)); |
6989 | } |
6990 | #ifdef _DEBUG |
6991 | else |
6992 | { |
6993 | //Beware, it is assumed that the mark array word straddling |
6994 | //start has been cleared before |
6995 | //verify that the array is empty. |
6996 | size_t markw = mark_word_of (align_on_mark_word (from)); |
6997 | size_t markw_end = mark_word_of (align_on_mark_word (end)); |
6998 | while (markw < markw_end) |
6999 | { |
7000 | assert (!(mark_array [markw])); |
7001 | markw++; |
7002 | } |
7003 | uint8_t* p = mark_word_address (markw_end); |
7004 | while (p < end) |
7005 | { |
7006 | assert (!(mark_array_marked (p))); |
7007 | p++; |
7008 | } |
7009 | } |
7010 | #endif //_DEBUG |
7011 | } |
7012 | } |
7013 | #endif //MARK_ARRAY |
7014 | |
7015 | //These work on untranslated card tables |
7016 | inline |
7017 | uint32_t*& card_table_next (uint32_t* c_table) |
7018 | { |
7019 | return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->next_card_table; |
7020 | } |
7021 | |
7022 | inline |
7023 | size_t& card_table_size (uint32_t* c_table) |
7024 | { |
7025 | return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->size; |
7026 | } |
7027 | |
7028 | void own_card_table (uint32_t* c_table) |
7029 | { |
7030 | card_table_refcount (c_table) += 1; |
7031 | } |
7032 | |
7033 | void destroy_card_table (uint32_t* c_table); |
7034 | |
7035 | void delete_next_card_table (uint32_t* c_table) |
7036 | { |
7037 | uint32_t* n_table = card_table_next (c_table); |
7038 | if (n_table) |
7039 | { |
7040 | if (card_table_next (n_table)) |
7041 | { |
7042 | delete_next_card_table (n_table); |
7043 | } |
7044 | if (card_table_refcount (n_table) == 0) |
7045 | { |
7046 | destroy_card_table (n_table); |
7047 | card_table_next (c_table) = 0; |
7048 | } |
7049 | } |
7050 | } |
7051 | |
7052 | void release_card_table (uint32_t* c_table) |
7053 | { |
7054 | assert (card_table_refcount (c_table) >0); |
7055 | card_table_refcount (c_table) -= 1; |
7056 | if (card_table_refcount (c_table) == 0) |
7057 | { |
7058 | delete_next_card_table (c_table); |
7059 | if (card_table_next (c_table) == 0) |
7060 | { |
7061 | destroy_card_table (c_table); |
7062 | // sever the link from the parent |
7063 | if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table) |
7064 | { |
7065 | g_gc_card_table = 0; |
7066 | |
7067 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7068 | g_gc_card_bundle_table = 0; |
7069 | #endif |
7070 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7071 | SoftwareWriteWatch::StaticClose(); |
7072 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7073 | } |
7074 | else |
7075 | { |
7076 | uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))]; |
7077 | if (p_table) |
7078 | { |
7079 | while (p_table && (card_table_next (p_table) != c_table)) |
7080 | p_table = card_table_next (p_table); |
7081 | card_table_next (p_table) = 0; |
7082 | } |
7083 | } |
7084 | } |
7085 | } |
7086 | } |
7087 | |
7088 | void destroy_card_table (uint32_t* c_table) |
7089 | { |
7090 | // delete (uint32_t*)&card_table_refcount(c_table); |
7091 | |
7092 | GCToOSInterface::VirtualRelease (&card_table_refcount(c_table), card_table_size(c_table)); |
7093 | dprintf (2, ("Table Virtual Free : %Ix" , (size_t)&card_table_refcount(c_table))); |
7094 | } |
7095 | |
7096 | uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) |
7097 | { |
7098 | assert (g_gc_lowest_address == start); |
7099 | assert (g_gc_highest_address == end); |
7100 | |
7101 | uint32_t virtual_reserve_flags = VirtualReserveFlags::None; |
7102 | |
7103 | size_t bs = size_brick_of (start, end); |
7104 | size_t cs = size_card_of (start, end); |
7105 | #ifdef MARK_ARRAY |
7106 | size_t ms = (gc_can_use_concurrent ? |
7107 | size_mark_array_of (start, end) : |
7108 | 0); |
7109 | #else |
7110 | size_t ms = 0; |
7111 | #endif //MARK_ARRAY |
7112 | |
7113 | size_t cb = 0; |
7114 | |
7115 | #ifdef CARD_BUNDLE |
7116 | if (can_use_write_watch_for_card_table()) |
7117 | { |
7118 | cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address); |
7119 | #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7120 | // If we're not manually managing the card bundles, we will need to use OS write |
7121 | // watch APIs over this region to track changes. |
7122 | virtual_reserve_flags |= VirtualReserveFlags::WriteWatch; |
7123 | #endif |
7124 | } |
7125 | #endif //CARD_BUNDLE |
7126 | |
7127 | size_t wws = 0; |
7128 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7129 | size_t sw_ww_table_offset = 0; |
7130 | if (gc_can_use_concurrent) |
7131 | { |
7132 | size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb; |
7133 | sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table); |
7134 | wws = sw_ww_table_offset - sw_ww_size_before_table + SoftwareWriteWatch::GetTableByteSize(start, end); |
7135 | } |
7136 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7137 | |
7138 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
7139 | size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address); |
7140 | size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws; |
7141 | size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset); |
7142 | |
7143 | st += (st_table_offset_aligned - st_table_offset); |
7144 | #else //GROWABLE_SEG_MAPPING_TABLE |
7145 | size_t st = 0; |
7146 | #endif //GROWABLE_SEG_MAPPING_TABLE |
7147 | |
7148 | // it is impossible for alloc_size to overflow due bounds on each of |
7149 | // its components. |
7150 | size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms); |
7151 | uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags); |
7152 | |
7153 | if (!mem) |
7154 | return 0; |
7155 | |
7156 | dprintf (2, ("Init - Card table alloc for %Id bytes: [%Ix, %Ix[" , |
7157 | alloc_size, (size_t)mem, (size_t)(mem+alloc_size))); |
7158 | |
7159 | // mark array will be committed separately (per segment). |
7160 | size_t commit_size = alloc_size - ms; |
7161 | |
7162 | if (!GCToOSInterface::VirtualCommit (mem, commit_size)) |
7163 | { |
7164 | dprintf (2, ("Card table commit failed" )); |
7165 | GCToOSInterface::VirtualRelease (mem, alloc_size); |
7166 | return 0; |
7167 | } |
7168 | |
7169 | // initialize the ref count |
7170 | uint32_t* ct = (uint32_t*)(mem+sizeof (card_table_info)); |
7171 | card_table_refcount (ct) = 0; |
7172 | card_table_lowest_address (ct) = start; |
7173 | card_table_highest_address (ct) = end; |
7174 | card_table_brick_table (ct) = (short*)((uint8_t*)ct + cs); |
7175 | card_table_size (ct) = alloc_size; |
7176 | card_table_next (ct) = 0; |
7177 | |
7178 | #ifdef CARD_BUNDLE |
7179 | card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs); |
7180 | |
7181 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7182 | g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), g_gc_lowest_address); |
7183 | #endif |
7184 | |
7185 | #endif //CARD_BUNDLE |
7186 | |
7187 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7188 | if (gc_can_use_concurrent) |
7189 | { |
7190 | SoftwareWriteWatch::InitializeUntranslatedTable(mem + sw_ww_table_offset, start); |
7191 | } |
7192 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7193 | |
7194 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
7195 | seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned); |
7196 | seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table - |
7197 | size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address)))); |
7198 | #endif //GROWABLE_SEG_MAPPING_TABLE |
7199 | |
7200 | #ifdef MARK_ARRAY |
7201 | if (gc_can_use_concurrent) |
7202 | card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st); |
7203 | else |
7204 | card_table_mark_array (ct) = NULL; |
7205 | #endif //MARK_ARRAY |
7206 | |
7207 | return translate_card_table(ct); |
7208 | } |
7209 | |
7210 | void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p) |
7211 | { |
7212 | #ifdef MULTIPLE_HEAPS |
7213 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
7214 | { |
7215 | gc_heap* hp = gc_heap::g_heaps [hn]; |
7216 | hp->fgm_result.set_fgm (f, s, loh_p); |
7217 | } |
7218 | #else //MULTIPLE_HEAPS |
7219 | fgm_result.set_fgm (f, s, loh_p); |
7220 | #endif //MULTIPLE_HEAPS |
7221 | } |
7222 | |
7223 | //returns 0 for success, -1 otherwise |
7224 | // We are doing all the decommitting here because we want to make sure we have |
7225 | // enough memory to do so - if we do this during copy_brick_card_table and |
7226 | // and fail to decommit it would make the failure case very complicated to |
7227 | // handle. This way we can waste some decommit if we call this multiple |
7228 | // times before the next FGC but it's easier to handle the failure case. |
7229 | int gc_heap::grow_brick_card_tables (uint8_t* start, |
7230 | uint8_t* end, |
7231 | size_t size, |
7232 | heap_segment* new_seg, |
7233 | gc_heap* hp, |
7234 | BOOL loh_p) |
7235 | { |
7236 | uint8_t* la = g_gc_lowest_address; |
7237 | uint8_t* ha = g_gc_highest_address; |
7238 | uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address); |
7239 | uint8_t* saved_g_highest_address = max (end, g_gc_highest_address); |
7240 | seg_mapping* new_seg_mapping_table = nullptr; |
7241 | #ifdef BACKGROUND_GC |
7242 | // This value is only for logging purpose - it's not necessarily exactly what we |
7243 | // would commit for mark array but close enough for diagnostics purpose. |
7244 | size_t logging_ma_commit_size = size_mark_array_of (0, (uint8_t*)size); |
7245 | #endif //BACKGROUND_GC |
7246 | |
7247 | // See if the address is already covered |
7248 | if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address)) |
7249 | { |
7250 | { |
7251 | //modify the higest address so the span covered |
7252 | //is twice the previous one. |
7253 | uint8_t* top = (uint8_t*)0 + Align (GCToOSInterface::GetVirtualMemoryLimit()); |
7254 | // On non-Windows systems, we get only an approximate value that can possibly be |
7255 | // slightly lower than the saved_g_highest_address. |
7256 | // In such case, we set the top to the saved_g_highest_address so that the |
7257 | // card and brick tables always cover the whole new range. |
7258 | if (top < saved_g_highest_address) |
7259 | { |
7260 | top = saved_g_highest_address; |
7261 | } |
7262 | size_t ps = ha-la; |
7263 | #ifdef BIT64 |
7264 | if (ps > (uint64_t)200*1024*1024*1024) |
7265 | ps += (uint64_t)100*1024*1024*1024; |
7266 | else |
7267 | #endif // BIT64 |
7268 | ps *= 2; |
7269 | |
7270 | if (saved_g_lowest_address < g_gc_lowest_address) |
7271 | { |
7272 | if (ps > (size_t)g_gc_lowest_address) |
7273 | saved_g_lowest_address = (uint8_t*)(size_t)OS_PAGE_SIZE; |
7274 | else |
7275 | { |
7276 | assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE); |
7277 | saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps)); |
7278 | } |
7279 | } |
7280 | |
7281 | if (saved_g_highest_address > g_gc_highest_address) |
7282 | { |
7283 | saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address); |
7284 | if (saved_g_highest_address > top) |
7285 | saved_g_highest_address = top; |
7286 | } |
7287 | } |
7288 | dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[" , |
7289 | (size_t)saved_g_lowest_address, |
7290 | (size_t)saved_g_highest_address)); |
7291 | |
7292 | bool write_barrier_updated = false; |
7293 | uint32_t virtual_reserve_flags = VirtualReserveFlags::None; |
7294 | uint32_t* saved_g_card_table = g_gc_card_table; |
7295 | |
7296 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7297 | uint32_t* saved_g_card_bundle_table = g_gc_card_bundle_table; |
7298 | #endif |
7299 | |
7300 | uint32_t* ct = 0; |
7301 | uint32_t* translated_ct = 0; |
7302 | short* bt = 0; |
7303 | |
7304 | size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address); |
7305 | size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address); |
7306 | |
7307 | #ifdef MARK_ARRAY |
7308 | size_t ms = (gc_heap::gc_can_use_concurrent ? |
7309 | size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) : |
7310 | 0); |
7311 | #else |
7312 | size_t ms = 0; |
7313 | #endif //MARK_ARRAY |
7314 | |
7315 | size_t cb = 0; |
7316 | |
7317 | #ifdef CARD_BUNDLE |
7318 | if (can_use_write_watch_for_card_table()) |
7319 | { |
7320 | cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address); |
7321 | |
7322 | #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7323 | // If we're not manually managing the card bundles, we will need to use OS write |
7324 | // watch APIs over this region to track changes. |
7325 | virtual_reserve_flags |= VirtualReserveFlags::WriteWatch; |
7326 | #endif |
7327 | } |
7328 | #endif //CARD_BUNDLE |
7329 | |
7330 | size_t wws = 0; |
7331 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7332 | size_t sw_ww_table_offset = 0; |
7333 | if (gc_can_use_concurrent) |
7334 | { |
7335 | size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb; |
7336 | sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table); |
7337 | wws = |
7338 | sw_ww_table_offset - |
7339 | sw_ww_size_before_table + |
7340 | SoftwareWriteWatch::GetTableByteSize(saved_g_lowest_address, saved_g_highest_address); |
7341 | } |
7342 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7343 | |
7344 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
7345 | size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address); |
7346 | size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws; |
7347 | size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset); |
7348 | st += (st_table_offset_aligned - st_table_offset); |
7349 | #else //GROWABLE_SEG_MAPPING_TABLE |
7350 | size_t st = 0; |
7351 | #endif //GROWABLE_SEG_MAPPING_TABLE |
7352 | |
7353 | // it is impossible for alloc_size to overflow due bounds on each of |
7354 | // its components. |
7355 | size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms); |
7356 | dprintf (GC_TABLE_LOG, ("card table: %Id; brick table: %Id; card bundle: %Id; sw ww table: %Id; seg table: %Id; mark array: %Id" , |
7357 | cs, bs, cb, wws, st, ms)); |
7358 | |
7359 | uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags); |
7360 | |
7361 | if (!mem) |
7362 | { |
7363 | set_fgm_result (fgm_grow_table, alloc_size, loh_p); |
7364 | goto fail; |
7365 | } |
7366 | |
7367 | dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[" , |
7368 | alloc_size, (size_t)mem, (size_t)((uint8_t*)mem+alloc_size))); |
7369 | |
7370 | { |
7371 | // mark array will be committed separately (per segment). |
7372 | size_t commit_size = alloc_size - ms; |
7373 | |
7374 | if (!GCToOSInterface::VirtualCommit (mem, commit_size)) |
7375 | { |
7376 | dprintf (GC_TABLE_LOG, ("Table commit failed" )); |
7377 | set_fgm_result (fgm_commit_table, commit_size, loh_p); |
7378 | goto fail; |
7379 | } |
7380 | } |
7381 | |
7382 | ct = (uint32_t*)(mem + sizeof (card_table_info)); |
7383 | card_table_refcount (ct) = 0; |
7384 | card_table_lowest_address (ct) = saved_g_lowest_address; |
7385 | card_table_highest_address (ct) = saved_g_highest_address; |
7386 | card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))]; |
7387 | |
7388 | //clear the card table |
7389 | /* |
7390 | memclr ((uint8_t*)ct, |
7391 | (((saved_g_highest_address - saved_g_lowest_address)*sizeof (uint32_t) / |
7392 | (card_size * card_word_width)) |
7393 | + sizeof (uint32_t))); |
7394 | */ |
7395 | |
7396 | bt = (short*)((uint8_t*)ct + cs); |
7397 | |
7398 | // No initialization needed, will be done in copy_brick_card |
7399 | |
7400 | card_table_brick_table (ct) = bt; |
7401 | |
7402 | #ifdef CARD_BUNDLE |
7403 | card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs); |
7404 | //set all bundle to look at all of the cards |
7405 | memset(card_table_card_bundle_table (ct), 0xFF, cb); |
7406 | #endif //CARD_BUNDLE |
7407 | |
7408 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
7409 | { |
7410 | new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned); |
7411 | new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table - |
7412 | size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address)))); |
7413 | memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)], |
7414 | &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)], |
7415 | size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address)); |
7416 | |
7417 | // new_seg_mapping_table gets assigned to seg_mapping_table at the bottom of this function, |
7418 | // not here. The reason for this is that, if we fail at mark array committing (OOM) and we've |
7419 | // already switched seg_mapping_table to point to the new mapping table, we'll decommit it and |
7420 | // run into trouble. By not assigning here, we're making sure that we will not change seg_mapping_table |
7421 | // if an OOM occurs. |
7422 | } |
7423 | #endif //GROWABLE_SEG_MAPPING_TABLE |
7424 | |
7425 | #ifdef MARK_ARRAY |
7426 | if(gc_can_use_concurrent) |
7427 | card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st); |
7428 | else |
7429 | card_table_mark_array (ct) = NULL; |
7430 | #endif //MARK_ARRAY |
7431 | |
7432 | translated_ct = translate_card_table (ct); |
7433 | |
7434 | dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix" , |
7435 | (size_t)ct, (size_t)translated_ct, (size_t)new_seg_mapping_table, (size_t)card_table_mark_array (ct))); |
7436 | |
7437 | #ifdef BACKGROUND_GC |
7438 | if (hp->should_commit_mark_array()) |
7439 | { |
7440 | dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)" , |
7441 | saved_g_lowest_address, saved_g_highest_address, |
7442 | card_table_mark_array (ct), |
7443 | translate_mark_array (card_table_mark_array (ct)))); |
7444 | uint32_t* new_mark_array = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address)); |
7445 | if (!commit_new_mark_array_global (new_mark_array)) |
7446 | { |
7447 | dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments" )); |
7448 | set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p); |
7449 | goto fail; |
7450 | } |
7451 | |
7452 | if (!commit_mark_array_new_seg (hp, new_seg, translated_ct, saved_g_lowest_address)) |
7453 | { |
7454 | dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg" )); |
7455 | set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p); |
7456 | goto fail; |
7457 | } |
7458 | } |
7459 | else |
7460 | { |
7461 | clear_commit_flag_global(); |
7462 | } |
7463 | #endif //BACKGROUND_GC |
7464 | |
7465 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7466 | if (gc_can_use_concurrent) |
7467 | { |
7468 | // The current design of software write watch requires that the runtime is suspended during resize. Suspending |
7469 | // on resize is preferred because it is a far less frequent operation than GetWriteWatch() / ResetWriteWatch(). |
7470 | // Suspending here allows copying dirty state from the old table into the new table, and not have to merge old |
7471 | // table info lazily as done for card tables. |
7472 | |
7473 | // Either this thread was the thread that did the suspension which means we are suspended; or this is called |
7474 | // from a GC thread which means we are in a blocking GC and also suspended. |
7475 | bool is_runtime_suspended = GCToEEInterface::IsGCThread(); |
7476 | if (!is_runtime_suspended) |
7477 | { |
7478 | // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the |
7479 | // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call. |
7480 | // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state |
7481 | // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and |
7482 | // g_gc_highest_address. |
7483 | suspend_EE(); |
7484 | } |
7485 | |
7486 | g_gc_card_table = translated_ct; |
7487 | |
7488 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7489 | g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address); |
7490 | #endif |
7491 | |
7492 | SoftwareWriteWatch::SetResizedUntranslatedTable( |
7493 | mem + sw_ww_table_offset, |
7494 | saved_g_lowest_address, |
7495 | saved_g_highest_address); |
7496 | |
7497 | seg_mapping_table = new_seg_mapping_table; |
7498 | |
7499 | // Since the runtime is already suspended, update the write barrier here as well. |
7500 | // This passes a bool telling whether we need to switch to the post |
7501 | // grow version of the write barrier. This test tells us if the new |
7502 | // segment was allocated at a lower address than the old, requiring |
7503 | // that we start doing an upper bounds check in the write barrier. |
7504 | g_gc_lowest_address = saved_g_lowest_address; |
7505 | g_gc_highest_address = saved_g_highest_address; |
7506 | stomp_write_barrier_resize(true, la != saved_g_lowest_address); |
7507 | write_barrier_updated = true; |
7508 | |
7509 | if (!is_runtime_suspended) |
7510 | { |
7511 | restart_EE(); |
7512 | } |
7513 | } |
7514 | else |
7515 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
7516 | { |
7517 | g_gc_card_table = translated_ct; |
7518 | |
7519 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7520 | g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address); |
7521 | #endif |
7522 | } |
7523 | |
7524 | if (!write_barrier_updated) |
7525 | { |
7526 | seg_mapping_table = new_seg_mapping_table; |
7527 | GCToOSInterface::FlushProcessWriteBuffers(); |
7528 | g_gc_lowest_address = saved_g_lowest_address; |
7529 | g_gc_highest_address = saved_g_highest_address; |
7530 | |
7531 | // This passes a bool telling whether we need to switch to the post |
7532 | // grow version of the write barrier. This test tells us if the new |
7533 | // segment was allocated at a lower address than the old, requiring |
7534 | // that we start doing an upper bounds check in the write barrier. |
7535 | // This will also suspend the runtime if the write barrier type needs |
7536 | // to be changed, so we are doing this after all global state has |
7537 | // been updated. See the comment above suspend_EE() above for more |
7538 | // info. |
7539 | stomp_write_barrier_resize(GCToEEInterface::IsGCThread(), la != saved_g_lowest_address); |
7540 | } |
7541 | |
7542 | return 0; |
7543 | |
7544 | fail: |
7545 | //cleanup mess and return -1; |
7546 | |
7547 | if (mem) |
7548 | { |
7549 | assert(g_gc_card_table == saved_g_card_table); |
7550 | |
7551 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7552 | assert(g_gc_card_bundle_table == saved_g_card_bundle_table); |
7553 | #endif |
7554 | |
7555 | //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info)); |
7556 | if (!GCToOSInterface::VirtualRelease (mem, alloc_size)) |
7557 | { |
7558 | dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualRelease failed" )); |
7559 | assert (!"release failed" ); |
7560 | } |
7561 | } |
7562 | |
7563 | return -1; |
7564 | } |
7565 | else |
7566 | { |
7567 | #ifdef BACKGROUND_GC |
7568 | if (hp->should_commit_mark_array()) |
7569 | { |
7570 | dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix" , new_seg, hp->mark_array)); |
7571 | if (!commit_mark_array_new_seg (hp, new_seg)) |
7572 | { |
7573 | dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range" )); |
7574 | set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p); |
7575 | return -1; |
7576 | } |
7577 | } |
7578 | #endif //BACKGROUND_GC |
7579 | } |
7580 | |
7581 | return 0; |
7582 | } |
7583 | |
7584 | //copy all of the arrays managed by the card table for a page aligned range |
7585 | void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table, |
7586 | short* old_brick_table, |
7587 | heap_segment* seg, |
7588 | uint8_t* start, uint8_t* end) |
7589 | { |
7590 | ptrdiff_t brick_offset = brick_of (start) - brick_of (la); |
7591 | |
7592 | |
7593 | dprintf (2, ("copying tables for range [%Ix %Ix[" , (size_t)start, (size_t)end)); |
7594 | |
7595 | // copy brick table |
7596 | short* brick_start = &brick_table [brick_of (start)]; |
7597 | if (old_brick_table) |
7598 | { |
7599 | // segments are always on page boundaries |
7600 | memcpy (brick_start, &old_brick_table[brick_offset], |
7601 | size_brick_of (start, end)); |
7602 | |
7603 | } |
7604 | else |
7605 | { |
7606 | // This is a large heap, just clear the brick table |
7607 | } |
7608 | |
7609 | uint32_t* old_ct = &old_card_table[card_word (card_of (la))]; |
7610 | #ifdef MARK_ARRAY |
7611 | #ifdef BACKGROUND_GC |
7612 | UNREFERENCED_PARAMETER(seg); |
7613 | if (recursive_gc_sync::background_running_p()) |
7614 | { |
7615 | uint32_t* old_mark_array = card_table_mark_array (old_ct); |
7616 | |
7617 | // We don't need to go through all the card tables here because |
7618 | // we only need to copy from the GC version of the mark array - when we |
7619 | // mark (even in allocate_large_object) we always use that mark array. |
7620 | if ((card_table_highest_address (old_ct) >= start) && |
7621 | (card_table_lowest_address (old_ct) <= end)) |
7622 | { |
7623 | if ((background_saved_highest_address >= start) && |
7624 | (background_saved_lowest_address <= end)) |
7625 | { |
7626 | //copy the mark bits |
7627 | // segments are always on page boundaries |
7628 | uint8_t* m_start = max (background_saved_lowest_address, start); |
7629 | uint8_t* m_end = min (background_saved_highest_address, end); |
7630 | memcpy (&mark_array[mark_word_of (m_start)], |
7631 | &old_mark_array[mark_word_of (m_start) - mark_word_of (la)], |
7632 | size_mark_array_of (m_start, m_end)); |
7633 | } |
7634 | } |
7635 | else |
7636 | { |
7637 | //only large segments can be out of range |
7638 | assert (old_brick_table == 0); |
7639 | } |
7640 | } |
7641 | #else //BACKGROUND_GC |
7642 | assert (seg != 0); |
7643 | clear_mark_array (start, heap_segment_committed(seg)); |
7644 | #endif //BACKGROUND_GC |
7645 | #endif //MARK_ARRAY |
7646 | |
7647 | // n way merge with all of the card table ever used in between |
7648 | uint32_t* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]); |
7649 | |
7650 | assert (ct); |
7651 | while (card_table_next (old_ct) != ct) |
7652 | { |
7653 | //copy if old card table contained [start, end[ |
7654 | if ((card_table_highest_address (ct) >= end) && |
7655 | (card_table_lowest_address (ct) <= start)) |
7656 | { |
7657 | // or the card_tables |
7658 | |
7659 | size_t start_word = card_word (card_of (start)); |
7660 | |
7661 | uint32_t* dest = &card_table[start_word]; |
7662 | uint32_t* src = &((translate_card_table (ct))[start_word]); |
7663 | ptrdiff_t count = count_card_of (start, end); |
7664 | for (int x = 0; x < count; x++) |
7665 | { |
7666 | *dest |= *src; |
7667 | |
7668 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
7669 | if (*src != 0) |
7670 | { |
7671 | card_bundle_set(cardw_card_bundle(start_word+x)); |
7672 | } |
7673 | #endif |
7674 | |
7675 | dest++; |
7676 | src++; |
7677 | } |
7678 | } |
7679 | ct = card_table_next (ct); |
7680 | } |
7681 | } |
7682 | |
7683 | //initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range |
7684 | void gc_heap::init_brick_card_range (heap_segment* seg) |
7685 | { |
7686 | dprintf (2, ("initialising tables for range [%Ix %Ix[" , |
7687 | (size_t)heap_segment_mem (seg), |
7688 | (size_t)heap_segment_allocated (seg))); |
7689 | |
7690 | // initialize the brick table |
7691 | for (size_t b = brick_of (heap_segment_mem (seg)); |
7692 | b < brick_of (align_on_brick (heap_segment_allocated (seg))); |
7693 | b++) |
7694 | { |
7695 | set_brick (b, -1); |
7696 | } |
7697 | |
7698 | #ifdef MARK_ARRAY |
7699 | if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed)) |
7700 | { |
7701 | assert (seg != 0); |
7702 | clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg)); |
7703 | } |
7704 | #endif //MARK_ARRAY |
7705 | |
7706 | clear_card_for_addresses (heap_segment_mem (seg), |
7707 | heap_segment_allocated (seg)); |
7708 | } |
7709 | |
7710 | void gc_heap::copy_brick_card_table() |
7711 | { |
7712 | uint8_t* la = lowest_address; |
7713 | uint8_t* ha = highest_address; |
7714 | MAYBE_UNUSED_VAR(ha); |
7715 | uint32_t* old_card_table = card_table; |
7716 | short* old_brick_table = brick_table; |
7717 | |
7718 | assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))])); |
7719 | assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))])); |
7720 | |
7721 | /* todo: Need a global lock for this */ |
7722 | uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))]; |
7723 | own_card_table (ct); |
7724 | card_table = translate_card_table (ct); |
7725 | /* End of global lock */ |
7726 | highest_address = card_table_highest_address (ct); |
7727 | lowest_address = card_table_lowest_address (ct); |
7728 | |
7729 | brick_table = card_table_brick_table (ct); |
7730 | |
7731 | #ifdef MARK_ARRAY |
7732 | if (gc_can_use_concurrent) |
7733 | { |
7734 | mark_array = translate_mark_array (card_table_mark_array (ct)); |
7735 | assert (mark_word_of (g_gc_highest_address) == |
7736 | mark_word_of (align_on_mark_word (g_gc_highest_address))); |
7737 | } |
7738 | else |
7739 | mark_array = NULL; |
7740 | #endif //MARK_ARRAY |
7741 | |
7742 | #ifdef CARD_BUNDLE |
7743 | #if defined(MARK_ARRAY) && defined(_DEBUG) |
7744 | size_t cb_end = (size_t)((uint8_t*)card_table_card_bundle_table (ct) + size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address)); |
7745 | #ifdef GROWABLE_SEG_MAPPING_TABLE |
7746 | size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address); |
7747 | size_t cb_end_aligned = align_for_seg_mapping_table (cb_end); |
7748 | st += (cb_end_aligned - cb_end); |
7749 | #else //GROWABLE_SEG_MAPPING_TABLE |
7750 | size_t st = 0; |
7751 | #endif //GROWABLE_SEG_MAPPING_TABLE |
7752 | #endif //MARK_ARRAY && _DEBUG |
7753 | card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address); |
7754 | |
7755 | // Ensure that the word that represents g_gc_lowest_address in the translated table is located at the |
7756 | // start of the untranslated table. |
7757 | assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] == |
7758 | card_table_card_bundle_table (ct)); |
7759 | |
7760 | //set the card table if we are in a heap growth scenario |
7761 | if (card_bundles_enabled()) |
7762 | { |
7763 | card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))), |
7764 | cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address))))); |
7765 | } |
7766 | //check if we need to turn on card_bundles. |
7767 | #ifdef MULTIPLE_HEAPS |
7768 | // use INT64 arithmetic here because of possible overflow on 32p |
7769 | uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*gc_heap::n_heaps; |
7770 | #else |
7771 | // use INT64 arithmetic here because of possible overflow on 32p |
7772 | uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE; |
7773 | #endif //MULTIPLE_HEAPS |
7774 | if (reserved_memory >= th) |
7775 | { |
7776 | enable_card_bundles(); |
7777 | } |
7778 | |
7779 | #endif //CARD_BUNDLE |
7780 | |
7781 | // for each of the segments and heaps, copy the brick table and |
7782 | // or the card table |
7783 | heap_segment* seg = generation_start_segment (generation_of (max_generation)); |
7784 | while (seg) |
7785 | { |
7786 | if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg)) |
7787 | { |
7788 | //check if it became in range |
7789 | if ((heap_segment_reserved (seg) > lowest_address) && |
7790 | (heap_segment_mem (seg) < highest_address)) |
7791 | { |
7792 | set_ro_segment_in_range (seg); |
7793 | } |
7794 | } |
7795 | else |
7796 | { |
7797 | |
7798 | uint8_t* end = align_on_page (heap_segment_allocated (seg)); |
7799 | copy_brick_card_range (la, old_card_table, |
7800 | old_brick_table, |
7801 | seg, |
7802 | align_lower_page (heap_segment_mem (seg)), |
7803 | end); |
7804 | } |
7805 | seg = heap_segment_next (seg); |
7806 | } |
7807 | |
7808 | seg = generation_start_segment (large_object_generation); |
7809 | while (seg) |
7810 | { |
7811 | if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg)) |
7812 | { |
7813 | //check if it became in range |
7814 | if ((heap_segment_reserved (seg) > lowest_address) && |
7815 | (heap_segment_mem (seg) < highest_address)) |
7816 | { |
7817 | set_ro_segment_in_range (seg); |
7818 | } |
7819 | } |
7820 | else |
7821 | { |
7822 | uint8_t* end = align_on_page (heap_segment_allocated (seg)); |
7823 | copy_brick_card_range (la, old_card_table, |
7824 | 0, |
7825 | seg, |
7826 | align_lower_page (heap_segment_mem (seg)), |
7827 | end); |
7828 | } |
7829 | seg = heap_segment_next (seg); |
7830 | } |
7831 | |
7832 | release_card_table (&old_card_table[card_word (card_of(la))]); |
7833 | } |
7834 | |
7835 | #ifdef FEATURE_BASICFREEZE |
7836 | BOOL gc_heap::insert_ro_segment (heap_segment* seg) |
7837 | { |
7838 | enter_spin_lock (&gc_heap::gc_lock); |
7839 | |
7840 | if (!gc_heap::seg_table->ensure_space_for_insert () |
7841 | || (should_commit_mark_array() && !commit_mark_array_new_seg(__this, seg))) |
7842 | { |
7843 | leave_spin_lock(&gc_heap::gc_lock); |
7844 | return FALSE; |
7845 | } |
7846 | |
7847 | //insert at the head of the segment list |
7848 | generation* gen2 = generation_of (max_generation); |
7849 | heap_segment* oldhead = generation_start_segment (gen2); |
7850 | heap_segment_next (seg) = oldhead; |
7851 | generation_start_segment (gen2) = seg; |
7852 | |
7853 | seg_table->insert (heap_segment_mem(seg), (size_t)seg); |
7854 | |
7855 | #ifdef SEG_MAPPING_TABLE |
7856 | seg_mapping_table_add_ro_segment (seg); |
7857 | #endif //SEG_MAPPING_TABLE |
7858 | |
7859 | //test if in range |
7860 | if ((heap_segment_reserved (seg) > lowest_address) && |
7861 | (heap_segment_mem (seg) < highest_address)) |
7862 | { |
7863 | set_ro_segment_in_range (seg); |
7864 | } |
7865 | |
7866 | FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg), (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), gc_etw_segment_read_only_heap); |
7867 | |
7868 | leave_spin_lock (&gc_heap::gc_lock); |
7869 | return TRUE; |
7870 | } |
7871 | |
7872 | // No one is calling this function right now. If this is getting called we need |
7873 | // to take care of decommitting the mark array for it - we will need to remember |
7874 | // which portion of the mark array was committed and only decommit that. |
7875 | void gc_heap::remove_ro_segment (heap_segment* seg) |
7876 | { |
7877 | //clear the mark bits so a new segment allocated in its place will have a clear mark bits |
7878 | #ifdef MARK_ARRAY |
7879 | if (gc_can_use_concurrent) |
7880 | { |
7881 | clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)), |
7882 | align_on_card_word (min (heap_segment_allocated (seg), highest_address)), |
7883 | false); // read_only segments need the mark clear |
7884 | } |
7885 | #endif //MARK_ARRAY |
7886 | |
7887 | enter_spin_lock (&gc_heap::gc_lock); |
7888 | |
7889 | seg_table->remove ((uint8_t*)seg); |
7890 | |
7891 | #ifdef SEG_MAPPING_TABLE |
7892 | seg_mapping_table_remove_ro_segment (seg); |
7893 | #endif //SEG_MAPPING_TABLE |
7894 | |
7895 | // Locate segment (and previous segment) in the list. |
7896 | generation* gen2 = generation_of (max_generation); |
7897 | heap_segment* curr_seg = generation_start_segment (gen2); |
7898 | heap_segment* prev_seg = NULL; |
7899 | |
7900 | while (curr_seg && curr_seg != seg) |
7901 | { |
7902 | prev_seg = curr_seg; |
7903 | curr_seg = heap_segment_next (curr_seg); |
7904 | } |
7905 | assert (curr_seg == seg); |
7906 | |
7907 | // Patch previous segment (or list head if there is none) to skip the removed segment. |
7908 | if (prev_seg) |
7909 | heap_segment_next (prev_seg) = heap_segment_next (curr_seg); |
7910 | else |
7911 | generation_start_segment (gen2) = heap_segment_next (curr_seg); |
7912 | |
7913 | leave_spin_lock (&gc_heap::gc_lock); |
7914 | } |
7915 | #endif //FEATURE_BASICFREEZE |
7916 | |
7917 | BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg) |
7918 | { |
7919 | //set it in range |
7920 | seg->flags |= heap_segment_flags_inrange; |
7921 | // init_brick_card_range (seg); |
7922 | ro_segments_in_range = TRUE; |
7923 | //right now, segments aren't protected |
7924 | //unprotect_segment (seg); |
7925 | return TRUE; |
7926 | } |
7927 | |
7928 | #ifdef MARK_LIST |
7929 | |
7930 | uint8_t** make_mark_list (size_t size) |
7931 | { |
7932 | uint8_t** mark_list = new (nothrow) uint8_t* [size]; |
7933 | return mark_list; |
7934 | } |
7935 | |
7936 | #define swap(a,b){uint8_t* t; t = a; a = b; b = t;} |
7937 | |
7938 | void verify_qsort_array (uint8_t* *low, uint8_t* *high) |
7939 | { |
7940 | uint8_t **i = 0; |
7941 | |
7942 | for (i = low+1; i <= high; i++) |
7943 | { |
7944 | if (*i < *(i-1)) |
7945 | { |
7946 | FATAL_GC_ERROR(); |
7947 | } |
7948 | } |
7949 | } |
7950 | |
7951 | #ifndef USE_INTROSORT |
7952 | void qsort1( uint8_t* *low, uint8_t* *high, unsigned int depth) |
7953 | { |
7954 | if (((low + 16) >= high) || (depth > 100)) |
7955 | { |
7956 | //insertion sort |
7957 | uint8_t **i, **j; |
7958 | for (i = low+1; i <= high; i++) |
7959 | { |
7960 | uint8_t* val = *i; |
7961 | for (j=i;j >low && val<*(j-1);j--) |
7962 | { |
7963 | *j=*(j-1); |
7964 | } |
7965 | *j=val; |
7966 | } |
7967 | } |
7968 | else |
7969 | { |
7970 | uint8_t *pivot, **left, **right; |
7971 | |
7972 | //sort low middle and high |
7973 | if (*(low+((high-low)/2)) < *low) |
7974 | swap (*(low+((high-low)/2)), *low); |
7975 | if (*high < *low) |
7976 | swap (*low, *high); |
7977 | if (*high < *(low+((high-low)/2))) |
7978 | swap (*(low+((high-low)/2)), *high); |
7979 | |
7980 | swap (*(low+((high-low)/2)), *(high-1)); |
7981 | pivot = *(high-1); |
7982 | left = low; right = high-1; |
7983 | while (1) { |
7984 | while (*(--right) > pivot); |
7985 | while (*(++left) < pivot); |
7986 | if (left < right) |
7987 | { |
7988 | swap(*left, *right); |
7989 | } |
7990 | else |
7991 | break; |
7992 | } |
7993 | swap (*left, *(high-1)); |
7994 | qsort1(low, left-1, depth+1); |
7995 | qsort1(left+1, high, depth+1); |
7996 | } |
7997 | } |
7998 | #endif //USE_INTROSORT |
7999 | void rqsort1( uint8_t* *low, uint8_t* *high) |
8000 | { |
8001 | if ((low + 16) >= high) |
8002 | { |
8003 | //insertion sort |
8004 | uint8_t **i, **j; |
8005 | for (i = low+1; i <= high; i++) |
8006 | { |
8007 | uint8_t* val = *i; |
8008 | for (j=i;j >low && val>*(j-1);j--) |
8009 | { |
8010 | *j=*(j-1); |
8011 | } |
8012 | *j=val; |
8013 | } |
8014 | } |
8015 | else |
8016 | { |
8017 | uint8_t *pivot, **left, **right; |
8018 | |
8019 | //sort low middle and high |
8020 | if (*(low+((high-low)/2)) > *low) |
8021 | swap (*(low+((high-low)/2)), *low); |
8022 | if (*high > *low) |
8023 | swap (*low, *high); |
8024 | if (*high > *(low+((high-low)/2))) |
8025 | swap (*(low+((high-low)/2)), *high); |
8026 | |
8027 | swap (*(low+((high-low)/2)), *(high-1)); |
8028 | pivot = *(high-1); |
8029 | left = low; right = high-1; |
8030 | while (1) { |
8031 | while (*(--right) < pivot); |
8032 | while (*(++left) > pivot); |
8033 | if (left < right) |
8034 | { |
8035 | swap(*left, *right); |
8036 | } |
8037 | else |
8038 | break; |
8039 | } |
8040 | swap (*left, *(high-1)); |
8041 | rqsort1(low, left-1); |
8042 | rqsort1(left+1, high); |
8043 | } |
8044 | } |
8045 | |
8046 | #ifdef USE_INTROSORT |
8047 | class introsort |
8048 | { |
8049 | |
8050 | private: |
8051 | static const int size_threshold = 64; |
8052 | static const int max_depth = 100; |
8053 | |
8054 | |
8055 | inline static void swap_elements(uint8_t** i,uint8_t** j) |
8056 | { |
8057 | uint8_t* t=*i; |
8058 | *i=*j; |
8059 | *j=t; |
8060 | } |
8061 | |
8062 | public: |
8063 | static void sort (uint8_t** begin, uint8_t** end, int ignored) |
8064 | { |
8065 | ignored = 0; |
8066 | introsort_loop (begin, end, max_depth); |
8067 | insertionsort (begin, end); |
8068 | } |
8069 | |
8070 | private: |
8071 | |
8072 | static void introsort_loop (uint8_t** lo, uint8_t** hi, int depth_limit) |
8073 | { |
8074 | while (hi-lo >= size_threshold) |
8075 | { |
8076 | if (depth_limit == 0) |
8077 | { |
8078 | heapsort (lo, hi); |
8079 | return; |
8080 | } |
8081 | uint8_t** p=median_partition (lo, hi); |
8082 | depth_limit=depth_limit-1; |
8083 | introsort_loop (p, hi, depth_limit); |
8084 | hi=p-1; |
8085 | } |
8086 | } |
8087 | |
8088 | static uint8_t** median_partition (uint8_t** low, uint8_t** high) |
8089 | { |
8090 | uint8_t *pivot, **left, **right; |
8091 | |
8092 | //sort low middle and high |
8093 | if (*(low+((high-low)/2)) < *low) |
8094 | swap_elements ((low+((high-low)/2)), low); |
8095 | if (*high < *low) |
8096 | swap_elements (low, high); |
8097 | if (*high < *(low+((high-low)/2))) |
8098 | swap_elements ((low+((high-low)/2)), high); |
8099 | |
8100 | swap_elements ((low+((high-low)/2)), (high-1)); |
8101 | pivot = *(high-1); |
8102 | left = low; right = high-1; |
8103 | while (1) { |
8104 | while (*(--right) > pivot); |
8105 | while (*(++left) < pivot); |
8106 | if (left < right) |
8107 | { |
8108 | swap_elements(left, right); |
8109 | } |
8110 | else |
8111 | break; |
8112 | } |
8113 | swap_elements (left, (high-1)); |
8114 | return left; |
8115 | } |
8116 | |
8117 | |
8118 | static void insertionsort (uint8_t** lo, uint8_t** hi) |
8119 | { |
8120 | for (uint8_t** i=lo+1; i <= hi; i++) |
8121 | { |
8122 | uint8_t** j = i; |
8123 | uint8_t* t = *i; |
8124 | while((j > lo) && (t <*(j-1))) |
8125 | { |
8126 | *j = *(j-1); |
8127 | j--; |
8128 | } |
8129 | *j = t; |
8130 | } |
8131 | } |
8132 | |
8133 | static void heapsort (uint8_t** lo, uint8_t** hi) |
8134 | { |
8135 | size_t n = hi - lo + 1; |
8136 | for (size_t i=n / 2; i >= 1; i--) |
8137 | { |
8138 | downheap (i,n,lo); |
8139 | } |
8140 | for (size_t i = n; i > 1; i--) |
8141 | { |
8142 | swap_elements (lo, lo + i - 1); |
8143 | downheap(1, i - 1, lo); |
8144 | } |
8145 | } |
8146 | |
8147 | static void downheap (size_t i, size_t n, uint8_t** lo) |
8148 | { |
8149 | uint8_t* d = *(lo + i - 1); |
8150 | size_t child; |
8151 | while (i <= n / 2) |
8152 | { |
8153 | child = 2*i; |
8154 | if (child < n && *(lo + child - 1)<(*(lo + child))) |
8155 | { |
8156 | child++; |
8157 | } |
8158 | if (!(d<*(lo + child - 1))) |
8159 | { |
8160 | break; |
8161 | } |
8162 | *(lo + i - 1) = *(lo + child - 1); |
8163 | i = child; |
8164 | } |
8165 | *(lo + i - 1) = d; |
8166 | } |
8167 | |
8168 | }; |
8169 | |
8170 | #endif //USE_INTROSORT |
8171 | |
8172 | #ifdef MULTIPLE_HEAPS |
8173 | #ifdef PARALLEL_MARK_LIST_SORT |
8174 | void gc_heap::sort_mark_list() |
8175 | { |
8176 | // if this heap had a mark list overflow, we don't do anything |
8177 | if (mark_list_index > mark_list_end) |
8178 | { |
8179 | // printf("sort_mark_list: overflow on heap %d\n", heap_number); |
8180 | return; |
8181 | } |
8182 | |
8183 | // if any other heap had a mark list overflow, we fake one too, |
8184 | // so we don't use an incomplete mark list by mistake |
8185 | for (int i = 0; i < n_heaps; i++) |
8186 | { |
8187 | if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end) |
8188 | { |
8189 | mark_list_index = mark_list_end + 1; |
8190 | // printf("sort_mark_list: overflow on heap %d\n", i); |
8191 | return; |
8192 | } |
8193 | } |
8194 | |
8195 | // unsigned long start = GetCycleCount32(); |
8196 | |
8197 | dprintf (3, ("Sorting mark lists" )); |
8198 | if (mark_list_index > mark_list) |
8199 | _sort (mark_list, mark_list_index - 1, 0); |
8200 | |
8201 | // 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); |
8202 | // start = GetCycleCount32(); |
8203 | |
8204 | // first set the pieces for all heaps to empty |
8205 | int heap_num; |
8206 | for (heap_num = 0; heap_num < n_heaps; heap_num++) |
8207 | { |
8208 | mark_list_piece_start[heap_num] = NULL; |
8209 | mark_list_piece_end[heap_num] = NULL; |
8210 | } |
8211 | |
8212 | uint8_t** x = mark_list; |
8213 | |
8214 | // predicate means: x is still within the mark list, and within the bounds of this heap |
8215 | #define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high)) |
8216 | |
8217 | heap_num = -1; |
8218 | while (x < mark_list_index) |
8219 | { |
8220 | gc_heap* heap; |
8221 | // find the heap x points into - searching cyclically from the last heap, |
8222 | // because in many cases the right heap is the next one or comes soon after |
8223 | int last_heap_num = heap_num; |
8224 | MAYBE_UNUSED_VAR(last_heap_num); |
8225 | do |
8226 | { |
8227 | heap_num++; |
8228 | if (heap_num >= n_heaps) |
8229 | heap_num = 0; |
8230 | assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not! |
8231 | heap = g_heaps[heap_num]; |
8232 | } |
8233 | while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high)); |
8234 | |
8235 | // x is the start of the mark list piece for this heap |
8236 | mark_list_piece_start[heap_num] = x; |
8237 | |
8238 | // to find the end of the mark list piece for this heap, find the first x |
8239 | // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list |
8240 | if (predicate(x)) |
8241 | { |
8242 | // let's see if we get lucky and the whole rest belongs to this piece |
8243 | if (predicate(mark_list_index-1)) |
8244 | { |
8245 | x = mark_list_index; |
8246 | mark_list_piece_end[heap_num] = x; |
8247 | break; |
8248 | } |
8249 | |
8250 | // we play a variant of binary search to find the point sooner. |
8251 | // the first loop advances by increasing steps until the predicate turns false. |
8252 | // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true. |
8253 | unsigned inc = 1; |
8254 | do |
8255 | { |
8256 | inc *= 2; |
8257 | uint8_t** temp_x = x; |
8258 | x += inc; |
8259 | if (temp_x > x) |
8260 | { |
8261 | break; |
8262 | } |
8263 | } |
8264 | while (predicate(x)); |
8265 | // we know that only the last step was wrong, so we undo it |
8266 | x -= inc; |
8267 | do |
8268 | { |
8269 | // loop invariant - predicate holds at x, but not x + inc |
8270 | assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc))); |
8271 | inc /= 2; |
8272 | if (((x + inc) > x) && predicate(x + inc)) |
8273 | { |
8274 | x += inc; |
8275 | } |
8276 | } |
8277 | while (inc > 1); |
8278 | // the termination condition and the loop invariant together imply this: |
8279 | assert(predicate(x) && !predicate(x + inc) && (inc == 1)); |
8280 | // so the spot we're looking for is one further |
8281 | x += 1; |
8282 | } |
8283 | mark_list_piece_end[heap_num] = x; |
8284 | } |
8285 | |
8286 | #undef predicate |
8287 | |
8288 | // printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start); |
8289 | } |
8290 | |
8291 | void gc_heap::append_to_mark_list(uint8_t **start, uint8_t **end) |
8292 | { |
8293 | size_t slots_needed = end - start; |
8294 | size_t slots_available = mark_list_end + 1 - mark_list_index; |
8295 | size_t slots_to_copy = min(slots_needed, slots_available); |
8296 | memcpy(mark_list_index, start, slots_to_copy*sizeof(*start)); |
8297 | mark_list_index += slots_to_copy; |
8298 | // printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy); |
8299 | } |
8300 | |
8301 | void gc_heap::merge_mark_lists() |
8302 | { |
8303 | uint8_t** source[MAX_SUPPORTED_CPUS]; |
8304 | uint8_t** source_end[MAX_SUPPORTED_CPUS]; |
8305 | int source_heap[MAX_SUPPORTED_CPUS]; |
8306 | int source_count = 0; |
8307 | |
8308 | // in case of mark list overflow, don't bother |
8309 | if (mark_list_index > mark_list_end) |
8310 | { |
8311 | // printf("merge_mark_lists: overflow\n"); |
8312 | return; |
8313 | } |
8314 | |
8315 | dprintf(3, ("merge_mark_lists: heap_number = %d starts out with %Id entries" , heap_number, mark_list_index - mark_list)); |
8316 | // unsigned long start = GetCycleCount32(); |
8317 | for (int i = 0; i < n_heaps; i++) |
8318 | { |
8319 | gc_heap* heap = g_heaps[i]; |
8320 | if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number]) |
8321 | { |
8322 | source[source_count] = heap->mark_list_piece_start[heap_number]; |
8323 | source_end[source_count] = heap->mark_list_piece_end[heap_number]; |
8324 | source_heap[source_count] = i; |
8325 | if (source_count < MAX_SUPPORTED_CPUS) |
8326 | source_count++; |
8327 | } |
8328 | } |
8329 | // printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start); |
8330 | |
8331 | dprintf(3, ("heap_number = %d has %d sources\n" , heap_number, source_count)); |
8332 | #if defined(_DEBUG) || defined(TRACE_GC) |
8333 | for (int j = 0; j < source_count; j++) |
8334 | { |
8335 | dprintf(3, ("heap_number = %d " , heap_number)); |
8336 | dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)" , |
8337 | (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j]))); |
8338 | // the sources should all be sorted |
8339 | for (uint8_t **x = source[j]; x < source_end[j] - 1; x++) |
8340 | { |
8341 | if (x[0] > x[1]) |
8342 | { |
8343 | dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n" , j, heap_number)); |
8344 | assert (0); |
8345 | } |
8346 | } |
8347 | } |
8348 | #endif //_DEBUG || TRACE_GC |
8349 | |
8350 | // start = GetCycleCount32(); |
8351 | |
8352 | mark_list = &g_mark_list_copy [heap_number*mark_list_size]; |
8353 | mark_list_index = mark_list; |
8354 | mark_list_end = &mark_list [mark_list_size-1]; |
8355 | int piece_count = 0; |
8356 | if (source_count == 0) |
8357 | { |
8358 | ; // nothing to do |
8359 | } |
8360 | else if (source_count == 1) |
8361 | { |
8362 | mark_list = source[0]; |
8363 | mark_list_index = source_end[0]; |
8364 | mark_list_end = mark_list_index; |
8365 | piece_count++; |
8366 | } |
8367 | else |
8368 | { |
8369 | while (source_count > 1) |
8370 | { |
8371 | // find the lowest and second lowest value in the sources we're merging from |
8372 | int lowest_source = 0; |
8373 | uint8_t *lowest = *source[0]; |
8374 | uint8_t *second_lowest = *source[1]; |
8375 | for (int i = 1; i < source_count; i++) |
8376 | { |
8377 | if (lowest > *source[i]) |
8378 | { |
8379 | second_lowest = lowest; |
8380 | lowest = *source[i]; |
8381 | lowest_source = i; |
8382 | } |
8383 | else if (second_lowest > *source[i]) |
8384 | { |
8385 | second_lowest = *source[i]; |
8386 | } |
8387 | } |
8388 | |
8389 | // find the point in the lowest source where it either runs out or is not <= second_lowest anymore |
8390 | |
8391 | // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common |
8392 | uint8_t **x; |
8393 | if (source_end[lowest_source][-1] <= second_lowest) |
8394 | x = source_end[lowest_source]; |
8395 | else |
8396 | { |
8397 | // use linear search to find the end -- could also use binary search as in sort_mark_list, |
8398 | // but saw no improvement doing that |
8399 | for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++) |
8400 | ; |
8401 | } |
8402 | |
8403 | // blast this piece to the mark list |
8404 | append_to_mark_list(source[lowest_source], x); |
8405 | piece_count++; |
8406 | |
8407 | source[lowest_source] = x; |
8408 | |
8409 | // check whether this source is now exhausted |
8410 | if (x >= source_end[lowest_source]) |
8411 | { |
8412 | // if it's not the source with the highest index, copy the source with the highest index |
8413 | // over it so the non-empty sources are always at the beginning |
8414 | if (lowest_source < source_count-1) |
8415 | { |
8416 | source[lowest_source] = source[source_count-1]; |
8417 | source_end[lowest_source] = source_end[source_count-1]; |
8418 | } |
8419 | source_count--; |
8420 | } |
8421 | } |
8422 | // we're left with just one source that we copy |
8423 | append_to_mark_list(source[0], source_end[0]); |
8424 | piece_count++; |
8425 | } |
8426 | |
8427 | // printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count); |
8428 | |
8429 | #if defined(_DEBUG) || defined(TRACE_GC) |
8430 | // the final mark list must be sorted |
8431 | for (uint8_t **x = mark_list; x < mark_list_index - 1; x++) |
8432 | { |
8433 | if (x[0] > x[1]) |
8434 | { |
8435 | dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists" , heap_number)); |
8436 | assert (0); |
8437 | } |
8438 | } |
8439 | #endif //defined(_DEBUG) || defined(TRACE_GC) |
8440 | } |
8441 | #else //PARALLEL_MARK_LIST_SORT |
8442 | void gc_heap::combine_mark_lists() |
8443 | { |
8444 | dprintf (3, ("Combining mark lists" )); |
8445 | //verify if a heap has overflowed its mark list |
8446 | BOOL use_mark_list = TRUE; |
8447 | for (int i = 0; i < n_heaps; i++) |
8448 | { |
8449 | if (g_heaps [i]->mark_list_index > g_heaps [i]->mark_list_end) |
8450 | { |
8451 | use_mark_list = FALSE; |
8452 | break; |
8453 | } |
8454 | } |
8455 | |
8456 | if (use_mark_list) |
8457 | { |
8458 | dprintf (3, ("Using mark list" )); |
8459 | //compact the gaps out of the mark list |
8460 | int gn = 0; |
8461 | uint8_t** current_gap = g_heaps [gn]->mark_list_index; |
8462 | uint8_t** current_gap_end = g_heaps[gn]->mark_list_end + 1; |
8463 | uint8_t** dst_last = current_gap-1; |
8464 | |
8465 | int srcn = n_heaps-1; |
8466 | gc_heap* srch = g_heaps [srcn]; |
8467 | uint8_t** src = srch->mark_list_index - 1; |
8468 | uint8_t** src_beg = srch->mark_list; |
8469 | |
8470 | while (current_gap <= src) |
8471 | { |
8472 | while ((gn < n_heaps-1) && (current_gap >= current_gap_end)) |
8473 | { |
8474 | //go to the next gap |
8475 | gn++; |
8476 | dprintf (3, ("Going to the next gap %d" , gn)); |
8477 | assert (gn < n_heaps); |
8478 | current_gap = g_heaps [gn]->mark_list_index; |
8479 | current_gap_end = g_heaps[gn]->mark_list_end + 1; |
8480 | assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list)); |
8481 | } |
8482 | while ((srcn > 0) && (src < src_beg)) |
8483 | { |
8484 | //go to the previous source |
8485 | srcn--; |
8486 | dprintf (3, ("going to the previous source %d" , srcn)); |
8487 | assert (srcn>=0); |
8488 | gc_heap* srch = g_heaps [srcn]; |
8489 | src = srch->mark_list_index - 1; |
8490 | src_beg = srch->mark_list; |
8491 | } |
8492 | if (current_gap < src) |
8493 | { |
8494 | dst_last = current_gap; |
8495 | *current_gap++ = *src--; |
8496 | } |
8497 | } |
8498 | dprintf (3, ("src: %Ix dst_last: %Ix" , (size_t)src, (size_t)dst_last)); |
8499 | |
8500 | uint8_t** end_of_list = max (src, dst_last); |
8501 | |
8502 | //sort the resulting compacted list |
8503 | assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]); |
8504 | if (end_of_list > &g_mark_list[0]) |
8505 | _sort (&g_mark_list[0], end_of_list, 0); |
8506 | //adjust the mark_list to the begining of the resulting mark list. |
8507 | for (int i = 0; i < n_heaps; i++) |
8508 | { |
8509 | g_heaps [i]->mark_list = g_mark_list; |
8510 | g_heaps [i]->mark_list_index = end_of_list + 1; |
8511 | g_heaps [i]->mark_list_end = end_of_list + 1; |
8512 | } |
8513 | } |
8514 | else |
8515 | { |
8516 | uint8_t** end_of_list = g_mark_list; |
8517 | //adjust the mark_list to the begining of the resulting mark list. |
8518 | //put the index beyond the end to turn off mark list processing |
8519 | for (int i = 0; i < n_heaps; i++) |
8520 | { |
8521 | g_heaps [i]->mark_list = g_mark_list; |
8522 | g_heaps [i]->mark_list_index = end_of_list + 1; |
8523 | g_heaps [i]->mark_list_end = end_of_list; |
8524 | } |
8525 | } |
8526 | } |
8527 | #endif // PARALLEL_MARK_LIST_SORT |
8528 | #endif //MULTIPLE_HEAPS |
8529 | #endif //MARK_LIST |
8530 | |
8531 | class seg_free_spaces |
8532 | { |
8533 | struct seg_free_space |
8534 | { |
8535 | BOOL is_plug; |
8536 | void* start; |
8537 | }; |
8538 | |
8539 | struct free_space_bucket |
8540 | { |
8541 | seg_free_space* free_space; |
8542 | ptrdiff_t count_add; // Assigned when we first contruct the array. |
8543 | ptrdiff_t count_fit; // How many items left when we are fitting plugs. |
8544 | }; |
8545 | |
8546 | void move_bucket (int old_power2, int new_power2) |
8547 | { |
8548 | // PREFAST warning 22015: old_power2 could be negative |
8549 | assert (old_power2 >= 0); |
8550 | assert (old_power2 >= new_power2); |
8551 | |
8552 | if (old_power2 == new_power2) |
8553 | { |
8554 | return; |
8555 | } |
8556 | |
8557 | seg_free_space* src_index = free_space_buckets[old_power2].free_space; |
8558 | for (int i = old_power2; i > new_power2; i--) |
8559 | { |
8560 | seg_free_space** dest = &(free_space_buckets[i].free_space); |
8561 | (*dest)++; |
8562 | |
8563 | seg_free_space* dest_index = free_space_buckets[i - 1].free_space; |
8564 | if (i > (new_power2 + 1)) |
8565 | { |
8566 | seg_free_space temp = *src_index; |
8567 | *src_index = *dest_index; |
8568 | *dest_index = temp; |
8569 | } |
8570 | src_index = dest_index; |
8571 | } |
8572 | |
8573 | free_space_buckets[old_power2].count_fit--; |
8574 | free_space_buckets[new_power2].count_fit++; |
8575 | } |
8576 | |
8577 | #ifdef _DEBUG |
8578 | |
8579 | void dump_free_space (seg_free_space* item) |
8580 | { |
8581 | uint8_t* addr = 0; |
8582 | size_t len = 0; |
8583 | |
8584 | if (item->is_plug) |
8585 | { |
8586 | mark* m = (mark*)(item->start); |
8587 | len = pinned_len (m); |
8588 | addr = pinned_plug (m) - len; |
8589 | } |
8590 | else |
8591 | { |
8592 | heap_segment* seg = (heap_segment*)(item->start); |
8593 | addr = heap_segment_plan_allocated (seg); |
8594 | len = heap_segment_committed (seg) - addr; |
8595 | } |
8596 | |
8597 | dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id" , heap_num, addr, len)); |
8598 | } |
8599 | |
8600 | void dump() |
8601 | { |
8602 | seg_free_space* item = NULL; |
8603 | int i = 0; |
8604 | |
8605 | dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:" , heap_num)); |
8606 | for (i = 0; i < (free_space_bucket_count - 1); i++) |
8607 | { |
8608 | dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:" , heap_num, (base_power2 + i))); |
8609 | dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s" , heap_num, "start" , "len" )); |
8610 | item = free_space_buckets[i].free_space; |
8611 | while (item < free_space_buckets[i + 1].free_space) |
8612 | { |
8613 | dump_free_space (item); |
8614 | item++; |
8615 | } |
8616 | dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------" , heap_num)); |
8617 | } |
8618 | |
8619 | dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:" , heap_num, (base_power2 + i))); |
8620 | dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s" , heap_num, "start" , "len" )); |
8621 | item = free_space_buckets[i].free_space; |
8622 | |
8623 | while (item <= &seg_free_space_array[free_space_item_count - 1]) |
8624 | { |
8625 | dump_free_space (item); |
8626 | item++; |
8627 | } |
8628 | dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------" , heap_num)); |
8629 | } |
8630 | |
8631 | #endif //_DEBUG |
8632 | |
8633 | free_space_bucket* free_space_buckets; |
8634 | seg_free_space* seg_free_space_array; |
8635 | ptrdiff_t free_space_bucket_count; |
8636 | ptrdiff_t free_space_item_count; |
8637 | int base_power2; |
8638 | int heap_num; |
8639 | #ifdef _DEBUG |
8640 | BOOL has_end_of_seg; |
8641 | #endif //_DEBUG |
8642 | |
8643 | public: |
8644 | |
8645 | seg_free_spaces (int h_number) |
8646 | { |
8647 | heap_num = h_number; |
8648 | } |
8649 | |
8650 | BOOL alloc () |
8651 | { |
8652 | size_t total_prealloc_size = |
8653 | MAX_NUM_BUCKETS * sizeof (free_space_bucket) + |
8654 | MAX_NUM_FREE_SPACES * sizeof (seg_free_space); |
8655 | |
8656 | free_space_buckets = (free_space_bucket*) new (nothrow) uint8_t[total_prealloc_size]; |
8657 | |
8658 | return (!!free_space_buckets); |
8659 | } |
8660 | |
8661 | // We take the ordered free space array we got from the 1st pass, |
8662 | // and feed the portion that we decided to use to this method, ie, |
8663 | // the largest item_count free spaces. |
8664 | void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count) |
8665 | { |
8666 | assert (free_space_buckets); |
8667 | assert (item_count <= (size_t)MAX_PTR); |
8668 | |
8669 | free_space_bucket_count = bucket_count; |
8670 | free_space_item_count = item_count; |
8671 | base_power2 = base; |
8672 | #ifdef _DEBUG |
8673 | has_end_of_seg = FALSE; |
8674 | #endif //_DEBUG |
8675 | |
8676 | ptrdiff_t total_item_count = 0; |
8677 | ptrdiff_t i = 0; |
8678 | |
8679 | seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count); |
8680 | |
8681 | for (i = 0; i < (ptrdiff_t)item_count; i++) |
8682 | { |
8683 | seg_free_space_array[i].start = 0; |
8684 | seg_free_space_array[i].is_plug = FALSE; |
8685 | } |
8686 | |
8687 | for (i = 0; i < bucket_count; i++) |
8688 | { |
8689 | free_space_buckets[i].count_add = ordered_free_spaces[i]; |
8690 | free_space_buckets[i].count_fit = ordered_free_spaces[i]; |
8691 | free_space_buckets[i].free_space = &seg_free_space_array[total_item_count]; |
8692 | total_item_count += free_space_buckets[i].count_add; |
8693 | } |
8694 | |
8695 | assert (total_item_count == (ptrdiff_t)item_count); |
8696 | } |
8697 | |
8698 | // If we are adding a free space before a plug we pass the |
8699 | // mark stack position so we can update the length; we could |
8700 | // also be adding the free space after the last plug in which |
8701 | // case start is the segment which we'll need to update the |
8702 | // heap_segment_plan_allocated. |
8703 | void add (void* start, BOOL plug_p, BOOL first_p) |
8704 | { |
8705 | size_t size = (plug_p ? |
8706 | pinned_len ((mark*)start) : |
8707 | (heap_segment_committed ((heap_segment*)start) - |
8708 | heap_segment_plan_allocated ((heap_segment*)start))); |
8709 | |
8710 | if (plug_p) |
8711 | { |
8712 | dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id" , heap_num, size)); |
8713 | } |
8714 | else |
8715 | { |
8716 | dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id" , heap_num, size)); |
8717 | #ifdef _DEBUG |
8718 | has_end_of_seg = TRUE; |
8719 | #endif //_DEBUG |
8720 | } |
8721 | |
8722 | if (first_p) |
8723 | { |
8724 | size_t eph_gen_starts = gc_heap::eph_gen_starts_size; |
8725 | size -= eph_gen_starts; |
8726 | if (plug_p) |
8727 | { |
8728 | mark* m = (mark*)(start); |
8729 | pinned_len (m) -= eph_gen_starts; |
8730 | } |
8731 | else |
8732 | { |
8733 | heap_segment* seg = (heap_segment*)start; |
8734 | heap_segment_plan_allocated (seg) += eph_gen_starts; |
8735 | } |
8736 | } |
8737 | |
8738 | int bucket_power2 = index_of_highest_set_bit (size); |
8739 | if (bucket_power2 < base_power2) |
8740 | { |
8741 | return; |
8742 | } |
8743 | |
8744 | free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2]; |
8745 | |
8746 | seg_free_space* bucket_free_space = bucket->free_space; |
8747 | assert (plug_p || (!plug_p && bucket->count_add)); |
8748 | |
8749 | if (bucket->count_add == 0) |
8750 | { |
8751 | dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d" , heap_num, bucket_power2)); |
8752 | return; |
8753 | } |
8754 | |
8755 | ptrdiff_t index = bucket->count_add - 1; |
8756 | |
8757 | dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)" , |
8758 | heap_num, |
8759 | (plug_p ? |
8760 | (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) : |
8761 | heap_segment_plan_allocated ((heap_segment*)start)), |
8762 | size, |
8763 | bucket_power2)); |
8764 | |
8765 | if (plug_p) |
8766 | { |
8767 | bucket_free_space[index].is_plug = TRUE; |
8768 | } |
8769 | |
8770 | bucket_free_space[index].start = start; |
8771 | bucket->count_add--; |
8772 | } |
8773 | |
8774 | #ifdef _DEBUG |
8775 | |
8776 | // Do a consistency check after all free spaces are added. |
8777 | void check() |
8778 | { |
8779 | ptrdiff_t i = 0; |
8780 | int end_of_seg_count = 0; |
8781 | |
8782 | for (i = 0; i < free_space_item_count; i++) |
8783 | { |
8784 | assert (seg_free_space_array[i].start); |
8785 | if (!(seg_free_space_array[i].is_plug)) |
8786 | { |
8787 | end_of_seg_count++; |
8788 | } |
8789 | } |
8790 | |
8791 | if (has_end_of_seg) |
8792 | { |
8793 | assert (end_of_seg_count == 1); |
8794 | } |
8795 | else |
8796 | { |
8797 | assert (end_of_seg_count == 0); |
8798 | } |
8799 | |
8800 | for (i = 0; i < free_space_bucket_count; i++) |
8801 | { |
8802 | assert (free_space_buckets[i].count_add == 0); |
8803 | } |
8804 | } |
8805 | |
8806 | #endif //_DEBUG |
8807 | |
8808 | uint8_t* fit (uint8_t* old_loc, |
8809 | #ifdef SHORT_PLUGS |
8810 | BOOL set_padding_on_saved_p, |
8811 | mark* pinned_plug_entry, |
8812 | #endif //SHORT_PLUGS |
8813 | size_t plug_size |
8814 | REQD_ALIGN_AND_OFFSET_DCL) |
8815 | { |
8816 | if (old_loc) |
8817 | { |
8818 | #ifdef SHORT_PLUGS |
8819 | assert (!is_plug_padded (old_loc)); |
8820 | #endif //SHORT_PLUGS |
8821 | assert (!node_realigned (old_loc)); |
8822 | } |
8823 | |
8824 | size_t saved_plug_size = plug_size; |
8825 | |
8826 | #ifdef FEATURE_STRUCTALIGN |
8827 | // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account |
8828 | _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false); |
8829 | #endif // FEATURE_STRUCTALIGN |
8830 | // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the |
8831 | // the bucket. |
8832 | |
8833 | size_t plug_size_to_fit = plug_size; |
8834 | |
8835 | // best fit is only done for gen1 to gen2 and we do not pad in gen2. |
8836 | int pad_in_front = 0; |
8837 | |
8838 | #ifdef SHORT_PLUGS |
8839 | plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0); |
8840 | #endif //SHORT_PLUGS |
8841 | |
8842 | int plug_power2 = index_of_highest_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size))); |
8843 | ptrdiff_t i; |
8844 | uint8_t* new_address = 0; |
8845 | |
8846 | if (plug_power2 < base_power2) |
8847 | { |
8848 | plug_power2 = base_power2; |
8849 | } |
8850 | |
8851 | int chosen_power2 = plug_power2 - base_power2; |
8852 | retry: |
8853 | for (i = chosen_power2; i < free_space_bucket_count; i++) |
8854 | { |
8855 | if (free_space_buckets[i].count_fit != 0) |
8856 | { |
8857 | break; |
8858 | } |
8859 | chosen_power2++; |
8860 | } |
8861 | |
8862 | dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space" , |
8863 | heap_num, |
8864 | plug_size, |
8865 | plug_power2, |
8866 | (chosen_power2 + base_power2))); |
8867 | |
8868 | assert (i < free_space_bucket_count); |
8869 | |
8870 | seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space; |
8871 | ptrdiff_t free_space_count = free_space_buckets[chosen_power2].count_fit; |
8872 | size_t new_free_space_size = 0; |
8873 | BOOL can_fit = FALSE; |
8874 | size_t pad = 0; |
8875 | |
8876 | for (i = 0; i < free_space_count; i++) |
8877 | { |
8878 | size_t free_space_size = 0; |
8879 | pad = 0; |
8880 | #ifdef SHORT_PLUGS |
8881 | BOOL short_plugs_padding_p = FALSE; |
8882 | #endif //SHORT_PLUGS |
8883 | BOOL realign_padding_p = FALSE; |
8884 | |
8885 | if (bucket_free_space[i].is_plug) |
8886 | { |
8887 | mark* m = (mark*)(bucket_free_space[i].start); |
8888 | uint8_t* plug_free_space_start = pinned_plug (m) - pinned_len (m); |
8889 | |
8890 | #ifdef SHORT_PLUGS |
8891 | if ((pad_in_front & USE_PADDING_FRONT) && |
8892 | (((plug_free_space_start - pin_allocation_context_start_region (m))==0) || |
8893 | ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH))) |
8894 | { |
8895 | pad = Align (min_obj_size); |
8896 | short_plugs_padding_p = TRUE; |
8897 | } |
8898 | #endif //SHORT_PLUGS |
8899 | |
8900 | if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad))) |
8901 | { |
8902 | pad += switch_alignment_size (pad != 0); |
8903 | realign_padding_p = TRUE; |
8904 | } |
8905 | |
8906 | plug_size = saved_plug_size + pad; |
8907 | |
8908 | free_space_size = pinned_len (m); |
8909 | new_address = pinned_plug (m) - pinned_len (m); |
8910 | |
8911 | if (free_space_size >= (plug_size + Align (min_obj_size)) || |
8912 | free_space_size == plug_size) |
8913 | { |
8914 | new_free_space_size = free_space_size - plug_size; |
8915 | pinned_len (m) = new_free_space_size; |
8916 | #ifdef SIMPLE_DPRINTF |
8917 | dprintf (SEG_REUSE_LOG_0, ("[%d]FP: 0x%Ix->0x%Ix(%Ix)(%Ix), [0x%Ix (2^%d) -> [0x%Ix (2^%d)" , |
8918 | heap_num, |
8919 | old_loc, |
8920 | new_address, |
8921 | (plug_size - pad), |
8922 | pad, |
8923 | pinned_plug (m), |
8924 | index_of_highest_set_bit (free_space_size), |
8925 | (pinned_plug (m) - pinned_len (m)), |
8926 | index_of_highest_set_bit (new_free_space_size))); |
8927 | #endif //SIMPLE_DPRINTF |
8928 | |
8929 | #ifdef SHORT_PLUGS |
8930 | if (short_plugs_padding_p) |
8931 | { |
8932 | pin_allocation_context_start_region (m) = plug_free_space_start; |
8933 | set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry); |
8934 | } |
8935 | #endif //SHORT_PLUGS |
8936 | |
8937 | if (realign_padding_p) |
8938 | { |
8939 | set_node_realigned (old_loc); |
8940 | } |
8941 | |
8942 | can_fit = TRUE; |
8943 | } |
8944 | } |
8945 | else |
8946 | { |
8947 | heap_segment* seg = (heap_segment*)(bucket_free_space[i].start); |
8948 | free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg); |
8949 | |
8950 | if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg)))) |
8951 | { |
8952 | pad = switch_alignment_size (FALSE); |
8953 | realign_padding_p = TRUE; |
8954 | } |
8955 | |
8956 | plug_size = saved_plug_size + pad; |
8957 | |
8958 | if (free_space_size >= (plug_size + Align (min_obj_size)) || |
8959 | free_space_size == plug_size) |
8960 | { |
8961 | new_address = heap_segment_plan_allocated (seg); |
8962 | new_free_space_size = free_space_size - plug_size; |
8963 | heap_segment_plan_allocated (seg) = new_address + plug_size; |
8964 | #ifdef SIMPLE_DPRINTF |
8965 | dprintf (SEG_REUSE_LOG_0, ("[%d]FS: 0x%Ix-> 0x%Ix(%Ix) (2^%d) -> 0x%Ix (2^%d)" , |
8966 | heap_num, |
8967 | old_loc, |
8968 | new_address, |
8969 | (plug_size - pad), |
8970 | index_of_highest_set_bit (free_space_size), |
8971 | heap_segment_plan_allocated (seg), |
8972 | index_of_highest_set_bit (new_free_space_size))); |
8973 | #endif //SIMPLE_DPRINTF |
8974 | |
8975 | if (realign_padding_p) |
8976 | set_node_realigned (old_loc); |
8977 | |
8978 | can_fit = TRUE; |
8979 | } |
8980 | } |
8981 | |
8982 | if (can_fit) |
8983 | { |
8984 | break; |
8985 | } |
8986 | } |
8987 | |
8988 | if (!can_fit) |
8989 | { |
8990 | assert (chosen_power2 == 0); |
8991 | chosen_power2 = 1; |
8992 | goto retry; |
8993 | } |
8994 | else |
8995 | { |
8996 | if (pad) |
8997 | { |
8998 | new_address += pad; |
8999 | } |
9000 | assert ((chosen_power2 && (i == 0)) || |
9001 | (!chosen_power2) && (i < free_space_count)); |
9002 | } |
9003 | |
9004 | int new_bucket_power2 = index_of_highest_set_bit (new_free_space_size); |
9005 | |
9006 | if (new_bucket_power2 < base_power2) |
9007 | { |
9008 | new_bucket_power2 = base_power2; |
9009 | } |
9010 | |
9011 | move_bucket (chosen_power2, new_bucket_power2 - base_power2); |
9012 | |
9013 | //dump(); |
9014 | |
9015 | return new_address; |
9016 | } |
9017 | |
9018 | void cleanup () |
9019 | { |
9020 | if (free_space_buckets) |
9021 | { |
9022 | delete [] free_space_buckets; |
9023 | } |
9024 | if (seg_free_space_array) |
9025 | { |
9026 | delete [] seg_free_space_array; |
9027 | } |
9028 | } |
9029 | }; |
9030 | |
9031 | |
9032 | #define marked(i) header(i)->IsMarked() |
9033 | #define set_marked(i) header(i)->SetMarked() |
9034 | #define clear_marked(i) header(i)->ClearMarked() |
9035 | #define pinned(i) header(i)->IsPinned() |
9036 | #define set_pinned(i) header(i)->SetPinned() |
9037 | #define clear_pinned(i) header(i)->GetHeader()->ClrGCBit(); |
9038 | |
9039 | inline size_t my_get_size (Object* ob) |
9040 | { |
9041 | MethodTable* mT = header(ob)->GetMethodTable(); |
9042 | return (mT->GetBaseSize() + |
9043 | (mT->HasComponentSize() ? |
9044 | ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0)); |
9045 | } |
9046 | |
9047 | //#define size(i) header(i)->GetSize() |
9048 | #define size(i) my_get_size (header(i)) |
9049 | |
9050 | #define contain_pointers(i) header(i)->ContainsPointers() |
9051 | #ifdef COLLECTIBLE_CLASS |
9052 | #define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible() |
9053 | |
9054 | #define get_class_object(i) GCToEEInterface::GetLoaderAllocatorObjectForGC((Object *)i) |
9055 | #define is_collectible(i) method_table(i)->Collectible() |
9056 | #else //COLLECTIBLE_CLASS |
9057 | #define contain_pointers_or_collectible(i) header(i)->ContainsPointers() |
9058 | #endif //COLLECTIBLE_CLASS |
9059 | |
9060 | #if defined (MARK_ARRAY) && defined (BACKGROUND_GC) |
9061 | inline |
9062 | void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg) |
9063 | { |
9064 | uint8_t* range_beg = 0; |
9065 | uint8_t* range_end = 0; |
9066 | if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end)) |
9067 | { |
9068 | clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE |
9069 | #ifdef FEATURE_BASICFREEZE |
9070 | , TRUE |
9071 | #endif // FEATURE_BASICFREEZE |
9072 | ); |
9073 | } |
9074 | } |
9075 | |
9076 | void gc_heap::clear_batch_mark_array_bits (uint8_t* start, uint8_t* end) |
9077 | { |
9078 | if ((start < background_saved_highest_address) && |
9079 | (end > background_saved_lowest_address)) |
9080 | { |
9081 | start = max (start, background_saved_lowest_address); |
9082 | end = min (end, background_saved_highest_address); |
9083 | |
9084 | size_t start_mark_bit = mark_bit_of (start); |
9085 | size_t end_mark_bit = mark_bit_of (end); |
9086 | unsigned int startbit = mark_bit_bit (start_mark_bit); |
9087 | unsigned int endbit = mark_bit_bit (end_mark_bit); |
9088 | size_t startwrd = mark_bit_word (start_mark_bit); |
9089 | size_t endwrd = mark_bit_word (end_mark_bit); |
9090 | |
9091 | dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix" , |
9092 | (size_t)start, (size_t)start_mark_bit, |
9093 | (size_t)end, (size_t)end_mark_bit)); |
9094 | |
9095 | unsigned int firstwrd = lowbits (~0, startbit); |
9096 | unsigned int lastwrd = highbits (~0, endbit); |
9097 | |
9098 | if (startwrd == endwrd) |
9099 | { |
9100 | unsigned int wrd = firstwrd | lastwrd; |
9101 | mark_array[startwrd] &= wrd; |
9102 | return; |
9103 | } |
9104 | |
9105 | // clear the first mark word. |
9106 | if (startbit) |
9107 | { |
9108 | mark_array[startwrd] &= firstwrd; |
9109 | startwrd++; |
9110 | } |
9111 | |
9112 | for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++) |
9113 | { |
9114 | mark_array[wrdtmp] = 0; |
9115 | } |
9116 | |
9117 | // clear the last mark word. |
9118 | if (endbit) |
9119 | { |
9120 | mark_array[endwrd] &= lastwrd; |
9121 | } |
9122 | } |
9123 | } |
9124 | |
9125 | void gc_heap::bgc_clear_batch_mark_array_bits (uint8_t* start, uint8_t* end) |
9126 | { |
9127 | if ((start < background_saved_highest_address) && |
9128 | (end > background_saved_lowest_address)) |
9129 | { |
9130 | start = max (start, background_saved_lowest_address); |
9131 | end = min (end, background_saved_highest_address); |
9132 | |
9133 | clear_batch_mark_array_bits (start, end); |
9134 | } |
9135 | } |
9136 | |
9137 | void gc_heap::clear_mark_array_by_objects (uint8_t* from, uint8_t* end, BOOL loh_p) |
9138 | { |
9139 | dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix" , |
9140 | from, end)); |
9141 | int align_const = get_alignment_constant (!loh_p); |
9142 | |
9143 | uint8_t* o = from; |
9144 | |
9145 | while (o < end) |
9146 | { |
9147 | uint8_t* next_o = o + Align (size (o), align_const); |
9148 | |
9149 | if (background_object_marked (o, TRUE)) |
9150 | { |
9151 | dprintf (3, ("%Ix was marked by bgc, is now cleared" , o)); |
9152 | } |
9153 | |
9154 | o = next_o; |
9155 | } |
9156 | } |
9157 | #endif //MARK_ARRAY && BACKGROUND_GC |
9158 | |
9159 | inline |
9160 | BOOL gc_heap::is_mark_set (uint8_t* o) |
9161 | { |
9162 | return marked (o); |
9163 | } |
9164 | |
9165 | #if defined (_MSC_VER) && defined (_TARGET_X86_) |
9166 | #pragma optimize("y", on) // Small critical routines, don't put in EBP frame |
9167 | #endif //_MSC_VER && _TARGET_X86_ |
9168 | |
9169 | // return the generation number of an object. |
9170 | // It is assumed that the object is valid. |
9171 | //Note that this will return max_generation for a LOH object |
9172 | int gc_heap::object_gennum (uint8_t* o) |
9173 | { |
9174 | if (in_range_for_segment (o, ephemeral_heap_segment) && |
9175 | (o >= generation_allocation_start (generation_of (max_generation-1)))) |
9176 | { |
9177 | // in an ephemeral generation. |
9178 | for ( int i = 0; i < max_generation-1; i++) |
9179 | { |
9180 | if ((o >= generation_allocation_start (generation_of (i)))) |
9181 | return i; |
9182 | } |
9183 | return max_generation-1; |
9184 | } |
9185 | else |
9186 | { |
9187 | return max_generation; |
9188 | } |
9189 | } |
9190 | |
9191 | int gc_heap::object_gennum_plan (uint8_t* o) |
9192 | { |
9193 | if (in_range_for_segment (o, ephemeral_heap_segment)) |
9194 | { |
9195 | for (int i = 0; i <= max_generation-1; i++) |
9196 | { |
9197 | uint8_t* plan_start = generation_plan_allocation_start (generation_of (i)); |
9198 | if (plan_start && (o >= plan_start)) |
9199 | { |
9200 | return i; |
9201 | } |
9202 | } |
9203 | } |
9204 | return max_generation; |
9205 | } |
9206 | |
9207 | #if defined(_MSC_VER) && defined(_TARGET_X86_) |
9208 | #pragma optimize("", on) // Go back to command line default optimizations |
9209 | #endif //_MSC_VER && _TARGET_X86_ |
9210 | |
9211 | heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h_number) |
9212 | { |
9213 | size_t initial_commit = SEGMENT_INITIAL_COMMIT; |
9214 | |
9215 | //Commit the first page |
9216 | if (!virtual_alloc_commit_for_heap (new_pages, initial_commit, h_number)) |
9217 | { |
9218 | return 0; |
9219 | } |
9220 | |
9221 | //overlay the heap_segment |
9222 | heap_segment* new_segment = (heap_segment*)new_pages; |
9223 | |
9224 | uint8_t* start = new_pages + segment_info_size; |
9225 | heap_segment_mem (new_segment) = start; |
9226 | heap_segment_used (new_segment) = start; |
9227 | heap_segment_reserved (new_segment) = new_pages + size; |
9228 | heap_segment_committed (new_segment) = new_pages + initial_commit; |
9229 | init_heap_segment (new_segment); |
9230 | dprintf (2, ("Creating heap segment %Ix" , (size_t)new_segment)); |
9231 | return new_segment; |
9232 | } |
9233 | |
9234 | void gc_heap::init_heap_segment (heap_segment* seg) |
9235 | { |
9236 | seg->flags = 0; |
9237 | heap_segment_next (seg) = 0; |
9238 | heap_segment_plan_allocated (seg) = heap_segment_mem (seg); |
9239 | heap_segment_allocated (seg) = heap_segment_mem (seg); |
9240 | #ifdef BACKGROUND_GC |
9241 | heap_segment_background_allocated (seg) = 0; |
9242 | heap_segment_saved_bg_allocated (seg) = 0; |
9243 | #endif //BACKGROUND_GC |
9244 | } |
9245 | |
9246 | //Releases the segment to the OS. |
9247 | // this is always called on one thread only so calling seg_table->remove is fine. |
9248 | void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding) |
9249 | { |
9250 | if (!heap_segment_loh_p (seg)) |
9251 | { |
9252 | //cleanup the brick table back to the empty value |
9253 | clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg)); |
9254 | } |
9255 | |
9256 | if (consider_hoarding) |
9257 | { |
9258 | assert ((heap_segment_mem (seg) - (uint8_t*)seg) <= ptrdiff_t(2*OS_PAGE_SIZE)); |
9259 | size_t ss = (size_t) (heap_segment_reserved (seg) - (uint8_t*)seg); |
9260 | //Don't keep the big ones. |
9261 | if (ss <= INITIAL_ALLOC) |
9262 | { |
9263 | dprintf (2, ("Hoarding segment %Ix" , (size_t)seg)); |
9264 | #ifdef BACKGROUND_GC |
9265 | // We don't need to clear the decommitted flag because when this segment is used |
9266 | // for a new segment the flags will be cleared. |
9267 | if (!heap_segment_decommitted_p (seg)) |
9268 | #endif //BACKGROUND_GC |
9269 | { |
9270 | decommit_heap_segment (seg); |
9271 | } |
9272 | |
9273 | #ifdef SEG_MAPPING_TABLE |
9274 | seg_mapping_table_remove_segment (seg); |
9275 | #endif //SEG_MAPPING_TABLE |
9276 | |
9277 | heap_segment_next (seg) = segment_standby_list; |
9278 | segment_standby_list = seg; |
9279 | seg = 0; |
9280 | } |
9281 | } |
9282 | |
9283 | if (seg != 0) |
9284 | { |
9285 | dprintf (2, ("h%d: del seg: [%Ix, %Ix[" , |
9286 | heap_number, (size_t)seg, |
9287 | (size_t)(heap_segment_reserved (seg)))); |
9288 | |
9289 | #ifdef BACKGROUND_GC |
9290 | ::record_changed_seg ((uint8_t*)seg, heap_segment_reserved (seg), |
9291 | settings.gc_index, current_bgc_state, |
9292 | seg_deleted); |
9293 | decommit_mark_array_by_seg (seg); |
9294 | #endif //BACKGROUND_GC |
9295 | |
9296 | #ifdef SEG_MAPPING_TABLE |
9297 | seg_mapping_table_remove_segment (seg); |
9298 | #else //SEG_MAPPING_TABLE |
9299 | seg_table->remove ((uint8_t*)seg); |
9300 | #endif //SEG_MAPPING_TABLE |
9301 | |
9302 | release_segment (seg); |
9303 | } |
9304 | } |
9305 | |
9306 | //resets the pages beyond allocates size so they won't be swapped out and back in |
9307 | |
9308 | void gc_heap::reset_heap_segment_pages (heap_segment* seg) |
9309 | { |
9310 | size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg)); |
9311 | size_t size = (size_t)heap_segment_committed (seg) - page_start; |
9312 | if (size != 0) |
9313 | GCToOSInterface::VirtualReset((void*)page_start, size, false /* unlock */); |
9314 | } |
9315 | |
9316 | void gc_heap::decommit_heap_segment_pages (heap_segment* seg, |
9317 | size_t ) |
9318 | { |
9319 | uint8_t* page_start = align_on_page (heap_segment_allocated(seg)); |
9320 | size_t size = heap_segment_committed (seg) - page_start; |
9321 | extra_space = align_on_page (extra_space); |
9322 | if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE)) |
9323 | { |
9324 | page_start += max(extra_space, 32*OS_PAGE_SIZE); |
9325 | size -= max (extra_space, 32*OS_PAGE_SIZE); |
9326 | |
9327 | GCToOSInterface::VirtualDecommit (page_start, size); |
9328 | dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)" , |
9329 | (size_t)page_start, |
9330 | (size_t)(page_start + size), |
9331 | size)); |
9332 | heap_segment_committed (seg) = page_start; |
9333 | if (heap_segment_used (seg) > heap_segment_committed (seg)) |
9334 | { |
9335 | heap_segment_used (seg) = heap_segment_committed (seg); |
9336 | } |
9337 | } |
9338 | } |
9339 | |
9340 | //decommit all pages except one or 2 |
9341 | void gc_heap::decommit_heap_segment (heap_segment* seg) |
9342 | { |
9343 | uint8_t* page_start = align_on_page (heap_segment_mem (seg)); |
9344 | |
9345 | dprintf (3, ("Decommitting heap segment %Ix" , (size_t)seg)); |
9346 | |
9347 | #ifdef BACKGROUND_GC |
9348 | page_start += OS_PAGE_SIZE; |
9349 | #endif //BACKGROUND_GC |
9350 | |
9351 | size_t size = heap_segment_committed (seg) - page_start; |
9352 | GCToOSInterface::VirtualDecommit (page_start, size); |
9353 | |
9354 | //re-init the segment object |
9355 | heap_segment_committed (seg) = page_start; |
9356 | if (heap_segment_used (seg) > heap_segment_committed (seg)) |
9357 | { |
9358 | heap_segment_used (seg) = heap_segment_committed (seg); |
9359 | } |
9360 | } |
9361 | |
9362 | void gc_heap::clear_gen0_bricks() |
9363 | { |
9364 | if (!gen0_bricks_cleared) |
9365 | { |
9366 | gen0_bricks_cleared = TRUE; |
9367 | //initialize brick table for gen 0 |
9368 | for (size_t b = brick_of (generation_allocation_start (generation_of (0))); |
9369 | b < brick_of (align_on_brick |
9370 | (heap_segment_allocated (ephemeral_heap_segment))); |
9371 | b++) |
9372 | { |
9373 | set_brick (b, -1); |
9374 | } |
9375 | } |
9376 | } |
9377 | |
9378 | #ifdef BACKGROUND_GC |
9379 | void gc_heap::rearrange_small_heap_segments() |
9380 | { |
9381 | heap_segment* seg = freeable_small_heap_segment; |
9382 | while (seg) |
9383 | { |
9384 | heap_segment* next_seg = heap_segment_next (seg); |
9385 | // TODO: we need to consider hoarding here. |
9386 | delete_heap_segment (seg, FALSE); |
9387 | seg = next_seg; |
9388 | } |
9389 | freeable_small_heap_segment = 0; |
9390 | } |
9391 | #endif //BACKGROUND_GC |
9392 | |
9393 | void gc_heap::rearrange_large_heap_segments() |
9394 | { |
9395 | dprintf (2, ("deleting empty large segments" )); |
9396 | heap_segment* seg = freeable_large_heap_segment; |
9397 | while (seg) |
9398 | { |
9399 | heap_segment* next_seg = heap_segment_next (seg); |
9400 | delete_heap_segment (seg, GCConfig::GetRetainVM()); |
9401 | seg = next_seg; |
9402 | } |
9403 | freeable_large_heap_segment = 0; |
9404 | } |
9405 | |
9406 | void gc_heap::rearrange_heap_segments(BOOL compacting) |
9407 | { |
9408 | heap_segment* seg = |
9409 | generation_start_segment (generation_of (max_generation)); |
9410 | |
9411 | heap_segment* prev_seg = 0; |
9412 | heap_segment* next_seg = 0; |
9413 | while (seg) |
9414 | { |
9415 | next_seg = heap_segment_next (seg); |
9416 | |
9417 | //link ephemeral segment when expanding |
9418 | if ((next_seg == 0) && (seg != ephemeral_heap_segment)) |
9419 | { |
9420 | seg->next = ephemeral_heap_segment; |
9421 | next_seg = heap_segment_next (seg); |
9422 | } |
9423 | |
9424 | //re-used expanded heap segment |
9425 | if ((seg == ephemeral_heap_segment) && next_seg) |
9426 | { |
9427 | heap_segment_next (prev_seg) = next_seg; |
9428 | heap_segment_next (seg) = 0; |
9429 | } |
9430 | else |
9431 | { |
9432 | uint8_t* end_segment = (compacting ? |
9433 | heap_segment_plan_allocated (seg) : |
9434 | heap_segment_allocated (seg)); |
9435 | // check if the segment was reached by allocation |
9436 | if ((end_segment == heap_segment_mem (seg))&& |
9437 | !heap_segment_read_only_p (seg)) |
9438 | { |
9439 | //if not, unthread and delete |
9440 | assert (prev_seg); |
9441 | assert (seg != ephemeral_heap_segment); |
9442 | heap_segment_next (prev_seg) = next_seg; |
9443 | delete_heap_segment (seg, GCConfig::GetRetainVM()); |
9444 | |
9445 | dprintf (2, ("Deleting heap segment %Ix" , (size_t)seg)); |
9446 | } |
9447 | else |
9448 | { |
9449 | if (!heap_segment_read_only_p (seg)) |
9450 | { |
9451 | if (compacting) |
9452 | { |
9453 | heap_segment_allocated (seg) = |
9454 | heap_segment_plan_allocated (seg); |
9455 | } |
9456 | |
9457 | // reset the pages between allocated and committed. |
9458 | if (seg != ephemeral_heap_segment) |
9459 | { |
9460 | decommit_heap_segment_pages (seg, 0); |
9461 | } |
9462 | } |
9463 | prev_seg = seg; |
9464 | } |
9465 | } |
9466 | |
9467 | seg = next_seg; |
9468 | } |
9469 | } |
9470 | |
9471 | |
9472 | #ifdef WRITE_WATCH |
9473 | |
9474 | uint8_t* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch |
9475 | |
9476 | #ifdef TIME_WRITE_WATCH |
9477 | static unsigned int tot_cycles = 0; |
9478 | #endif //TIME_WRITE_WATCH |
9479 | |
9480 | #ifdef CARD_BUNDLE |
9481 | |
9482 | inline void gc_heap::verify_card_bundle_bits_set(size_t first_card_word, size_t last_card_word) |
9483 | { |
9484 | #ifdef _DEBUG |
9485 | for (size_t x = cardw_card_bundle (first_card_word); x < cardw_card_bundle (last_card_word); x++) |
9486 | { |
9487 | if (!card_bundle_set_p (x)) |
9488 | { |
9489 | assert (!"Card bundle not set" ); |
9490 | dprintf (3, ("Card bundle %Ix not set" , x)); |
9491 | } |
9492 | } |
9493 | #endif |
9494 | } |
9495 | |
9496 | // Verifies that any bundles that are not set represent only cards that are not set. |
9497 | inline void gc_heap::verify_card_bundles() |
9498 | { |
9499 | #ifdef _DEBUG |
9500 | size_t lowest_card = card_word (card_of (lowest_address)); |
9501 | size_t highest_card = card_word (card_of (highest_address)); |
9502 | size_t cardb = cardw_card_bundle (lowest_card); |
9503 | size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card)); |
9504 | |
9505 | while (cardb < end_cardb) |
9506 | { |
9507 | uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)]; |
9508 | uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)]; |
9509 | |
9510 | if (card_bundle_set_p (cardb) == 0) |
9511 | { |
9512 | // Verify that no card is set |
9513 | while (card_word < card_word_end) |
9514 | { |
9515 | if (*card_word != 0) |
9516 | { |
9517 | dprintf (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear" , |
9518 | dd_collection_count (dynamic_data_of (0)), |
9519 | (size_t)(card_word-&card_table[0]), |
9520 | (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb)); |
9521 | } |
9522 | |
9523 | assert((*card_word)==0); |
9524 | card_word++; |
9525 | } |
9526 | } |
9527 | |
9528 | cardb++; |
9529 | } |
9530 | #endif |
9531 | } |
9532 | |
9533 | // If card bundles are enabled, use write watch to find pages in the card table that have |
9534 | // been dirtied, and set the corresponding card bundle bits. |
9535 | void gc_heap::update_card_table_bundle() |
9536 | { |
9537 | if (card_bundles_enabled()) |
9538 | { |
9539 | // The address of the card word containing the card representing the lowest heap address |
9540 | uint8_t* base_address = (uint8_t*)(&card_table[card_word (card_of (lowest_address))]); |
9541 | |
9542 | // The address of the card word containing the card representing the highest heap address |
9543 | uint8_t* high_address = (uint8_t*)(&card_table[card_word (card_of (highest_address))]); |
9544 | |
9545 | uint8_t* saved_base_address = base_address; |
9546 | uintptr_t bcount = array_size; |
9547 | size_t saved_region_size = align_on_page (high_address) - saved_base_address; |
9548 | |
9549 | do |
9550 | { |
9551 | size_t region_size = align_on_page (high_address) - base_address; |
9552 | |
9553 | dprintf (3,("Probing card table pages [%Ix, %Ix[" , (size_t)base_address, (size_t)base_address+region_size)); |
9554 | bool success = GCToOSInterface::GetWriteWatch(false /* resetState */, |
9555 | base_address, |
9556 | region_size, |
9557 | (void**)g_addresses, |
9558 | &bcount); |
9559 | assert (success && "GetWriteWatch failed!" ); |
9560 | |
9561 | dprintf (3,("Found %d pages written" , bcount)); |
9562 | for (unsigned i = 0; i < bcount; i++) |
9563 | { |
9564 | // Offset of the dirty page from the start of the card table (clamped to base_address) |
9565 | size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0]; |
9566 | |
9567 | // Offset of the end of the page from the start of the card table (clamped to high addr) |
9568 | size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0]; |
9569 | assert (bcardw >= card_word (card_of (g_gc_lowest_address))); |
9570 | |
9571 | // Set the card bundle bits representing the dirty card table page |
9572 | card_bundles_set (cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))); |
9573 | dprintf (3,("Set Card bundle [%Ix, %Ix[" , cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw)))); |
9574 | |
9575 | verify_card_bundle_bits_set(bcardw, ecardw); |
9576 | } |
9577 | |
9578 | if (bcount >= array_size) |
9579 | { |
9580 | base_address = g_addresses [array_size-1] + OS_PAGE_SIZE; |
9581 | bcount = array_size; |
9582 | } |
9583 | |
9584 | } while ((bcount >= array_size) && (base_address < high_address)); |
9585 | |
9586 | // Now that we've updated the card bundle bits, reset the write-tracking state. |
9587 | GCToOSInterface::ResetWriteWatch (saved_base_address, saved_region_size); |
9588 | } |
9589 | } |
9590 | #endif //CARD_BUNDLE |
9591 | |
9592 | // static |
9593 | void gc_heap::reset_write_watch_for_gc_heap(void* base_address, size_t region_size) |
9594 | { |
9595 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9596 | SoftwareWriteWatch::ClearDirty(base_address, region_size); |
9597 | #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9598 | GCToOSInterface::ResetWriteWatch(base_address, region_size); |
9599 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9600 | } |
9601 | |
9602 | // static |
9603 | void gc_heap::get_write_watch_for_gc_heap(bool reset, void *base_address, size_t region_size, void** dirty_pages, uintptr_t* dirty_page_count_ref, bool is_runtime_suspended) |
9604 | { |
9605 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9606 | SoftwareWriteWatch::GetDirty(base_address, region_size, dirty_pages, dirty_page_count_ref, reset, is_runtime_suspended); |
9607 | #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9608 | UNREFERENCED_PARAMETER(is_runtime_suspended); |
9609 | bool success = GCToOSInterface::GetWriteWatch(reset, base_address, region_size, dirty_pages, dirty_page_count_ref); |
9610 | assert(success); |
9611 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9612 | } |
9613 | |
9614 | const size_t ww_reset_quantum = 128*1024*1024; |
9615 | |
9616 | inline |
9617 | void gc_heap::switch_one_quantum() |
9618 | { |
9619 | enable_preemptive (); |
9620 | GCToOSInterface::Sleep (1); |
9621 | disable_preemptive (true); |
9622 | } |
9623 | |
9624 | void gc_heap::reset_ww_by_chunk (uint8_t* start_address, size_t total_reset_size) |
9625 | { |
9626 | size_t reset_size = 0; |
9627 | size_t remaining_reset_size = 0; |
9628 | size_t next_reset_size = 0; |
9629 | |
9630 | while (reset_size != total_reset_size) |
9631 | { |
9632 | remaining_reset_size = total_reset_size - reset_size; |
9633 | next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size); |
9634 | if (next_reset_size) |
9635 | { |
9636 | reset_write_watch_for_gc_heap(start_address, next_reset_size); |
9637 | reset_size += next_reset_size; |
9638 | |
9639 | switch_one_quantum(); |
9640 | } |
9641 | } |
9642 | |
9643 | assert (reset_size == total_reset_size); |
9644 | } |
9645 | |
9646 | // This does a Sleep(1) for every reset ww_reset_quantum bytes of reset |
9647 | // we do concurrently. |
9648 | void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size) |
9649 | { |
9650 | if (concurrent_p) |
9651 | { |
9652 | *current_total_reset_size += last_reset_size; |
9653 | |
9654 | dprintf (2, ("reset %Id bytes so far" , *current_total_reset_size)); |
9655 | |
9656 | if (*current_total_reset_size > ww_reset_quantum) |
9657 | { |
9658 | switch_one_quantum(); |
9659 | |
9660 | *current_total_reset_size = 0; |
9661 | } |
9662 | } |
9663 | } |
9664 | |
9665 | void gc_heap::reset_write_watch (BOOL concurrent_p) |
9666 | { |
9667 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9668 | // Software write watch currently requires the runtime to be suspended during reset. See SoftwareWriteWatch::ClearDirty(). |
9669 | assert(!concurrent_p); |
9670 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9671 | |
9672 | heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
9673 | |
9674 | PREFIX_ASSUME(seg != NULL); |
9675 | |
9676 | size_t reset_size = 0; |
9677 | size_t region_size = 0; |
9678 | |
9679 | dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix" , background_saved_lowest_address, background_saved_highest_address)); |
9680 | |
9681 | while (seg) |
9682 | { |
9683 | uint8_t* base_address = align_lower_page (heap_segment_mem (seg)); |
9684 | base_address = max (base_address, background_saved_lowest_address); |
9685 | |
9686 | uint8_t* high_address = 0; |
9687 | high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg)); |
9688 | high_address = min (high_address, background_saved_highest_address); |
9689 | |
9690 | if (base_address < high_address) |
9691 | { |
9692 | region_size = high_address - base_address; |
9693 | |
9694 | #ifdef TIME_WRITE_WATCH |
9695 | unsigned int time_start = GetCycleCount32(); |
9696 | #endif //TIME_WRITE_WATCH |
9697 | dprintf (3, ("h%d: soh ww: [%Ix(%Id)" , heap_number, (size_t)base_address, region_size)); |
9698 | //reset_ww_by_chunk (base_address, region_size); |
9699 | reset_write_watch_for_gc_heap(base_address, region_size); |
9700 | |
9701 | #ifdef TIME_WRITE_WATCH |
9702 | unsigned int time_stop = GetCycleCount32(); |
9703 | tot_cycles += time_stop - time_start; |
9704 | printf ("ResetWriteWatch Duration: %d, total: %d\n" , |
9705 | time_stop - time_start, tot_cycles); |
9706 | #endif //TIME_WRITE_WATCH |
9707 | |
9708 | switch_on_reset (concurrent_p, &reset_size, region_size); |
9709 | } |
9710 | |
9711 | seg = heap_segment_next_rw (seg); |
9712 | |
9713 | concurrent_print_time_delta ("CRWW soh" ); |
9714 | } |
9715 | |
9716 | //concurrent_print_time_delta ("CRW soh"); |
9717 | |
9718 | seg = heap_segment_rw (generation_start_segment (large_object_generation)); |
9719 | |
9720 | PREFIX_ASSUME(seg != NULL); |
9721 | |
9722 | while (seg) |
9723 | { |
9724 | uint8_t* base_address = align_lower_page (heap_segment_mem (seg)); |
9725 | uint8_t* high_address = heap_segment_allocated (seg); |
9726 | |
9727 | base_address = max (base_address, background_saved_lowest_address); |
9728 | high_address = min (high_address, background_saved_highest_address); |
9729 | |
9730 | if (base_address < high_address) |
9731 | { |
9732 | region_size = high_address - base_address; |
9733 | |
9734 | #ifdef TIME_WRITE_WATCH |
9735 | unsigned int time_start = GetCycleCount32(); |
9736 | #endif //TIME_WRITE_WATCH |
9737 | dprintf (3, ("h%d: loh ww: [%Ix(%Id)" , heap_number, (size_t)base_address, region_size)); |
9738 | //reset_ww_by_chunk (base_address, region_size); |
9739 | reset_write_watch_for_gc_heap(base_address, region_size); |
9740 | |
9741 | #ifdef TIME_WRITE_WATCH |
9742 | unsigned int time_stop = GetCycleCount32(); |
9743 | tot_cycles += time_stop - time_start; |
9744 | printf ("ResetWriteWatch Duration: %d, total: %d\n" , |
9745 | time_stop - time_start, tot_cycles); |
9746 | #endif //TIME_WRITE_WATCH |
9747 | |
9748 | switch_on_reset (concurrent_p, &reset_size, region_size); |
9749 | } |
9750 | |
9751 | seg = heap_segment_next_rw (seg); |
9752 | |
9753 | concurrent_print_time_delta ("CRWW loh" ); |
9754 | } |
9755 | |
9756 | #ifdef DEBUG_WRITE_WATCH |
9757 | debug_write_watch = (uint8_t**)~0; |
9758 | #endif //DEBUG_WRITE_WATCH |
9759 | } |
9760 | |
9761 | #endif //WRITE_WATCH |
9762 | |
9763 | #ifdef BACKGROUND_GC |
9764 | void gc_heap::restart_vm() |
9765 | { |
9766 | //assert (generation_allocation_pointer (youngest_generation) == 0); |
9767 | dprintf (3, ("Restarting EE" )); |
9768 | STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n" ); |
9769 | ee_proceed_event.Set(); |
9770 | } |
9771 | |
9772 | inline |
9773 | void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p) |
9774 | { |
9775 | if (awr != awr_ignored) |
9776 | { |
9777 | if (begin_p) |
9778 | { |
9779 | FIRE_EVENT(BGCAllocWaitBegin, awr); |
9780 | } |
9781 | else |
9782 | { |
9783 | FIRE_EVENT(BGCAllocWaitEnd, awr); |
9784 | } |
9785 | } |
9786 | } |
9787 | |
9788 | |
9789 | void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr) |
9790 | { |
9791 | fire_alloc_wait_event (awr, TRUE); |
9792 | } |
9793 | |
9794 | |
9795 | void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr) |
9796 | { |
9797 | fire_alloc_wait_event (awr, FALSE); |
9798 | } |
9799 | #endif //BACKGROUND_GC |
9800 | void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* start, uint8_t* pointer) |
9801 | { |
9802 | gen.allocation_start = start; |
9803 | gen.allocation_context.alloc_ptr = pointer; |
9804 | gen.allocation_context.alloc_limit = pointer; |
9805 | gen.allocation_context.alloc_bytes = 0; |
9806 | gen.allocation_context.alloc_bytes_loh = 0; |
9807 | gen.allocation_context_start_region = pointer; |
9808 | gen.start_segment = seg; |
9809 | gen.allocation_segment = seg; |
9810 | gen.plan_allocation_start = 0; |
9811 | gen.free_list_space = 0; |
9812 | gen.pinned_allocated = 0; |
9813 | gen.free_list_allocated = 0; |
9814 | gen.end_seg_allocated = 0; |
9815 | gen.condemned_allocated = 0; |
9816 | gen.free_obj_space = 0; |
9817 | gen.allocation_size = 0; |
9818 | gen.pinned_allocation_sweep_size = 0; |
9819 | gen.pinned_allocation_compact_size = 0; |
9820 | gen.allocate_end_seg_p = FALSE; |
9821 | gen.free_list_allocator.clear(); |
9822 | |
9823 | #ifdef FREE_USAGE_STATS |
9824 | memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces)); |
9825 | memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces)); |
9826 | memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs)); |
9827 | #endif //FREE_USAGE_STATS |
9828 | } |
9829 | |
9830 | void gc_heap::adjust_ephemeral_limits () |
9831 | { |
9832 | ephemeral_low = generation_allocation_start (generation_of (max_generation - 1)); |
9833 | ephemeral_high = heap_segment_reserved (ephemeral_heap_segment); |
9834 | |
9835 | dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix" , |
9836 | (size_t)ephemeral_low, (size_t)ephemeral_high)) |
9837 | |
9838 | #ifndef MULTIPLE_HEAPS |
9839 | // This updates the write barrier helpers with the new info. |
9840 | stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high); |
9841 | #endif // MULTIPLE_HEAPS |
9842 | } |
9843 | |
9844 | #if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN) |
9845 | FILE* CreateLogFile(const GCConfigStringHolder& temp_logfile_name, bool is_config) |
9846 | { |
9847 | FILE* logFile; |
9848 | |
9849 | if (!temp_logfile_name.Get()) |
9850 | { |
9851 | return nullptr; |
9852 | } |
9853 | |
9854 | char logfile_name[MAX_LONGPATH+1]; |
9855 | uint32_t pid = GCToOSInterface::GetCurrentProcessId(); |
9856 | const char* suffix = is_config ? ".config.log" : ".log" ; |
9857 | _snprintf_s(logfile_name, MAX_LONGPATH+1, _TRUNCATE, "%s.%d%s" , temp_logfile_name.Get(), pid, suffix); |
9858 | logFile = fopen(logfile_name, "wb" ); |
9859 | return logFile; |
9860 | } |
9861 | #endif //TRACE_GC || GC_CONFIG_DRIVEN |
9862 | |
9863 | HRESULT gc_heap::initialize_gc (size_t segment_size, |
9864 | size_t heap_size |
9865 | #ifdef MULTIPLE_HEAPS |
9866 | ,unsigned number_of_heaps |
9867 | #endif //MULTIPLE_HEAPS |
9868 | ) |
9869 | { |
9870 | #ifdef TRACE_GC |
9871 | if (GCConfig::GetLogEnabled()) |
9872 | { |
9873 | gc_log = CreateLogFile(GCConfig::GetLogFile(), false); |
9874 | |
9875 | if (gc_log == NULL) |
9876 | return E_FAIL; |
9877 | |
9878 | // GCLogFileSize in MBs. |
9879 | gc_log_file_size = static_cast<size_t>(GCConfig::GetLogFileSize()); |
9880 | |
9881 | if (gc_log_file_size <= 0 || gc_log_file_size > 500) |
9882 | { |
9883 | fclose (gc_log); |
9884 | return E_FAIL; |
9885 | } |
9886 | |
9887 | gc_log_lock.Initialize(); |
9888 | gc_log_buffer = new (nothrow) uint8_t [gc_log_buffer_size]; |
9889 | if (!gc_log_buffer) |
9890 | { |
9891 | fclose(gc_log); |
9892 | return E_FAIL; |
9893 | } |
9894 | |
9895 | memset (gc_log_buffer, '*', gc_log_buffer_size); |
9896 | |
9897 | max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size; |
9898 | } |
9899 | #endif // TRACE_GC |
9900 | |
9901 | #ifdef GC_CONFIG_DRIVEN |
9902 | if (GCConfig::GetConfigLogEnabled()) |
9903 | { |
9904 | gc_config_log = CreateLogFile(GCConfig::GetConfigLogFile(), true); |
9905 | |
9906 | if (gc_config_log == NULL) |
9907 | return E_FAIL; |
9908 | |
9909 | gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size]; |
9910 | if (!gc_config_log_buffer) |
9911 | { |
9912 | fclose(gc_config_log); |
9913 | return E_FAIL; |
9914 | } |
9915 | |
9916 | compact_ratio = static_cast<int>(GCConfig::GetCompactRatio()); |
9917 | |
9918 | // h# | GC | gen | C | EX | NF | BF | ML | DM || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP | |
9919 | cprintf (("%2s | %6s | %1s | %1s | %2s | %2s | %2s | %2s | %2s || %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s |" , |
9920 | "h#" , // heap index |
9921 | "GC" , // GC index |
9922 | "g" , // generation |
9923 | "C" , // compaction (empty means sweeping), 'M' means it was mandatory, 'W' means it was not |
9924 | "EX" , // heap expansion |
9925 | "NF" , // normal fit |
9926 | "BF" , // best fit (if it indicates neither NF nor BF it means it had to acquire a new seg. |
9927 | "ML" , // mark list |
9928 | "DM" , // demotion |
9929 | "PreS" , // short object before pinned plug |
9930 | "PostS" , // short object after pinned plug |
9931 | "Merge" , // merged pinned plugs |
9932 | "Conv" , // converted to pinned plug |
9933 | "Pre" , // plug before pinned plug but not after |
9934 | "Post" , // plug after pinned plug but not before |
9935 | "PrPo" , // plug both before and after pinned plug |
9936 | "PreP" , // pre short object padded |
9937 | "PostP" // post short object padded |
9938 | )); |
9939 | } |
9940 | #endif //GC_CONFIG_DRIVEN |
9941 | |
9942 | #ifdef GC_STATS |
9943 | GCConfigStringHolder logFileName = GCConfig::GetMixLogFile(); |
9944 | if (logFileName.Get() != nullptr) |
9945 | { |
9946 | GCStatistics::logFileName = _strdup(logFileName.Get()); |
9947 | GCStatistics::logFile = fopen(GCStatistics::logFileName, "a" ); |
9948 | if (!GCStatistics::logFile) |
9949 | { |
9950 | return E_FAIL; |
9951 | } |
9952 | } |
9953 | #endif // GC_STATS |
9954 | |
9955 | HRESULT hres = S_OK; |
9956 | |
9957 | #ifdef WRITE_WATCH |
9958 | hardware_write_watch_api_supported(); |
9959 | #ifdef BACKGROUND_GC |
9960 | if (can_use_write_watch_for_gc_heap() && GCConfig::GetConcurrentGC()) |
9961 | { |
9962 | gc_can_use_concurrent = true; |
9963 | #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9964 | virtual_alloc_hardware_write_watch = true; |
9965 | #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
9966 | } |
9967 | else |
9968 | { |
9969 | gc_can_use_concurrent = false; |
9970 | } |
9971 | #endif //BACKGROUND_GC |
9972 | #endif //WRITE_WATCH |
9973 | |
9974 | #ifdef BACKGROUND_GC |
9975 | // leave the first page to contain only segment info |
9976 | // because otherwise we could need to revisit the first page frequently in |
9977 | // background GC. |
9978 | segment_info_size = OS_PAGE_SIZE; |
9979 | #else |
9980 | segment_info_size = Align (sizeof (heap_segment), get_alignment_constant (FALSE)); |
9981 | #endif //BACKGROUND_GC |
9982 | |
9983 | reserved_memory = 0; |
9984 | unsigned block_count; |
9985 | #ifdef MULTIPLE_HEAPS |
9986 | reserved_memory_limit = (segment_size + heap_size) * number_of_heaps; |
9987 | block_count = number_of_heaps; |
9988 | n_heaps = number_of_heaps; |
9989 | #else //MULTIPLE_HEAPS |
9990 | reserved_memory_limit = segment_size + heap_size; |
9991 | block_count = 1; |
9992 | #endif //MULTIPLE_HEAPS |
9993 | |
9994 | if (!reserve_initial_memory(segment_size,heap_size,block_count)) |
9995 | return E_OUTOFMEMORY; |
9996 | |
9997 | #ifdef CARD_BUNDLE |
9998 | //check if we need to turn on card_bundles. |
9999 | #ifdef MULTIPLE_HEAPS |
10000 | // use INT64 arithmetic here because of possible overflow on 32p |
10001 | uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*number_of_heaps; |
10002 | #else |
10003 | // use INT64 arithmetic here because of possible overflow on 32p |
10004 | uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE; |
10005 | #endif //MULTIPLE_HEAPS |
10006 | |
10007 | if (can_use_write_watch_for_card_table() && reserved_memory >= th) |
10008 | { |
10009 | settings.card_bundles = TRUE; |
10010 | } |
10011 | else |
10012 | { |
10013 | settings.card_bundles = FALSE; |
10014 | } |
10015 | #endif //CARD_BUNDLE |
10016 | |
10017 | settings.first_init(); |
10018 | |
10019 | int latency_level_from_config = static_cast<int>(GCConfig::GetLatencyLevel()); |
10020 | if (latency_level_from_config >= latency_level_first && latency_level_from_config <= latency_level_last) |
10021 | { |
10022 | gc_heap::latency_level = static_cast<gc_latency_level>(latency_level_from_config); |
10023 | } |
10024 | |
10025 | init_static_data(); |
10026 | |
10027 | g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address); |
10028 | |
10029 | if (!g_gc_card_table) |
10030 | return E_OUTOFMEMORY; |
10031 | |
10032 | gc_started = FALSE; |
10033 | |
10034 | #ifdef MULTIPLE_HEAPS |
10035 | g_heaps = new (nothrow) gc_heap* [number_of_heaps]; |
10036 | if (!g_heaps) |
10037 | return E_OUTOFMEMORY; |
10038 | |
10039 | #ifdef _PREFAST_ |
10040 | #pragma warning(push) |
10041 | #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow |
10042 | #endif // _PREFAST_ |
10043 | g_promoted = new (nothrow) size_t [number_of_heaps*16]; |
10044 | g_bpromoted = new (nothrow) size_t [number_of_heaps*16]; |
10045 | #ifdef MH_SC_MARK |
10046 | g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)]; |
10047 | #endif //MH_SC_MARK |
10048 | #ifdef _PREFAST_ |
10049 | #pragma warning(pop) |
10050 | #endif // _PREFAST_ |
10051 | if (!g_promoted || !g_bpromoted) |
10052 | return E_OUTOFMEMORY; |
10053 | |
10054 | #ifdef MH_SC_MARK |
10055 | if (!g_mark_stack_busy) |
10056 | return E_OUTOFMEMORY; |
10057 | #endif //MH_SC_MARK |
10058 | |
10059 | if (!create_thread_support (number_of_heaps)) |
10060 | return E_OUTOFMEMORY; |
10061 | |
10062 | if (!heap_select::init (number_of_heaps)) |
10063 | return E_OUTOFMEMORY; |
10064 | |
10065 | #endif //MULTIPLE_HEAPS |
10066 | |
10067 | #ifdef MULTIPLE_HEAPS |
10068 | yp_spin_count_unit = 32 * number_of_heaps; |
10069 | #else |
10070 | yp_spin_count_unit = 32 * g_num_processors; |
10071 | #endif //MULTIPLE_HEAPS |
10072 | |
10073 | if (!init_semi_shared()) |
10074 | { |
10075 | hres = E_FAIL; |
10076 | } |
10077 | |
10078 | return hres; |
10079 | } |
10080 | |
10081 | //Initializes PER_HEAP_ISOLATED data members. |
10082 | int |
10083 | gc_heap::init_semi_shared() |
10084 | { |
10085 | int ret = 0; |
10086 | |
10087 | // This is used for heap expansion - it's to fix exactly the start for gen 0 |
10088 | // through (max_generation-1). When we expand the heap we allocate all these |
10089 | // gen starts at the beginning of the new ephemeral seg. |
10090 | eph_gen_starts_size = (Align (min_obj_size)) * max_generation; |
10091 | |
10092 | #ifdef MARK_LIST |
10093 | #ifdef MULTIPLE_HEAPS |
10094 | mark_list_size = min (150*1024, max (8192, soh_segment_size/(2*10*32))); |
10095 | g_mark_list = make_mark_list (mark_list_size*n_heaps); |
10096 | |
10097 | min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2; |
10098 | #ifdef PARALLEL_MARK_LIST_SORT |
10099 | g_mark_list_copy = make_mark_list (mark_list_size*n_heaps); |
10100 | if (!g_mark_list_copy) |
10101 | { |
10102 | goto cleanup; |
10103 | } |
10104 | #endif //PARALLEL_MARK_LIST_SORT |
10105 | |
10106 | #else //MULTIPLE_HEAPS |
10107 | |
10108 | mark_list_size = max (8192, soh_segment_size/(64*32)); |
10109 | g_mark_list = make_mark_list (mark_list_size); |
10110 | |
10111 | #endif //MULTIPLE_HEAPS |
10112 | |
10113 | dprintf (3, ("mark_list_size: %d" , mark_list_size)); |
10114 | |
10115 | if (!g_mark_list) |
10116 | { |
10117 | goto cleanup; |
10118 | } |
10119 | #endif //MARK_LIST |
10120 | |
10121 | #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE) |
10122 | if (!seg_mapping_table_init()) |
10123 | goto cleanup; |
10124 | #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE |
10125 | |
10126 | #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE) |
10127 | seg_table = sorted_table::make_sorted_table(); |
10128 | |
10129 | if (!seg_table) |
10130 | goto cleanup; |
10131 | #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE |
10132 | |
10133 | segment_standby_list = 0; |
10134 | |
10135 | if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE)) |
10136 | { |
10137 | goto cleanup; |
10138 | } |
10139 | if (!full_gc_end_event.CreateManualEventNoThrow(FALSE)) |
10140 | { |
10141 | goto cleanup; |
10142 | } |
10143 | |
10144 | fgn_maxgen_percent = 0; |
10145 | fgn_loh_percent = 0; |
10146 | full_gc_approach_event_set = false; |
10147 | |
10148 | memset (full_gc_counts, 0, sizeof (full_gc_counts)); |
10149 | |
10150 | last_gc_index = 0; |
10151 | should_expand_in_full_gc = FALSE; |
10152 | |
10153 | #ifdef FEATURE_LOH_COMPACTION |
10154 | loh_compaction_always_p = GCConfig::GetLOHCompactionMode() != 0; |
10155 | loh_compaction_mode = loh_compaction_default; |
10156 | #endif //FEATURE_LOH_COMPACTION |
10157 | |
10158 | loh_size_threshold = (size_t)GCConfig::GetLOHThreshold(); |
10159 | assert (loh_size_threshold >= LARGE_OBJECT_SIZE); |
10160 | |
10161 | #ifdef BACKGROUND_GC |
10162 | memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts)); |
10163 | bgc_alloc_spin_count = static_cast<uint32_t>(GCConfig::GetBGCSpinCount()); |
10164 | bgc_alloc_spin = static_cast<uint32_t>(GCConfig::GetBGCSpin()); |
10165 | |
10166 | { |
10167 | int number_bgc_threads = 1; |
10168 | #ifdef MULTIPLE_HEAPS |
10169 | number_bgc_threads = n_heaps; |
10170 | #endif //MULTIPLE_HEAPS |
10171 | if (!create_bgc_threads_support (number_bgc_threads)) |
10172 | { |
10173 | goto cleanup; |
10174 | } |
10175 | } |
10176 | #endif //BACKGROUND_GC |
10177 | |
10178 | memset (¤t_no_gc_region_info, 0, sizeof (current_no_gc_region_info)); |
10179 | |
10180 | #ifdef GC_CONFIG_DRIVEN |
10181 | compact_or_sweep_gcs[0] = 0; |
10182 | compact_or_sweep_gcs[1] = 0; |
10183 | #endif //GC_CONFIG_DRIVEN |
10184 | |
10185 | #ifdef SHORT_PLUGS |
10186 | short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size)); |
10187 | #endif //SHORT_PLUGS |
10188 | |
10189 | ret = 1; |
10190 | |
10191 | cleanup: |
10192 | |
10193 | if (!ret) |
10194 | { |
10195 | if (full_gc_approach_event.IsValid()) |
10196 | { |
10197 | full_gc_approach_event.CloseEvent(); |
10198 | } |
10199 | if (full_gc_end_event.IsValid()) |
10200 | { |
10201 | full_gc_end_event.CloseEvent(); |
10202 | } |
10203 | } |
10204 | |
10205 | return ret; |
10206 | } |
10207 | |
10208 | gc_heap* gc_heap::make_gc_heap ( |
10209 | #ifdef MULTIPLE_HEAPS |
10210 | GCHeap* vm_hp, |
10211 | int heap_number |
10212 | #endif //MULTIPLE_HEAPS |
10213 | ) |
10214 | { |
10215 | gc_heap* res = 0; |
10216 | |
10217 | #ifdef MULTIPLE_HEAPS |
10218 | res = new (nothrow) gc_heap; |
10219 | if (!res) |
10220 | return 0; |
10221 | |
10222 | res->vm_heap = vm_hp; |
10223 | res->alloc_context_count = 0; |
10224 | |
10225 | #ifdef MARK_LIST |
10226 | #ifdef PARALLEL_MARK_LIST_SORT |
10227 | res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps]; |
10228 | if (!res->mark_list_piece_start) |
10229 | return 0; |
10230 | |
10231 | #ifdef _PREFAST_ |
10232 | #pragma warning(push) |
10233 | #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow |
10234 | #endif // _PREFAST_ |
10235 | res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing |
10236 | #ifdef _PREFAST_ |
10237 | #pragma warning(pop) |
10238 | #endif // _PREFAST_ |
10239 | |
10240 | if (!res->mark_list_piece_end) |
10241 | return 0; |
10242 | #endif //PARALLEL_MARK_LIST_SORT |
10243 | #endif //MARK_LIST |
10244 | |
10245 | |
10246 | #endif //MULTIPLE_HEAPS |
10247 | |
10248 | if (res->init_gc_heap ( |
10249 | #ifdef MULTIPLE_HEAPS |
10250 | heap_number |
10251 | #else //MULTIPLE_HEAPS |
10252 | 0 |
10253 | #endif //MULTIPLE_HEAPS |
10254 | )==0) |
10255 | { |
10256 | return 0; |
10257 | } |
10258 | |
10259 | #ifdef MULTIPLE_HEAPS |
10260 | return res; |
10261 | #else |
10262 | return (gc_heap*)1; |
10263 | #endif //MULTIPLE_HEAPS |
10264 | } |
10265 | |
10266 | uint32_t |
10267 | gc_heap::wait_for_gc_done(int32_t timeOut) |
10268 | { |
10269 | bool cooperative_mode = enable_preemptive (); |
10270 | |
10271 | uint32_t dwWaitResult = NOERROR; |
10272 | |
10273 | gc_heap* wait_heap = NULL; |
10274 | while (gc_heap::gc_started) |
10275 | { |
10276 | #ifdef MULTIPLE_HEAPS |
10277 | wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap; |
10278 | dprintf(2, ("waiting for the gc_done_event on heap %d" , wait_heap->heap_number)); |
10279 | #endif // MULTIPLE_HEAPS |
10280 | |
10281 | #ifdef _PREFAST_ |
10282 | PREFIX_ASSUME(wait_heap != NULL); |
10283 | #endif // _PREFAST_ |
10284 | |
10285 | dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE); |
10286 | } |
10287 | disable_preemptive (cooperative_mode); |
10288 | |
10289 | return dwWaitResult; |
10290 | } |
10291 | |
10292 | void |
10293 | gc_heap::set_gc_done() |
10294 | { |
10295 | enter_gc_done_event_lock(); |
10296 | if (!gc_done_event_set) |
10297 | { |
10298 | gc_done_event_set = true; |
10299 | dprintf (2, ("heap %d: setting gc_done_event" , heap_number)); |
10300 | gc_done_event.Set(); |
10301 | } |
10302 | exit_gc_done_event_lock(); |
10303 | } |
10304 | |
10305 | void |
10306 | gc_heap::reset_gc_done() |
10307 | { |
10308 | enter_gc_done_event_lock(); |
10309 | if (gc_done_event_set) |
10310 | { |
10311 | gc_done_event_set = false; |
10312 | dprintf (2, ("heap %d: resetting gc_done_event" , heap_number)); |
10313 | gc_done_event.Reset(); |
10314 | } |
10315 | exit_gc_done_event_lock(); |
10316 | } |
10317 | |
10318 | void |
10319 | gc_heap::enter_gc_done_event_lock() |
10320 | { |
10321 | uint32_t dwSwitchCount = 0; |
10322 | retry: |
10323 | |
10324 | if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0) |
10325 | { |
10326 | while (gc_done_event_lock >= 0) |
10327 | { |
10328 | if (g_num_processors > 1) |
10329 | { |
10330 | int spin_count = yp_spin_count_unit; |
10331 | for (int j = 0; j < spin_count; j++) |
10332 | { |
10333 | if (gc_done_event_lock < 0) |
10334 | break; |
10335 | YieldProcessor(); // indicate to the processor that we are spining |
10336 | } |
10337 | if (gc_done_event_lock >= 0) |
10338 | GCToOSInterface::YieldThread(++dwSwitchCount); |
10339 | } |
10340 | else |
10341 | GCToOSInterface::YieldThread(++dwSwitchCount); |
10342 | } |
10343 | goto retry; |
10344 | } |
10345 | } |
10346 | |
10347 | void |
10348 | gc_heap::exit_gc_done_event_lock() |
10349 | { |
10350 | gc_done_event_lock = -1; |
10351 | } |
10352 | |
10353 | #ifndef MULTIPLE_HEAPS |
10354 | |
10355 | #ifdef RECORD_LOH_STATE |
10356 | int gc_heap::loh_state_index = 0; |
10357 | gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states]; |
10358 | #endif //RECORD_LOH_STATE |
10359 | |
10360 | VOLATILE(int32_t) gc_heap::gc_done_event_lock; |
10361 | VOLATILE(bool) gc_heap::gc_done_event_set; |
10362 | GCEvent gc_heap::gc_done_event; |
10363 | #endif //!MULTIPLE_HEAPS |
10364 | VOLATILE(bool) gc_heap::internal_gc_done; |
10365 | |
10366 | void gc_heap::add_saved_spinlock_info ( |
10367 | bool loh_p, |
10368 | msl_enter_state enter_state, |
10369 | msl_take_state take_state) |
10370 | |
10371 | { |
10372 | #ifdef SPINLOCK_HISTORY |
10373 | spinlock_info* current = &last_spinlock_info[spinlock_info_index]; |
10374 | |
10375 | current->enter_state = enter_state; |
10376 | current->take_state = take_state; |
10377 | current->thread_id.SetToCurrentThread(); |
10378 | current->loh_p = loh_p; |
10379 | dprintf (SPINLOCK_LOG, ("[%d]%s %s %s" , |
10380 | heap_number, |
10381 | (loh_p ? "loh" : "soh" ), |
10382 | ((enter_state == me_acquire) ? "E" : "L" ), |
10383 | msl_take_state_str[take_state])); |
10384 | |
10385 | spinlock_info_index++; |
10386 | |
10387 | assert (spinlock_info_index <= max_saved_spinlock_info); |
10388 | |
10389 | if (spinlock_info_index >= max_saved_spinlock_info) |
10390 | { |
10391 | spinlock_info_index = 0; |
10392 | } |
10393 | #else |
10394 | MAYBE_UNUSED_VAR(enter_state); |
10395 | MAYBE_UNUSED_VAR(take_state); |
10396 | #endif //SPINLOCK_HISTORY |
10397 | } |
10398 | |
10399 | int |
10400 | gc_heap::init_gc_heap (int h_number) |
10401 | { |
10402 | #ifdef MULTIPLE_HEAPS |
10403 | |
10404 | time_bgc_last = 0; |
10405 | |
10406 | #ifdef SPINLOCK_HISTORY |
10407 | spinlock_info_index = 0; |
10408 | memset (last_spinlock_info, 0, sizeof(last_spinlock_info)); |
10409 | #endif //SPINLOCK_HISTORY |
10410 | |
10411 | // initialize per heap members. |
10412 | ephemeral_low = (uint8_t*)1; |
10413 | |
10414 | ephemeral_high = MAX_PTR; |
10415 | |
10416 | ephemeral_heap_segment = 0; |
10417 | |
10418 | freeable_large_heap_segment = 0; |
10419 | |
10420 | condemned_generation_num = 0; |
10421 | |
10422 | blocking_collection = FALSE; |
10423 | |
10424 | generation_skip_ratio = 100; |
10425 | |
10426 | mark_stack_tos = 0; |
10427 | |
10428 | mark_stack_bos = 0; |
10429 | |
10430 | mark_stack_array_length = 0; |
10431 | |
10432 | mark_stack_array = 0; |
10433 | |
10434 | #if defined (_DEBUG) && defined (VERIFY_HEAP) |
10435 | verify_pinned_queue_p = FALSE; |
10436 | #endif // _DEBUG && VERIFY_HEAP |
10437 | |
10438 | loh_pinned_queue_tos = 0; |
10439 | |
10440 | loh_pinned_queue_bos = 0; |
10441 | |
10442 | loh_pinned_queue_length = 0; |
10443 | |
10444 | loh_pinned_queue_decay = LOH_PIN_DECAY; |
10445 | |
10446 | loh_pinned_queue = 0; |
10447 | |
10448 | min_overflow_address = MAX_PTR; |
10449 | |
10450 | max_overflow_address = 0; |
10451 | |
10452 | gen0_bricks_cleared = FALSE; |
10453 | |
10454 | gen0_must_clear_bricks = 0; |
10455 | |
10456 | allocation_quantum = CLR_SIZE; |
10457 | |
10458 | more_space_lock_soh = gc_lock; |
10459 | |
10460 | more_space_lock_loh = gc_lock; |
10461 | |
10462 | ro_segments_in_range = FALSE; |
10463 | |
10464 | loh_alloc_since_cg = 0; |
10465 | |
10466 | new_heap_segment = NULL; |
10467 | |
10468 | gen0_allocated_after_gc_p = false; |
10469 | |
10470 | #ifdef RECORD_LOH_STATE |
10471 | loh_state_index = 0; |
10472 | #endif //RECORD_LOH_STATE |
10473 | #endif //MULTIPLE_HEAPS |
10474 | |
10475 | #ifdef MULTIPLE_HEAPS |
10476 | if (h_number > n_heaps) |
10477 | { |
10478 | assert (!"Number of heaps exceeded" ); |
10479 | return 0; |
10480 | } |
10481 | |
10482 | heap_number = h_number; |
10483 | #endif //MULTIPLE_HEAPS |
10484 | |
10485 | memset (&oom_info, 0, sizeof (oom_info)); |
10486 | memset (&fgm_result, 0, sizeof (fgm_result)); |
10487 | if (!gc_done_event.CreateManualEventNoThrow(FALSE)) |
10488 | { |
10489 | return 0; |
10490 | } |
10491 | gc_done_event_lock = -1; |
10492 | gc_done_event_set = false; |
10493 | |
10494 | #ifndef SEG_MAPPING_TABLE |
10495 | if (!gc_heap::seg_table->ensure_space_for_insert ()) |
10496 | { |
10497 | return 0; |
10498 | } |
10499 | #endif //!SEG_MAPPING_TABLE |
10500 | |
10501 | heap_segment* seg = get_initial_segment (soh_segment_size, h_number); |
10502 | if (!seg) |
10503 | return 0; |
10504 | |
10505 | FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg), |
10506 | (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), |
10507 | gc_etw_segment_small_object_heap); |
10508 | |
10509 | #ifdef SEG_MAPPING_TABLE |
10510 | seg_mapping_table_add_segment (seg, __this); |
10511 | #else //SEG_MAPPING_TABLE |
10512 | seg_table->insert ((uint8_t*)seg, sdelta); |
10513 | #endif //SEG_MAPPING_TABLE |
10514 | |
10515 | #ifdef MULTIPLE_HEAPS |
10516 | heap_segment_heap (seg) = this; |
10517 | #endif //MULTIPLE_HEAPS |
10518 | |
10519 | /* todo: Need a global lock for this */ |
10520 | uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))]; |
10521 | own_card_table (ct); |
10522 | card_table = translate_card_table (ct); |
10523 | /* End of global lock */ |
10524 | |
10525 | brick_table = card_table_brick_table (ct); |
10526 | highest_address = card_table_highest_address (ct); |
10527 | lowest_address = card_table_lowest_address (ct); |
10528 | |
10529 | #ifdef CARD_BUNDLE |
10530 | card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address); |
10531 | assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] == |
10532 | card_table_card_bundle_table (ct)); |
10533 | #endif //CARD_BUNDLE |
10534 | |
10535 | #ifdef MARK_ARRAY |
10536 | if (gc_can_use_concurrent) |
10537 | mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))])); |
10538 | else |
10539 | mark_array = NULL; |
10540 | #endif //MARK_ARRAY |
10541 | |
10542 | uint8_t* start = heap_segment_mem (seg); |
10543 | |
10544 | for (int i = 0; i < 1 + max_generation; i++) |
10545 | { |
10546 | make_generation (generation_table [ (max_generation - i) ], |
10547 | seg, start, 0); |
10548 | generation_table [(max_generation - i)].gen_num = max_generation - i; |
10549 | start += Align (min_obj_size); |
10550 | } |
10551 | |
10552 | heap_segment_allocated (seg) = start; |
10553 | alloc_allocated = start; |
10554 | heap_segment_used (seg) = start - plug_skew; |
10555 | |
10556 | ephemeral_heap_segment = seg; |
10557 | |
10558 | #ifndef SEG_MAPPING_TABLE |
10559 | if (!gc_heap::seg_table->ensure_space_for_insert ()) |
10560 | { |
10561 | return 0; |
10562 | } |
10563 | #endif //!SEG_MAPPING_TABLE |
10564 | //Create the large segment generation |
10565 | heap_segment* lseg = get_initial_segment(min_loh_segment_size, h_number); |
10566 | if (!lseg) |
10567 | return 0; |
10568 | lseg->flags |= heap_segment_flags_loh; |
10569 | |
10570 | FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(lseg), |
10571 | (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)), |
10572 | gc_etw_segment_large_object_heap); |
10573 | |
10574 | #ifdef SEG_MAPPING_TABLE |
10575 | seg_mapping_table_add_segment (lseg, __this); |
10576 | #else //SEG_MAPPING_TABLE |
10577 | seg_table->insert ((uint8_t*)lseg, sdelta); |
10578 | #endif //SEG_MAPPING_TABLE |
10579 | |
10580 | generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list); |
10581 | //assign the alloc_list for the large generation |
10582 | generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list); |
10583 | generation_table [max_generation+1].gen_num = max_generation+1; |
10584 | make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0); |
10585 | heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE)); |
10586 | heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew; |
10587 | |
10588 | for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++) |
10589 | { |
10590 | generation* gen = generation_of (gen_num); |
10591 | make_unused_array (generation_allocation_start (gen), Align (min_obj_size)); |
10592 | } |
10593 | |
10594 | #ifdef MULTIPLE_HEAPS |
10595 | heap_segment_heap (lseg) = this; |
10596 | |
10597 | //initialize the alloc context heap |
10598 | generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap); |
10599 | |
10600 | //initialize the alloc context heap |
10601 | generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap); |
10602 | |
10603 | #endif //MULTIPLE_HEAPS |
10604 | |
10605 | //Do this only once |
10606 | #ifdef MULTIPLE_HEAPS |
10607 | if (h_number == 0) |
10608 | #endif //MULTIPLE_HEAPS |
10609 | { |
10610 | #ifndef INTERIOR_POINTERS |
10611 | //set the brick_table for large objects |
10612 | //but default value is clearded |
10613 | //clear_brick_table ((uint8_t*)heap_segment_mem (lseg), |
10614 | // (uint8_t*)heap_segment_reserved (lseg)); |
10615 | |
10616 | #else //INTERIOR_POINTERS |
10617 | |
10618 | //Because of the interior pointer business, we have to clear |
10619 | //the whole brick table |
10620 | //but the default value is cleared |
10621 | // clear_brick_table (lowest_address, highest_address); |
10622 | #endif //INTERIOR_POINTERS |
10623 | } |
10624 | |
10625 | if (!init_dynamic_data()) |
10626 | { |
10627 | return 0; |
10628 | } |
10629 | |
10630 | etw_allocation_running_amount[0] = 0; |
10631 | etw_allocation_running_amount[1] = 0; |
10632 | |
10633 | //needs to be done after the dynamic data has been initialized |
10634 | #ifndef MULTIPLE_HEAPS |
10635 | allocation_running_amount = dd_min_size (dynamic_data_of (0)); |
10636 | #endif //!MULTIPLE_HEAPS |
10637 | |
10638 | fgn_last_alloc = dd_min_size (dynamic_data_of (0)); |
10639 | |
10640 | mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]); |
10641 | if (!arr) |
10642 | return 0; |
10643 | |
10644 | make_mark_stack(arr); |
10645 | |
10646 | #ifdef BACKGROUND_GC |
10647 | freeable_small_heap_segment = 0; |
10648 | gchist_index_per_heap = 0; |
10649 | uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]); |
10650 | if (!b_arr) |
10651 | return 0; |
10652 | |
10653 | make_background_mark_stack (b_arr); |
10654 | #endif //BACKGROUND_GC |
10655 | |
10656 | ephemeral_low = generation_allocation_start(generation_of(max_generation - 1)); |
10657 | ephemeral_high = heap_segment_reserved(ephemeral_heap_segment); |
10658 | if (heap_number == 0) |
10659 | { |
10660 | stomp_write_barrier_initialize( |
10661 | #ifdef MULTIPLE_HEAPS |
10662 | reinterpret_cast<uint8_t*>(1), reinterpret_cast<uint8_t*>(~0) |
10663 | #else |
10664 | ephemeral_low, ephemeral_high |
10665 | #endif //!MULTIPLE_HEAPS |
10666 | ); |
10667 | } |
10668 | |
10669 | #ifdef MARK_ARRAY |
10670 | // why would we clear the mark array for this page? it should be cleared.. |
10671 | // clear the first committed page |
10672 | //if(gc_can_use_concurrent) |
10673 | //{ |
10674 | // clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg)); |
10675 | //} |
10676 | #endif //MARK_ARRAY |
10677 | |
10678 | #ifdef MULTIPLE_HEAPS |
10679 | //register the heap in the heaps array |
10680 | |
10681 | if (!create_gc_thread ()) |
10682 | return 0; |
10683 | |
10684 | g_heaps [heap_number] = this; |
10685 | |
10686 | #endif //MULTIPLE_HEAPS |
10687 | |
10688 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
10689 | HRESULT hr = AllocateCFinalize(&finalize_queue); |
10690 | if (FAILED(hr)) |
10691 | return 0; |
10692 | #endif // FEATURE_PREMORTEM_FINALIZATION |
10693 | |
10694 | max_free_space_items = MAX_NUM_FREE_SPACES; |
10695 | |
10696 | bestfit_seg = new (nothrow) seg_free_spaces (heap_number); |
10697 | |
10698 | if (!bestfit_seg) |
10699 | { |
10700 | return 0; |
10701 | } |
10702 | |
10703 | if (!bestfit_seg->alloc()) |
10704 | { |
10705 | return 0; |
10706 | } |
10707 | |
10708 | last_gc_before_oom = FALSE; |
10709 | |
10710 | sufficient_gen0_space_p = FALSE; |
10711 | |
10712 | #ifdef MULTIPLE_HEAPS |
10713 | |
10714 | #ifdef HEAP_ANALYZE |
10715 | |
10716 | heap_analyze_success = TRUE; |
10717 | |
10718 | internal_root_array = 0; |
10719 | |
10720 | internal_root_array_index = 0; |
10721 | |
10722 | internal_root_array_length = initial_internal_roots; |
10723 | |
10724 | current_obj = 0; |
10725 | |
10726 | current_obj_size = 0; |
10727 | |
10728 | #endif //HEAP_ANALYZE |
10729 | |
10730 | #endif // MULTIPLE_HEAPS |
10731 | |
10732 | #ifdef BACKGROUND_GC |
10733 | bgc_thread_id.Clear(); |
10734 | |
10735 | if (!create_bgc_thread_support()) |
10736 | { |
10737 | return 0; |
10738 | } |
10739 | |
10740 | bgc_alloc_lock = new (nothrow) exclusive_sync; |
10741 | if (!bgc_alloc_lock) |
10742 | { |
10743 | return 0; |
10744 | } |
10745 | |
10746 | bgc_alloc_lock->init(); |
10747 | |
10748 | if (h_number == 0) |
10749 | { |
10750 | if (!recursive_gc_sync::init()) |
10751 | return 0; |
10752 | } |
10753 | |
10754 | bgc_thread_running = 0; |
10755 | bgc_thread = 0; |
10756 | bgc_threads_timeout_cs.Initialize(); |
10757 | expanded_in_fgc = 0; |
10758 | current_bgc_state = bgc_not_in_process; |
10759 | background_soh_alloc_count = 0; |
10760 | background_loh_alloc_count = 0; |
10761 | bgc_overflow_count = 0; |
10762 | end_loh_size = dd_min_size (dynamic_data_of (max_generation + 1)); |
10763 | #endif //BACKGROUND_GC |
10764 | |
10765 | #ifdef GC_CONFIG_DRIVEN |
10766 | memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap)); |
10767 | memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap)); |
10768 | memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap)); |
10769 | memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap)); |
10770 | #endif //GC_CONFIG_DRIVEN |
10771 | |
10772 | return 1; |
10773 | } |
10774 | |
10775 | void |
10776 | gc_heap::destroy_semi_shared() |
10777 | { |
10778 | //TODO: will need to move this to per heap |
10779 | //#ifdef BACKGROUND_GC |
10780 | // if (c_mark_list) |
10781 | // delete c_mark_list; |
10782 | //#endif //BACKGROUND_GC |
10783 | |
10784 | #ifdef MARK_LIST |
10785 | if (g_mark_list) |
10786 | delete g_mark_list; |
10787 | #endif //MARK_LIST |
10788 | |
10789 | #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE) |
10790 | if (seg_mapping_table) |
10791 | delete seg_mapping_table; |
10792 | #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE |
10793 | |
10794 | #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE) |
10795 | //destroy the segment map |
10796 | seg_table->delete_sorted_table(); |
10797 | #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE |
10798 | } |
10799 | |
10800 | void |
10801 | gc_heap::self_destroy() |
10802 | { |
10803 | #ifdef BACKGROUND_GC |
10804 | kill_gc_thread(); |
10805 | #endif //BACKGROUND_GC |
10806 | |
10807 | if (gc_done_event.IsValid()) |
10808 | { |
10809 | gc_done_event.CloseEvent(); |
10810 | } |
10811 | |
10812 | // destroy every segment. |
10813 | heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
10814 | |
10815 | PREFIX_ASSUME(seg != NULL); |
10816 | |
10817 | heap_segment* next_seg; |
10818 | while (seg) |
10819 | { |
10820 | next_seg = heap_segment_next_rw (seg); |
10821 | delete_heap_segment (seg); |
10822 | seg = next_seg; |
10823 | } |
10824 | |
10825 | seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1))); |
10826 | |
10827 | PREFIX_ASSUME(seg != NULL); |
10828 | |
10829 | while (seg) |
10830 | { |
10831 | next_seg = heap_segment_next_rw (seg); |
10832 | delete_heap_segment (seg); |
10833 | seg = next_seg; |
10834 | } |
10835 | |
10836 | // get rid of the card table |
10837 | release_card_table (card_table); |
10838 | |
10839 | // destroy the mark stack |
10840 | delete mark_stack_array; |
10841 | |
10842 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
10843 | if (finalize_queue) |
10844 | delete finalize_queue; |
10845 | #endif // FEATURE_PREMORTEM_FINALIZATION |
10846 | } |
10847 | |
10848 | void |
10849 | gc_heap::destroy_gc_heap(gc_heap* heap) |
10850 | { |
10851 | heap->self_destroy(); |
10852 | delete heap; |
10853 | } |
10854 | |
10855 | // Destroys resources owned by gc. It is assumed that a last GC has been performed and that |
10856 | // the finalizer queue has been drained. |
10857 | void gc_heap::shutdown_gc() |
10858 | { |
10859 | destroy_semi_shared(); |
10860 | |
10861 | #ifdef MULTIPLE_HEAPS |
10862 | //delete the heaps array |
10863 | delete g_heaps; |
10864 | destroy_thread_support(); |
10865 | n_heaps = 0; |
10866 | #endif //MULTIPLE_HEAPS |
10867 | //destroy seg_manager |
10868 | |
10869 | destroy_initial_memory(); |
10870 | |
10871 | GCToOSInterface::Shutdown(); |
10872 | } |
10873 | |
10874 | inline |
10875 | BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit, |
10876 | uint8_t* old_loc, int use_padding) |
10877 | { |
10878 | BOOL already_padded = FALSE; |
10879 | #ifdef SHORT_PLUGS |
10880 | if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT)) |
10881 | { |
10882 | alloc_pointer = alloc_pointer + Align (min_obj_size); |
10883 | already_padded = TRUE; |
10884 | } |
10885 | #endif //SHORT_PLUGS |
10886 | |
10887 | if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer))) |
10888 | size = size + switch_alignment_size (already_padded); |
10889 | |
10890 | #ifdef FEATURE_STRUCTALIGN |
10891 | alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset); |
10892 | #endif // FEATURE_STRUCTALIGN |
10893 | |
10894 | // in allocate_in_condemned_generation we can have this when we |
10895 | // set the alloc_limit to plan_allocated which could be less than |
10896 | // alloc_ptr |
10897 | if (alloc_limit < alloc_pointer) |
10898 | { |
10899 | return FALSE; |
10900 | } |
10901 | |
10902 | if (old_loc != 0) |
10903 | { |
10904 | return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0))) |
10905 | #ifdef SHORT_PLUGS |
10906 | ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit)) |
10907 | #else //SHORT_PLUGS |
10908 | ||((alloc_pointer + size) == alloc_limit) |
10909 | #endif //SHORT_PLUGS |
10910 | ); |
10911 | } |
10912 | else |
10913 | { |
10914 | assert (size == Align (min_obj_size)); |
10915 | return ((size_t)(alloc_limit - alloc_pointer) >= size); |
10916 | } |
10917 | } |
10918 | |
10919 | inline |
10920 | BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit, |
10921 | int align_const) |
10922 | { |
10923 | // We could have run into cases where this is true when alloc_allocated is the |
10924 | // the same as the seg committed. |
10925 | if (alloc_limit < alloc_pointer) |
10926 | { |
10927 | return FALSE; |
10928 | } |
10929 | |
10930 | return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const))); |
10931 | } |
10932 | |
10933 | // Grow by committing more pages |
10934 | BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address) |
10935 | { |
10936 | assert (high_address <= heap_segment_reserved (seg)); |
10937 | |
10938 | //return 0 if we are at the end of the segment. |
10939 | if (align_on_page (high_address) > heap_segment_reserved (seg)) |
10940 | return FALSE; |
10941 | |
10942 | if (high_address <= heap_segment_committed (seg)) |
10943 | return TRUE; |
10944 | |
10945 | size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg))); |
10946 | c_size = max (c_size, 16*OS_PAGE_SIZE); |
10947 | c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg))); |
10948 | |
10949 | if (c_size == 0) |
10950 | return FALSE; |
10951 | |
10952 | STRESS_LOG2(LF_GC, LL_INFO10000, |
10953 | "Growing heap_segment: %Ix high address: %Ix\n" , |
10954 | (size_t)seg, (size_t)high_address); |
10955 | |
10956 | dprintf(3, ("Growing segment allocation %Ix %Ix" , (size_t)heap_segment_committed(seg),c_size)); |
10957 | |
10958 | if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size, heap_number)) |
10959 | { |
10960 | dprintf(3, ("Cannot grow heap segment" )); |
10961 | return FALSE; |
10962 | } |
10963 | #ifdef MARK_ARRAY |
10964 | #ifndef BACKGROUND_GC |
10965 | clear_mark_array (heap_segment_committed (seg), |
10966 | heap_segment_committed (seg)+c_size, TRUE); |
10967 | #endif //BACKGROUND_GC |
10968 | #endif //MARK_ARRAY |
10969 | heap_segment_committed (seg) += c_size; |
10970 | STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix" , |
10971 | (size_t)heap_segment_committed (seg)); |
10972 | |
10973 | assert (heap_segment_committed (seg) <= heap_segment_reserved (seg)); |
10974 | |
10975 | assert (high_address <= heap_segment_committed (seg)); |
10976 | |
10977 | return TRUE; |
10978 | } |
10979 | |
10980 | inline |
10981 | int gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* allocated, uint8_t* old_loc, size_t size, BOOL pad_front_p REQD_ALIGN_AND_OFFSET_DCL) |
10982 | { |
10983 | #ifdef SHORT_PLUGS |
10984 | if ((old_loc != 0) && pad_front_p) |
10985 | { |
10986 | allocated = allocated + Align (min_obj_size); |
10987 | } |
10988 | #endif //SHORT_PLUGS |
10989 | |
10990 | if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated))) |
10991 | size = size + switch_alignment_size (FALSE); |
10992 | #ifdef FEATURE_STRUCTALIGN |
10993 | size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset); |
10994 | return grow_heap_segment (seg, allocated + pad + size); |
10995 | #else // FEATURE_STRUCTALIGN |
10996 | return grow_heap_segment (seg, allocated + size); |
10997 | #endif // FEATURE_STRUCTALIGN |
10998 | } |
10999 | |
11000 | //used only in older generation allocation (i.e during gc). |
11001 | void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen, |
11002 | int gennum) |
11003 | { |
11004 | UNREFERENCED_PARAMETER(gennum); |
11005 | dprintf (3, ("gc Expanding segment allocation" )); |
11006 | heap_segment* seg = generation_allocation_segment (gen); |
11007 | if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg))) |
11008 | { |
11009 | if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg)) |
11010 | { |
11011 | assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg)); |
11012 | assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg)); |
11013 | heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen); |
11014 | } |
11015 | else |
11016 | { |
11017 | uint8_t* hole = generation_allocation_pointer (gen); |
11018 | size_t size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen)); |
11019 | |
11020 | if (size != 0) |
11021 | { |
11022 | dprintf (3, ("filling up hole: %Ix, size %Ix" , hole, size)); |
11023 | size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen); |
11024 | if (size >= Align (min_free_list)) |
11025 | { |
11026 | if (allocated_size < min_free_list) |
11027 | { |
11028 | if (size >= (Align (min_free_list) + Align (min_obj_size))) |
11029 | { |
11030 | //split hole into min obj + threadable free item |
11031 | make_unused_array (hole, min_obj_size); |
11032 | generation_free_obj_space (gen) += Align (min_obj_size); |
11033 | make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size)); |
11034 | generation_free_list_space (gen) += size - Align (min_obj_size); |
11035 | generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size), |
11036 | size - Align (min_obj_size)); |
11037 | add_gen_free (gen->gen_num, (size - Align (min_obj_size))); |
11038 | } |
11039 | else |
11040 | { |
11041 | dprintf (3, ("allocated size too small, can't put back rest on free list %Ix" , allocated_size)); |
11042 | make_unused_array (hole, size); |
11043 | generation_free_obj_space (gen) += size; |
11044 | } |
11045 | } |
11046 | else |
11047 | { |
11048 | dprintf (3, ("threading hole in front of free list" )); |
11049 | make_unused_array (hole, size); |
11050 | generation_free_list_space (gen) += size; |
11051 | generation_allocator(gen)->thread_item_front (hole, size); |
11052 | add_gen_free (gen->gen_num, size); |
11053 | } |
11054 | } |
11055 | else |
11056 | { |
11057 | make_unused_array (hole, size); |
11058 | generation_free_obj_space (gen) += size; |
11059 | } |
11060 | } |
11061 | } |
11062 | generation_allocation_pointer (gen) = start; |
11063 | generation_allocation_context_start_region (gen) = start; |
11064 | } |
11065 | generation_allocation_limit (gen) = (start + limit_size); |
11066 | } |
11067 | |
11068 | void verify_mem_cleared (uint8_t* start, size_t size) |
11069 | { |
11070 | if (!Aligned (size)) |
11071 | { |
11072 | FATAL_GC_ERROR(); |
11073 | } |
11074 | |
11075 | PTR_PTR curr_ptr = (PTR_PTR) start; |
11076 | for (size_t i = 0; i < size / sizeof(PTR_PTR); i++) |
11077 | { |
11078 | if (*(curr_ptr++) != 0) |
11079 | { |
11080 | FATAL_GC_ERROR(); |
11081 | } |
11082 | } |
11083 | } |
11084 | |
11085 | #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC) |
11086 | void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end) |
11087 | { |
11088 | size_t start_mark_bit = mark_bit_of (start); |
11089 | size_t end_mark_bit = mark_bit_of (end); |
11090 | unsigned int startbit = mark_bit_bit (start_mark_bit); |
11091 | unsigned int endbit = mark_bit_bit (end_mark_bit); |
11092 | size_t startwrd = mark_bit_word (start_mark_bit); |
11093 | size_t endwrd = mark_bit_word (end_mark_bit); |
11094 | |
11095 | dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix" , |
11096 | (size_t)start, (size_t)start_mark_bit, |
11097 | (size_t)end, (size_t)end_mark_bit)); |
11098 | |
11099 | unsigned int firstwrd = ~(lowbits (~0, startbit)); |
11100 | unsigned int lastwrd = ~(highbits (~0, endbit)); |
11101 | |
11102 | if (startwrd == endwrd) |
11103 | { |
11104 | unsigned int wrd = firstwrd & lastwrd; |
11105 | mark_array[startwrd] |= wrd; |
11106 | return; |
11107 | } |
11108 | |
11109 | // set the first mark word. |
11110 | if (startbit) |
11111 | { |
11112 | mark_array[startwrd] |= firstwrd; |
11113 | startwrd++; |
11114 | } |
11115 | |
11116 | for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++) |
11117 | { |
11118 | mark_array[wrdtmp] = ~(unsigned int)0; |
11119 | } |
11120 | |
11121 | // set the last mark word. |
11122 | if (endbit) |
11123 | { |
11124 | mark_array[endwrd] |= lastwrd; |
11125 | } |
11126 | } |
11127 | |
11128 | // makes sure that the mark array bits between start and end are 0. |
11129 | void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end) |
11130 | { |
11131 | size_t start_mark_bit = mark_bit_of (start); |
11132 | size_t end_mark_bit = mark_bit_of (end); |
11133 | unsigned int startbit = mark_bit_bit (start_mark_bit); |
11134 | unsigned int endbit = mark_bit_bit (end_mark_bit); |
11135 | size_t startwrd = mark_bit_word (start_mark_bit); |
11136 | size_t endwrd = mark_bit_word (end_mark_bit); |
11137 | |
11138 | //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", |
11139 | // (size_t)start, (size_t)start_mark_bit, |
11140 | // (size_t)end, (size_t)end_mark_bit)); |
11141 | |
11142 | unsigned int firstwrd = ~(lowbits (~0, startbit)); |
11143 | unsigned int lastwrd = ~(highbits (~0, endbit)); |
11144 | |
11145 | if (startwrd == endwrd) |
11146 | { |
11147 | unsigned int wrd = firstwrd & lastwrd; |
11148 | if (mark_array[startwrd] & wrd) |
11149 | { |
11150 | dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
11151 | wrd, startwrd, |
11152 | mark_array [startwrd], mark_word_address (startwrd))); |
11153 | FATAL_GC_ERROR(); |
11154 | } |
11155 | return; |
11156 | } |
11157 | |
11158 | // set the first mark word. |
11159 | if (startbit) |
11160 | { |
11161 | if (mark_array[startwrd] & firstwrd) |
11162 | { |
11163 | dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
11164 | firstwrd, startwrd, |
11165 | mark_array [startwrd], mark_word_address (startwrd))); |
11166 | FATAL_GC_ERROR(); |
11167 | } |
11168 | |
11169 | startwrd++; |
11170 | } |
11171 | |
11172 | for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++) |
11173 | { |
11174 | if (mark_array[wrdtmp]) |
11175 | { |
11176 | dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
11177 | wrdtmp, |
11178 | mark_array [wrdtmp], mark_word_address (wrdtmp))); |
11179 | FATAL_GC_ERROR(); |
11180 | } |
11181 | } |
11182 | |
11183 | // set the last mark word. |
11184 | if (endbit) |
11185 | { |
11186 | if (mark_array[endwrd] & lastwrd) |
11187 | { |
11188 | dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
11189 | lastwrd, lastwrd, |
11190 | mark_array [lastwrd], mark_word_address (lastwrd))); |
11191 | FATAL_GC_ERROR(); |
11192 | } |
11193 | } |
11194 | } |
11195 | #endif //VERIFY_HEAP && BACKGROUND_GC |
11196 | |
11197 | allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b) |
11198 | { |
11199 | assert (num_b < MAX_BUCKET_COUNT); |
11200 | num_buckets = num_b; |
11201 | frst_bucket_size = fbs; |
11202 | buckets = b; |
11203 | } |
11204 | |
11205 | alloc_list& allocator::alloc_list_of (unsigned int bn) |
11206 | { |
11207 | assert (bn < num_buckets); |
11208 | if (bn == 0) |
11209 | return first_bucket; |
11210 | else |
11211 | return buckets [bn-1]; |
11212 | } |
11213 | |
11214 | size_t& allocator::alloc_list_damage_count_of (unsigned int bn) |
11215 | { |
11216 | assert (bn < num_buckets); |
11217 | if (bn == 0) |
11218 | return first_bucket.alloc_list_damage_count(); |
11219 | else |
11220 | return buckets [bn-1].alloc_list_damage_count(); |
11221 | } |
11222 | |
11223 | void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p) |
11224 | { |
11225 | //unlink the free_item |
11226 | alloc_list* al = &alloc_list_of (bn); |
11227 | if (prev_item) |
11228 | { |
11229 | if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY)) |
11230 | { |
11231 | assert (item == free_list_slot (prev_item)); |
11232 | free_list_undo (prev_item) = item; |
11233 | alloc_list_damage_count_of (bn)++; |
11234 | } |
11235 | free_list_slot (prev_item) = free_list_slot(item); |
11236 | } |
11237 | else |
11238 | { |
11239 | al->alloc_list_head() = (uint8_t*)free_list_slot(item); |
11240 | } |
11241 | if (al->alloc_list_tail() == item) |
11242 | { |
11243 | al->alloc_list_tail() = prev_item; |
11244 | } |
11245 | } |
11246 | |
11247 | void allocator::clear() |
11248 | { |
11249 | for (unsigned int i = 0; i < num_buckets; i++) |
11250 | { |
11251 | alloc_list_head_of (i) = 0; |
11252 | alloc_list_tail_of (i) = 0; |
11253 | } |
11254 | } |
11255 | |
11256 | //always thread to the end. |
11257 | void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail) |
11258 | { |
11259 | free_list_slot (item) = 0; |
11260 | free_list_undo (item) = UNDO_EMPTY; |
11261 | assert (item != head); |
11262 | |
11263 | if (head == 0) |
11264 | { |
11265 | head = item; |
11266 | } |
11267 | //TODO: This shouldn't happen anymore - verify that's the case. |
11268 | //the following is necessary because the last free element |
11269 | //may have been truncated, and tail isn't updated. |
11270 | else if (free_list_slot (head) == 0) |
11271 | { |
11272 | free_list_slot (head) = item; |
11273 | } |
11274 | else |
11275 | { |
11276 | assert (item != tail); |
11277 | assert (free_list_slot(tail) == 0); |
11278 | free_list_slot (tail) = item; |
11279 | } |
11280 | tail = item; |
11281 | } |
11282 | |
11283 | void allocator::thread_item (uint8_t* item, size_t size) |
11284 | { |
11285 | size_t sz = frst_bucket_size; |
11286 | unsigned int a_l_number = 0; |
11287 | |
11288 | for (; a_l_number < (num_buckets-1); a_l_number++) |
11289 | { |
11290 | if (size < sz) |
11291 | { |
11292 | break; |
11293 | } |
11294 | sz = sz * 2; |
11295 | } |
11296 | alloc_list* al = &alloc_list_of (a_l_number); |
11297 | thread_free_item (item, |
11298 | al->alloc_list_head(), |
11299 | al->alloc_list_tail()); |
11300 | } |
11301 | |
11302 | void allocator::thread_item_front (uint8_t* item, size_t size) |
11303 | { |
11304 | //find right free list |
11305 | size_t sz = frst_bucket_size; |
11306 | unsigned int a_l_number = 0; |
11307 | for (; a_l_number < (num_buckets-1); a_l_number++) |
11308 | { |
11309 | if (size < sz) |
11310 | { |
11311 | break; |
11312 | } |
11313 | sz = sz * 2; |
11314 | } |
11315 | alloc_list* al = &alloc_list_of (a_l_number); |
11316 | free_list_slot (item) = al->alloc_list_head(); |
11317 | free_list_undo (item) = UNDO_EMPTY; |
11318 | |
11319 | if (al->alloc_list_tail() == 0) |
11320 | { |
11321 | al->alloc_list_tail() = al->alloc_list_head(); |
11322 | } |
11323 | al->alloc_list_head() = item; |
11324 | if (al->alloc_list_tail() == 0) |
11325 | { |
11326 | al->alloc_list_tail() = item; |
11327 | } |
11328 | } |
11329 | |
11330 | void allocator::copy_to_alloc_list (alloc_list* toalist) |
11331 | { |
11332 | for (unsigned int i = 0; i < num_buckets; i++) |
11333 | { |
11334 | toalist [i] = alloc_list_of (i); |
11335 | #ifdef FL_VERIFICATION |
11336 | uint8_t* free_item = alloc_list_head_of (i); |
11337 | size_t count = 0; |
11338 | while (free_item) |
11339 | { |
11340 | count++; |
11341 | free_item = free_list_slot (free_item); |
11342 | } |
11343 | |
11344 | toalist[i].item_count = count; |
11345 | #endif //FL_VERIFICATION |
11346 | } |
11347 | } |
11348 | |
11349 | void allocator::copy_from_alloc_list (alloc_list* fromalist) |
11350 | { |
11351 | BOOL repair_list = !discard_if_no_fit_p (); |
11352 | for (unsigned int i = 0; i < num_buckets; i++) |
11353 | { |
11354 | size_t count = alloc_list_damage_count_of (i); |
11355 | alloc_list_of (i) = fromalist [i]; |
11356 | assert (alloc_list_damage_count_of (i) == 0); |
11357 | |
11358 | if (repair_list) |
11359 | { |
11360 | //repair the the list |
11361 | //new items may have been added during the plan phase |
11362 | //items may have been unlinked. |
11363 | uint8_t* free_item = alloc_list_head_of (i); |
11364 | while (free_item && count) |
11365 | { |
11366 | assert (((CObjectHeader*)free_item)->IsFree()); |
11367 | if ((free_list_undo (free_item) != UNDO_EMPTY)) |
11368 | { |
11369 | count--; |
11370 | free_list_slot (free_item) = free_list_undo (free_item); |
11371 | free_list_undo (free_item) = UNDO_EMPTY; |
11372 | } |
11373 | |
11374 | free_item = free_list_slot (free_item); |
11375 | } |
11376 | |
11377 | #ifdef FL_VERIFICATION |
11378 | free_item = alloc_list_head_of (i); |
11379 | size_t item_count = 0; |
11380 | while (free_item) |
11381 | { |
11382 | item_count++; |
11383 | free_item = free_list_slot (free_item); |
11384 | } |
11385 | |
11386 | assert (item_count == alloc_list_of (i).item_count); |
11387 | #endif //FL_VERIFICATION |
11388 | } |
11389 | #ifdef DEBUG |
11390 | uint8_t* tail_item = alloc_list_tail_of (i); |
11391 | assert ((tail_item == 0) || (free_list_slot (tail_item) == 0)); |
11392 | #endif |
11393 | } |
11394 | } |
11395 | |
11396 | void allocator::commit_alloc_list_changes() |
11397 | { |
11398 | BOOL repair_list = !discard_if_no_fit_p (); |
11399 | if (repair_list) |
11400 | { |
11401 | for (unsigned int i = 0; i < num_buckets; i++) |
11402 | { |
11403 | //remove the undo info from list. |
11404 | uint8_t* free_item = alloc_list_head_of (i); |
11405 | size_t count = alloc_list_damage_count_of (i); |
11406 | while (free_item && count) |
11407 | { |
11408 | assert (((CObjectHeader*)free_item)->IsFree()); |
11409 | |
11410 | if (free_list_undo (free_item) != UNDO_EMPTY) |
11411 | { |
11412 | free_list_undo (free_item) = UNDO_EMPTY; |
11413 | count--; |
11414 | } |
11415 | |
11416 | free_item = free_list_slot (free_item); |
11417 | } |
11418 | |
11419 | alloc_list_damage_count_of (i) = 0; |
11420 | } |
11421 | } |
11422 | } |
11423 | |
11424 | void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, |
11425 | alloc_context* acontext, heap_segment* seg, |
11426 | int align_const, int gen_number) |
11427 | { |
11428 | bool loh_p = (gen_number > 0); |
11429 | GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh; |
11430 | |
11431 | size_t aligned_min_obj_size = Align(min_obj_size, align_const); |
11432 | |
11433 | if (seg) |
11434 | { |
11435 | assert (heap_segment_used (seg) <= heap_segment_committed (seg)); |
11436 | } |
11437 | |
11438 | #ifdef MULTIPLE_HEAPS |
11439 | if (gen_number == 0) |
11440 | { |
11441 | if (!gen0_allocated_after_gc_p) |
11442 | { |
11443 | gen0_allocated_after_gc_p = true; |
11444 | } |
11445 | } |
11446 | #endif //MULTIPLE_HEAPS |
11447 | |
11448 | dprintf (3, ("Expanding segment allocation [%Ix, %Ix[" , (size_t)start, |
11449 | (size_t)start + limit_size - aligned_min_obj_size)); |
11450 | |
11451 | if ((acontext->alloc_limit != start) && |
11452 | (acontext->alloc_limit + aligned_min_obj_size)!= start) |
11453 | { |
11454 | uint8_t* hole = acontext->alloc_ptr; |
11455 | if (hole != 0) |
11456 | { |
11457 | size_t size = (acontext->alloc_limit - acontext->alloc_ptr); |
11458 | dprintf (3, ("filling up hole [%Ix, %Ix[" , (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const))); |
11459 | // when we are finishing an allocation from a free list |
11460 | // we know that the free area was Align(min_obj_size) larger |
11461 | acontext->alloc_bytes -= size; |
11462 | size_t free_obj_size = size + aligned_min_obj_size; |
11463 | make_unused_array (hole, free_obj_size); |
11464 | generation_free_obj_space (generation_of (gen_number)) += free_obj_size; |
11465 | } |
11466 | acontext->alloc_ptr = start; |
11467 | } |
11468 | else |
11469 | { |
11470 | if (gen_number == 0) |
11471 | { |
11472 | size_t pad_size = Align (min_obj_size, align_const); |
11473 | make_unused_array (acontext->alloc_ptr, pad_size); |
11474 | dprintf (3, ("contigous ac: making min obj gap %Ix->%Ix(%Id)" , |
11475 | acontext->alloc_ptr, (acontext->alloc_ptr + pad_size), pad_size)); |
11476 | acontext->alloc_ptr += pad_size; |
11477 | } |
11478 | } |
11479 | acontext->alloc_limit = (start + limit_size - aligned_min_obj_size); |
11480 | acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0); |
11481 | |
11482 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
11483 | if (g_fEnableAppDomainMonitoring) |
11484 | { |
11485 | GCToEEInterface::RecordAllocatedBytesForHeap(limit_size, heap_number); |
11486 | } |
11487 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
11488 | |
11489 | uint8_t* saved_used = 0; |
11490 | |
11491 | if (seg) |
11492 | { |
11493 | saved_used = heap_segment_used (seg); |
11494 | } |
11495 | |
11496 | if (seg == ephemeral_heap_segment) |
11497 | { |
11498 | //Sometimes the allocated size is advanced without clearing the |
11499 | //memory. Let's catch up here |
11500 | if (heap_segment_used (seg) < (alloc_allocated - plug_skew)) |
11501 | { |
11502 | #ifdef MARK_ARRAY |
11503 | #ifndef BACKGROUND_GC |
11504 | clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated); |
11505 | #endif //BACKGROUND_GC |
11506 | #endif //MARK_ARRAY |
11507 | heap_segment_used (seg) = alloc_allocated - plug_skew; |
11508 | } |
11509 | } |
11510 | #ifdef BACKGROUND_GC |
11511 | else if (seg) |
11512 | { |
11513 | uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size; |
11514 | #ifdef FEATURE_LOH_COMPACTION |
11515 | old_allocated -= Align (loh_padding_obj_size, align_const); |
11516 | #endif //FEATURE_LOH_COMPACTION |
11517 | |
11518 | assert (heap_segment_used (seg) >= old_allocated); |
11519 | } |
11520 | #endif //BACKGROUND_GC |
11521 | if ((seg == 0) || |
11522 | (start - plug_skew + limit_size) <= heap_segment_used (seg)) |
11523 | { |
11524 | add_saved_spinlock_info (loh_p, me_release, mt_clr_mem); |
11525 | leave_spin_lock (msl); |
11526 | dprintf (3, ("clearing memory at %Ix for %d bytes" , (start - plug_skew), limit_size)); |
11527 | memclr (start - plug_skew, limit_size); |
11528 | } |
11529 | else |
11530 | { |
11531 | uint8_t* used = heap_segment_used (seg); |
11532 | heap_segment_used (seg) = start + limit_size - plug_skew; |
11533 | |
11534 | add_saved_spinlock_info (loh_p, me_release, mt_clr_mem); |
11535 | leave_spin_lock (msl); |
11536 | |
11537 | if ((start - plug_skew) < used) |
11538 | { |
11539 | if (used != saved_used) |
11540 | { |
11541 | FATAL_GC_ERROR (); |
11542 | } |
11543 | |
11544 | dprintf (2, ("clearing memory before used at %Ix for %Id bytes" , |
11545 | (start - plug_skew), (plug_skew + used - start))); |
11546 | memclr (start - plug_skew, used - (start - plug_skew)); |
11547 | } |
11548 | } |
11549 | |
11550 | //this portion can be done after we release the lock |
11551 | if (seg == ephemeral_heap_segment) |
11552 | { |
11553 | #ifdef FFIND_OBJECT |
11554 | if (gen0_must_clear_bricks > 0) |
11555 | { |
11556 | //set the brick table to speed up find_object |
11557 | size_t b = brick_of (acontext->alloc_ptr); |
11558 | set_brick (b, acontext->alloc_ptr - brick_address (b)); |
11559 | b++; |
11560 | dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[" , |
11561 | b, brick_of (align_on_brick (start + limit_size)))); |
11562 | volatile short* x = &brick_table [b]; |
11563 | short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))]; |
11564 | |
11565 | for (;x < end_x;x++) |
11566 | *x = -1; |
11567 | } |
11568 | else |
11569 | #endif //FFIND_OBJECT |
11570 | { |
11571 | gen0_bricks_cleared = FALSE; |
11572 | } |
11573 | } |
11574 | |
11575 | // verifying the memory is completely cleared. |
11576 | //verify_mem_cleared (start - plug_skew, limit_size); |
11577 | } |
11578 | |
11579 | size_t gc_heap::new_allocation_limit (size_t size, size_t physical_limit, int gen_number) |
11580 | { |
11581 | dynamic_data* dd = dynamic_data_of (gen_number); |
11582 | ptrdiff_t new_alloc = dd_new_allocation (dd); |
11583 | assert (new_alloc == (ptrdiff_t)Align (new_alloc, |
11584 | get_alignment_constant (!(gen_number == (max_generation+1))))); |
11585 | |
11586 | ptrdiff_t logical_limit = max (new_alloc, (ptrdiff_t)size); |
11587 | size_t limit = min (logical_limit, (ptrdiff_t)physical_limit); |
11588 | assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1))))); |
11589 | dd_new_allocation (dd) = (new_alloc - limit); |
11590 | return limit; |
11591 | } |
11592 | |
11593 | size_t gc_heap::limit_from_size (size_t size, size_t physical_limit, int gen_number, |
11594 | int align_const) |
11595 | { |
11596 | size_t padded_size = size + Align (min_obj_size, align_const); |
11597 | // for LOH this is not true...we could select a physical_limit that's exactly the same |
11598 | // as size. |
11599 | assert ((gen_number != 0) || (physical_limit >= padded_size)); |
11600 | size_t min_size_to_allocate = ((gen_number == 0) ? allocation_quantum : 0); |
11601 | |
11602 | // For SOH if the size asked for is very small, we want to allocate more than |
11603 | // just what's asked for if possible. |
11604 | size_t desired_size_to_allocate = max (padded_size, min_size_to_allocate); |
11605 | size_t new_physical_limit = min (physical_limit, desired_size_to_allocate); |
11606 | |
11607 | size_t new_limit = new_allocation_limit (padded_size, |
11608 | new_physical_limit, |
11609 | gen_number); |
11610 | assert (new_limit >= (size + Align (min_obj_size, align_const))); |
11611 | dprintf (100, ("requested to allocate %Id bytes, actual size is %Id" , size, new_limit)); |
11612 | return new_limit; |
11613 | } |
11614 | |
11615 | void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size, |
11616 | uint8_t* allocated, uint8_t* reserved) |
11617 | { |
11618 | dprintf (1, ("total committed on the heap is %Id" , get_total_committed_size())); |
11619 | |
11620 | UNREFERENCED_PARAMETER(heap_num); |
11621 | |
11622 | if (reason == oom_budget) |
11623 | { |
11624 | alloc_size = dd_min_size (dynamic_data_of (0)) / 2; |
11625 | } |
11626 | |
11627 | if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure))) |
11628 | { |
11629 | // This means during the last GC we needed to reserve and/or commit more memory |
11630 | // but we couldn't. We proceeded with the GC and ended up not having enough |
11631 | // memory at the end. This is a legitimate OOM situtation. Otherwise we |
11632 | // probably made a mistake and didn't expand the heap when we should have. |
11633 | reason = oom_low_mem; |
11634 | } |
11635 | |
11636 | oom_info.reason = reason; |
11637 | oom_info.allocated = allocated; |
11638 | oom_info.reserved = reserved; |
11639 | oom_info.alloc_size = alloc_size; |
11640 | oom_info.gc_index = settings.gc_index; |
11641 | oom_info.fgm = fgm_result.fgm; |
11642 | oom_info.size = fgm_result.size; |
11643 | oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb; |
11644 | oom_info.loh_p = fgm_result.loh_p; |
11645 | |
11646 | fgm_result.fgm = fgm_no_failure; |
11647 | |
11648 | // Break early - before the more_space_lock is release so no other threads |
11649 | // could have allocated on the same heap when OOM happened. |
11650 | if (GCConfig::GetBreakOnOOM()) |
11651 | { |
11652 | GCToOSInterface::DebugBreak(); |
11653 | } |
11654 | } |
11655 | |
11656 | #ifdef BACKGROUND_GC |
11657 | BOOL gc_heap::background_allowed_p() |
11658 | { |
11659 | return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) ); |
11660 | } |
11661 | #endif //BACKGROUND_GC |
11662 | |
11663 | void gc_heap::check_for_full_gc (int gen_num, size_t size) |
11664 | { |
11665 | BOOL should_notify = FALSE; |
11666 | // if we detect full gc because of the allocation budget specified this is TRUE; |
11667 | // it's FALSE if it's due to other factors. |
11668 | BOOL alloc_factor = TRUE; |
11669 | int i = 0; |
11670 | int n = 0; |
11671 | int n_initial = gen_num; |
11672 | BOOL local_blocking_collection = FALSE; |
11673 | BOOL local_elevation_requested = FALSE; |
11674 | int new_alloc_remain_percent = 0; |
11675 | |
11676 | if (full_gc_approach_event_set) |
11677 | { |
11678 | return; |
11679 | } |
11680 | |
11681 | if (gen_num != (max_generation + 1)) |
11682 | { |
11683 | gen_num = max_generation; |
11684 | } |
11685 | |
11686 | dynamic_data* dd_full = dynamic_data_of (gen_num); |
11687 | ptrdiff_t new_alloc_remain = 0; |
11688 | uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent); |
11689 | |
11690 | for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++) |
11691 | { |
11692 | dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)" , |
11693 | heap_number, gen_index, |
11694 | dd_new_allocation (dynamic_data_of (gen_index)), |
11695 | dd_desired_allocation (dynamic_data_of (gen_index)))); |
11696 | } |
11697 | |
11698 | // For small object allocations we only check every fgn_check_quantum bytes. |
11699 | if (n_initial == 0) |
11700 | { |
11701 | dprintf (2, ("FGN: gen0 last recorded alloc: %Id" , fgn_last_alloc)); |
11702 | dynamic_data* dd_0 = dynamic_data_of (n_initial); |
11703 | if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) && |
11704 | (dd_new_allocation (dd_0) >= 0)) |
11705 | { |
11706 | return; |
11707 | } |
11708 | else |
11709 | { |
11710 | fgn_last_alloc = dd_new_allocation (dd_0); |
11711 | dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id" , fgn_last_alloc)); |
11712 | } |
11713 | |
11714 | // We don't consider the size that came from soh 'cause it doesn't contribute to the |
11715 | // gen2 budget. |
11716 | size = 0; |
11717 | } |
11718 | |
11719 | for (i = n+1; i <= max_generation; i++) |
11720 | { |
11721 | if (get_new_allocation (i) <= 0) |
11722 | { |
11723 | n = min (i, max_generation); |
11724 | } |
11725 | else |
11726 | break; |
11727 | } |
11728 | |
11729 | dprintf (2, ("FGN: h#%d: gen%d budget exceeded" , heap_number, n)); |
11730 | if (gen_num == max_generation) |
11731 | { |
11732 | // If it's small object heap we should first see if we will even be looking at gen2 budget |
11733 | // in the next GC or not. If not we should go directly to checking other factors. |
11734 | if (n < (max_generation - 1)) |
11735 | { |
11736 | goto check_other_factors; |
11737 | } |
11738 | } |
11739 | |
11740 | new_alloc_remain = dd_new_allocation (dd_full) - size; |
11741 | |
11742 | new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100); |
11743 | |
11744 | dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%" , |
11745 | gen_num, pct, new_alloc_remain_percent)); |
11746 | |
11747 | if (new_alloc_remain_percent <= (int)pct) |
11748 | { |
11749 | #ifdef BACKGROUND_GC |
11750 | // If background GC is enabled, we still want to check whether this will |
11751 | // be a blocking GC or not because we only want to notify when it's a |
11752 | // blocking full GC. |
11753 | if (background_allowed_p()) |
11754 | { |
11755 | goto check_other_factors; |
11756 | } |
11757 | #endif //BACKGROUND_GC |
11758 | |
11759 | should_notify = TRUE; |
11760 | goto done; |
11761 | } |
11762 | |
11763 | check_other_factors: |
11764 | |
11765 | dprintf (2, ("FGC: checking other factors" )); |
11766 | n = generation_to_condemn (n, |
11767 | &local_blocking_collection, |
11768 | &local_elevation_requested, |
11769 | TRUE); |
11770 | |
11771 | if (local_elevation_requested && (n == max_generation)) |
11772 | { |
11773 | if (settings.should_lock_elevation) |
11774 | { |
11775 | int local_elevation_locked_count = settings.elevation_locked_count + 1; |
11776 | if (local_elevation_locked_count != 6) |
11777 | { |
11778 | dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1" , |
11779 | local_elevation_locked_count)); |
11780 | n = max_generation - 1; |
11781 | } |
11782 | } |
11783 | } |
11784 | |
11785 | dprintf (2, ("FGN: we estimate gen%d will be collected" , n)); |
11786 | |
11787 | #ifdef BACKGROUND_GC |
11788 | // When background GC is enabled it decreases the accuracy of our predictability - |
11789 | // by the time the GC happens, we may not be under BGC anymore. If we try to |
11790 | // predict often enough it should be ok. |
11791 | if ((n == max_generation) && |
11792 | (recursive_gc_sync::background_running_p())) |
11793 | { |
11794 | n = max_generation - 1; |
11795 | dprintf (2, ("FGN: bgc - 1 instead of 2" )); |
11796 | } |
11797 | |
11798 | if ((n == max_generation) && !local_blocking_collection) |
11799 | { |
11800 | if (!background_allowed_p()) |
11801 | { |
11802 | local_blocking_collection = TRUE; |
11803 | } |
11804 | } |
11805 | #endif //BACKGROUND_GC |
11806 | |
11807 | dprintf (2, ("FGN: we estimate gen%d will be collected: %s" , |
11808 | n, |
11809 | (local_blocking_collection ? "blocking" : "background" ))); |
11810 | |
11811 | if ((n == max_generation) && local_blocking_collection) |
11812 | { |
11813 | alloc_factor = FALSE; |
11814 | should_notify = TRUE; |
11815 | goto done; |
11816 | } |
11817 | |
11818 | done: |
11819 | |
11820 | if (should_notify) |
11821 | { |
11822 | dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)" , |
11823 | n_initial, |
11824 | (alloc_factor ? "alloc" : "other" ), |
11825 | dd_collection_count (dynamic_data_of (0)), |
11826 | new_alloc_remain_percent, |
11827 | gen_num)); |
11828 | |
11829 | send_full_gc_notification (n_initial, alloc_factor); |
11830 | } |
11831 | } |
11832 | |
11833 | void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p) |
11834 | { |
11835 | if (!full_gc_approach_event_set) |
11836 | { |
11837 | assert (full_gc_approach_event.IsValid()); |
11838 | FIRE_EVENT(GCFullNotify_V1, gen_num, due_to_alloc_p); |
11839 | |
11840 | full_gc_end_event.Reset(); |
11841 | full_gc_approach_event.Set(); |
11842 | full_gc_approach_event_set = true; |
11843 | } |
11844 | } |
11845 | |
11846 | wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms) |
11847 | { |
11848 | if (fgn_maxgen_percent == 0) |
11849 | { |
11850 | return wait_full_gc_na; |
11851 | } |
11852 | |
11853 | uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms); |
11854 | |
11855 | if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT)) |
11856 | { |
11857 | if (fgn_maxgen_percent == 0) |
11858 | { |
11859 | return wait_full_gc_cancelled; |
11860 | } |
11861 | |
11862 | if (wait_result == WAIT_OBJECT_0) |
11863 | { |
11864 | #ifdef BACKGROUND_GC |
11865 | if (fgn_last_gc_was_concurrent) |
11866 | { |
11867 | fgn_last_gc_was_concurrent = FALSE; |
11868 | return wait_full_gc_na; |
11869 | } |
11870 | else |
11871 | #endif //BACKGROUND_GC |
11872 | { |
11873 | return wait_full_gc_success; |
11874 | } |
11875 | } |
11876 | else |
11877 | { |
11878 | return wait_full_gc_timeout; |
11879 | } |
11880 | } |
11881 | else |
11882 | { |
11883 | return wait_full_gc_failed; |
11884 | } |
11885 | } |
11886 | |
11887 | size_t gc_heap::get_full_compact_gc_count() |
11888 | { |
11889 | return full_gc_counts[gc_type_compacting]; |
11890 | } |
11891 | |
11892 | // DTREVIEW - we should check this in dt_low_ephemeral_space_p |
11893 | // as well. |
11894 | inline |
11895 | BOOL gc_heap::short_on_end_of_seg (int gen_number, |
11896 | heap_segment* seg, |
11897 | int align_const) |
11898 | { |
11899 | UNREFERENCED_PARAMETER(gen_number); |
11900 | uint8_t* allocated = heap_segment_allocated(seg); |
11901 | |
11902 | BOOL sufficient_p = a_size_fit_p (end_space_after_gc(), |
11903 | allocated, |
11904 | heap_segment_reserved (seg), |
11905 | align_const); |
11906 | |
11907 | if (!sufficient_p) |
11908 | { |
11909 | if (sufficient_gen0_space_p) |
11910 | { |
11911 | dprintf (GTC_LOG, ("gen0 has enough free space" )); |
11912 | } |
11913 | |
11914 | sufficient_p = sufficient_gen0_space_p; |
11915 | } |
11916 | |
11917 | return !sufficient_p; |
11918 | } |
11919 | |
11920 | #ifdef _MSC_VER |
11921 | #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function. |
11922 | #endif // _MSC_VER |
11923 | |
11924 | inline |
11925 | BOOL gc_heap::a_fit_free_list_p (int gen_number, |
11926 | size_t size, |
11927 | alloc_context* acontext, |
11928 | int align_const) |
11929 | { |
11930 | BOOL can_fit = FALSE; |
11931 | generation* gen = generation_of (gen_number); |
11932 | allocator* gen_allocator = generation_allocator (gen); |
11933 | size_t sz_list = gen_allocator->first_bucket_size(); |
11934 | for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++) |
11935 | { |
11936 | if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1))) |
11937 | { |
11938 | uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx); |
11939 | uint8_t* prev_free_item = 0; |
11940 | |
11941 | while (free_list != 0) |
11942 | { |
11943 | dprintf (3, ("considering free list %Ix" , (size_t)free_list)); |
11944 | size_t free_list_size = unused_array_size (free_list); |
11945 | if ((size + Align (min_obj_size, align_const)) <= free_list_size) |
11946 | { |
11947 | dprintf (3, ("Found adequate unused area: [%Ix, size: %Id" , |
11948 | (size_t)free_list, free_list_size)); |
11949 | |
11950 | gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE); |
11951 | // We ask for more Align (min_obj_size) |
11952 | // to make sure that we can insert a free object |
11953 | // in adjust_limit will set the limit lower |
11954 | size_t limit = limit_from_size (size, free_list_size, gen_number, align_const); |
11955 | |
11956 | uint8_t* remain = (free_list + limit); |
11957 | size_t remain_size = (free_list_size - limit); |
11958 | if (remain_size >= Align(min_free_list, align_const)) |
11959 | { |
11960 | make_unused_array (remain, remain_size); |
11961 | gen_allocator->thread_item_front (remain, remain_size); |
11962 | assert (remain_size >= Align (min_obj_size, align_const)); |
11963 | } |
11964 | else |
11965 | { |
11966 | //absorb the entire free list |
11967 | limit += remain_size; |
11968 | } |
11969 | generation_free_list_space (gen) -= limit; |
11970 | |
11971 | adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number); |
11972 | |
11973 | can_fit = TRUE; |
11974 | goto end; |
11975 | } |
11976 | else if (gen_allocator->discard_if_no_fit_p()) |
11977 | { |
11978 | assert (prev_free_item == 0); |
11979 | dprintf (3, ("couldn't use this free area, discarding" )); |
11980 | generation_free_obj_space (gen) += free_list_size; |
11981 | |
11982 | gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE); |
11983 | generation_free_list_space (gen) -= free_list_size; |
11984 | } |
11985 | else |
11986 | { |
11987 | prev_free_item = free_list; |
11988 | } |
11989 | free_list = free_list_slot (free_list); |
11990 | } |
11991 | } |
11992 | sz_list = sz_list * 2; |
11993 | } |
11994 | end: |
11995 | return can_fit; |
11996 | } |
11997 | |
11998 | |
11999 | #ifdef BACKGROUND_GC |
12000 | void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start, |
12001 | size_t size, |
12002 | alloc_context* acontext, |
12003 | int align_const, |
12004 | int lock_index, |
12005 | BOOL check_used_p, |
12006 | heap_segment* seg) |
12007 | { |
12008 | make_unused_array (alloc_start, size); |
12009 | |
12010 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
12011 | if (g_fEnableAppDomainMonitoring) |
12012 | { |
12013 | GCToEEInterface::RecordAllocatedBytesForHeap(size, heap_number); |
12014 | } |
12015 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
12016 | |
12017 | size_t size_of_array_base = sizeof(ArrayBase); |
12018 | |
12019 | bgc_alloc_lock->loh_alloc_done_with_index (lock_index); |
12020 | |
12021 | // clear memory while not holding the lock. |
12022 | size_t size_to_skip = size_of_array_base; |
12023 | size_t size_to_clear = size - size_to_skip - plug_skew; |
12024 | size_t saved_size_to_clear = size_to_clear; |
12025 | if (check_used_p) |
12026 | { |
12027 | uint8_t* end = alloc_start + size - plug_skew; |
12028 | uint8_t* used = heap_segment_used (seg); |
12029 | if (used < end) |
12030 | { |
12031 | if ((alloc_start + size_to_skip) < used) |
12032 | { |
12033 | size_to_clear = used - (alloc_start + size_to_skip); |
12034 | } |
12035 | else |
12036 | { |
12037 | size_to_clear = 0; |
12038 | } |
12039 | dprintf (2, ("bgc loh: setting used to %Ix" , end)); |
12040 | heap_segment_used (seg) = end; |
12041 | } |
12042 | |
12043 | dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes" , |
12044 | used, alloc_start, end, size_to_clear)); |
12045 | } |
12046 | else |
12047 | { |
12048 | dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)" , alloc_start, alloc_start+size, size)); |
12049 | } |
12050 | |
12051 | #ifdef VERIFY_HEAP |
12052 | // since we filled in 0xcc for free object when we verify heap, |
12053 | // we need to make sure we clear those bytes. |
12054 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
12055 | { |
12056 | if (size_to_clear < saved_size_to_clear) |
12057 | { |
12058 | size_to_clear = saved_size_to_clear; |
12059 | } |
12060 | } |
12061 | #endif //VERIFY_HEAP |
12062 | |
12063 | dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj" , heap_number)); |
12064 | add_saved_spinlock_info (true, me_release, mt_clr_large_mem); |
12065 | leave_spin_lock (&more_space_lock_loh); |
12066 | memclr (alloc_start + size_to_skip, size_to_clear); |
12067 | |
12068 | bgc_alloc_lock->loh_alloc_set (alloc_start); |
12069 | |
12070 | acontext->alloc_ptr = alloc_start; |
12071 | acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const)); |
12072 | |
12073 | // need to clear the rest of the object before we hand it out. |
12074 | clear_unused_array(alloc_start, size); |
12075 | } |
12076 | #endif //BACKGROUND_GC |
12077 | |
12078 | BOOL gc_heap::a_fit_free_list_large_p (size_t size, |
12079 | alloc_context* acontext, |
12080 | int align_const) |
12081 | { |
12082 | BOOL can_fit = FALSE; |
12083 | int gen_number = max_generation + 1; |
12084 | generation* gen = generation_of (gen_number); |
12085 | allocator* loh_allocator = generation_allocator (gen); |
12086 | |
12087 | #ifdef FEATURE_LOH_COMPACTION |
12088 | size_t loh_pad = Align (loh_padding_obj_size, align_const); |
12089 | #endif //FEATURE_LOH_COMPACTION |
12090 | |
12091 | #ifdef BACKGROUND_GC |
12092 | int cookie = -1; |
12093 | #endif //BACKGROUND_GC |
12094 | size_t sz_list = loh_allocator->first_bucket_size(); |
12095 | for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++) |
12096 | { |
12097 | if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1))) |
12098 | { |
12099 | uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx); |
12100 | uint8_t* prev_free_item = 0; |
12101 | while (free_list != 0) |
12102 | { |
12103 | dprintf (3, ("considering free list %Ix" , (size_t)free_list)); |
12104 | |
12105 | size_t free_list_size = unused_array_size(free_list); |
12106 | |
12107 | #ifdef FEATURE_LOH_COMPACTION |
12108 | if ((size + loh_pad) <= free_list_size) |
12109 | #else |
12110 | if (((size + Align (min_obj_size, align_const)) <= free_list_size)|| |
12111 | (size == free_list_size)) |
12112 | #endif //FEATURE_LOH_COMPACTION |
12113 | { |
12114 | #ifdef BACKGROUND_GC |
12115 | cookie = bgc_alloc_lock->loh_alloc_set (free_list); |
12116 | bgc_track_loh_alloc(); |
12117 | #endif //BACKGROUND_GC |
12118 | |
12119 | //unlink the free_item |
12120 | loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE); |
12121 | |
12122 | // Substract min obj size because limit_from_size adds it. Not needed for LOH |
12123 | size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size, |
12124 | gen_number, align_const); |
12125 | |
12126 | #ifdef FEATURE_LOH_COMPACTION |
12127 | make_unused_array (free_list, loh_pad); |
12128 | limit -= loh_pad; |
12129 | free_list += loh_pad; |
12130 | free_list_size -= loh_pad; |
12131 | #endif //FEATURE_LOH_COMPACTION |
12132 | |
12133 | uint8_t* remain = (free_list + limit); |
12134 | size_t remain_size = (free_list_size - limit); |
12135 | if (remain_size != 0) |
12136 | { |
12137 | assert (remain_size >= Align (min_obj_size, align_const)); |
12138 | make_unused_array (remain, remain_size); |
12139 | } |
12140 | if (remain_size >= Align(min_free_list, align_const)) |
12141 | { |
12142 | loh_thread_gap_front (remain, remain_size, gen); |
12143 | assert (remain_size >= Align (min_obj_size, align_const)); |
12144 | } |
12145 | else |
12146 | { |
12147 | generation_free_obj_space (gen) += remain_size; |
12148 | } |
12149 | generation_free_list_space (gen) -= free_list_size; |
12150 | dprintf (3, ("found fit on loh at %Ix" , free_list)); |
12151 | #ifdef BACKGROUND_GC |
12152 | if (cookie != -1) |
12153 | { |
12154 | bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0); |
12155 | } |
12156 | else |
12157 | #endif //BACKGROUND_GC |
12158 | { |
12159 | adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number); |
12160 | } |
12161 | |
12162 | //fix the limit to compensate for adjust_limit_clr making it too short |
12163 | acontext->alloc_limit += Align (min_obj_size, align_const); |
12164 | can_fit = TRUE; |
12165 | goto exit; |
12166 | } |
12167 | prev_free_item = free_list; |
12168 | free_list = free_list_slot (free_list); |
12169 | } |
12170 | } |
12171 | sz_list = sz_list * 2; |
12172 | } |
12173 | exit: |
12174 | return can_fit; |
12175 | } |
12176 | |
12177 | #ifdef _MSC_VER |
12178 | #pragma warning(default:4706) |
12179 | #endif // _MSC_VER |
12180 | |
12181 | BOOL gc_heap::a_fit_segment_end_p (int gen_number, |
12182 | heap_segment* seg, |
12183 | size_t size, |
12184 | alloc_context* acontext, |
12185 | int align_const, |
12186 | BOOL* commit_failed_p) |
12187 | { |
12188 | *commit_failed_p = FALSE; |
12189 | size_t limit = 0; |
12190 | #ifdef BACKGROUND_GC |
12191 | int cookie = -1; |
12192 | #endif //BACKGROUND_GC |
12193 | |
12194 | uint8_t*& allocated = ((gen_number == 0) ? |
12195 | alloc_allocated : |
12196 | heap_segment_allocated(seg)); |
12197 | |
12198 | size_t pad = Align (min_obj_size, align_const); |
12199 | |
12200 | #ifdef FEATURE_LOH_COMPACTION |
12201 | size_t loh_pad = Align (loh_padding_obj_size, align_const); |
12202 | if (gen_number == (max_generation + 1)) |
12203 | { |
12204 | pad += loh_pad; |
12205 | } |
12206 | #endif //FEATURE_LOH_COMPACTION |
12207 | |
12208 | uint8_t* end = heap_segment_committed (seg) - pad; |
12209 | |
12210 | if (a_size_fit_p (size, allocated, end, align_const)) |
12211 | { |
12212 | limit = limit_from_size (size, |
12213 | (end - allocated), |
12214 | gen_number, align_const); |
12215 | goto found_fit; |
12216 | } |
12217 | |
12218 | end = heap_segment_reserved (seg) - pad; |
12219 | |
12220 | if (a_size_fit_p (size, allocated, end, align_const)) |
12221 | { |
12222 | limit = limit_from_size (size, |
12223 | (end - allocated), |
12224 | gen_number, align_const); |
12225 | if (grow_heap_segment (seg, allocated + limit)) |
12226 | { |
12227 | goto found_fit; |
12228 | } |
12229 | else |
12230 | { |
12231 | dprintf (2, ("can't grow segment, doing a full gc" )); |
12232 | *commit_failed_p = TRUE; |
12233 | } |
12234 | } |
12235 | goto found_no_fit; |
12236 | |
12237 | found_fit: |
12238 | |
12239 | #ifdef BACKGROUND_GC |
12240 | if (gen_number != 0) |
12241 | { |
12242 | cookie = bgc_alloc_lock->loh_alloc_set (allocated); |
12243 | bgc_track_loh_alloc(); |
12244 | } |
12245 | #endif //BACKGROUND_GC |
12246 | |
12247 | uint8_t* old_alloc; |
12248 | old_alloc = allocated; |
12249 | #ifdef FEATURE_LOH_COMPACTION |
12250 | if (gen_number == (max_generation + 1)) |
12251 | { |
12252 | make_unused_array (old_alloc, loh_pad); |
12253 | old_alloc += loh_pad; |
12254 | allocated += loh_pad; |
12255 | limit -= loh_pad; |
12256 | } |
12257 | #endif //FEATURE_LOH_COMPACTION |
12258 | |
12259 | #if defined (VERIFY_HEAP) && defined (_DEBUG) |
12260 | ((void**) allocated)[-1] = 0; //clear the sync block |
12261 | #endif //VERIFY_HEAP && _DEBUG |
12262 | allocated += limit; |
12263 | |
12264 | dprintf (3, ("found fit at end of seg: %Ix" , old_alloc)); |
12265 | |
12266 | #ifdef BACKGROUND_GC |
12267 | if (cookie != -1) |
12268 | { |
12269 | bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg); |
12270 | } |
12271 | else |
12272 | #endif //BACKGROUND_GC |
12273 | { |
12274 | adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number); |
12275 | } |
12276 | |
12277 | return TRUE; |
12278 | |
12279 | found_no_fit: |
12280 | |
12281 | return FALSE; |
12282 | } |
12283 | |
12284 | BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number, |
12285 | size_t size, |
12286 | alloc_context* acontext, |
12287 | int align_const, |
12288 | BOOL* commit_failed_p, |
12289 | oom_reason* oom_r) |
12290 | { |
12291 | *commit_failed_p = FALSE; |
12292 | heap_segment* seg = generation_allocation_segment (generation_of (gen_number)); |
12293 | BOOL can_allocate_p = FALSE; |
12294 | |
12295 | while (seg) |
12296 | { |
12297 | #ifdef BACKGROUND_GC |
12298 | if (seg->flags & heap_segment_flags_loh_delete) |
12299 | { |
12300 | dprintf (3, ("h%d skipping seg %Ix to be deleted" , heap_number, (size_t)seg)); |
12301 | } |
12302 | else |
12303 | #endif //BACKGROUND_GC |
12304 | { |
12305 | if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)), |
12306 | acontext, align_const, commit_failed_p)) |
12307 | { |
12308 | acontext->alloc_limit += Align (min_obj_size, align_const); |
12309 | can_allocate_p = TRUE; |
12310 | break; |
12311 | } |
12312 | |
12313 | if (*commit_failed_p) |
12314 | { |
12315 | *oom_r = oom_cant_commit; |
12316 | break; |
12317 | } |
12318 | } |
12319 | |
12320 | seg = heap_segment_next_rw (seg); |
12321 | } |
12322 | |
12323 | return can_allocate_p; |
12324 | } |
12325 | |
12326 | #ifdef BACKGROUND_GC |
12327 | inline |
12328 | void gc_heap::wait_for_background (alloc_wait_reason awr, bool loh_p) |
12329 | { |
12330 | GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh; |
12331 | |
12332 | dprintf (2, ("BGC is already in progress, waiting for it to finish" )); |
12333 | add_saved_spinlock_info (loh_p, me_release, mt_wait_bgc); |
12334 | leave_spin_lock (msl); |
12335 | background_gc_wait (awr); |
12336 | enter_spin_lock (msl); |
12337 | add_saved_spinlock_info (loh_p, me_acquire, mt_wait_bgc); |
12338 | } |
12339 | |
12340 | void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr, bool loh_p) |
12341 | { |
12342 | if (recursive_gc_sync::background_running_p()) |
12343 | { |
12344 | uint32_t memory_load; |
12345 | get_memory_info (&memory_load); |
12346 | if (memory_load >= m_high_memory_load_th) |
12347 | { |
12348 | dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d" , awr)); |
12349 | wait_for_background (awr, loh_p); |
12350 | } |
12351 | } |
12352 | } |
12353 | |
12354 | #endif //BACKGROUND_GC |
12355 | |
12356 | // We request to trigger an ephemeral GC but we may get a full compacting GC. |
12357 | // return TRUE if that's the case. |
12358 | BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr) |
12359 | { |
12360 | #ifdef BACKGROUND_GC |
12361 | wait_for_bgc_high_memory (awr_loh_oos_bgc, false); |
12362 | #endif //BACKGROUND_GC |
12363 | |
12364 | BOOL did_full_compact_gc = FALSE; |
12365 | |
12366 | dprintf (2, ("triggering a gen1 GC" )); |
12367 | size_t last_full_compact_gc_count = get_full_compact_gc_count(); |
12368 | vm_heap->GarbageCollectGeneration(max_generation - 1, gr); |
12369 | |
12370 | #ifdef MULTIPLE_HEAPS |
12371 | enter_spin_lock (&more_space_lock_soh); |
12372 | add_saved_spinlock_info (false, me_acquire, mt_t_eph_gc); |
12373 | #endif //MULTIPLE_HEAPS |
12374 | |
12375 | size_t current_full_compact_gc_count = get_full_compact_gc_count(); |
12376 | |
12377 | if (current_full_compact_gc_count > last_full_compact_gc_count) |
12378 | { |
12379 | dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC" )); |
12380 | did_full_compact_gc = TRUE; |
12381 | } |
12382 | |
12383 | return did_full_compact_gc; |
12384 | } |
12385 | |
12386 | BOOL gc_heap::soh_try_fit (int gen_number, |
12387 | size_t size, |
12388 | alloc_context* acontext, |
12389 | int align_const, |
12390 | BOOL* commit_failed_p, |
12391 | BOOL* short_seg_end_p) |
12392 | { |
12393 | BOOL can_allocate = TRUE; |
12394 | if (short_seg_end_p) |
12395 | { |
12396 | *short_seg_end_p = FALSE; |
12397 | } |
12398 | |
12399 | can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const); |
12400 | if (!can_allocate) |
12401 | { |
12402 | if (short_seg_end_p) |
12403 | { |
12404 | *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const); |
12405 | } |
12406 | // If the caller doesn't care, we always try to fit at the end of seg; |
12407 | // otherwise we would only try if we are actually not short at end of seg. |
12408 | if (!short_seg_end_p || !(*short_seg_end_p)) |
12409 | { |
12410 | can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size, |
12411 | acontext, align_const, commit_failed_p); |
12412 | } |
12413 | } |
12414 | |
12415 | return can_allocate; |
12416 | } |
12417 | |
12418 | BOOL gc_heap::allocate_small (int gen_number, |
12419 | size_t size, |
12420 | alloc_context* acontext, |
12421 | int align_const) |
12422 | { |
12423 | #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS) |
12424 | if (recursive_gc_sync::background_running_p()) |
12425 | { |
12426 | background_soh_alloc_count++; |
12427 | if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0) |
12428 | { |
12429 | add_saved_spinlock_info (false, me_release, mt_alloc_small); |
12430 | leave_spin_lock (&more_space_lock_soh); |
12431 | bool cooperative_mode = enable_preemptive(); |
12432 | GCToOSInterface::Sleep (bgc_alloc_spin); |
12433 | disable_preemptive (cooperative_mode); |
12434 | enter_spin_lock (&more_space_lock_soh); |
12435 | add_saved_spinlock_info (false, me_acquire, mt_alloc_small); |
12436 | } |
12437 | else |
12438 | { |
12439 | //GCToOSInterface::YieldThread (0); |
12440 | } |
12441 | } |
12442 | #endif //BACKGROUND_GC && !MULTIPLE_HEAPS |
12443 | |
12444 | gc_reason gr = reason_oos_soh; |
12445 | oom_reason oom_r = oom_no_failure; |
12446 | |
12447 | // No variable values should be "carried over" from one state to the other. |
12448 | // That's why there are local variable for each state |
12449 | |
12450 | allocation_state soh_alloc_state = a_state_start; |
12451 | |
12452 | // If we can get a new seg it means allocation will succeed. |
12453 | while (1) |
12454 | { |
12455 | dprintf (3, ("[h%d]soh state is %s" , heap_number, allocation_state_str[soh_alloc_state])); |
12456 | switch (soh_alloc_state) |
12457 | { |
12458 | case a_state_can_allocate: |
12459 | case a_state_cant_allocate: |
12460 | { |
12461 | goto exit; |
12462 | } |
12463 | case a_state_start: |
12464 | { |
12465 | soh_alloc_state = a_state_try_fit; |
12466 | break; |
12467 | } |
12468 | case a_state_try_fit: |
12469 | { |
12470 | BOOL commit_failed_p = FALSE; |
12471 | BOOL can_use_existing_p = FALSE; |
12472 | |
12473 | can_use_existing_p = soh_try_fit (gen_number, size, acontext, |
12474 | align_const, &commit_failed_p, |
12475 | NULL); |
12476 | soh_alloc_state = (can_use_existing_p ? |
12477 | a_state_can_allocate : |
12478 | (commit_failed_p ? |
12479 | a_state_trigger_full_compact_gc : |
12480 | a_state_trigger_ephemeral_gc)); |
12481 | break; |
12482 | } |
12483 | case a_state_try_fit_after_bgc: |
12484 | { |
12485 | BOOL commit_failed_p = FALSE; |
12486 | BOOL can_use_existing_p = FALSE; |
12487 | BOOL short_seg_end_p = FALSE; |
12488 | |
12489 | can_use_existing_p = soh_try_fit (gen_number, size, acontext, |
12490 | align_const, &commit_failed_p, |
12491 | &short_seg_end_p); |
12492 | soh_alloc_state = (can_use_existing_p ? |
12493 | a_state_can_allocate : |
12494 | (short_seg_end_p ? |
12495 | a_state_trigger_2nd_ephemeral_gc : |
12496 | a_state_trigger_full_compact_gc)); |
12497 | break; |
12498 | } |
12499 | case a_state_try_fit_after_cg: |
12500 | { |
12501 | BOOL commit_failed_p = FALSE; |
12502 | BOOL can_use_existing_p = FALSE; |
12503 | BOOL short_seg_end_p = FALSE; |
12504 | |
12505 | can_use_existing_p = soh_try_fit (gen_number, size, acontext, |
12506 | align_const, &commit_failed_p, |
12507 | &short_seg_end_p); |
12508 | |
12509 | if (can_use_existing_p) |
12510 | { |
12511 | soh_alloc_state = a_state_can_allocate; |
12512 | } |
12513 | #ifdef MULTIPLE_HEAPS |
12514 | else if (gen0_allocated_after_gc_p) |
12515 | { |
12516 | // some other threads already grabbed the more space lock and allocated |
12517 | // so we should attempt an ephemeral GC again. |
12518 | soh_alloc_state = a_state_trigger_ephemeral_gc; |
12519 | } |
12520 | #endif //MULTIPLE_HEAPS |
12521 | else if (short_seg_end_p) |
12522 | { |
12523 | soh_alloc_state = a_state_cant_allocate; |
12524 | oom_r = oom_budget; |
12525 | } |
12526 | else |
12527 | { |
12528 | assert (commit_failed_p); |
12529 | soh_alloc_state = a_state_cant_allocate; |
12530 | oom_r = oom_cant_commit; |
12531 | } |
12532 | break; |
12533 | } |
12534 | case a_state_check_and_wait_for_bgc: |
12535 | { |
12536 | BOOL bgc_in_progress_p = FALSE; |
12537 | BOOL did_full_compacting_gc = FALSE; |
12538 | |
12539 | bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc, false); |
12540 | soh_alloc_state = (did_full_compacting_gc ? |
12541 | a_state_try_fit_after_cg : |
12542 | a_state_try_fit_after_bgc); |
12543 | break; |
12544 | } |
12545 | case a_state_trigger_ephemeral_gc: |
12546 | { |
12547 | BOOL commit_failed_p = FALSE; |
12548 | BOOL can_use_existing_p = FALSE; |
12549 | BOOL short_seg_end_p = FALSE; |
12550 | BOOL bgc_in_progress_p = FALSE; |
12551 | BOOL did_full_compacting_gc = FALSE; |
12552 | |
12553 | did_full_compacting_gc = trigger_ephemeral_gc (gr); |
12554 | if (did_full_compacting_gc) |
12555 | { |
12556 | soh_alloc_state = a_state_try_fit_after_cg; |
12557 | } |
12558 | else |
12559 | { |
12560 | can_use_existing_p = soh_try_fit (gen_number, size, acontext, |
12561 | align_const, &commit_failed_p, |
12562 | &short_seg_end_p); |
12563 | #ifdef BACKGROUND_GC |
12564 | bgc_in_progress_p = recursive_gc_sync::background_running_p(); |
12565 | #endif //BACKGROUND_GC |
12566 | |
12567 | if (can_use_existing_p) |
12568 | { |
12569 | soh_alloc_state = a_state_can_allocate; |
12570 | } |
12571 | else |
12572 | { |
12573 | if (short_seg_end_p) |
12574 | { |
12575 | if (should_expand_in_full_gc) |
12576 | { |
12577 | dprintf (2, ("gen1 GC wanted to expand!" )); |
12578 | soh_alloc_state = a_state_trigger_full_compact_gc; |
12579 | } |
12580 | else |
12581 | { |
12582 | soh_alloc_state = (bgc_in_progress_p ? |
12583 | a_state_check_and_wait_for_bgc : |
12584 | a_state_trigger_full_compact_gc); |
12585 | } |
12586 | } |
12587 | else if (commit_failed_p) |
12588 | { |
12589 | soh_alloc_state = a_state_trigger_full_compact_gc; |
12590 | } |
12591 | else |
12592 | { |
12593 | #ifdef MULTIPLE_HEAPS |
12594 | // some other threads already grabbed the more space lock and allocated |
12595 | // so we should attemp an ephemeral GC again. |
12596 | assert (gen0_allocated_after_gc_p); |
12597 | soh_alloc_state = a_state_trigger_ephemeral_gc; |
12598 | #else //MULTIPLE_HEAPS |
12599 | assert (!"shouldn't get here" ); |
12600 | #endif //MULTIPLE_HEAPS |
12601 | } |
12602 | } |
12603 | } |
12604 | break; |
12605 | } |
12606 | case a_state_trigger_2nd_ephemeral_gc: |
12607 | { |
12608 | BOOL commit_failed_p = FALSE; |
12609 | BOOL can_use_existing_p = FALSE; |
12610 | BOOL short_seg_end_p = FALSE; |
12611 | BOOL did_full_compacting_gc = FALSE; |
12612 | |
12613 | |
12614 | did_full_compacting_gc = trigger_ephemeral_gc (gr); |
12615 | |
12616 | if (did_full_compacting_gc) |
12617 | { |
12618 | soh_alloc_state = a_state_try_fit_after_cg; |
12619 | } |
12620 | else |
12621 | { |
12622 | can_use_existing_p = soh_try_fit (gen_number, size, acontext, |
12623 | align_const, &commit_failed_p, |
12624 | &short_seg_end_p); |
12625 | if (short_seg_end_p || commit_failed_p) |
12626 | { |
12627 | soh_alloc_state = a_state_trigger_full_compact_gc; |
12628 | } |
12629 | else |
12630 | { |
12631 | assert (can_use_existing_p); |
12632 | soh_alloc_state = a_state_can_allocate; |
12633 | } |
12634 | } |
12635 | break; |
12636 | } |
12637 | case a_state_trigger_full_compact_gc: |
12638 | { |
12639 | if (fgn_maxgen_percent) |
12640 | { |
12641 | dprintf (2, ("FGN: SOH doing last GC before we throw OOM" )); |
12642 | send_full_gc_notification (max_generation, FALSE); |
12643 | } |
12644 | |
12645 | BOOL got_full_compacting_gc = FALSE; |
12646 | |
12647 | got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, false); |
12648 | soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate); |
12649 | break; |
12650 | } |
12651 | default: |
12652 | { |
12653 | assert (!"Invalid state!" ); |
12654 | break; |
12655 | } |
12656 | } |
12657 | } |
12658 | |
12659 | exit: |
12660 | if (soh_alloc_state == a_state_cant_allocate) |
12661 | { |
12662 | assert (oom_r != oom_no_failure); |
12663 | handle_oom (heap_number, |
12664 | oom_r, |
12665 | size, |
12666 | heap_segment_allocated (ephemeral_heap_segment), |
12667 | heap_segment_reserved (ephemeral_heap_segment)); |
12668 | |
12669 | add_saved_spinlock_info (false, me_release, mt_alloc_small_cant); |
12670 | leave_spin_lock (&more_space_lock_soh); |
12671 | } |
12672 | |
12673 | return (soh_alloc_state == a_state_can_allocate); |
12674 | } |
12675 | |
12676 | #ifdef BACKGROUND_GC |
12677 | inline |
12678 | void gc_heap::bgc_track_loh_alloc() |
12679 | { |
12680 | if (current_c_gc_state == c_gc_state_planning) |
12681 | { |
12682 | Interlocked::Increment (&loh_alloc_thread_count); |
12683 | dprintf (3, ("h%d: inc lc: %d" , heap_number, loh_alloc_thread_count)); |
12684 | } |
12685 | } |
12686 | |
12687 | inline |
12688 | void gc_heap::bgc_untrack_loh_alloc() |
12689 | { |
12690 | if (current_c_gc_state == c_gc_state_planning) |
12691 | { |
12692 | Interlocked::Decrement (&loh_alloc_thread_count); |
12693 | dprintf (3, ("h%d: dec lc: %d" , heap_number, loh_alloc_thread_count)); |
12694 | } |
12695 | } |
12696 | |
12697 | BOOL gc_heap::bgc_loh_should_allocate() |
12698 | { |
12699 | size_t min_gc_size = dd_min_size (dynamic_data_of (max_generation + 1)); |
12700 | |
12701 | if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10)) |
12702 | { |
12703 | return TRUE; |
12704 | } |
12705 | |
12706 | if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size)) |
12707 | { |
12708 | if ((bgc_begin_loh_size / end_loh_size) > 2) |
12709 | { |
12710 | dprintf (3, ("alloc-ed too much before bgc started" )); |
12711 | } |
12712 | else |
12713 | { |
12714 | dprintf (3, ("alloc-ed too much after bgc started" )); |
12715 | } |
12716 | return FALSE; |
12717 | } |
12718 | else |
12719 | { |
12720 | bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10); |
12721 | return TRUE; |
12722 | } |
12723 | } |
12724 | #endif //BACKGROUND_GC |
12725 | |
12726 | size_t gc_heap::get_large_seg_size (size_t size) |
12727 | { |
12728 | size_t default_seg_size = min_loh_segment_size; |
12729 | #ifdef SEG_MAPPING_TABLE |
12730 | size_t align_size = default_seg_size; |
12731 | #else //SEG_MAPPING_TABLE |
12732 | size_t align_size = default_seg_size / 2; |
12733 | #endif //SEG_MAPPING_TABLE |
12734 | int align_const = get_alignment_constant (FALSE); |
12735 | size_t large_seg_size = align_on_page ( |
12736 | max (default_seg_size, |
12737 | ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE + |
12738 | align_size) / align_size * align_size))); |
12739 | return large_seg_size; |
12740 | } |
12741 | |
12742 | BOOL gc_heap::loh_get_new_seg (generation* gen, |
12743 | size_t size, |
12744 | int align_const, |
12745 | BOOL* did_full_compact_gc, |
12746 | oom_reason* oom_r) |
12747 | { |
12748 | UNREFERENCED_PARAMETER(gen); |
12749 | UNREFERENCED_PARAMETER(align_const); |
12750 | |
12751 | *did_full_compact_gc = FALSE; |
12752 | |
12753 | size_t seg_size = get_large_seg_size (size); |
12754 | |
12755 | heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc); |
12756 | |
12757 | if (new_seg) |
12758 | { |
12759 | loh_alloc_since_cg += seg_size; |
12760 | } |
12761 | else |
12762 | { |
12763 | *oom_r = oom_loh; |
12764 | } |
12765 | |
12766 | return (new_seg != 0); |
12767 | } |
12768 | |
12769 | BOOL gc_heap::retry_full_compact_gc (size_t size) |
12770 | { |
12771 | size_t seg_size = get_large_seg_size (size); |
12772 | |
12773 | if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size)) |
12774 | { |
12775 | return TRUE; |
12776 | } |
12777 | |
12778 | #ifdef MULTIPLE_HEAPS |
12779 | uint64_t total_alloc_size = 0; |
12780 | for (int i = 0; i < n_heaps; i++) |
12781 | { |
12782 | total_alloc_size += g_heaps[i]->loh_alloc_since_cg; |
12783 | } |
12784 | |
12785 | if (total_alloc_size >= (2 * (uint64_t)seg_size)) |
12786 | { |
12787 | return TRUE; |
12788 | } |
12789 | #endif //MULTIPLE_HEAPS |
12790 | |
12791 | return FALSE; |
12792 | } |
12793 | |
12794 | BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr, |
12795 | BOOL* did_full_compact_gc, |
12796 | bool loh_p) |
12797 | { |
12798 | BOOL bgc_in_progress = FALSE; |
12799 | *did_full_compact_gc = FALSE; |
12800 | #ifdef BACKGROUND_GC |
12801 | if (recursive_gc_sync::background_running_p()) |
12802 | { |
12803 | bgc_in_progress = TRUE; |
12804 | size_t last_full_compact_gc_count = get_full_compact_gc_count(); |
12805 | wait_for_background (awr, loh_p); |
12806 | size_t current_full_compact_gc_count = get_full_compact_gc_count(); |
12807 | if (current_full_compact_gc_count > last_full_compact_gc_count) |
12808 | { |
12809 | *did_full_compact_gc = TRUE; |
12810 | } |
12811 | } |
12812 | #endif //BACKGROUND_GC |
12813 | |
12814 | return bgc_in_progress; |
12815 | } |
12816 | |
12817 | BOOL gc_heap::loh_try_fit (int gen_number, |
12818 | size_t size, |
12819 | alloc_context* acontext, |
12820 | int align_const, |
12821 | BOOL* commit_failed_p, |
12822 | oom_reason* oom_r) |
12823 | { |
12824 | BOOL can_allocate = TRUE; |
12825 | |
12826 | if (!a_fit_free_list_large_p (size, acontext, align_const)) |
12827 | { |
12828 | can_allocate = loh_a_fit_segment_end_p (gen_number, size, |
12829 | acontext, align_const, |
12830 | commit_failed_p, oom_r); |
12831 | |
12832 | #ifdef BACKGROUND_GC |
12833 | if (can_allocate && recursive_gc_sync::background_running_p()) |
12834 | { |
12835 | bgc_loh_size_increased += size; |
12836 | } |
12837 | #endif //BACKGROUND_GC |
12838 | } |
12839 | #ifdef BACKGROUND_GC |
12840 | else |
12841 | { |
12842 | if (recursive_gc_sync::background_running_p()) |
12843 | { |
12844 | bgc_loh_allocated_in_free += size; |
12845 | } |
12846 | } |
12847 | #endif //BACKGROUND_GC |
12848 | |
12849 | return can_allocate; |
12850 | } |
12851 | |
12852 | BOOL gc_heap::trigger_full_compact_gc (gc_reason gr, |
12853 | oom_reason* oom_r, |
12854 | bool loh_p) |
12855 | { |
12856 | BOOL did_full_compact_gc = FALSE; |
12857 | |
12858 | size_t last_full_compact_gc_count = get_full_compact_gc_count(); |
12859 | |
12860 | // Set this so the next GC will be a full compacting GC. |
12861 | if (!last_gc_before_oom) |
12862 | { |
12863 | last_gc_before_oom = TRUE; |
12864 | } |
12865 | |
12866 | #ifdef BACKGROUND_GC |
12867 | if (recursive_gc_sync::background_running_p()) |
12868 | { |
12869 | wait_for_background (((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc), loh_p); |
12870 | dprintf (2, ("waited for BGC - done" )); |
12871 | } |
12872 | #endif //BACKGROUND_GC |
12873 | |
12874 | GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh; |
12875 | size_t current_full_compact_gc_count = get_full_compact_gc_count(); |
12876 | if (current_full_compact_gc_count > last_full_compact_gc_count) |
12877 | { |
12878 | dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)" , last_full_compact_gc_count, current_full_compact_gc_count)); |
12879 | assert (current_full_compact_gc_count > last_full_compact_gc_count); |
12880 | did_full_compact_gc = TRUE; |
12881 | goto exit; |
12882 | } |
12883 | |
12884 | dprintf (3, ("h%d full GC" , heap_number)); |
12885 | |
12886 | trigger_gc_for_alloc (max_generation, gr, msl, loh_p, mt_t_full_gc); |
12887 | |
12888 | current_full_compact_gc_count = get_full_compact_gc_count(); |
12889 | |
12890 | if (current_full_compact_gc_count == last_full_compact_gc_count) |
12891 | { |
12892 | dprintf (2, ("attempted to trigger a full compacting GC but didn't get it" )); |
12893 | // We requested a full GC but didn't get because of the elevation logic |
12894 | // which means we should fail. |
12895 | *oom_r = oom_unproductive_full_gc; |
12896 | } |
12897 | else |
12898 | { |
12899 | dprintf (3, ("h%d: T full compacting GC (%d->%d)" , |
12900 | heap_number, |
12901 | last_full_compact_gc_count, |
12902 | current_full_compact_gc_count)); |
12903 | |
12904 | assert (current_full_compact_gc_count > last_full_compact_gc_count); |
12905 | did_full_compact_gc = TRUE; |
12906 | } |
12907 | |
12908 | exit: |
12909 | return did_full_compact_gc; |
12910 | } |
12911 | |
12912 | #ifdef RECORD_LOH_STATE |
12913 | void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id) |
12914 | { |
12915 | // When the state is can_allocate we already have released the more |
12916 | // space lock. So we are not logging states here since this code |
12917 | // is not thread safe. |
12918 | if (loh_state_to_save != a_state_can_allocate) |
12919 | { |
12920 | last_loh_states[loh_state_index].alloc_state = loh_state_to_save; |
12921 | last_loh_states[loh_state_index].thread_id = thread_id; |
12922 | loh_state_index++; |
12923 | |
12924 | if (loh_state_index == max_saved_loh_states) |
12925 | { |
12926 | loh_state_index = 0; |
12927 | } |
12928 | |
12929 | assert (loh_state_index < max_saved_loh_states); |
12930 | } |
12931 | } |
12932 | #endif //RECORD_LOH_STATE |
12933 | |
12934 | BOOL gc_heap::allocate_large (int gen_number, |
12935 | size_t size, |
12936 | alloc_context* acontext, |
12937 | int align_const) |
12938 | { |
12939 | #ifdef BACKGROUND_GC |
12940 | if (recursive_gc_sync::background_running_p()) |
12941 | { |
12942 | background_loh_alloc_count++; |
12943 | //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0) |
12944 | { |
12945 | if (bgc_loh_should_allocate()) |
12946 | { |
12947 | if (!bgc_alloc_spin_loh) |
12948 | { |
12949 | add_saved_spinlock_info (true, me_release, mt_alloc_large); |
12950 | leave_spin_lock (&more_space_lock_loh); |
12951 | bool cooperative_mode = enable_preemptive(); |
12952 | GCToOSInterface::YieldThread (bgc_alloc_spin_loh); |
12953 | disable_preemptive (cooperative_mode); |
12954 | enter_spin_lock (&more_space_lock_loh); |
12955 | add_saved_spinlock_info (true, me_acquire, mt_alloc_large); |
12956 | dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh" , heap_number)); |
12957 | } |
12958 | } |
12959 | else |
12960 | { |
12961 | wait_for_background (awr_loh_alloc_during_bgc, true); |
12962 | } |
12963 | } |
12964 | } |
12965 | #endif //BACKGROUND_GC |
12966 | |
12967 | gc_reason gr = reason_oos_loh; |
12968 | generation* gen = generation_of (gen_number); |
12969 | oom_reason oom_r = oom_no_failure; |
12970 | size_t current_full_compact_gc_count = 0; |
12971 | |
12972 | // No variable values should be "carried over" from one state to the other. |
12973 | // That's why there are local variable for each state |
12974 | allocation_state loh_alloc_state = a_state_start; |
12975 | #ifdef RECORD_LOH_STATE |
12976 | EEThreadId current_thread_id; |
12977 | current_thread_id.SetToCurrentThread(); |
12978 | #endif //RECORD_LOH_STATE |
12979 | |
12980 | // If we can get a new seg it means allocation will succeed. |
12981 | while (1) |
12982 | { |
12983 | dprintf (3, ("[h%d]loh state is %s" , heap_number, allocation_state_str[loh_alloc_state])); |
12984 | |
12985 | #ifdef RECORD_LOH_STATE |
12986 | add_saved_loh_state (loh_alloc_state, current_thread_id); |
12987 | #endif //RECORD_LOH_STATE |
12988 | switch (loh_alloc_state) |
12989 | { |
12990 | case a_state_can_allocate: |
12991 | case a_state_cant_allocate: |
12992 | { |
12993 | goto exit; |
12994 | } |
12995 | case a_state_start: |
12996 | { |
12997 | loh_alloc_state = a_state_try_fit; |
12998 | break; |
12999 | } |
13000 | case a_state_try_fit: |
13001 | { |
13002 | BOOL commit_failed_p = FALSE; |
13003 | BOOL can_use_existing_p = FALSE; |
13004 | |
13005 | can_use_existing_p = loh_try_fit (gen_number, size, acontext, |
13006 | align_const, &commit_failed_p, &oom_r); |
13007 | loh_alloc_state = (can_use_existing_p ? |
13008 | a_state_can_allocate : |
13009 | (commit_failed_p ? |
13010 | a_state_trigger_full_compact_gc : |
13011 | a_state_acquire_seg)); |
13012 | assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0)); |
13013 | break; |
13014 | } |
13015 | case a_state_try_fit_new_seg: |
13016 | { |
13017 | BOOL commit_failed_p = FALSE; |
13018 | BOOL can_use_existing_p = FALSE; |
13019 | |
13020 | can_use_existing_p = loh_try_fit (gen_number, size, acontext, |
13021 | align_const, &commit_failed_p, &oom_r); |
13022 | // Even after we got a new seg it doesn't necessarily mean we can allocate, |
13023 | // another LOH allocating thread could have beat us to acquire the msl so |
13024 | // we need to try again. |
13025 | loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit); |
13026 | assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0)); |
13027 | break; |
13028 | } |
13029 | case a_state_try_fit_new_seg_after_cg: |
13030 | { |
13031 | BOOL commit_failed_p = FALSE; |
13032 | BOOL can_use_existing_p = FALSE; |
13033 | |
13034 | can_use_existing_p = loh_try_fit (gen_number, size, acontext, |
13035 | align_const, &commit_failed_p, &oom_r); |
13036 | // Even after we got a new seg it doesn't necessarily mean we can allocate, |
13037 | // another LOH allocating thread could have beat us to acquire the msl so |
13038 | // we need to try again. However, if we failed to commit, which means we |
13039 | // did have space on the seg, we bail right away 'cause we already did a |
13040 | // full compacting GC. |
13041 | loh_alloc_state = (can_use_existing_p ? |
13042 | a_state_can_allocate : |
13043 | (commit_failed_p ? |
13044 | a_state_cant_allocate : |
13045 | a_state_acquire_seg_after_cg)); |
13046 | assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0)); |
13047 | break; |
13048 | } |
13049 | case a_state_try_fit_no_seg: |
13050 | { |
13051 | BOOL commit_failed_p = FALSE; |
13052 | BOOL can_use_existing_p = FALSE; |
13053 | |
13054 | can_use_existing_p = loh_try_fit (gen_number, size, acontext, |
13055 | align_const, &commit_failed_p, &oom_r); |
13056 | loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate); |
13057 | assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0)); |
13058 | assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure)); |
13059 | break; |
13060 | } |
13061 | case a_state_try_fit_after_cg: |
13062 | { |
13063 | BOOL commit_failed_p = FALSE; |
13064 | BOOL can_use_existing_p = FALSE; |
13065 | |
13066 | can_use_existing_p = loh_try_fit (gen_number, size, acontext, |
13067 | align_const, &commit_failed_p, &oom_r); |
13068 | loh_alloc_state = (can_use_existing_p ? |
13069 | a_state_can_allocate : |
13070 | (commit_failed_p ? |
13071 | a_state_cant_allocate : |
13072 | a_state_acquire_seg_after_cg)); |
13073 | assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0)); |
13074 | break; |
13075 | } |
13076 | case a_state_try_fit_after_bgc: |
13077 | { |
13078 | BOOL commit_failed_p = FALSE; |
13079 | BOOL can_use_existing_p = FALSE; |
13080 | |
13081 | can_use_existing_p = loh_try_fit (gen_number, size, acontext, |
13082 | align_const, &commit_failed_p, &oom_r); |
13083 | loh_alloc_state = (can_use_existing_p ? |
13084 | a_state_can_allocate : |
13085 | (commit_failed_p ? |
13086 | a_state_trigger_full_compact_gc : |
13087 | a_state_acquire_seg_after_bgc)); |
13088 | assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0)); |
13089 | break; |
13090 | } |
13091 | case a_state_acquire_seg: |
13092 | { |
13093 | BOOL can_get_new_seg_p = FALSE; |
13094 | BOOL did_full_compacting_gc = FALSE; |
13095 | |
13096 | current_full_compact_gc_count = get_full_compact_gc_count(); |
13097 | |
13098 | can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); |
13099 | loh_alloc_state = (can_get_new_seg_p ? |
13100 | a_state_try_fit_new_seg : |
13101 | (did_full_compacting_gc ? |
13102 | a_state_check_retry_seg : |
13103 | a_state_check_and_wait_for_bgc)); |
13104 | break; |
13105 | } |
13106 | case a_state_acquire_seg_after_cg: |
13107 | { |
13108 | BOOL can_get_new_seg_p = FALSE; |
13109 | BOOL did_full_compacting_gc = FALSE; |
13110 | |
13111 | current_full_compact_gc_count = get_full_compact_gc_count(); |
13112 | |
13113 | can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); |
13114 | // Since we release the msl before we try to allocate a seg, other |
13115 | // threads could have allocated a bunch of segments before us so |
13116 | // we might need to retry. |
13117 | loh_alloc_state = (can_get_new_seg_p ? |
13118 | a_state_try_fit_new_seg_after_cg : |
13119 | a_state_check_retry_seg); |
13120 | break; |
13121 | } |
13122 | case a_state_acquire_seg_after_bgc: |
13123 | { |
13124 | BOOL can_get_new_seg_p = FALSE; |
13125 | BOOL did_full_compacting_gc = FALSE; |
13126 | |
13127 | current_full_compact_gc_count = get_full_compact_gc_count(); |
13128 | |
13129 | can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); |
13130 | loh_alloc_state = (can_get_new_seg_p ? |
13131 | a_state_try_fit_new_seg : |
13132 | (did_full_compacting_gc ? |
13133 | a_state_check_retry_seg : |
13134 | a_state_trigger_full_compact_gc)); |
13135 | assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure)); |
13136 | break; |
13137 | } |
13138 | case a_state_check_and_wait_for_bgc: |
13139 | { |
13140 | BOOL bgc_in_progress_p = FALSE; |
13141 | BOOL did_full_compacting_gc = FALSE; |
13142 | |
13143 | bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc, true); |
13144 | loh_alloc_state = (!bgc_in_progress_p ? |
13145 | a_state_trigger_full_compact_gc : |
13146 | (did_full_compacting_gc ? |
13147 | a_state_try_fit_after_cg : |
13148 | a_state_try_fit_after_bgc)); |
13149 | break; |
13150 | } |
13151 | case a_state_trigger_full_compact_gc: |
13152 | { |
13153 | if (fgn_maxgen_percent) |
13154 | { |
13155 | dprintf (2, ("FGN: LOH doing last GC before we throw OOM" )); |
13156 | send_full_gc_notification (max_generation, FALSE); |
13157 | } |
13158 | |
13159 | BOOL got_full_compacting_gc = FALSE; |
13160 | |
13161 | got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, true); |
13162 | loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate); |
13163 | assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure)); |
13164 | break; |
13165 | } |
13166 | case a_state_check_retry_seg: |
13167 | { |
13168 | BOOL should_retry_gc = retry_full_compact_gc (size); |
13169 | BOOL should_retry_get_seg = FALSE; |
13170 | if (!should_retry_gc) |
13171 | { |
13172 | size_t last_full_compact_gc_count = current_full_compact_gc_count; |
13173 | current_full_compact_gc_count = get_full_compact_gc_count(); |
13174 | |
13175 | if (current_full_compact_gc_count > (last_full_compact_gc_count + 1)) |
13176 | { |
13177 | should_retry_get_seg = TRUE; |
13178 | } |
13179 | } |
13180 | |
13181 | loh_alloc_state = (should_retry_gc ? |
13182 | a_state_trigger_full_compact_gc : |
13183 | (should_retry_get_seg ? |
13184 | a_state_acquire_seg_after_cg : |
13185 | a_state_cant_allocate)); |
13186 | assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure)); |
13187 | break; |
13188 | } |
13189 | default: |
13190 | { |
13191 | assert (!"Invalid state!" ); |
13192 | break; |
13193 | } |
13194 | } |
13195 | } |
13196 | |
13197 | exit: |
13198 | if (loh_alloc_state == a_state_cant_allocate) |
13199 | { |
13200 | assert (oom_r != oom_no_failure); |
13201 | handle_oom (heap_number, |
13202 | oom_r, |
13203 | size, |
13204 | 0, |
13205 | 0); |
13206 | |
13207 | add_saved_spinlock_info (true, me_release, mt_alloc_large_cant); |
13208 | leave_spin_lock (&more_space_lock_loh); |
13209 | } |
13210 | |
13211 | return (loh_alloc_state == a_state_can_allocate); |
13212 | } |
13213 | |
13214 | // BGC's final mark phase will acquire the msl, so release it here and re-acquire. |
13215 | void gc_heap::trigger_gc_for_alloc (int gen_number, gc_reason gr, |
13216 | GCSpinLock* msl, bool loh_p, |
13217 | msl_take_state take_state) |
13218 | { |
13219 | #ifdef BACKGROUND_GC |
13220 | if (loh_p) |
13221 | { |
13222 | add_saved_spinlock_info (loh_p, me_release, take_state); |
13223 | leave_spin_lock (msl); |
13224 | } |
13225 | #endif //BACKGROUND_GC |
13226 | |
13227 | vm_heap->GarbageCollectGeneration (gen_number, gr); |
13228 | |
13229 | #ifdef MULTIPLE_HEAPS |
13230 | if (!loh_p) |
13231 | { |
13232 | enter_spin_lock (msl); |
13233 | add_saved_spinlock_info (loh_p, me_acquire, take_state); |
13234 | } |
13235 | #endif //MULTIPLE_HEAPS |
13236 | |
13237 | #ifdef BACKGROUND_GC |
13238 | if (loh_p) |
13239 | { |
13240 | enter_spin_lock (msl); |
13241 | add_saved_spinlock_info (loh_p, me_acquire, take_state); |
13242 | } |
13243 | #endif //BACKGROUND_GC |
13244 | } |
13245 | |
13246 | int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size, |
13247 | int gen_number) |
13248 | { |
13249 | if (gc_heap::gc_started) |
13250 | { |
13251 | wait_for_gc_done(); |
13252 | return -1; |
13253 | } |
13254 | |
13255 | bool loh_p = (gen_number > 0); |
13256 | GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh; |
13257 | |
13258 | #ifdef SYNCHRONIZATION_STATS |
13259 | int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter(); |
13260 | #endif //SYNCHRONIZATION_STATS |
13261 | enter_spin_lock (msl); |
13262 | add_saved_spinlock_info (loh_p, me_acquire, mt_try_alloc); |
13263 | dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc" , heap_number)); |
13264 | #ifdef SYNCHRONIZATION_STATS |
13265 | int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start; |
13266 | total_msl_acquire += msl_acquire; |
13267 | num_msl_acquired++; |
13268 | if (msl_acquire > 200) |
13269 | { |
13270 | num_high_msl_acquire++; |
13271 | } |
13272 | else |
13273 | { |
13274 | num_low_msl_acquire++; |
13275 | } |
13276 | #endif //SYNCHRONIZATION_STATS |
13277 | |
13278 | /* |
13279 | // We are commenting this out 'cause we don't see the point - we already |
13280 | // have checked gc_started when we were acquiring the msl - no need to check |
13281 | // again. This complicates the logic in bgc_suspend_EE 'cause that one would |
13282 | // need to release msl which causes all sorts of trouble. |
13283 | if (gc_heap::gc_started) |
13284 | { |
13285 | #ifdef SYNCHRONIZATION_STATS |
13286 | good_suspension++; |
13287 | #endif //SYNCHRONIZATION_STATS |
13288 | BOOL fStress = (g_pConfig->GetGCStressLevel() & GCConfig::GCSTRESS_TRANSITION) != 0; |
13289 | if (!fStress) |
13290 | { |
13291 | //Rendez vous early (MP scaling issue) |
13292 | //dprintf (1, ("[%d]waiting for gc", heap_number)); |
13293 | wait_for_gc_done(); |
13294 | #ifdef MULTIPLE_HEAPS |
13295 | return -1; |
13296 | #endif //MULTIPLE_HEAPS |
13297 | } |
13298 | } |
13299 | */ |
13300 | |
13301 | dprintf (3, ("requested to allocate %d bytes on gen%d" , size, gen_number)); |
13302 | |
13303 | int align_const = get_alignment_constant (gen_number != (max_generation+1)); |
13304 | |
13305 | if (fgn_maxgen_percent) |
13306 | { |
13307 | check_for_full_gc (gen_number, size); |
13308 | } |
13309 | |
13310 | if (!(new_allocation_allowed (gen_number))) |
13311 | { |
13312 | if (fgn_maxgen_percent && (gen_number == 0)) |
13313 | { |
13314 | // We only check gen0 every so often, so take this opportunity to check again. |
13315 | check_for_full_gc (gen_number, size); |
13316 | } |
13317 | |
13318 | #ifdef BACKGROUND_GC |
13319 | wait_for_bgc_high_memory (awr_gen0_alloc, loh_p); |
13320 | #endif //BACKGROUND_GC |
13321 | |
13322 | #ifdef SYNCHRONIZATION_STATS |
13323 | bad_suspension++; |
13324 | #endif //SYNCHRONIZATION_STATS |
13325 | dprintf (/*100*/ 2, ("running out of budget on gen%d, gc" , gen_number)); |
13326 | |
13327 | if (!settings.concurrent || (gen_number == 0)) |
13328 | { |
13329 | trigger_gc_for_alloc (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh), |
13330 | msl, loh_p, mt_try_budget); |
13331 | } |
13332 | } |
13333 | |
13334 | BOOL can_allocate = ((gen_number == 0) ? |
13335 | allocate_small (gen_number, size, acontext, align_const) : |
13336 | allocate_large (gen_number, size, acontext, align_const)); |
13337 | |
13338 | if (can_allocate) |
13339 | { |
13340 | size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr; |
13341 | int etw_allocation_index = ((gen_number == 0) ? 0 : 1); |
13342 | |
13343 | etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes; |
13344 | |
13345 | |
13346 | if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick) |
13347 | { |
13348 | #ifdef FEATURE_REDHAWK |
13349 | FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index], |
13350 | (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh); |
13351 | #else |
13352 | // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled. |
13353 | // The ones that do are much less efficient. |
13354 | #if defined(FEATURE_EVENT_TRACE) |
13355 | if (EVENT_ENABLED(GCAllocationTick_V3)) |
13356 | { |
13357 | fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr); |
13358 | } |
13359 | #endif //FEATURE_EVENT_TRACE |
13360 | #endif //FEATURE_REDHAWK |
13361 | etw_allocation_running_amount[etw_allocation_index] = 0; |
13362 | } |
13363 | } |
13364 | |
13365 | return (int)can_allocate; |
13366 | } |
13367 | |
13368 | #ifdef MULTIPLE_HEAPS |
13369 | void gc_heap::balance_heaps (alloc_context* acontext) |
13370 | { |
13371 | |
13372 | if (acontext->alloc_count < 4) |
13373 | { |
13374 | if (acontext->alloc_count == 0) |
13375 | { |
13376 | acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) )); |
13377 | gc_heap* hp = acontext->get_home_heap()->pGenGCHeap; |
13378 | dprintf (3, ("First allocation for context %Ix on heap %d\n" , (size_t)acontext, (size_t)hp->heap_number)); |
13379 | acontext->set_alloc_heap(acontext->get_home_heap()); |
13380 | hp->alloc_context_count++; |
13381 | } |
13382 | } |
13383 | else |
13384 | { |
13385 | BOOL set_home_heap = FALSE; |
13386 | int hint = 0; |
13387 | |
13388 | if (heap_select::can_find_heap_fast()) |
13389 | { |
13390 | if (acontext->get_home_heap() != NULL) |
13391 | hint = acontext->get_home_heap()->pGenGCHeap->heap_number; |
13392 | if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0)) |
13393 | { |
13394 | set_home_heap = TRUE; |
13395 | } |
13396 | } |
13397 | else |
13398 | { |
13399 | // can't use gdt |
13400 | if ((acontext->alloc_count & 3) == 0) |
13401 | set_home_heap = TRUE; |
13402 | } |
13403 | |
13404 | if (set_home_heap) |
13405 | { |
13406 | /* |
13407 | // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this. |
13408 | if (n_heaps > MAX_SUPPORTED_CPUS) |
13409 | { |
13410 | // on machines with many processors cache affinity is really king, so don't even try |
13411 | // to balance on these. |
13412 | acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ); |
13413 | acontext->alloc_heap = acontext->home_heap; |
13414 | } |
13415 | else |
13416 | */ |
13417 | { |
13418 | gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap; |
13419 | |
13420 | dynamic_data* dd = org_hp->dynamic_data_of (0); |
13421 | ptrdiff_t org_size = dd_new_allocation (dd); |
13422 | int org_alloc_context_count; |
13423 | int max_alloc_context_count; |
13424 | gc_heap* max_hp; |
13425 | ptrdiff_t max_size; |
13426 | size_t delta = dd_min_size (dd)/4; |
13427 | |
13428 | int start, end, finish; |
13429 | heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end); |
13430 | finish = start + n_heaps; |
13431 | |
13432 | try_again: |
13433 | do |
13434 | { |
13435 | max_hp = org_hp; |
13436 | max_size = org_size + delta; |
13437 | acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) )); |
13438 | |
13439 | if (org_hp == acontext->get_home_heap()->pGenGCHeap) |
13440 | max_size = max_size + delta; |
13441 | |
13442 | org_alloc_context_count = org_hp->alloc_context_count; |
13443 | max_alloc_context_count = org_alloc_context_count; |
13444 | if (max_alloc_context_count > 1) |
13445 | max_size /= max_alloc_context_count; |
13446 | |
13447 | for (int i = start; i < end; i++) |
13448 | { |
13449 | gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap; |
13450 | dd = hp->dynamic_data_of (0); |
13451 | ptrdiff_t size = dd_new_allocation (dd); |
13452 | if (hp == acontext->get_home_heap()->pGenGCHeap) |
13453 | size = size + delta; |
13454 | int hp_alloc_context_count = hp->alloc_context_count; |
13455 | if (hp_alloc_context_count > 0) |
13456 | size /= (hp_alloc_context_count + 1); |
13457 | if (size > max_size) |
13458 | { |
13459 | max_hp = hp; |
13460 | max_size = size; |
13461 | max_alloc_context_count = hp_alloc_context_count; |
13462 | } |
13463 | } |
13464 | } |
13465 | while (org_alloc_context_count != org_hp->alloc_context_count || |
13466 | max_alloc_context_count != max_hp->alloc_context_count); |
13467 | |
13468 | if ((max_hp == org_hp) && (end < finish)) |
13469 | { |
13470 | start = end; end = finish; |
13471 | delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA. |
13472 | goto try_again; |
13473 | } |
13474 | |
13475 | if (max_hp != org_hp) |
13476 | { |
13477 | org_hp->alloc_context_count--; |
13478 | max_hp->alloc_context_count++; |
13479 | acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number)); |
13480 | if (!gc_thread_no_affinitize_p) |
13481 | { |
13482 | if (GCToOSInterface::CanEnableGCCPUGroups()) |
13483 | { //only set ideal processor when max_hp and org_hp are in the same cpu |
13484 | //group. DO NOT MOVE THREADS ACROSS CPU GROUPS |
13485 | uint16_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number); |
13486 | uint16_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number); |
13487 | if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough |
13488 | { |
13489 | uint16_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number); |
13490 | |
13491 | GCThreadAffinity affinity; |
13492 | affinity.Processor = group_proc_no; |
13493 | affinity.Group = org_gn; |
13494 | if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity)) |
13495 | { |
13496 | dprintf (3, ("Failed to set the ideal processor and group for heap %d." , |
13497 | org_hp->heap_number)); |
13498 | } |
13499 | } |
13500 | } |
13501 | else |
13502 | { |
13503 | uint16_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number); |
13504 | |
13505 | GCThreadAffinity affinity; |
13506 | affinity.Processor = proc_no; |
13507 | affinity.Group = GCThreadAffinity::None; |
13508 | |
13509 | if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity)) |
13510 | { |
13511 | dprintf (3, ("Failed to set the ideal processor for heap %d." , |
13512 | org_hp->heap_number)); |
13513 | } |
13514 | } |
13515 | } |
13516 | dprintf (3, ("Switching context %p (home heap %d) " , |
13517 | acontext, |
13518 | acontext->get_home_heap()->pGenGCHeap->heap_number)); |
13519 | dprintf (3, (" from heap %d (%Id free bytes, %d contexts) " , |
13520 | org_hp->heap_number, |
13521 | org_size, |
13522 | org_alloc_context_count)); |
13523 | dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n" , |
13524 | max_hp->heap_number, |
13525 | dd_new_allocation(max_hp->dynamic_data_of(0)), |
13526 | max_alloc_context_count)); |
13527 | } |
13528 | } |
13529 | } |
13530 | } |
13531 | acontext->alloc_count++; |
13532 | } |
13533 | |
13534 | gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t /*size*/) |
13535 | { |
13536 | gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap; |
13537 | //dprintf (1, ("LA: %Id", size)); |
13538 | |
13539 | //if (size > 128*1024) |
13540 | if (1) |
13541 | { |
13542 | dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1); |
13543 | |
13544 | ptrdiff_t org_size = dd_new_allocation (dd); |
13545 | gc_heap* max_hp; |
13546 | ptrdiff_t max_size; |
13547 | size_t delta = dd_min_size (dd) * 4; |
13548 | |
13549 | int start, end, finish; |
13550 | heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end); |
13551 | finish = start + n_heaps; |
13552 | |
13553 | try_again: |
13554 | { |
13555 | max_hp = org_hp; |
13556 | max_size = org_size + delta; |
13557 | dprintf (3, ("orig hp: %d, max size: %d" , |
13558 | org_hp->heap_number, |
13559 | max_size)); |
13560 | |
13561 | for (int i = start; i < end; i++) |
13562 | { |
13563 | gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap; |
13564 | dd = hp->dynamic_data_of (max_generation + 1); |
13565 | ptrdiff_t size = dd_new_allocation (dd); |
13566 | dprintf (3, ("hp: %d, size: %d" , |
13567 | hp->heap_number, |
13568 | size)); |
13569 | if (size > max_size) |
13570 | { |
13571 | max_hp = hp; |
13572 | max_size = size; |
13573 | dprintf (3, ("max hp: %d, max size: %d" , |
13574 | max_hp->heap_number, |
13575 | max_size)); |
13576 | } |
13577 | } |
13578 | } |
13579 | |
13580 | if ((max_hp == org_hp) && (end < finish)) |
13581 | { |
13582 | start = end; end = finish; |
13583 | delta = dd_min_size(dd) * 4; // Need to tuning delta |
13584 | goto try_again; |
13585 | } |
13586 | |
13587 | if (max_hp != org_hp) |
13588 | { |
13589 | dprintf (3, ("loh: %d(%Id)->%d(%Id)" , |
13590 | org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)), |
13591 | max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1)))); |
13592 | } |
13593 | |
13594 | return max_hp; |
13595 | } |
13596 | else |
13597 | { |
13598 | return org_hp; |
13599 | } |
13600 | } |
13601 | #endif //MULTIPLE_HEAPS |
13602 | |
13603 | BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size, |
13604 | int alloc_generation_number) |
13605 | { |
13606 | int status; |
13607 | do |
13608 | { |
13609 | #ifdef MULTIPLE_HEAPS |
13610 | if (alloc_generation_number == 0) |
13611 | { |
13612 | balance_heaps (acontext); |
13613 | status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number); |
13614 | } |
13615 | else |
13616 | { |
13617 | gc_heap* alloc_heap = balance_heaps_loh (acontext, size); |
13618 | status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number); |
13619 | } |
13620 | #else |
13621 | status = try_allocate_more_space (acontext, size, alloc_generation_number); |
13622 | #endif //MULTIPLE_HEAPS |
13623 | } |
13624 | while (status == -1); |
13625 | |
13626 | return (status != 0); |
13627 | } |
13628 | |
13629 | inline |
13630 | CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext) |
13631 | { |
13632 | size_t size = Align (jsize); |
13633 | assert (size >= Align (min_obj_size)); |
13634 | { |
13635 | retry: |
13636 | uint8_t* result = acontext->alloc_ptr; |
13637 | acontext->alloc_ptr+=size; |
13638 | if (acontext->alloc_ptr <= acontext->alloc_limit) |
13639 | { |
13640 | CObjectHeader* obj = (CObjectHeader*)result; |
13641 | assert (obj != 0); |
13642 | return obj; |
13643 | } |
13644 | else |
13645 | { |
13646 | acontext->alloc_ptr -= size; |
13647 | |
13648 | #ifdef _MSC_VER |
13649 | #pragma inline_depth(0) |
13650 | #endif //_MSC_VER |
13651 | |
13652 | if (! allocate_more_space (acontext, size, 0)) |
13653 | return 0; |
13654 | |
13655 | #ifdef _MSC_VER |
13656 | #pragma inline_depth(20) |
13657 | #endif //_MSC_VER |
13658 | |
13659 | goto retry; |
13660 | } |
13661 | } |
13662 | } |
13663 | |
13664 | inline |
13665 | CObjectHeader* gc_heap::try_fast_alloc (size_t jsize) |
13666 | { |
13667 | size_t size = Align (jsize); |
13668 | assert (size >= Align (min_obj_size)); |
13669 | generation* gen = generation_of (0); |
13670 | uint8_t* result = generation_allocation_pointer (gen); |
13671 | generation_allocation_pointer (gen) += size; |
13672 | if (generation_allocation_pointer (gen) <= |
13673 | generation_allocation_limit (gen)) |
13674 | { |
13675 | return (CObjectHeader*)result; |
13676 | } |
13677 | else |
13678 | { |
13679 | generation_allocation_pointer (gen) -= size; |
13680 | return 0; |
13681 | } |
13682 | } |
13683 | void gc_heap::leave_allocation_segment (generation* gen) |
13684 | { |
13685 | adjust_limit (0, 0, gen, max_generation); |
13686 | } |
13687 | |
13688 | void gc_heap::init_free_and_plug() |
13689 | { |
13690 | #ifdef FREE_USAGE_STATS |
13691 | for (int i = 0; i <= settings.condemned_generation; i++) |
13692 | { |
13693 | generation* gen = generation_of (i); |
13694 | memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces)); |
13695 | memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs)); |
13696 | memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces)); |
13697 | } |
13698 | |
13699 | if (settings.condemned_generation != max_generation) |
13700 | { |
13701 | for (int i = (settings.condemned_generation + 1); i <= max_generation; i++) |
13702 | { |
13703 | generation* gen = generation_of (i); |
13704 | memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs)); |
13705 | } |
13706 | } |
13707 | #endif //FREE_USAGE_STATS |
13708 | } |
13709 | |
13710 | void gc_heap::print_free_and_plug (const char* msg) |
13711 | { |
13712 | #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF) |
13713 | int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1)); |
13714 | for (int i = 0; i <= older_gen; i++) |
13715 | { |
13716 | generation* gen = generation_of (i); |
13717 | for (int j = 0; j < NUM_GEN_POWER2; j++) |
13718 | { |
13719 | if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0)) |
13720 | { |
13721 | dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id" , |
13722 | msg, |
13723 | heap_number, |
13724 | (settings.concurrent ? "BGC" : "GC" ), |
13725 | settings.gc_index, |
13726 | i, |
13727 | (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j])); |
13728 | } |
13729 | } |
13730 | } |
13731 | #else |
13732 | UNREFERENCED_PARAMETER(msg); |
13733 | #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF |
13734 | } |
13735 | |
13736 | void gc_heap::add_gen_plug (int gen_number, size_t plug_size) |
13737 | { |
13738 | #ifdef FREE_USAGE_STATS |
13739 | dprintf (3, ("adding plug size %Id to gen%d" , plug_size, gen_number)); |
13740 | generation* gen = generation_of (gen_number); |
13741 | size_t sz = BASE_GEN_SIZE; |
13742 | int i = 0; |
13743 | |
13744 | for (; i < NUM_GEN_POWER2; i++) |
13745 | { |
13746 | if (plug_size < sz) |
13747 | { |
13748 | break; |
13749 | } |
13750 | sz = sz * 2; |
13751 | } |
13752 | |
13753 | (gen->gen_plugs[i])++; |
13754 | #else |
13755 | UNREFERENCED_PARAMETER(gen_number); |
13756 | UNREFERENCED_PARAMETER(plug_size); |
13757 | #endif //FREE_USAGE_STATS |
13758 | } |
13759 | |
13760 | void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size) |
13761 | { |
13762 | #ifdef FREE_USAGE_STATS |
13763 | generation* gen = generation_of (gen_number); |
13764 | size_t sz = BASE_GEN_SIZE; |
13765 | int i = 0; |
13766 | |
13767 | for (; i < NUM_GEN_POWER2; i++) |
13768 | { |
13769 | if (free_size < sz) |
13770 | { |
13771 | break; |
13772 | } |
13773 | sz = sz * 2; |
13774 | } |
13775 | |
13776 | (gen->gen_current_pinned_free_spaces[i])++; |
13777 | generation_pinned_free_obj_space (gen) += free_size; |
13778 | dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)" , |
13779 | free_size, (i + 10), gen_number, |
13780 | generation_pinned_free_obj_space (gen), |
13781 | gen->gen_current_pinned_free_spaces[i])); |
13782 | #else |
13783 | UNREFERENCED_PARAMETER(gen_number); |
13784 | UNREFERENCED_PARAMETER(free_size); |
13785 | #endif //FREE_USAGE_STATS |
13786 | } |
13787 | |
13788 | void gc_heap::add_gen_free (int gen_number, size_t free_size) |
13789 | { |
13790 | #ifdef FREE_USAGE_STATS |
13791 | dprintf (3, ("adding free size %Id to gen%d" , free_size, gen_number)); |
13792 | generation* gen = generation_of (gen_number); |
13793 | size_t sz = BASE_GEN_SIZE; |
13794 | int i = 0; |
13795 | |
13796 | for (; i < NUM_GEN_POWER2; i++) |
13797 | { |
13798 | if (free_size < sz) |
13799 | { |
13800 | break; |
13801 | } |
13802 | sz = sz * 2; |
13803 | } |
13804 | |
13805 | (gen->gen_free_spaces[i])++; |
13806 | #else |
13807 | UNREFERENCED_PARAMETER(gen_number); |
13808 | UNREFERENCED_PARAMETER(free_size); |
13809 | #endif //FREE_USAGE_STATS |
13810 | } |
13811 | |
13812 | void gc_heap::remove_gen_free (int gen_number, size_t free_size) |
13813 | { |
13814 | #ifdef FREE_USAGE_STATS |
13815 | dprintf (3, ("removing free %Id from gen%d" , free_size, gen_number)); |
13816 | generation* gen = generation_of (gen_number); |
13817 | size_t sz = BASE_GEN_SIZE; |
13818 | int i = 0; |
13819 | |
13820 | for (; i < NUM_GEN_POWER2; i++) |
13821 | { |
13822 | if (free_size < sz) |
13823 | { |
13824 | break; |
13825 | } |
13826 | sz = sz * 2; |
13827 | } |
13828 | |
13829 | (gen->gen_free_spaces[i])--; |
13830 | #else |
13831 | UNREFERENCED_PARAMETER(gen_number); |
13832 | UNREFERENCED_PARAMETER(free_size); |
13833 | #endif //FREE_USAGE_STATS |
13834 | } |
13835 | |
13836 | uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size, |
13837 | int from_gen_number, |
13838 | uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL) |
13839 | { |
13840 | size = Align (size); |
13841 | assert (size >= Align (min_obj_size)); |
13842 | assert (from_gen_number < max_generation); |
13843 | assert (from_gen_number >= 0); |
13844 | assert (generation_of (from_gen_number + 1) == gen); |
13845 | |
13846 | allocator* gen_allocator = generation_allocator (gen); |
13847 | BOOL discard_p = gen_allocator->discard_if_no_fit_p (); |
13848 | int pad_in_front = ((old_loc != 0) && ((from_gen_number+1) != max_generation)) ? USE_PADDING_FRONT : 0; |
13849 | |
13850 | size_t real_size = size + Align (min_obj_size); |
13851 | if (pad_in_front) |
13852 | real_size += Align (min_obj_size); |
13853 | |
13854 | if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen), |
13855 | generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))) |
13856 | { |
13857 | size_t sz_list = gen_allocator->first_bucket_size(); |
13858 | for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++) |
13859 | { |
13860 | if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1))) |
13861 | { |
13862 | uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx); |
13863 | uint8_t* prev_free_item = 0; |
13864 | while (free_list != 0) |
13865 | { |
13866 | dprintf (3, ("considering free list %Ix" , (size_t)free_list)); |
13867 | |
13868 | size_t free_list_size = unused_array_size (free_list); |
13869 | |
13870 | if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size), |
13871 | old_loc, USE_PADDING_TAIL | pad_in_front)) |
13872 | { |
13873 | dprintf (4, ("F:%Ix-%Id" , |
13874 | (size_t)free_list, free_list_size)); |
13875 | |
13876 | gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p); |
13877 | generation_free_list_space (gen) -= free_list_size; |
13878 | remove_gen_free (gen->gen_num, free_list_size); |
13879 | |
13880 | adjust_limit (free_list, free_list_size, gen, from_gen_number+1); |
13881 | generation_allocate_end_seg_p (gen) = FALSE; |
13882 | goto finished; |
13883 | } |
13884 | // We do first fit on bucket 0 because we are not guaranteed to find a fit there. |
13885 | else if (discard_p || (a_l_idx == 0)) |
13886 | { |
13887 | dprintf (3, ("couldn't use this free area, discarding" )); |
13888 | generation_free_obj_space (gen) += free_list_size; |
13889 | |
13890 | gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE); |
13891 | generation_free_list_space (gen) -= free_list_size; |
13892 | remove_gen_free (gen->gen_num, free_list_size); |
13893 | } |
13894 | else |
13895 | { |
13896 | prev_free_item = free_list; |
13897 | } |
13898 | free_list = free_list_slot (free_list); |
13899 | } |
13900 | } |
13901 | sz_list = sz_list * 2; |
13902 | } |
13903 | //go back to the beginning of the segment list |
13904 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
13905 | if (seg != generation_allocation_segment (gen)) |
13906 | { |
13907 | leave_allocation_segment (gen); |
13908 | generation_allocation_segment (gen) = seg; |
13909 | } |
13910 | while (seg != ephemeral_heap_segment) |
13911 | { |
13912 | if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg), |
13913 | heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front)) |
13914 | { |
13915 | dprintf (3, ("using what's left in committed" )); |
13916 | adjust_limit (heap_segment_plan_allocated (seg), |
13917 | heap_segment_committed (seg) - |
13918 | heap_segment_plan_allocated (seg), |
13919 | gen, from_gen_number+1); |
13920 | generation_allocate_end_seg_p (gen) = TRUE; |
13921 | // dformat (t, 3, "Expanding segment allocation"); |
13922 | heap_segment_plan_allocated (seg) = |
13923 | heap_segment_committed (seg); |
13924 | goto finished; |
13925 | } |
13926 | else |
13927 | { |
13928 | if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg), |
13929 | heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) && |
13930 | grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)) |
13931 | { |
13932 | dprintf (3, ("using what's left in reserved" )); |
13933 | adjust_limit (heap_segment_plan_allocated (seg), |
13934 | heap_segment_committed (seg) - |
13935 | heap_segment_plan_allocated (seg), |
13936 | gen, from_gen_number+1); |
13937 | generation_allocate_end_seg_p (gen) = TRUE; |
13938 | heap_segment_plan_allocated (seg) = |
13939 | heap_segment_committed (seg); |
13940 | |
13941 | goto finished; |
13942 | } |
13943 | else |
13944 | { |
13945 | leave_allocation_segment (gen); |
13946 | heap_segment* next_seg = heap_segment_next_rw (seg); |
13947 | if (next_seg) |
13948 | { |
13949 | dprintf (3, ("getting next segment" )); |
13950 | generation_allocation_segment (gen) = next_seg; |
13951 | generation_allocation_pointer (gen) = heap_segment_mem (next_seg); |
13952 | generation_allocation_limit (gen) = generation_allocation_pointer (gen); |
13953 | } |
13954 | else |
13955 | { |
13956 | size = 0; |
13957 | goto finished; |
13958 | } |
13959 | } |
13960 | } |
13961 | seg = generation_allocation_segment (gen); |
13962 | } |
13963 | //No need to fix the last region. Will be done later |
13964 | size = 0; |
13965 | goto finished; |
13966 | } |
13967 | finished: |
13968 | if (0 == size) |
13969 | { |
13970 | return 0; |
13971 | } |
13972 | else |
13973 | { |
13974 | uint8_t* result = generation_allocation_pointer (gen); |
13975 | size_t pad = 0; |
13976 | |
13977 | #ifdef SHORT_PLUGS |
13978 | if ((pad_in_front & USE_PADDING_FRONT) && |
13979 | (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) || |
13980 | ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH))) |
13981 | { |
13982 | pad = Align (min_obj_size); |
13983 | set_plug_padded (old_loc); |
13984 | } |
13985 | #endif //SHORT_PLUGS |
13986 | |
13987 | #ifdef FEATURE_STRUCTALIGN |
13988 | _ASSERTE(!old_loc || alignmentOffset != 0); |
13989 | _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT); |
13990 | if (old_loc != 0) |
13991 | { |
13992 | size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset); |
13993 | set_node_aligninfo (old_loc, requiredAlignment, pad1); |
13994 | pad += pad1; |
13995 | } |
13996 | #else // FEATURE_STRUCTALIGN |
13997 | if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad))) |
13998 | { |
13999 | pad += switch_alignment_size (is_plug_padded (old_loc)); |
14000 | set_node_realigned (old_loc); |
14001 | dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix" , |
14002 | (size_t)old_loc, (size_t)(result+pad))); |
14003 | assert (same_large_alignment_p (result + pad, old_loc)); |
14004 | } |
14005 | #endif // FEATURE_STRUCTALIGN |
14006 | dprintf (3, ("Allocate %Id bytes" , size)); |
14007 | |
14008 | if ((old_loc == 0) || (pad != 0)) |
14009 | { |
14010 | //allocating a non plug or a gap, so reset the start region |
14011 | generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen); |
14012 | } |
14013 | |
14014 | generation_allocation_pointer (gen) += size + pad; |
14015 | assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen)); |
14016 | if (generation_allocate_end_seg_p (gen)) |
14017 | { |
14018 | generation_end_seg_allocated (gen) += size; |
14019 | } |
14020 | else |
14021 | { |
14022 | generation_free_list_allocated (gen) += size; |
14023 | } |
14024 | generation_allocation_size (gen) += size; |
14025 | |
14026 | dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix" , |
14027 | generation_allocation_pointer (gen), generation_allocation_limit (gen), |
14028 | generation_allocation_context_start_region (gen))); |
14029 | |
14030 | return result + pad;; |
14031 | } |
14032 | } |
14033 | |
14034 | void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen) |
14035 | { |
14036 | //make sure that every generation has a planned allocation start |
14037 | int gen_number = max_generation - 1; |
14038 | while (gen_number>= 0) |
14039 | { |
14040 | generation* gen = generation_of (gen_number); |
14041 | if (0 == generation_plan_allocation_start (gen)) |
14042 | { |
14043 | realloc_plan_generation_start (gen, consing_gen); |
14044 | |
14045 | assert (generation_plan_allocation_start (gen)); |
14046 | } |
14047 | gen_number--; |
14048 | } |
14049 | |
14050 | // now we know the planned allocation size |
14051 | size_t size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen)); |
14052 | heap_segment* seg = generation_allocation_segment (consing_gen); |
14053 | if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg)) |
14054 | { |
14055 | if (size != 0) |
14056 | { |
14057 | heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen); |
14058 | } |
14059 | } |
14060 | else |
14061 | { |
14062 | assert (settings.condemned_generation == max_generation); |
14063 | uint8_t* first_address = generation_allocation_limit (consing_gen); |
14064 | //look through the pinned plugs for relevant ones. |
14065 | //Look for the right pinned plug to start from. |
14066 | size_t mi = 0; |
14067 | mark* m = 0; |
14068 | while (mi != mark_stack_tos) |
14069 | { |
14070 | m = pinned_plug_of (mi); |
14071 | if ((pinned_plug (m) == first_address)) |
14072 | break; |
14073 | else |
14074 | mi++; |
14075 | } |
14076 | assert (mi != mark_stack_tos); |
14077 | pinned_len (m) = size; |
14078 | } |
14079 | } |
14080 | |
14081 | //tododefrag optimize for new segment (plan_allocated == mem) |
14082 | uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen, |
14083 | size_t size, |
14084 | BOOL& adjacentp, |
14085 | uint8_t* old_loc, |
14086 | #ifdef SHORT_PLUGS |
14087 | BOOL set_padding_on_saved_p, |
14088 | mark* pinned_plug_entry, |
14089 | #endif //SHORT_PLUGS |
14090 | BOOL consider_bestfit, |
14091 | int active_new_gen_number |
14092 | REQD_ALIGN_AND_OFFSET_DCL) |
14093 | { |
14094 | UNREFERENCED_PARAMETER(active_new_gen_number); |
14095 | dprintf (3, ("aie: P: %Ix, size: %Ix" , old_loc, size)); |
14096 | |
14097 | size = Align (size); |
14098 | assert (size >= Align (min_obj_size)); |
14099 | int pad_in_front = ((old_loc != 0) && (active_new_gen_number != max_generation)) ? USE_PADDING_FRONT : 0; |
14100 | |
14101 | if (consider_bestfit && use_bestfit) |
14102 | { |
14103 | assert (bestfit_seg); |
14104 | dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id" , |
14105 | old_loc, size)); |
14106 | return bestfit_seg->fit (old_loc, |
14107 | #ifdef SHORT_PLUGS |
14108 | set_padding_on_saved_p, |
14109 | pinned_plug_entry, |
14110 | #endif //SHORT_PLUGS |
14111 | size REQD_ALIGN_AND_OFFSET_ARG); |
14112 | } |
14113 | |
14114 | heap_segment* seg = generation_allocation_segment (gen); |
14115 | |
14116 | if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen), |
14117 | generation_allocation_limit (gen), old_loc, |
14118 | ((generation_allocation_limit (gen) != |
14119 | heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front))) |
14120 | { |
14121 | dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix" , generation_allocation_pointer (gen), |
14122 | generation_allocation_limit (gen))); |
14123 | |
14124 | adjacentp = FALSE; |
14125 | uint8_t* first_address = (generation_allocation_limit (gen) ? |
14126 | generation_allocation_limit (gen) : |
14127 | heap_segment_mem (seg)); |
14128 | assert (in_range_for_segment (first_address, seg)); |
14129 | |
14130 | uint8_t* end_address = heap_segment_reserved (seg); |
14131 | |
14132 | dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix" , |
14133 | first_address, generation_allocation_limit (gen), end_address)); |
14134 | |
14135 | size_t mi = 0; |
14136 | mark* m = 0; |
14137 | |
14138 | if (heap_segment_allocated (seg) != heap_segment_mem (seg)) |
14139 | { |
14140 | assert (settings.condemned_generation == max_generation); |
14141 | //look through the pinned plugs for relevant ones. |
14142 | //Look for the right pinned plug to start from. |
14143 | while (mi != mark_stack_tos) |
14144 | { |
14145 | m = pinned_plug_of (mi); |
14146 | if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)) |
14147 | { |
14148 | dprintf (3, ("aie: found pin: %Ix" , pinned_plug (m))); |
14149 | break; |
14150 | } |
14151 | else |
14152 | mi++; |
14153 | } |
14154 | if (mi != mark_stack_tos) |
14155 | { |
14156 | //fix old free list. |
14157 | size_t hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen)); |
14158 | { |
14159 | dprintf(3,("gc filling up hole" )); |
14160 | ptrdiff_t mi1 = (ptrdiff_t)mi; |
14161 | while ((mi1 >= 0) && |
14162 | (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen))) |
14163 | { |
14164 | dprintf (3, ("aie: checking pin %Ix" , pinned_plug (pinned_plug_of(mi1)))); |
14165 | mi1--; |
14166 | } |
14167 | if (mi1 >= 0) |
14168 | { |
14169 | size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1)); |
14170 | pinned_len (pinned_plug_of(mi1)) = hsize; |
14171 | dprintf (3, ("changing %Ix len %Ix->%Ix" , |
14172 | pinned_plug (pinned_plug_of(mi1)), |
14173 | saved_pinned_len, pinned_len (pinned_plug_of(mi1)))); |
14174 | } |
14175 | } |
14176 | } |
14177 | } |
14178 | else |
14179 | { |
14180 | assert (generation_allocation_limit (gen) == |
14181 | generation_allocation_pointer (gen)); |
14182 | mi = mark_stack_tos; |
14183 | } |
14184 | |
14185 | while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg)) |
14186 | { |
14187 | size_t len = pinned_len (m); |
14188 | uint8_t* free_list = (pinned_plug (m) - len); |
14189 | dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)" , |
14190 | free_list, (free_list + len), len)); |
14191 | if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front)) |
14192 | { |
14193 | dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id" , |
14194 | (size_t)free_list, len)); |
14195 | { |
14196 | generation_allocation_pointer (gen) = free_list; |
14197 | generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen); |
14198 | generation_allocation_limit (gen) = (free_list + len); |
14199 | } |
14200 | goto allocate_in_free; |
14201 | } |
14202 | mi++; |
14203 | m = pinned_plug_of (mi); |
14204 | } |
14205 | |
14206 | //switch to the end of the segment. |
14207 | generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg); |
14208 | generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen); |
14209 | heap_segment_plan_allocated (seg) = heap_segment_committed (seg); |
14210 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
14211 | dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)" , |
14212 | generation_allocation_pointer (gen), generation_allocation_limit (gen), |
14213 | (generation_allocation_limit (gen) - generation_allocation_pointer (gen)))); |
14214 | |
14215 | if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen), |
14216 | generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)) |
14217 | { |
14218 | dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc" , generation_allocation_pointer (gen), |
14219 | generation_allocation_limit (gen))); |
14220 | assert (!"Can't allocate if no free space" ); |
14221 | return 0; |
14222 | } |
14223 | } |
14224 | else |
14225 | { |
14226 | adjacentp = TRUE; |
14227 | } |
14228 | |
14229 | allocate_in_free: |
14230 | { |
14231 | uint8_t* result = generation_allocation_pointer (gen); |
14232 | size_t pad = 0; |
14233 | |
14234 | #ifdef SHORT_PLUGS |
14235 | if ((pad_in_front & USE_PADDING_FRONT) && |
14236 | (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) || |
14237 | ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH))) |
14238 | |
14239 | { |
14240 | pad = Align (min_obj_size); |
14241 | set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry); |
14242 | } |
14243 | #endif //SHORT_PLUGS |
14244 | |
14245 | #ifdef FEATURE_STRUCTALIGN |
14246 | _ASSERTE(!old_loc || alignmentOffset != 0); |
14247 | _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT); |
14248 | if (old_loc != 0) |
14249 | { |
14250 | size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset); |
14251 | set_node_aligninfo (old_loc, requiredAlignment, pad1); |
14252 | pad += pad1; |
14253 | adjacentp = FALSE; |
14254 | } |
14255 | #else // FEATURE_STRUCTALIGN |
14256 | if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad))) |
14257 | { |
14258 | pad += switch_alignment_size (is_plug_padded (old_loc)); |
14259 | set_node_realigned (old_loc); |
14260 | dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix" , |
14261 | (size_t)old_loc, (size_t)(result+pad))); |
14262 | assert (same_large_alignment_p (result + pad, old_loc)); |
14263 | adjacentp = FALSE; |
14264 | } |
14265 | #endif // FEATURE_STRUCTALIGN |
14266 | |
14267 | if ((old_loc == 0) || (pad != 0)) |
14268 | { |
14269 | //allocating a non plug or a gap, so reset the start region |
14270 | generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen); |
14271 | } |
14272 | |
14273 | generation_allocation_pointer (gen) += size + pad; |
14274 | assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen)); |
14275 | dprintf (3, ("Allocated in expanded heap %Ix:%Id" , (size_t)(result+pad), size)); |
14276 | |
14277 | dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix" , |
14278 | generation_allocation_pointer (gen), generation_allocation_limit (gen), |
14279 | generation_allocation_context_start_region (gen))); |
14280 | |
14281 | return result + pad; |
14282 | } |
14283 | } |
14284 | |
14285 | generation* gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen) |
14286 | { |
14287 | heap_segment* seg = generation_allocation_segment (consing_gen); |
14288 | if (seg != ephemeral_heap_segment) |
14289 | { |
14290 | assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg)); |
14291 | assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg)); |
14292 | |
14293 | //fix the allocated size of the segment. |
14294 | heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen); |
14295 | |
14296 | generation* new_consing_gen = generation_of (max_generation - 1); |
14297 | generation_allocation_pointer (new_consing_gen) = |
14298 | heap_segment_mem (ephemeral_heap_segment); |
14299 | generation_allocation_limit (new_consing_gen) = |
14300 | generation_allocation_pointer (new_consing_gen); |
14301 | generation_allocation_context_start_region (new_consing_gen) = |
14302 | generation_allocation_pointer (new_consing_gen); |
14303 | generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment; |
14304 | |
14305 | return new_consing_gen; |
14306 | } |
14307 | else |
14308 | return consing_gen; |
14309 | } |
14310 | |
14311 | uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen, |
14312 | size_t size, |
14313 | int from_gen_number, |
14314 | #ifdef SHORT_PLUGS |
14315 | BOOL* convert_to_pinned_p, |
14316 | uint8_t* next_pinned_plug, |
14317 | heap_segment* current_seg, |
14318 | #endif //SHORT_PLUGS |
14319 | uint8_t* old_loc |
14320 | REQD_ALIGN_AND_OFFSET_DCL) |
14321 | { |
14322 | // Make sure that the youngest generation gap hasn't been allocated |
14323 | if (settings.promotion) |
14324 | { |
14325 | assert (generation_plan_allocation_start (youngest_generation) == 0); |
14326 | } |
14327 | |
14328 | size = Align (size); |
14329 | assert (size >= Align (min_obj_size)); |
14330 | int to_gen_number = from_gen_number; |
14331 | if (from_gen_number != (int)max_generation) |
14332 | { |
14333 | to_gen_number = from_gen_number + (settings.promotion ? 1 : 0); |
14334 | } |
14335 | |
14336 | dprintf (3, ("aic gen%d: s: %Id" , gen->gen_num, size)); |
14337 | |
14338 | int pad_in_front = ((old_loc != 0) && (to_gen_number != max_generation)) ? USE_PADDING_FRONT : 0; |
14339 | |
14340 | if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion) |
14341 | { |
14342 | generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size; |
14343 | generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size; |
14344 | } |
14345 | retry: |
14346 | { |
14347 | heap_segment* seg = generation_allocation_segment (gen); |
14348 | if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen), |
14349 | generation_allocation_limit (gen), old_loc, |
14350 | ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front))) |
14351 | { |
14352 | if ((! (pinned_plug_que_empty_p()) && |
14353 | (generation_allocation_limit (gen) == |
14354 | pinned_plug (oldest_pin())))) |
14355 | { |
14356 | size_t entry = deque_pinned_plug(); |
14357 | mark* pinned_plug_entry = pinned_plug_of (entry); |
14358 | size_t len = pinned_len (pinned_plug_entry); |
14359 | uint8_t* plug = pinned_plug (pinned_plug_entry); |
14360 | set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen)); |
14361 | |
14362 | #ifdef FREE_USAGE_STATS |
14363 | generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen); |
14364 | dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id" , |
14365 | generation_allocated_since_last_pin (gen), |
14366 | plug, |
14367 | generation_allocated_in_pinned_free (gen))); |
14368 | generation_allocated_since_last_pin (gen) = 0; |
14369 | |
14370 | add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry))); |
14371 | #endif //FREE_USAGE_STATS |
14372 | |
14373 | dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix" , |
14374 | mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry)))); |
14375 | |
14376 | assert(mark_stack_array[entry].len == 0 || |
14377 | mark_stack_array[entry].len >= Align(min_obj_size)); |
14378 | generation_allocation_pointer (gen) = plug + len; |
14379 | generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen); |
14380 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
14381 | set_allocator_next_pin (gen); |
14382 | |
14383 | //Add the size of the pinned plug to the right pinned allocations |
14384 | //find out which gen this pinned plug came from |
14385 | int frgn = object_gennum (plug); |
14386 | if ((frgn != (int)max_generation) && settings.promotion) |
14387 | { |
14388 | generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len; |
14389 | int togn = object_gennum_plan (plug); |
14390 | if (frgn < togn) |
14391 | { |
14392 | generation_pinned_allocation_compact_size (generation_of (togn)) += len; |
14393 | } |
14394 | } |
14395 | goto retry; |
14396 | } |
14397 | |
14398 | if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg)) |
14399 | { |
14400 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
14401 | dprintf (3, ("changed limit to plan alloc: %Ix" , generation_allocation_limit (gen))); |
14402 | } |
14403 | else |
14404 | { |
14405 | if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg)) |
14406 | { |
14407 | heap_segment_plan_allocated (seg) = heap_segment_committed (seg); |
14408 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
14409 | dprintf (3, ("changed limit to commit: %Ix" , generation_allocation_limit (gen))); |
14410 | } |
14411 | else |
14412 | { |
14413 | #ifndef RESPECT_LARGE_ALIGNMENT |
14414 | assert (gen != youngest_generation); |
14415 | #endif //RESPECT_LARGE_ALIGNMENT |
14416 | |
14417 | if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen), |
14418 | heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) && |
14419 | (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc, |
14420 | size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))) |
14421 | { |
14422 | dprintf (3, ("Expanded segment allocation by committing more memory" )); |
14423 | heap_segment_plan_allocated (seg) = heap_segment_committed (seg); |
14424 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
14425 | } |
14426 | else |
14427 | { |
14428 | heap_segment* next_seg = heap_segment_next (seg); |
14429 | assert (generation_allocation_pointer (gen)>= |
14430 | heap_segment_mem (seg)); |
14431 | // Verify that all pinned plugs for this segment are consumed |
14432 | if (!pinned_plug_que_empty_p() && |
14433 | ((pinned_plug (oldest_pin()) < |
14434 | heap_segment_allocated (seg)) && |
14435 | (pinned_plug (oldest_pin()) >= |
14436 | generation_allocation_pointer (gen)))) |
14437 | { |
14438 | LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation" , |
14439 | pinned_plug (oldest_pin()))); |
14440 | FATAL_GC_ERROR(); |
14441 | } |
14442 | assert (generation_allocation_pointer (gen)>= |
14443 | heap_segment_mem (seg)); |
14444 | assert (generation_allocation_pointer (gen)<= |
14445 | heap_segment_committed (seg)); |
14446 | heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen); |
14447 | |
14448 | if (next_seg) |
14449 | { |
14450 | generation_allocation_segment (gen) = next_seg; |
14451 | generation_allocation_pointer (gen) = heap_segment_mem (next_seg); |
14452 | generation_allocation_limit (gen) = generation_allocation_pointer (gen); |
14453 | generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen); |
14454 | } |
14455 | else |
14456 | { |
14457 | return 0; //should only happen during allocation of generation 0 gap |
14458 | // in that case we are going to grow the heap anyway |
14459 | } |
14460 | } |
14461 | } |
14462 | } |
14463 | set_allocator_next_pin (gen); |
14464 | |
14465 | goto retry; |
14466 | } |
14467 | } |
14468 | |
14469 | { |
14470 | assert (generation_allocation_pointer (gen)>= |
14471 | heap_segment_mem (generation_allocation_segment (gen))); |
14472 | uint8_t* result = generation_allocation_pointer (gen); |
14473 | size_t pad = 0; |
14474 | #ifdef SHORT_PLUGS |
14475 | if ((pad_in_front & USE_PADDING_FRONT) && |
14476 | (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) || |
14477 | ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH))) |
14478 | { |
14479 | ptrdiff_t dist = old_loc - result; |
14480 | if (dist == 0) |
14481 | { |
14482 | dprintf (3, ("old alloc: %Ix, same as new alloc, not padding" , old_loc)); |
14483 | pad = 0; |
14484 | } |
14485 | else |
14486 | { |
14487 | if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size))) |
14488 | { |
14489 | dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen" , old_loc, dist)); |
14490 | FATAL_GC_ERROR(); |
14491 | } |
14492 | |
14493 | pad = Align (min_obj_size); |
14494 | set_plug_padded (old_loc); |
14495 | } |
14496 | } |
14497 | #endif //SHORT_PLUGS |
14498 | #ifdef FEATURE_STRUCTALIGN |
14499 | _ASSERTE(!old_loc || alignmentOffset != 0); |
14500 | _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT); |
14501 | if ((old_loc != 0)) |
14502 | { |
14503 | size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset); |
14504 | set_node_aligninfo (old_loc, requiredAlignment, pad1); |
14505 | pad += pad1; |
14506 | } |
14507 | #else // FEATURE_STRUCTALIGN |
14508 | if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad))) |
14509 | { |
14510 | pad += switch_alignment_size (is_plug_padded (old_loc)); |
14511 | set_node_realigned(old_loc); |
14512 | dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix" , |
14513 | (size_t)old_loc, (size_t)(result+pad))); |
14514 | assert (same_large_alignment_p (result + pad, old_loc)); |
14515 | } |
14516 | #endif // FEATURE_STRUCTALIGN |
14517 | |
14518 | #ifdef SHORT_PLUGS |
14519 | if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg)) |
14520 | { |
14521 | assert (old_loc != 0); |
14522 | ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad)); |
14523 | assert (dist_to_next_pin >= 0); |
14524 | |
14525 | if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size))) |
14526 | { |
14527 | dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP" , |
14528 | old_loc, |
14529 | generation_allocation_pointer (gen), |
14530 | generation_allocation_limit (gen), |
14531 | next_pinned_plug, |
14532 | size, |
14533 | dist_to_next_pin)); |
14534 | clear_plug_padded (old_loc); |
14535 | pad = 0; |
14536 | *convert_to_pinned_p = TRUE; |
14537 | record_interesting_data_point (idp_converted_pin); |
14538 | |
14539 | return 0; |
14540 | } |
14541 | } |
14542 | #endif //SHORT_PLUGS |
14543 | |
14544 | if ((old_loc == 0) || (pad != 0)) |
14545 | { |
14546 | //allocating a non plug or a gap, so reset the start region |
14547 | generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen); |
14548 | } |
14549 | |
14550 | generation_allocation_pointer (gen) += size + pad; |
14551 | assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen)); |
14552 | |
14553 | #ifdef FREE_USAGE_STATS |
14554 | generation_allocated_since_last_pin (gen) += size; |
14555 | #endif //FREE_USAGE_STATS |
14556 | |
14557 | dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix" , |
14558 | generation_allocation_pointer (gen), generation_allocation_limit (gen), |
14559 | generation_allocation_context_start_region (gen))); |
14560 | |
14561 | assert (result + pad); |
14562 | return result + pad; |
14563 | } |
14564 | } |
14565 | |
14566 | inline int power (int x, int y) |
14567 | { |
14568 | int z = 1; |
14569 | for (int i = 0; i < y; i++) |
14570 | { |
14571 | z = z*x; |
14572 | } |
14573 | return z; |
14574 | } |
14575 | |
14576 | int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, |
14577 | int initial_gen, |
14578 | int current_gen, |
14579 | BOOL* blocking_collection_p |
14580 | STRESS_HEAP_ARG(int n_original)) |
14581 | { |
14582 | int n = current_gen; |
14583 | #ifdef MULTIPLE_HEAPS |
14584 | BOOL joined_last_gc_before_oom = FALSE; |
14585 | for (int i = 0; i < n_heaps; i++) |
14586 | { |
14587 | if (g_heaps[i]->last_gc_before_oom) |
14588 | { |
14589 | dprintf (GTC_LOG, ("h%d is setting blocking to TRUE" , i)); |
14590 | joined_last_gc_before_oom = TRUE; |
14591 | break; |
14592 | } |
14593 | } |
14594 | #else |
14595 | BOOL joined_last_gc_before_oom = last_gc_before_oom; |
14596 | #endif //MULTIPLE_HEAPS |
14597 | |
14598 | if (joined_last_gc_before_oom && settings.pause_mode != pause_low_latency) |
14599 | { |
14600 | assert (*blocking_collection_p); |
14601 | } |
14602 | |
14603 | if (should_evaluate_elevation && (n == max_generation)) |
14604 | { |
14605 | dprintf (GTC_LOG, ("lock: %d(%d)" , |
14606 | (settings.should_lock_elevation ? 1 : 0), |
14607 | settings.elevation_locked_count)); |
14608 | |
14609 | if (settings.should_lock_elevation) |
14610 | { |
14611 | settings.elevation_locked_count++; |
14612 | if (settings.elevation_locked_count == 6) |
14613 | { |
14614 | settings.elevation_locked_count = 0; |
14615 | } |
14616 | else |
14617 | { |
14618 | n = max_generation - 1; |
14619 | settings.elevation_reduced = TRUE; |
14620 | } |
14621 | } |
14622 | else |
14623 | { |
14624 | settings.elevation_locked_count = 0; |
14625 | } |
14626 | } |
14627 | else |
14628 | { |
14629 | settings.should_lock_elevation = FALSE; |
14630 | settings.elevation_locked_count = 0; |
14631 | } |
14632 | |
14633 | if (provisional_mode_triggered && (n == max_generation)) |
14634 | { |
14635 | // There are a few cases where we should not reduce the generation. |
14636 | if ((initial_gen == max_generation) || (settings.reason == reason_alloc_loh)) |
14637 | { |
14638 | // If we are doing a full GC in the provisional mode, we always |
14639 | // make it blocking because we don't want to get into a situation |
14640 | // where foreground GCs are asking for a compacting full GC right away |
14641 | // and not getting it. |
14642 | dprintf (GTC_LOG, ("full GC induced, not reducing gen" )); |
14643 | *blocking_collection_p = TRUE; |
14644 | } |
14645 | else if (should_expand_in_full_gc || joined_last_gc_before_oom) |
14646 | { |
14647 | dprintf (GTC_LOG, ("need full blocking GCs to expand heap or avoid OOM, not reducing gen" )); |
14648 | assert (*blocking_collection_p); |
14649 | } |
14650 | else |
14651 | { |
14652 | dprintf (GTC_LOG, ("reducing gen in PM: %d->%d->%d" , initial_gen, n, (max_generation - 1))); |
14653 | n = max_generation - 1; |
14654 | } |
14655 | } |
14656 | |
14657 | if (should_expand_in_full_gc) |
14658 | { |
14659 | should_expand_in_full_gc = FALSE; |
14660 | } |
14661 | |
14662 | if ((n == max_generation) && (*blocking_collection_p == FALSE)) |
14663 | { |
14664 | // If we are doing a gen2 we should reset elevation regardless and let the gen2 |
14665 | // decide if we should lock again or in the bgc case by design we will not retract |
14666 | // gen1 start. |
14667 | settings.should_lock_elevation = FALSE; |
14668 | settings.elevation_locked_count = 0; |
14669 | dprintf (1, ("doing bgc, reset elevation" )); |
14670 | } |
14671 | |
14672 | #ifdef STRESS_HEAP |
14673 | #ifdef BACKGROUND_GC |
14674 | // We can only do Concurrent GC Stress if the caller did not explicitly ask for all |
14675 | // generations to be collected, |
14676 | // |
14677 | // [LOCALGC TODO] STRESS_HEAP is not defined for a standalone GC so there are multiple |
14678 | // things that need to be fixed in this code block. |
14679 | if (n_original != max_generation && |
14680 | g_pConfig->GetGCStressLevel() && gc_can_use_concurrent) |
14681 | { |
14682 | #ifndef FEATURE_REDHAWK |
14683 | // for the GC stress mix mode throttle down gen2 collections |
14684 | if (g_pConfig->IsGCStressMix()) |
14685 | { |
14686 | size_t current_gc_count = 0; |
14687 | |
14688 | #ifdef MULTIPLE_HEAPS |
14689 | current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0)); |
14690 | #else |
14691 | current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0)); |
14692 | #endif //MULTIPLE_HEAPS |
14693 | // in gc stress, only escalate every 10th non-gen2 collection to a gen2... |
14694 | if ((current_gc_count % 10) == 0) |
14695 | { |
14696 | n = max_generation; |
14697 | } |
14698 | } |
14699 | // for traditional GC stress |
14700 | else |
14701 | #endif // !FEATURE_REDHAWK |
14702 | if (*blocking_collection_p) |
14703 | { |
14704 | // We call StressHeap() a lot for Concurrent GC Stress. However, |
14705 | // if we can not do a concurrent collection, no need to stress anymore. |
14706 | // @TODO: Enable stress when the memory pressure goes down again |
14707 | GCStressPolicy::GlobalDisable(); |
14708 | } |
14709 | else |
14710 | { |
14711 | n = max_generation; |
14712 | } |
14713 | } |
14714 | #endif //BACKGROUND_GC |
14715 | #endif //STRESS_HEAP |
14716 | |
14717 | return n; |
14718 | } |
14719 | |
14720 | inline |
14721 | size_t get_survived_size (gc_history_per_heap* hist) |
14722 | { |
14723 | size_t surv_size = 0; |
14724 | gc_generation_data* gen_data; |
14725 | |
14726 | for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++) |
14727 | { |
14728 | gen_data = &(hist->gen_data[gen_number]); |
14729 | surv_size += (gen_data->size_after - |
14730 | gen_data->free_list_space_after - |
14731 | gen_data->free_obj_space_after); |
14732 | } |
14733 | |
14734 | return surv_size; |
14735 | } |
14736 | |
14737 | size_t gc_heap::get_total_survived_size() |
14738 | { |
14739 | size_t total_surv_size = 0; |
14740 | #ifdef MULTIPLE_HEAPS |
14741 | for (int i = 0; i < gc_heap::n_heaps; i++) |
14742 | { |
14743 | gc_heap* hp = gc_heap::g_heaps[i]; |
14744 | gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap(); |
14745 | total_surv_size += get_survived_size (current_gc_data_per_heap); |
14746 | } |
14747 | #else |
14748 | gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); |
14749 | total_surv_size = get_survived_size (current_gc_data_per_heap); |
14750 | #endif //MULTIPLE_HEAPS |
14751 | return total_surv_size; |
14752 | } |
14753 | |
14754 | // Gets what's allocated on both SOH and LOH that hasn't been collected. |
14755 | size_t gc_heap::get_current_allocated() |
14756 | { |
14757 | dynamic_data* dd = dynamic_data_of (0); |
14758 | size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd); |
14759 | dd = dynamic_data_of (max_generation + 1); |
14760 | current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd); |
14761 | |
14762 | return current_alloc; |
14763 | } |
14764 | |
14765 | size_t gc_heap::get_total_allocated() |
14766 | { |
14767 | size_t total_current_allocated = 0; |
14768 | #ifdef MULTIPLE_HEAPS |
14769 | for (int i = 0; i < gc_heap::n_heaps; i++) |
14770 | { |
14771 | gc_heap* hp = gc_heap::g_heaps[i]; |
14772 | total_current_allocated += hp->get_current_allocated(); |
14773 | } |
14774 | #else |
14775 | total_current_allocated = get_current_allocated(); |
14776 | #endif //MULTIPLE_HEAPS |
14777 | return total_current_allocated; |
14778 | } |
14779 | |
14780 | size_t gc_heap::current_generation_size (int gen_number) |
14781 | { |
14782 | dynamic_data* dd = dynamic_data_of (gen_number); |
14783 | size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd) |
14784 | - dd_new_allocation (dd)); |
14785 | |
14786 | return gen_size; |
14787 | } |
14788 | |
14789 | #ifdef _PREFAST_ |
14790 | #pragma warning(push) |
14791 | #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function. |
14792 | #endif //_PREFAST_ |
14793 | |
14794 | /* |
14795 | This is called by when we are actually doing a GC, or when we are just checking whether |
14796 | we would do a full blocking GC, in which case check_only_p is TRUE. |
14797 | |
14798 | The difference between calling this with check_only_p TRUE and FALSE is that when it's |
14799 | TRUE: |
14800 | settings.reason is ignored |
14801 | budgets are not checked (since they are checked before this is called) |
14802 | it doesn't change anything non local like generation_skip_ratio |
14803 | */ |
14804 | int gc_heap::generation_to_condemn (int n_initial, |
14805 | BOOL* blocking_collection_p, |
14806 | BOOL* elevation_requested_p, |
14807 | BOOL check_only_p) |
14808 | { |
14809 | gc_mechanisms temp_settings = settings; |
14810 | gen_to_condemn_tuning temp_condemn_reasons; |
14811 | gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings); |
14812 | gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons); |
14813 | if (!check_only_p) |
14814 | { |
14815 | if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh)) |
14816 | { |
14817 | assert (n_initial >= 1); |
14818 | } |
14819 | |
14820 | assert (settings.reason != reason_empty); |
14821 | } |
14822 | |
14823 | local_condemn_reasons->init(); |
14824 | |
14825 | int n = n_initial; |
14826 | int n_alloc = n; |
14827 | if (heap_number == 0) |
14828 | { |
14829 | dprintf (GTC_LOG, ("init: %d(%d)" , n_initial, settings.reason)); |
14830 | } |
14831 | int i = 0; |
14832 | int temp_gen = 0; |
14833 | BOOL low_memory_detected = g_low_memory_status; |
14834 | uint32_t memory_load = 0; |
14835 | uint64_t available_physical = 0; |
14836 | uint64_t available_page_file = 0; |
14837 | BOOL check_memory = FALSE; |
14838 | BOOL high_fragmentation = FALSE; |
14839 | BOOL v_high_memory_load = FALSE; |
14840 | BOOL high_memory_load = FALSE; |
14841 | BOOL low_ephemeral_space = FALSE; |
14842 | BOOL evaluate_elevation = TRUE; |
14843 | *elevation_requested_p = FALSE; |
14844 | *blocking_collection_p = FALSE; |
14845 | |
14846 | BOOL check_max_gen_alloc = TRUE; |
14847 | |
14848 | #ifdef STRESS_HEAP |
14849 | int orig_gen = n; |
14850 | #endif //STRESS_HEAP |
14851 | |
14852 | if (!check_only_p) |
14853 | { |
14854 | dd_fragmentation (dynamic_data_of (0)) = |
14855 | generation_free_list_space (youngest_generation) + |
14856 | generation_free_obj_space (youngest_generation); |
14857 | |
14858 | dd_fragmentation (dynamic_data_of (max_generation + 1)) = |
14859 | generation_free_list_space (large_object_generation) + |
14860 | generation_free_obj_space (large_object_generation); |
14861 | |
14862 | //save new_allocation |
14863 | for (i = 0; i <= max_generation+1; i++) |
14864 | { |
14865 | dynamic_data* dd = dynamic_data_of (i); |
14866 | dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)" , |
14867 | heap_number, i, |
14868 | dd_new_allocation (dd), |
14869 | dd_desired_allocation (dd))); |
14870 | dd_gc_new_allocation (dd) = dd_new_allocation (dd); |
14871 | } |
14872 | |
14873 | local_condemn_reasons->set_gen (gen_initial, n); |
14874 | temp_gen = n; |
14875 | |
14876 | #ifdef BACKGROUND_GC |
14877 | if (recursive_gc_sync::background_running_p()) |
14878 | { |
14879 | dprintf (GTC_LOG, ("bgc in prog, 1" )); |
14880 | check_max_gen_alloc = FALSE; |
14881 | } |
14882 | #endif //BACKGROUND_GC |
14883 | |
14884 | if (check_max_gen_alloc) |
14885 | { |
14886 | //figure out if large objects need to be collected. |
14887 | if (get_new_allocation (max_generation+1) <= 0) |
14888 | { |
14889 | n = max_generation; |
14890 | local_condemn_reasons->set_gen (gen_alloc_budget, n); |
14891 | } |
14892 | } |
14893 | |
14894 | //figure out which generation ran out of allocation |
14895 | for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++) |
14896 | { |
14897 | if (get_new_allocation (i) <= 0) |
14898 | { |
14899 | n = i; |
14900 | } |
14901 | else |
14902 | break; |
14903 | } |
14904 | } |
14905 | |
14906 | if (n > temp_gen) |
14907 | { |
14908 | local_condemn_reasons->set_gen (gen_alloc_budget, n); |
14909 | } |
14910 | |
14911 | dprintf (GTC_LOG, ("h%d: g%d budget" , heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n))); |
14912 | |
14913 | n_alloc = n; |
14914 | |
14915 | #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS) |
14916 | //time based tuning |
14917 | // if enough time has elapsed since the last gc |
14918 | // and the number of gc is too low (1/10 of lower gen) then collect |
14919 | // This should also be enabled if we have memory concerns |
14920 | int n_time_max = max_generation; |
14921 | |
14922 | if (!check_only_p) |
14923 | { |
14924 | if (recursive_gc_sync::background_running_p()) |
14925 | { |
14926 | n_time_max = max_generation - 1; |
14927 | } |
14928 | } |
14929 | |
14930 | if ((local_settings->pause_mode == pause_interactive) || |
14931 | (local_settings->pause_mode == pause_sustained_low_latency)) |
14932 | { |
14933 | dynamic_data* dd0 = dynamic_data_of (0); |
14934 | size_t now = GetHighPrecisionTimeStamp(); |
14935 | temp_gen = n; |
14936 | for (i = (temp_gen+1); i <= n_time_max; i++) |
14937 | { |
14938 | dynamic_data* dd = dynamic_data_of (i); |
14939 | if ((now > dd_time_clock(dd) + dd_time_clock_interval(dd)) && |
14940 | (dd_gc_clock (dd0) > (dd_gc_clock (dd) + dd_gc_clock_interval(dd))) && |
14941 | ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0))))) |
14942 | { |
14943 | n = min (i, n_time_max); |
14944 | dprintf (GTC_LOG, ("time %d" , n)); |
14945 | } |
14946 | } |
14947 | if (n > temp_gen) |
14948 | { |
14949 | local_condemn_reasons->set_gen (gen_time_tuning, n); |
14950 | } |
14951 | } |
14952 | |
14953 | if (n != n_alloc) |
14954 | { |
14955 | dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation" , n)); |
14956 | } |
14957 | #endif //BACKGROUND_GC && !MULTIPLE_HEAPS |
14958 | |
14959 | if (n < (max_generation - 1)) |
14960 | { |
14961 | if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen)) |
14962 | { |
14963 | n = max (n, max_generation - 1); |
14964 | local_settings->promotion = TRUE; |
14965 | dprintf (GTC_LOG, ("h%d: skip %d, c %d" , |
14966 | heap_number, generation_skip_ratio, n)); |
14967 | local_condemn_reasons->set_condition (gen_low_card_p); |
14968 | } |
14969 | } |
14970 | |
14971 | if (!check_only_p) |
14972 | { |
14973 | generation_skip_ratio = 100; |
14974 | } |
14975 | |
14976 | if (dt_low_ephemeral_space_p (check_only_p ? |
14977 | tuning_deciding_full_gc : |
14978 | tuning_deciding_condemned_gen)) |
14979 | { |
14980 | low_ephemeral_space = TRUE; |
14981 | |
14982 | n = max (n, max_generation - 1); |
14983 | local_condemn_reasons->set_condition (gen_low_ephemeral_p); |
14984 | dprintf (GTC_LOG, ("h%d: low eph" , heap_number)); |
14985 | |
14986 | if (!provisional_mode_triggered) |
14987 | { |
14988 | #ifdef BACKGROUND_GC |
14989 | if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0)) |
14990 | #endif //BACKGROUND_GC |
14991 | { |
14992 | //It is better to defragment first if we are running out of space for |
14993 | //the ephemeral generation but we have enough fragmentation to make up for it |
14994 | //in the non ephemeral generation. Essentially we are trading a gen2 for |
14995 | // having to expand heap in ephemeral collections. |
14996 | if (dt_high_frag_p (tuning_deciding_condemned_gen, |
14997 | max_generation - 1, |
14998 | TRUE)) |
14999 | { |
15000 | high_fragmentation = TRUE; |
15001 | local_condemn_reasons->set_condition (gen_max_high_frag_e_p); |
15002 | dprintf (GTC_LOG, ("heap%d: gen1 frag" , heap_number)); |
15003 | } |
15004 | } |
15005 | } |
15006 | } |
15007 | |
15008 | //figure out which ephemeral generation is too fragramented |
15009 | temp_gen = n; |
15010 | for (i = n+1; i < max_generation; i++) |
15011 | { |
15012 | if (dt_high_frag_p (tuning_deciding_condemned_gen, i)) |
15013 | { |
15014 | dprintf (GTC_LOG, ("h%d g%d too frag" , heap_number, i)); |
15015 | n = i; |
15016 | } |
15017 | else |
15018 | break; |
15019 | } |
15020 | |
15021 | if (low_ephemeral_space) |
15022 | { |
15023 | //enable promotion |
15024 | local_settings->promotion = TRUE; |
15025 | } |
15026 | |
15027 | if (n > temp_gen) |
15028 | { |
15029 | local_condemn_reasons->set_condition (gen_eph_high_frag_p); |
15030 | } |
15031 | |
15032 | if (!check_only_p) |
15033 | { |
15034 | if (settings.pause_mode == pause_low_latency) |
15035 | { |
15036 | if (!is_induced (settings.reason)) |
15037 | { |
15038 | n = min (n, max_generation - 1); |
15039 | dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d" , n)); |
15040 | evaluate_elevation = FALSE; |
15041 | goto exit; |
15042 | } |
15043 | } |
15044 | } |
15045 | |
15046 | // It's hard to catch when we get to the point that the memory load is so high |
15047 | // we get an induced GC from the finalizer thread so we are checking the memory load |
15048 | // for every gen0 GC. |
15049 | check_memory = (check_only_p ? |
15050 | (n >= 0) : |
15051 | ((n >= 1) || low_memory_detected)); |
15052 | |
15053 | if (check_memory) |
15054 | { |
15055 | //find out if we are short on memory |
15056 | get_memory_info (&memory_load, &available_physical, &available_page_file); |
15057 | if (heap_number == 0) |
15058 | { |
15059 | dprintf (GTC_LOG, ("ml: %d" , memory_load)); |
15060 | } |
15061 | |
15062 | // Need to get it early enough for all heaps to use. |
15063 | entry_available_physical_mem = available_physical; |
15064 | local_settings->entry_memory_load = memory_load; |
15065 | |
15066 | // @TODO: Force compaction more often under GCSTRESS |
15067 | if (memory_load >= high_memory_load_th || low_memory_detected) |
15068 | { |
15069 | #ifdef SIMPLE_DPRINTF |
15070 | // stress log can't handle any parameter that's bigger than a void*. |
15071 | if (heap_number == 0) |
15072 | { |
15073 | dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d" , total_physical_mem, available_physical)); |
15074 | } |
15075 | #endif //SIMPLE_DPRINTF |
15076 | |
15077 | high_memory_load = TRUE; |
15078 | |
15079 | if (memory_load >= v_high_memory_load_th || low_memory_detected) |
15080 | { |
15081 | // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since |
15082 | // gen1/gen0 may take a lot more memory than gen2. |
15083 | if (!high_fragmentation) |
15084 | { |
15085 | high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation); |
15086 | } |
15087 | v_high_memory_load = TRUE; |
15088 | } |
15089 | else |
15090 | { |
15091 | if (!high_fragmentation) |
15092 | { |
15093 | high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical); |
15094 | } |
15095 | } |
15096 | |
15097 | if (high_fragmentation) |
15098 | { |
15099 | if (high_memory_load) |
15100 | { |
15101 | local_condemn_reasons->set_condition (gen_max_high_frag_m_p); |
15102 | } |
15103 | else if (v_high_memory_load) |
15104 | { |
15105 | local_condemn_reasons->set_condition (gen_max_high_frag_vm_p); |
15106 | } |
15107 | } |
15108 | } |
15109 | } |
15110 | |
15111 | dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d" , |
15112 | heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load, |
15113 | high_fragmentation)); |
15114 | |
15115 | if (should_expand_in_full_gc) |
15116 | { |
15117 | dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK" , heap_number)); |
15118 | *blocking_collection_p = TRUE; |
15119 | evaluate_elevation = FALSE; |
15120 | n = max_generation; |
15121 | local_condemn_reasons->set_condition (gen_expand_fullgc_p); |
15122 | } |
15123 | |
15124 | if (last_gc_before_oom) |
15125 | { |
15126 | dprintf (GTC_LOG, ("h%d: alloc full - BLOCK" , heap_number)); |
15127 | n = max_generation; |
15128 | *blocking_collection_p = TRUE; |
15129 | if ((local_settings->reason == reason_oos_loh) || |
15130 | (local_settings->reason == reason_alloc_loh)) |
15131 | { |
15132 | evaluate_elevation = FALSE; |
15133 | } |
15134 | |
15135 | local_condemn_reasons->set_condition (gen_before_oom); |
15136 | } |
15137 | |
15138 | if (!check_only_p) |
15139 | { |
15140 | if (is_induced_blocking (settings.reason) && |
15141 | n_initial == max_generation |
15142 | IN_STRESS_HEAP( && !settings.stress_induced )) |
15143 | { |
15144 | if (heap_number == 0) |
15145 | { |
15146 | dprintf (GTC_LOG, ("induced - BLOCK" )); |
15147 | } |
15148 | |
15149 | *blocking_collection_p = TRUE; |
15150 | local_condemn_reasons->set_condition (gen_induced_fullgc_p); |
15151 | evaluate_elevation = FALSE; |
15152 | } |
15153 | |
15154 | if (settings.reason == reason_induced_noforce) |
15155 | { |
15156 | local_condemn_reasons->set_condition (gen_induced_noforce_p); |
15157 | evaluate_elevation = FALSE; |
15158 | } |
15159 | } |
15160 | |
15161 | if (!provisional_mode_triggered && evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load)) |
15162 | { |
15163 | *elevation_requested_p = TRUE; |
15164 | #ifdef BIT64 |
15165 | // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now. |
15166 | if (high_memory_load || v_high_memory_load) |
15167 | { |
15168 | dynamic_data* dd_max = dynamic_data_of (max_generation); |
15169 | if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9) |
15170 | { |
15171 | dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)" , |
15172 | dd_new_allocation (dd_max), dd_desired_allocation (dd_max))); |
15173 | n = max_generation; |
15174 | local_condemn_reasons->set_condition (gen_almost_max_alloc); |
15175 | } |
15176 | } |
15177 | |
15178 | if (n <= max_generation) |
15179 | { |
15180 | #endif // BIT64 |
15181 | if (high_fragmentation) |
15182 | { |
15183 | //elevate to max_generation |
15184 | n = max_generation; |
15185 | dprintf (GTC_LOG, ("h%d: f full" , heap_number)); |
15186 | |
15187 | #ifdef BACKGROUND_GC |
15188 | if (high_memory_load || v_high_memory_load) |
15189 | { |
15190 | // For background GC we want to do blocking collections more eagerly because we don't |
15191 | // want to get into the situation where the memory load becomes high while we are in |
15192 | // a background GC and we'd have to wait for the background GC to finish to start |
15193 | // a blocking collection (right now the implemenation doesn't handle converting |
15194 | // a background GC to a blocking collection midway. |
15195 | dprintf (GTC_LOG, ("h%d: bgc - BLOCK" , heap_number)); |
15196 | *blocking_collection_p = TRUE; |
15197 | } |
15198 | #else |
15199 | if (v_high_memory_load) |
15200 | { |
15201 | dprintf (GTC_LOG, ("h%d: - BLOCK" , heap_number)); |
15202 | *blocking_collection_p = TRUE; |
15203 | } |
15204 | #endif //BACKGROUND_GC |
15205 | } |
15206 | else |
15207 | { |
15208 | n = max (n, max_generation - 1); |
15209 | dprintf (GTC_LOG, ("h%d: nf c %d" , heap_number, n)); |
15210 | } |
15211 | #ifdef BIT64 |
15212 | } |
15213 | #endif // BIT64 |
15214 | } |
15215 | |
15216 | if (!provisional_mode_triggered && (n == (max_generation - 1)) && (n_alloc < (max_generation -1))) |
15217 | { |
15218 | dprintf (GTC_LOG, ("h%d: budget %d, check 2" , |
15219 | heap_number, n_alloc)); |
15220 | if (get_new_allocation (max_generation) <= 0) |
15221 | { |
15222 | dprintf (GTC_LOG, ("h%d: budget alloc" , heap_number)); |
15223 | n = max_generation; |
15224 | local_condemn_reasons->set_condition (gen_max_gen1); |
15225 | } |
15226 | } |
15227 | |
15228 | //figure out if max_generation is too fragmented -> blocking collection |
15229 | if (!provisional_mode_triggered && (n == max_generation)) |
15230 | { |
15231 | if (dt_high_frag_p (tuning_deciding_condemned_gen, n)) |
15232 | { |
15233 | dprintf (GTC_LOG, ("h%d: g%d too frag" , heap_number, n)); |
15234 | local_condemn_reasons->set_condition (gen_max_high_frag_p); |
15235 | if (local_settings->pause_mode != pause_sustained_low_latency) |
15236 | { |
15237 | *blocking_collection_p = TRUE; |
15238 | } |
15239 | } |
15240 | } |
15241 | |
15242 | #ifdef BACKGROUND_GC |
15243 | if (n == max_generation) |
15244 | { |
15245 | if (heap_number == 0) |
15246 | { |
15247 | BOOL bgc_heap_too_small = TRUE; |
15248 | size_t gen2size = 0; |
15249 | size_t gen3size = 0; |
15250 | #ifdef MULTIPLE_HEAPS |
15251 | for (int i = 0; i < n_heaps; i++) |
15252 | { |
15253 | if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) || |
15254 | ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap)) |
15255 | { |
15256 | bgc_heap_too_small = FALSE; |
15257 | break; |
15258 | } |
15259 | } |
15260 | #else //MULTIPLE_HEAPS |
15261 | if ((current_generation_size (max_generation) > bgc_min_per_heap) || |
15262 | (current_generation_size (max_generation + 1) > bgc_min_per_heap)) |
15263 | { |
15264 | bgc_heap_too_small = FALSE; |
15265 | } |
15266 | #endif //MULTIPLE_HEAPS |
15267 | |
15268 | if (bgc_heap_too_small) |
15269 | { |
15270 | dprintf (GTC_LOG, ("gen2 and gen3 too small" )); |
15271 | |
15272 | #ifdef STRESS_HEAP |
15273 | // do not turn stress-induced collections into blocking GCs |
15274 | if (!settings.stress_induced) |
15275 | #endif //STRESS_HEAP |
15276 | { |
15277 | *blocking_collection_p = TRUE; |
15278 | } |
15279 | |
15280 | local_condemn_reasons->set_condition (gen_gen2_too_small); |
15281 | } |
15282 | } |
15283 | } |
15284 | #endif //BACKGROUND_GC |
15285 | |
15286 | exit: |
15287 | if (!check_only_p) |
15288 | { |
15289 | #ifdef STRESS_HEAP |
15290 | #ifdef BACKGROUND_GC |
15291 | // We can only do Concurrent GC Stress if the caller did not explicitly ask for all |
15292 | // generations to be collected, |
15293 | |
15294 | if (orig_gen != max_generation && |
15295 | g_pConfig->GetGCStressLevel() && gc_can_use_concurrent) |
15296 | { |
15297 | *elevation_requested_p = FALSE; |
15298 | } |
15299 | #endif //BACKGROUND_GC |
15300 | #endif //STRESS_HEAP |
15301 | |
15302 | if (check_memory) |
15303 | { |
15304 | fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024)); |
15305 | } |
15306 | |
15307 | local_condemn_reasons->set_gen (gen_final_per_heap, n); |
15308 | get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons); |
15309 | |
15310 | #ifdef DT_LOG |
15311 | local_condemn_reasons->print (heap_number); |
15312 | #endif //DT_LOG |
15313 | |
15314 | if ((local_settings->reason == reason_oos_soh) || |
15315 | (local_settings->reason == reason_oos_loh)) |
15316 | { |
15317 | assert (n >= 1); |
15318 | } |
15319 | } |
15320 | |
15321 | return n; |
15322 | } |
15323 | |
15324 | #ifdef _PREFAST_ |
15325 | #pragma warning(pop) |
15326 | #endif //_PREFAST_ |
15327 | |
15328 | inline |
15329 | size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps) |
15330 | { |
15331 | // if the memory load is higher, the threshold we'd want to collect gets lower. |
15332 | size_t min_mem_based_on_available = |
15333 | (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps; |
15334 | size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10); |
15335 | uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps; |
15336 | |
15337 | #ifdef SIMPLE_DPRINTF |
15338 | dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d" , |
15339 | min_mem_based_on_available, ten_percent_size, three_percent_mem)); |
15340 | #endif //SIMPLE_DPRINTF |
15341 | return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem))); |
15342 | } |
15343 | |
15344 | inline |
15345 | uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps) |
15346 | { |
15347 | return min (available_mem, (256*1024*1024)) / num_heaps; |
15348 | } |
15349 | |
15350 | enum { |
15351 | CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC' |
15352 | }; |
15353 | |
15354 | |
15355 | #ifdef BACKGROUND_GC |
15356 | void gc_heap::init_background_gc () |
15357 | { |
15358 | //reset the allocation so foreground gc can allocate into older (max_generation) generation |
15359 | generation* gen = generation_of (max_generation); |
15360 | generation_allocation_pointer (gen)= 0; |
15361 | generation_allocation_limit (gen) = 0; |
15362 | generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen)); |
15363 | |
15364 | PREFIX_ASSUME(generation_allocation_segment(gen) != NULL); |
15365 | |
15366 | //reset the plan allocation for each segment |
15367 | for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment; |
15368 | seg = heap_segment_next_rw (seg)) |
15369 | { |
15370 | heap_segment_plan_allocated (seg) = heap_segment_allocated (seg); |
15371 | } |
15372 | |
15373 | if (heap_number == 0) |
15374 | { |
15375 | dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix" , |
15376 | heap_number, |
15377 | background_saved_lowest_address, |
15378 | background_saved_highest_address)); |
15379 | } |
15380 | |
15381 | gc_lh_block_event.Reset(); |
15382 | } |
15383 | |
15384 | #endif //BACKGROUND_GC |
15385 | |
15386 | inline |
15387 | void fire_drain_mark_list_event (size_t mark_list_objects) |
15388 | { |
15389 | FIRE_EVENT(BGCDrainMark, mark_list_objects); |
15390 | } |
15391 | |
15392 | inline |
15393 | void fire_revisit_event (size_t dirtied_pages, |
15394 | size_t marked_objects, |
15395 | BOOL large_objects_p) |
15396 | { |
15397 | FIRE_EVENT(BGCRevisit, dirtied_pages, marked_objects, large_objects_p); |
15398 | } |
15399 | |
15400 | inline |
15401 | void fire_overflow_event (uint8_t* overflow_min, |
15402 | uint8_t* overflow_max, |
15403 | size_t marked_objects, |
15404 | int large_objects_p) |
15405 | { |
15406 | FIRE_EVENT(BGCOverflow, (uint64_t)overflow_min, (uint64_t)overflow_max, marked_objects, large_objects_p); |
15407 | } |
15408 | |
15409 | void gc_heap::concurrent_print_time_delta (const char* msg) |
15410 | { |
15411 | #ifdef TRACE_GC |
15412 | size_t current_time = GetHighPrecisionTimeStamp(); |
15413 | size_t elapsed_time = current_time - time_bgc_last; |
15414 | time_bgc_last = current_time; |
15415 | |
15416 | dprintf (2, ("h%d: %s T %Id ms" , heap_number, msg, elapsed_time)); |
15417 | #else |
15418 | UNREFERENCED_PARAMETER(msg); |
15419 | #endif //TRACE_GC |
15420 | } |
15421 | |
15422 | void gc_heap::free_list_info (int gen_num, const char* msg) |
15423 | { |
15424 | UNREFERENCED_PARAMETER(gen_num); |
15425 | #if defined (BACKGROUND_GC) && defined (TRACE_GC) |
15426 | dprintf (3, ("h%d: %s" , heap_number, msg)); |
15427 | for (int i = 0; i <= (max_generation + 1); i++) |
15428 | { |
15429 | generation* gen = generation_of (i); |
15430 | if ((generation_allocation_size (gen) == 0) && |
15431 | (generation_free_list_space (gen) == 0) && |
15432 | (generation_free_obj_space (gen) == 0)) |
15433 | { |
15434 | // don't print if everything is 0. |
15435 | } |
15436 | else |
15437 | { |
15438 | dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id" , |
15439 | heap_number, i, |
15440 | generation_allocation_size (gen), |
15441 | generation_free_list_space (gen), |
15442 | generation_free_obj_space (gen))); |
15443 | } |
15444 | } |
15445 | #else |
15446 | UNREFERENCED_PARAMETER(msg); |
15447 | #endif // BACKGROUND_GC && TRACE_GC |
15448 | } |
15449 | |
15450 | void gc_heap::update_collection_counts_for_no_gc() |
15451 | { |
15452 | assert (settings.pause_mode == pause_no_gc); |
15453 | |
15454 | settings.condemned_generation = max_generation; |
15455 | #ifdef MULTIPLE_HEAPS |
15456 | for (int i = 0; i < n_heaps; i++) |
15457 | g_heaps[i]->update_collection_counts(); |
15458 | #else //MULTIPLE_HEAPS |
15459 | update_collection_counts(); |
15460 | #endif //MULTIPLE_HEAPS |
15461 | |
15462 | full_gc_counts[gc_type_blocking]++; |
15463 | } |
15464 | |
15465 | BOOL gc_heap::should_proceed_with_gc() |
15466 | { |
15467 | if (gc_heap::settings.pause_mode == pause_no_gc) |
15468 | { |
15469 | if (current_no_gc_region_info.started) |
15470 | { |
15471 | // The no_gc mode was already in progress yet we triggered another GC, |
15472 | // this effectively exits the no_gc mode. |
15473 | restore_data_for_no_gc(); |
15474 | } |
15475 | else |
15476 | return should_proceed_for_no_gc(); |
15477 | } |
15478 | |
15479 | return TRUE; |
15480 | } |
15481 | |
15482 | //internal part of gc used by the serial and concurrent version |
15483 | void gc_heap::gc1() |
15484 | { |
15485 | #ifdef BACKGROUND_GC |
15486 | assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread())); |
15487 | #endif //BACKGROUND_GC |
15488 | |
15489 | #ifdef TIME_GC |
15490 | mark_time = plan_time = reloc_time = compact_time = sweep_time = 0; |
15491 | #endif //TIME_GC |
15492 | |
15493 | verify_soh_segment_list(); |
15494 | |
15495 | int n = settings.condemned_generation; |
15496 | |
15497 | if (settings.reason == reason_pm_full_gc) |
15498 | { |
15499 | assert (n == max_generation); |
15500 | init_records(); |
15501 | |
15502 | gen_to_condemn_tuning* local_condemn_reasons = &(get_gc_data_per_heap()->gen_to_condemn_reasons); |
15503 | local_condemn_reasons->init(); |
15504 | local_condemn_reasons->set_gen (gen_initial, n); |
15505 | local_condemn_reasons->set_gen (gen_final_per_heap, n); |
15506 | } |
15507 | |
15508 | update_collection_counts (); |
15509 | |
15510 | #ifdef BACKGROUND_GC |
15511 | bgc_alloc_lock->check(); |
15512 | #endif //BACKGROUND_GC |
15513 | |
15514 | free_list_info (max_generation, "beginning" ); |
15515 | |
15516 | vm_heap->GcCondemnedGeneration = settings.condemned_generation; |
15517 | |
15518 | assert (g_gc_card_table == card_table); |
15519 | |
15520 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
15521 | assert (g_gc_card_bundle_table == card_bundle_table); |
15522 | #endif |
15523 | |
15524 | { |
15525 | if (n == max_generation) |
15526 | { |
15527 | gc_low = lowest_address; |
15528 | gc_high = highest_address; |
15529 | } |
15530 | else |
15531 | { |
15532 | gc_low = generation_allocation_start (generation_of (n)); |
15533 | gc_high = heap_segment_reserved (ephemeral_heap_segment); |
15534 | } |
15535 | #ifdef BACKGROUND_GC |
15536 | if (settings.concurrent) |
15537 | { |
15538 | #ifdef TRACE_GC |
15539 | time_bgc_last = GetHighPrecisionTimeStamp(); |
15540 | #endif //TRACE_GC |
15541 | |
15542 | FIRE_EVENT(BGCBegin); |
15543 | |
15544 | concurrent_print_time_delta ("BGC" ); |
15545 | |
15546 | //#ifdef WRITE_WATCH |
15547 | //reset_write_watch (FALSE); |
15548 | //#endif //WRITE_WATCH |
15549 | |
15550 | concurrent_print_time_delta ("RW" ); |
15551 | background_mark_phase(); |
15552 | free_list_info (max_generation, "after mark phase" ); |
15553 | |
15554 | background_sweep(); |
15555 | free_list_info (max_generation, "after sweep phase" ); |
15556 | } |
15557 | else |
15558 | #endif //BACKGROUND_GC |
15559 | { |
15560 | mark_phase (n, FALSE); |
15561 | |
15562 | GCScan::GcRuntimeStructuresValid (FALSE); |
15563 | plan_phase (n); |
15564 | GCScan::GcRuntimeStructuresValid (TRUE); |
15565 | } |
15566 | } |
15567 | |
15568 | size_t end_gc_time = GetHighPrecisionTimeStamp(); |
15569 | // printf ("generation: %d, elapsed time: %Id\n", n, end_gc_time - dd_time_clock (dynamic_data_of (0))); |
15570 | |
15571 | //adjust the allocation size from the pinned quantities. |
15572 | for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++) |
15573 | { |
15574 | generation* gn = generation_of (gen_number); |
15575 | if (settings.compaction) |
15576 | { |
15577 | generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn); |
15578 | generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn); |
15579 | } |
15580 | else |
15581 | { |
15582 | generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn); |
15583 | generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn); |
15584 | } |
15585 | generation_pinned_allocation_sweep_size (gn) = 0; |
15586 | generation_pinned_allocation_compact_size (gn) = 0; |
15587 | } |
15588 | |
15589 | #ifdef BACKGROUND_GC |
15590 | if (settings.concurrent) |
15591 | { |
15592 | dynamic_data* dd = dynamic_data_of (n); |
15593 | dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd); |
15594 | |
15595 | free_list_info (max_generation, "after computing new dynamic data" ); |
15596 | |
15597 | gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); |
15598 | |
15599 | for (int gen_number = 0; gen_number < max_generation; gen_number++) |
15600 | { |
15601 | dprintf (2, ("end of BGC: gen%d new_alloc: %Id" , |
15602 | gen_number, dd_desired_allocation (dynamic_data_of (gen_number)))); |
15603 | current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number); |
15604 | current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number)); |
15605 | current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number)); |
15606 | } |
15607 | } |
15608 | else |
15609 | #endif //BACKGROUND_GC |
15610 | { |
15611 | free_list_info (max_generation, "end" ); |
15612 | for (int gen_number = 0; gen_number <= n; gen_number++) |
15613 | { |
15614 | dynamic_data* dd = dynamic_data_of (gen_number); |
15615 | dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd); |
15616 | compute_new_dynamic_data (gen_number); |
15617 | } |
15618 | |
15619 | if (n != max_generation) |
15620 | { |
15621 | int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1)); |
15622 | for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++) |
15623 | { |
15624 | get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number); |
15625 | get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number)); |
15626 | get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number)); |
15627 | } |
15628 | } |
15629 | |
15630 | get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100); |
15631 | |
15632 | free_list_info (max_generation, "after computing new dynamic data" ); |
15633 | |
15634 | if (heap_number == 0) |
15635 | { |
15636 | dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms" , |
15637 | dd_collection_count (dynamic_data_of (0)), |
15638 | settings.condemned_generation, |
15639 | dd_gc_elapsed_time (dynamic_data_of (0)))); |
15640 | } |
15641 | |
15642 | for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++) |
15643 | { |
15644 | dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id" , |
15645 | gen_number, dd_desired_allocation (dynamic_data_of (gen_number)))); |
15646 | } |
15647 | } |
15648 | |
15649 | if (n < max_generation) |
15650 | { |
15651 | compute_promoted_allocation (1 + n); |
15652 | |
15653 | dynamic_data* dd = dynamic_data_of (1 + n); |
15654 | size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + |
15655 | generation_free_obj_space (generation_of (1 + n)); |
15656 | |
15657 | #ifdef BACKGROUND_GC |
15658 | if (current_c_gc_state != c_gc_state_planning) |
15659 | #endif //BACKGROUND_GC |
15660 | { |
15661 | if (settings.promotion) |
15662 | { |
15663 | dd_fragmentation (dd) = new_fragmentation; |
15664 | } |
15665 | else |
15666 | { |
15667 | //assert (dd_fragmentation (dd) == new_fragmentation); |
15668 | } |
15669 | } |
15670 | } |
15671 | |
15672 | #ifdef BACKGROUND_GC |
15673 | if (!settings.concurrent) |
15674 | #endif //BACKGROUND_GC |
15675 | { |
15676 | #ifndef FEATURE_REDHAWK |
15677 | // GCToEEInterface::IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR. |
15678 | assert(GCToEEInterface::IsGCThread()); |
15679 | #endif // FEATURE_REDHAWK |
15680 | adjust_ephemeral_limits(); |
15681 | } |
15682 | |
15683 | #ifdef BACKGROUND_GC |
15684 | assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1))); |
15685 | assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment)); |
15686 | #endif //BACKGROUND_GC |
15687 | |
15688 | if (fgn_maxgen_percent) |
15689 | { |
15690 | if (settings.condemned_generation == (max_generation - 1)) |
15691 | { |
15692 | check_for_full_gc (max_generation - 1, 0); |
15693 | } |
15694 | else if (settings.condemned_generation == max_generation) |
15695 | { |
15696 | if (full_gc_approach_event_set |
15697 | #ifdef MULTIPLE_HEAPS |
15698 | && (heap_number == 0) |
15699 | #endif //MULTIPLE_HEAPS |
15700 | ) |
15701 | { |
15702 | dprintf (2, ("FGN-GC: setting gen2 end event" )); |
15703 | |
15704 | full_gc_approach_event.Reset(); |
15705 | #ifdef BACKGROUND_GC |
15706 | // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A |
15707 | fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE; |
15708 | #endif //BACKGROUND_GC |
15709 | full_gc_end_event.Set(); |
15710 | full_gc_approach_event_set = false; |
15711 | } |
15712 | } |
15713 | } |
15714 | |
15715 | #ifdef BACKGROUND_GC |
15716 | if (!settings.concurrent) |
15717 | #endif //BACKGROUND_GC |
15718 | { |
15719 | //decide on the next allocation quantum |
15720 | if (alloc_contexts_used >= 1) |
15721 | { |
15722 | allocation_quantum = Align (min ((size_t)CLR_SIZE, |
15723 | (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))), |
15724 | get_alignment_constant(FALSE)); |
15725 | dprintf (3, ("New allocation quantum: %d(0x%Ix)" , allocation_quantum, allocation_quantum)); |
15726 | } |
15727 | } |
15728 | |
15729 | descr_generations (FALSE); |
15730 | |
15731 | verify_soh_segment_list(); |
15732 | |
15733 | #ifdef BACKGROUND_GC |
15734 | add_to_history_per_heap(); |
15735 | if (heap_number == 0) |
15736 | { |
15737 | add_to_history(); |
15738 | } |
15739 | #endif // BACKGROUND_GC |
15740 | |
15741 | #ifdef GC_STATS |
15742 | if (GCStatistics::Enabled() && heap_number == 0) |
15743 | g_GCStatistics.AddGCStats(settings, |
15744 | dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation))); |
15745 | #endif // GC_STATS |
15746 | |
15747 | #ifdef TIME_GC |
15748 | fprintf (stdout, "%d,%d,%d,%d,%d,%d\n" , |
15749 | n, mark_time, plan_time, reloc_time, compact_time, sweep_time); |
15750 | #endif //TIME_GC |
15751 | |
15752 | #ifdef BACKGROUND_GC |
15753 | assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread())); |
15754 | #endif //BACKGROUND_GC |
15755 | |
15756 | #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)) |
15757 | if (FALSE |
15758 | #ifdef VERIFY_HEAP |
15759 | // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same |
15760 | // value. If we ever allow randomly adjusting this as the process runs, |
15761 | // we cannot call it this way as joins need to match - we must have the same |
15762 | // value for all heaps like we do with bgc_heap_walk_for_etw_p. |
15763 | || (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
15764 | #endif |
15765 | #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC) |
15766 | || (bgc_heap_walk_for_etw_p && settings.concurrent) |
15767 | #endif |
15768 | ) |
15769 | { |
15770 | #ifdef BACKGROUND_GC |
15771 | bool cooperative_mode = true; |
15772 | |
15773 | if (settings.concurrent) |
15774 | { |
15775 | cooperative_mode = enable_preemptive (); |
15776 | |
15777 | #ifdef MULTIPLE_HEAPS |
15778 | bgc_t_join.join(this, gc_join_suspend_ee_verify); |
15779 | if (bgc_t_join.joined()) |
15780 | { |
15781 | bgc_threads_sync_event.Reset(); |
15782 | |
15783 | dprintf(2, ("Joining BGC threads to suspend EE for verify heap" )); |
15784 | bgc_t_join.restart(); |
15785 | } |
15786 | if (heap_number == 0) |
15787 | { |
15788 | suspend_EE(); |
15789 | bgc_threads_sync_event.Set(); |
15790 | } |
15791 | else |
15792 | { |
15793 | bgc_threads_sync_event.Wait(INFINITE, FALSE); |
15794 | dprintf (2, ("bgc_threads_sync_event is signalled" )); |
15795 | } |
15796 | #else |
15797 | suspend_EE(); |
15798 | #endif //MULTIPLE_HEAPS |
15799 | |
15800 | //fix the allocation area so verify_heap can proceed. |
15801 | fix_allocation_contexts (FALSE); |
15802 | } |
15803 | #endif //BACKGROUND_GC |
15804 | |
15805 | #ifdef BACKGROUND_GC |
15806 | assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread())); |
15807 | #ifdef FEATURE_EVENT_TRACE |
15808 | if (bgc_heap_walk_for_etw_p && settings.concurrent) |
15809 | { |
15810 | GCToEEInterface::DiagWalkBGCSurvivors(__this); |
15811 | |
15812 | #ifdef MULTIPLE_HEAPS |
15813 | bgc_t_join.join(this, gc_join_after_profiler_heap_walk); |
15814 | if (bgc_t_join.joined()) |
15815 | { |
15816 | bgc_t_join.restart(); |
15817 | } |
15818 | #endif // MULTIPLE_HEAPS |
15819 | } |
15820 | #endif // FEATURE_EVENT_TRACE |
15821 | #endif //BACKGROUND_GC |
15822 | |
15823 | #ifdef VERIFY_HEAP |
15824 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
15825 | verify_heap (FALSE); |
15826 | #endif // VERIFY_HEAP |
15827 | |
15828 | #ifdef BACKGROUND_GC |
15829 | if (settings.concurrent) |
15830 | { |
15831 | repair_allocation_contexts (TRUE); |
15832 | |
15833 | #ifdef MULTIPLE_HEAPS |
15834 | bgc_t_join.join(this, gc_join_restart_ee_verify); |
15835 | if (bgc_t_join.joined()) |
15836 | { |
15837 | bgc_threads_sync_event.Reset(); |
15838 | |
15839 | dprintf(2, ("Joining BGC threads to restart EE after verify heap" )); |
15840 | bgc_t_join.restart(); |
15841 | } |
15842 | if (heap_number == 0) |
15843 | { |
15844 | restart_EE(); |
15845 | bgc_threads_sync_event.Set(); |
15846 | } |
15847 | else |
15848 | { |
15849 | bgc_threads_sync_event.Wait(INFINITE, FALSE); |
15850 | dprintf (2, ("bgc_threads_sync_event is signalled" )); |
15851 | } |
15852 | #else |
15853 | restart_EE(); |
15854 | #endif //MULTIPLE_HEAPS |
15855 | |
15856 | disable_preemptive (cooperative_mode); |
15857 | } |
15858 | #endif //BACKGROUND_GC |
15859 | } |
15860 | #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)) |
15861 | |
15862 | #ifdef MULTIPLE_HEAPS |
15863 | if (!settings.concurrent) |
15864 | { |
15865 | gc_t_join.join(this, gc_join_done); |
15866 | if (gc_t_join.joined ()) |
15867 | { |
15868 | gc_heap::internal_gc_done = false; |
15869 | |
15870 | //equalize the new desired size of the generations |
15871 | int limit = settings.condemned_generation; |
15872 | if (limit == max_generation) |
15873 | { |
15874 | limit = max_generation+1; |
15875 | } |
15876 | for (int gen = 0; gen <= limit; gen++) |
15877 | { |
15878 | size_t total_desired = 0; |
15879 | |
15880 | for (int i = 0; i < gc_heap::n_heaps; i++) |
15881 | { |
15882 | gc_heap* hp = gc_heap::g_heaps[i]; |
15883 | dynamic_data* dd = hp->dynamic_data_of (gen); |
15884 | size_t temp_total_desired = total_desired + dd_desired_allocation (dd); |
15885 | if (temp_total_desired < total_desired) |
15886 | { |
15887 | // we overflowed. |
15888 | total_desired = (size_t)MAX_PTR; |
15889 | break; |
15890 | } |
15891 | total_desired = temp_total_desired; |
15892 | } |
15893 | |
15894 | size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps, |
15895 | get_alignment_constant ((gen != (max_generation+1)))); |
15896 | |
15897 | if (gen == 0) |
15898 | { |
15899 | #if 1 //subsumed by the linear allocation model |
15900 | // to avoid spikes in mem usage due to short terms fluctuations in survivorship, |
15901 | // apply some smoothing. |
15902 | static size_t smoothed_desired_per_heap = 0; |
15903 | size_t smoothing = 3; // exponential smoothing factor |
15904 | if (smoothing > VolatileLoad(&settings.gc_index)) |
15905 | smoothing = VolatileLoad(&settings.gc_index); |
15906 | smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1)); |
15907 | dprintf (1, ("sn = %Id n = %Id" , smoothed_desired_per_heap, desired_per_heap)); |
15908 | desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true)); |
15909 | #endif //0 |
15910 | |
15911 | // if desired_per_heap is close to min_gc_size, trim it |
15912 | // down to min_gc_size to stay in the cache |
15913 | gc_heap* hp = gc_heap::g_heaps[0]; |
15914 | dynamic_data* dd = hp->dynamic_data_of (gen); |
15915 | size_t min_gc_size = dd_min_size(dd); |
15916 | // if min GC size larger than true on die cache, then don't bother |
15917 | // limiting the desired size |
15918 | if ((min_gc_size <= GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)) && |
15919 | desired_per_heap <= 2*min_gc_size) |
15920 | { |
15921 | desired_per_heap = min_gc_size; |
15922 | } |
15923 | #ifdef BIT64 |
15924 | desired_per_heap = joined_youngest_desired (desired_per_heap); |
15925 | dprintf (2, ("final gen0 new_alloc: %Id" , desired_per_heap)); |
15926 | #endif // BIT64 |
15927 | |
15928 | gc_data_global.final_youngest_desired = desired_per_heap; |
15929 | } |
15930 | #if 1 //subsumed by the linear allocation model |
15931 | if (gen == (max_generation + 1)) |
15932 | { |
15933 | // to avoid spikes in mem usage due to short terms fluctuations in survivorship, |
15934 | // apply some smoothing. |
15935 | static size_t smoothed_desired_per_heap_loh = 0; |
15936 | size_t smoothing = 3; // exponential smoothing factor |
15937 | size_t loh_count = dd_collection_count (dynamic_data_of (max_generation)); |
15938 | if (smoothing > loh_count) |
15939 | smoothing = loh_count; |
15940 | smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1)); |
15941 | dprintf( 2, ("smoothed_desired_per_heap_loh = %Id desired_per_heap = %Id" , smoothed_desired_per_heap_loh, desired_per_heap)); |
15942 | desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false)); |
15943 | } |
15944 | #endif //0 |
15945 | for (int i = 0; i < gc_heap::n_heaps; i++) |
15946 | { |
15947 | gc_heap* hp = gc_heap::g_heaps[i]; |
15948 | dynamic_data* dd = hp->dynamic_data_of (gen); |
15949 | dd_desired_allocation (dd) = desired_per_heap; |
15950 | dd_gc_new_allocation (dd) = desired_per_heap; |
15951 | dd_new_allocation (dd) = desired_per_heap; |
15952 | |
15953 | if (gen == 0) |
15954 | { |
15955 | hp->fgn_last_alloc = desired_per_heap; |
15956 | } |
15957 | } |
15958 | } |
15959 | |
15960 | #ifdef FEATURE_LOH_COMPACTION |
15961 | BOOL all_heaps_compacted_p = TRUE; |
15962 | #endif //FEATURE_LOH_COMPACTION |
15963 | for (int i = 0; i < gc_heap::n_heaps; i++) |
15964 | { |
15965 | gc_heap* hp = gc_heap::g_heaps[i]; |
15966 | hp->decommit_ephemeral_segment_pages(); |
15967 | hp->rearrange_large_heap_segments(); |
15968 | #ifdef FEATURE_LOH_COMPACTION |
15969 | all_heaps_compacted_p &= hp->loh_compacted_p; |
15970 | #endif //FEATURE_LOH_COMPACTION |
15971 | } |
15972 | |
15973 | #ifdef FEATURE_LOH_COMPACTION |
15974 | check_loh_compact_mode (all_heaps_compacted_p); |
15975 | #endif //FEATURE_LOH_COMPACTION |
15976 | |
15977 | fire_pevents(); |
15978 | pm_full_gc_init_or_clear(); |
15979 | |
15980 | gc_t_join.restart(); |
15981 | } |
15982 | alloc_context_count = 0; |
15983 | heap_select::mark_heap (heap_number); |
15984 | } |
15985 | |
15986 | #else |
15987 | gc_data_global.final_youngest_desired = |
15988 | dd_desired_allocation (dynamic_data_of (0)); |
15989 | |
15990 | check_loh_compact_mode (loh_compacted_p); |
15991 | |
15992 | decommit_ephemeral_segment_pages(); |
15993 | fire_pevents(); |
15994 | |
15995 | if (!(settings.concurrent)) |
15996 | { |
15997 | rearrange_large_heap_segments(); |
15998 | do_post_gc(); |
15999 | } |
16000 | |
16001 | pm_full_gc_init_or_clear(); |
16002 | |
16003 | #ifdef BACKGROUND_GC |
16004 | recover_bgc_settings(); |
16005 | #endif //BACKGROUND_GC |
16006 | #endif //MULTIPLE_HEAPS |
16007 | } |
16008 | |
16009 | void gc_heap::save_data_for_no_gc() |
16010 | { |
16011 | current_no_gc_region_info.saved_pause_mode = settings.pause_mode; |
16012 | #ifdef MULTIPLE_HEAPS |
16013 | // This is to affect heap balancing. |
16014 | for (int i = 0; i < n_heaps; i++) |
16015 | { |
16016 | current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0)); |
16017 | dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold; |
16018 | current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)); |
16019 | dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0; |
16020 | } |
16021 | #endif //MULTIPLE_HEAPS |
16022 | } |
16023 | |
16024 | void gc_heap::restore_data_for_no_gc() |
16025 | { |
16026 | gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode; |
16027 | #ifdef MULTIPLE_HEAPS |
16028 | for (int i = 0; i < n_heaps; i++) |
16029 | { |
16030 | dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size; |
16031 | dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size; |
16032 | } |
16033 | #endif //MULTIPLE_HEAPS |
16034 | } |
16035 | |
16036 | start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size, |
16037 | BOOL loh_size_known, |
16038 | uint64_t loh_size, |
16039 | BOOL disallow_full_blocking) |
16040 | { |
16041 | if (current_no_gc_region_info.started) |
16042 | { |
16043 | return start_no_gc_in_progress; |
16044 | } |
16045 | |
16046 | start_no_gc_region_status status = start_no_gc_success; |
16047 | |
16048 | save_data_for_no_gc(); |
16049 | settings.pause_mode = pause_no_gc; |
16050 | current_no_gc_region_info.start_status = start_no_gc_success; |
16051 | |
16052 | uint64_t allocation_no_gc_loh = 0; |
16053 | uint64_t allocation_no_gc_soh = 0; |
16054 | assert(total_size != 0); |
16055 | if (loh_size_known) |
16056 | { |
16057 | assert(loh_size != 0); |
16058 | assert(loh_size <= total_size); |
16059 | allocation_no_gc_loh = loh_size; |
16060 | allocation_no_gc_soh = total_size - loh_size; |
16061 | } |
16062 | else |
16063 | { |
16064 | allocation_no_gc_soh = total_size; |
16065 | allocation_no_gc_loh = total_size; |
16066 | } |
16067 | |
16068 | int soh_align_const = get_alignment_constant (TRUE); |
16069 | size_t max_soh_allocated = soh_segment_size - segment_info_size - eph_gen_starts_size; |
16070 | size_t size_per_heap = 0; |
16071 | const double scale_factor = 1.05; |
16072 | |
16073 | int num_heaps = 1; |
16074 | #ifdef MULTIPLE_HEAPS |
16075 | num_heaps = n_heaps; |
16076 | #endif // MULTIPLE_HEAPS |
16077 | |
16078 | uint64_t total_allowed_soh_allocation = max_soh_allocated * num_heaps; |
16079 | // [LOCALGC TODO] |
16080 | // In theory, the upper limit here is the physical memory of the machine, not |
16081 | // SIZE_T_MAX. This is not true today because total_physical_mem can be |
16082 | // larger than SIZE_T_MAX if running in wow64 on a machine with more than |
16083 | // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing |
16084 | // more freely between branches, it would be good to clean this up to use |
16085 | // total_physical_mem instead of SIZE_T_MAX. |
16086 | assert(total_allowed_soh_allocation <= SIZE_T_MAX); |
16087 | uint64_t total_allowed_loh_allocation = SIZE_T_MAX; |
16088 | uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0; |
16089 | uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0; |
16090 | |
16091 | if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled || |
16092 | allocation_no_gc_loh > total_allowed_loh_alloc_scaled) |
16093 | { |
16094 | status = start_no_gc_too_large; |
16095 | goto done; |
16096 | } |
16097 | |
16098 | if (allocation_no_gc_soh > 0) |
16099 | { |
16100 | allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor); |
16101 | allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled); |
16102 | } |
16103 | |
16104 | if (allocation_no_gc_loh > 0) |
16105 | { |
16106 | allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor); |
16107 | allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled); |
16108 | } |
16109 | |
16110 | if (disallow_full_blocking) |
16111 | current_no_gc_region_info.minimal_gc_p = TRUE; |
16112 | |
16113 | if (allocation_no_gc_soh != 0) |
16114 | { |
16115 | current_no_gc_region_info.soh_allocation_size = static_cast<size_t>(allocation_no_gc_soh); |
16116 | size_per_heap = current_no_gc_region_info.soh_allocation_size; |
16117 | #ifdef MULTIPLE_HEAPS |
16118 | size_per_heap /= n_heaps; |
16119 | for (int i = 0; i < n_heaps; i++) |
16120 | { |
16121 | // due to heap balancing we need to allow some room before we even look to balance to another heap. |
16122 | g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated); |
16123 | } |
16124 | #else //MULTIPLE_HEAPS |
16125 | soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated); |
16126 | #endif //MULTIPLE_HEAPS |
16127 | } |
16128 | |
16129 | if (allocation_no_gc_loh != 0) |
16130 | { |
16131 | current_no_gc_region_info.loh_allocation_size = static_cast<size_t>(allocation_no_gc_loh); |
16132 | size_per_heap = current_no_gc_region_info.loh_allocation_size; |
16133 | #ifdef MULTIPLE_HEAPS |
16134 | size_per_heap /= n_heaps; |
16135 | for (int i = 0; i < n_heaps; i++) |
16136 | g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE)); |
16137 | #else //MULTIPLE_HEAPS |
16138 | loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE)); |
16139 | #endif //MULTIPLE_HEAPS |
16140 | } |
16141 | |
16142 | done: |
16143 | if (status != start_no_gc_success) |
16144 | restore_data_for_no_gc(); |
16145 | return status; |
16146 | } |
16147 | |
16148 | void gc_heap::handle_failure_for_no_gc() |
16149 | { |
16150 | gc_heap::restore_data_for_no_gc(); |
16151 | // sets current_no_gc_region_info.started to FALSE here. |
16152 | memset (¤t_no_gc_region_info, 0, sizeof (current_no_gc_region_info)); |
16153 | } |
16154 | |
16155 | start_no_gc_region_status gc_heap::get_start_no_gc_region_status() |
16156 | { |
16157 | return current_no_gc_region_info.start_status; |
16158 | } |
16159 | |
16160 | void gc_heap::record_gcs_during_no_gc() |
16161 | { |
16162 | if (current_no_gc_region_info.started) |
16163 | { |
16164 | current_no_gc_region_info.num_gcs++; |
16165 | if (is_induced (settings.reason)) |
16166 | current_no_gc_region_info.num_gcs_induced++; |
16167 | } |
16168 | } |
16169 | |
16170 | BOOL gc_heap::find_loh_free_for_no_gc() |
16171 | { |
16172 | allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1)); |
16173 | size_t sz_list = loh_allocator->first_bucket_size(); |
16174 | size_t size = loh_allocation_no_gc; |
16175 | for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++) |
16176 | { |
16177 | if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1))) |
16178 | { |
16179 | uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx); |
16180 | while (free_list) |
16181 | { |
16182 | size_t free_list_size = unused_array_size(free_list); |
16183 | |
16184 | if (free_list_size > loh_allocation_no_gc) |
16185 | { |
16186 | dprintf (3, ("free item %Ix(%Id) for no gc" , (size_t)free_list, free_list_size)); |
16187 | return TRUE; |
16188 | } |
16189 | |
16190 | free_list = free_list_slot (free_list); |
16191 | } |
16192 | } |
16193 | sz_list = sz_list * 2; |
16194 | } |
16195 | |
16196 | return FALSE; |
16197 | } |
16198 | |
16199 | BOOL gc_heap::find_loh_space_for_no_gc() |
16200 | { |
16201 | saved_loh_segment_no_gc = 0; |
16202 | |
16203 | if (find_loh_free_for_no_gc()) |
16204 | return TRUE; |
16205 | |
16206 | heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1)); |
16207 | |
16208 | while (seg) |
16209 | { |
16210 | size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg); |
16211 | if (remaining >= loh_allocation_no_gc) |
16212 | { |
16213 | saved_loh_segment_no_gc = seg; |
16214 | break; |
16215 | } |
16216 | seg = heap_segment_next (seg); |
16217 | } |
16218 | |
16219 | if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p) |
16220 | { |
16221 | // If no full GC is allowed, we try to get a new seg right away. |
16222 | saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc) |
16223 | #ifdef MULTIPLE_HEAPS |
16224 | , this |
16225 | #endif //MULTIPLE_HEAPS |
16226 | ); |
16227 | } |
16228 | |
16229 | return (saved_loh_segment_no_gc != 0); |
16230 | } |
16231 | |
16232 | BOOL gc_heap::loh_allocated_for_no_gc() |
16233 | { |
16234 | if (!saved_loh_segment_no_gc) |
16235 | return FALSE; |
16236 | |
16237 | heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1)); |
16238 | do |
16239 | { |
16240 | if (seg == saved_loh_segment_no_gc) |
16241 | { |
16242 | return FALSE; |
16243 | } |
16244 | seg = heap_segment_next (seg); |
16245 | } while (seg); |
16246 | |
16247 | return TRUE; |
16248 | } |
16249 | |
16250 | BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg) |
16251 | { |
16252 | uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc; |
16253 | assert (end_committed <= heap_segment_reserved (seg)); |
16254 | return (grow_heap_segment (seg, end_committed)); |
16255 | } |
16256 | |
16257 | void gc_heap::thread_no_gc_loh_segments() |
16258 | { |
16259 | #ifdef MULTIPLE_HEAPS |
16260 | for (int i = 0; i < n_heaps; i++) |
16261 | { |
16262 | gc_heap* hp = g_heaps[i]; |
16263 | if (hp->loh_allocated_for_no_gc()) |
16264 | { |
16265 | hp->thread_loh_segment (hp->saved_loh_segment_no_gc); |
16266 | hp->saved_loh_segment_no_gc = 0; |
16267 | } |
16268 | } |
16269 | #else //MULTIPLE_HEAPS |
16270 | if (loh_allocated_for_no_gc()) |
16271 | { |
16272 | thread_loh_segment (saved_loh_segment_no_gc); |
16273 | saved_loh_segment_no_gc = 0; |
16274 | } |
16275 | #endif //MULTIPLE_HEAPS |
16276 | } |
16277 | |
16278 | void gc_heap::set_loh_allocations_for_no_gc() |
16279 | { |
16280 | if (current_no_gc_region_info.loh_allocation_size != 0) |
16281 | { |
16282 | dynamic_data* dd = dynamic_data_of (max_generation + 1); |
16283 | dd_new_allocation (dd) = loh_allocation_no_gc; |
16284 | dd_gc_new_allocation (dd) = dd_new_allocation (dd); |
16285 | } |
16286 | } |
16287 | |
16288 | void gc_heap::set_soh_allocations_for_no_gc() |
16289 | { |
16290 | if (current_no_gc_region_info.soh_allocation_size != 0) |
16291 | { |
16292 | dynamic_data* dd = dynamic_data_of (0); |
16293 | dd_new_allocation (dd) = soh_allocation_no_gc; |
16294 | dd_gc_new_allocation (dd) = dd_new_allocation (dd); |
16295 | #ifdef MULTIPLE_HEAPS |
16296 | alloc_context_count = 0; |
16297 | #endif //MULTIPLE_HEAPS |
16298 | } |
16299 | } |
16300 | |
16301 | void gc_heap::set_allocations_for_no_gc() |
16302 | { |
16303 | #ifdef MULTIPLE_HEAPS |
16304 | for (int i = 0; i < n_heaps; i++) |
16305 | { |
16306 | gc_heap* hp = g_heaps[i]; |
16307 | hp->set_loh_allocations_for_no_gc(); |
16308 | hp->set_soh_allocations_for_no_gc(); |
16309 | } |
16310 | #else //MULTIPLE_HEAPS |
16311 | set_loh_allocations_for_no_gc(); |
16312 | set_soh_allocations_for_no_gc(); |
16313 | #endif //MULTIPLE_HEAPS |
16314 | } |
16315 | |
16316 | BOOL gc_heap::should_proceed_for_no_gc() |
16317 | { |
16318 | BOOL gc_requested = FALSE; |
16319 | BOOL loh_full_gc_requested = FALSE; |
16320 | BOOL soh_full_gc_requested = FALSE; |
16321 | BOOL no_gc_requested = FALSE; |
16322 | BOOL get_new_loh_segments = FALSE; |
16323 | |
16324 | if (current_no_gc_region_info.soh_allocation_size) |
16325 | { |
16326 | #ifdef MULTIPLE_HEAPS |
16327 | for (int i = 0; i < n_heaps; i++) |
16328 | { |
16329 | gc_heap* hp = g_heaps[i]; |
16330 | if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc) |
16331 | { |
16332 | gc_requested = TRUE; |
16333 | break; |
16334 | } |
16335 | } |
16336 | #else //MULTIPLE_HEAPS |
16337 | if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc) |
16338 | gc_requested = TRUE; |
16339 | #endif //MULTIPLE_HEAPS |
16340 | |
16341 | if (!gc_requested) |
16342 | { |
16343 | #ifdef MULTIPLE_HEAPS |
16344 | for (int i = 0; i < n_heaps; i++) |
16345 | { |
16346 | gc_heap* hp = g_heaps[i]; |
16347 | if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc)))) |
16348 | { |
16349 | soh_full_gc_requested = TRUE; |
16350 | break; |
16351 | } |
16352 | } |
16353 | #else //MULTIPLE_HEAPS |
16354 | if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc))) |
16355 | soh_full_gc_requested = TRUE; |
16356 | #endif //MULTIPLE_HEAPS |
16357 | } |
16358 | } |
16359 | |
16360 | if (!current_no_gc_region_info.minimal_gc_p && gc_requested) |
16361 | { |
16362 | soh_full_gc_requested = TRUE; |
16363 | } |
16364 | |
16365 | no_gc_requested = !(soh_full_gc_requested || gc_requested); |
16366 | |
16367 | if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p) |
16368 | { |
16369 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16370 | goto done; |
16371 | } |
16372 | |
16373 | if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size) |
16374 | { |
16375 | // Check to see if we have enough reserved space. |
16376 | #ifdef MULTIPLE_HEAPS |
16377 | for (int i = 0; i < n_heaps; i++) |
16378 | { |
16379 | gc_heap* hp = g_heaps[i]; |
16380 | if (!hp->find_loh_space_for_no_gc()) |
16381 | { |
16382 | loh_full_gc_requested = TRUE; |
16383 | break; |
16384 | } |
16385 | } |
16386 | #else //MULTIPLE_HEAPS |
16387 | if (!find_loh_space_for_no_gc()) |
16388 | loh_full_gc_requested = TRUE; |
16389 | #endif //MULTIPLE_HEAPS |
16390 | |
16391 | // Check to see if we have committed space. |
16392 | if (!loh_full_gc_requested) |
16393 | { |
16394 | #ifdef MULTIPLE_HEAPS |
16395 | for (int i = 0; i < n_heaps; i++) |
16396 | { |
16397 | gc_heap* hp = g_heaps[i]; |
16398 | if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc)) |
16399 | { |
16400 | loh_full_gc_requested = TRUE; |
16401 | break; |
16402 | } |
16403 | } |
16404 | #else //MULTIPLE_HEAPS |
16405 | if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc)) |
16406 | loh_full_gc_requested = TRUE; |
16407 | #endif //MULTIPLE_HEAPS |
16408 | } |
16409 | } |
16410 | |
16411 | if (loh_full_gc_requested || soh_full_gc_requested) |
16412 | { |
16413 | if (current_no_gc_region_info.minimal_gc_p) |
16414 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16415 | } |
16416 | |
16417 | no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested); |
16418 | |
16419 | if (current_no_gc_region_info.start_status == start_no_gc_success) |
16420 | { |
16421 | if (no_gc_requested) |
16422 | set_allocations_for_no_gc(); |
16423 | } |
16424 | |
16425 | done: |
16426 | |
16427 | if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested) |
16428 | return TRUE; |
16429 | else |
16430 | { |
16431 | // We are done with starting the no_gc_region. |
16432 | current_no_gc_region_info.started = TRUE; |
16433 | return FALSE; |
16434 | } |
16435 | } |
16436 | |
16437 | end_no_gc_region_status gc_heap::end_no_gc_region() |
16438 | { |
16439 | dprintf (1, ("end no gc called" )); |
16440 | |
16441 | end_no_gc_region_status status = end_no_gc_success; |
16442 | |
16443 | if (!(current_no_gc_region_info.started)) |
16444 | status = end_no_gc_not_in_progress; |
16445 | if (current_no_gc_region_info.num_gcs_induced) |
16446 | status = end_no_gc_induced; |
16447 | else if (current_no_gc_region_info.num_gcs) |
16448 | status = end_no_gc_alloc_exceeded; |
16449 | |
16450 | if (settings.pause_mode == pause_no_gc) |
16451 | restore_data_for_no_gc(); |
16452 | |
16453 | // sets current_no_gc_region_info.started to FALSE here. |
16454 | memset (¤t_no_gc_region_info, 0, sizeof (current_no_gc_region_info)); |
16455 | |
16456 | return status; |
16457 | } |
16458 | |
16459 | //update counters |
16460 | void gc_heap::update_collection_counts () |
16461 | { |
16462 | dynamic_data* dd0 = dynamic_data_of (0); |
16463 | dd_gc_clock (dd0) += 1; |
16464 | |
16465 | size_t now = GetHighPrecisionTimeStamp(); |
16466 | |
16467 | for (int i = 0; i <= settings.condemned_generation;i++) |
16468 | { |
16469 | dynamic_data* dd = dynamic_data_of (i); |
16470 | dd_collection_count (dd)++; |
16471 | //this is needed by the linear allocation model |
16472 | if (i == max_generation) |
16473 | dd_collection_count (dynamic_data_of (max_generation+1))++; |
16474 | dd_gc_clock (dd) = dd_gc_clock (dd0); |
16475 | dd_time_clock (dd) = now; |
16476 | } |
16477 | } |
16478 | |
16479 | BOOL gc_heap::expand_soh_with_minimal_gc() |
16480 | { |
16481 | if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc) |
16482 | return TRUE; |
16483 | |
16484 | heap_segment* new_seg = soh_get_segment_to_expand(); |
16485 | if (new_seg) |
16486 | { |
16487 | if (g_gc_card_table != card_table) |
16488 | copy_brick_card_table(); |
16489 | |
16490 | settings.promotion = TRUE; |
16491 | settings.demotion = FALSE; |
16492 | ephemeral_promotion = TRUE; |
16493 | int condemned_gen_number = max_generation - 1; |
16494 | |
16495 | generation* gen = 0; |
16496 | int align_const = get_alignment_constant (TRUE); |
16497 | |
16498 | for (int i = 0; i <= condemned_gen_number; i++) |
16499 | { |
16500 | gen = generation_of (i); |
16501 | saved_ephemeral_plan_start[i] = generation_allocation_start (gen); |
16502 | saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const); |
16503 | } |
16504 | |
16505 | // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2 |
16506 | // and need to make sure that there are no left over bricks from the previous GCs for the space |
16507 | // we just used for gen0 allocation. We will need to go through the bricks for these objects for |
16508 | // ephemeral GCs later. |
16509 | for (size_t b = brick_of (generation_allocation_start (generation_of (0))); |
16510 | b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment))); |
16511 | b++) |
16512 | { |
16513 | set_brick (b, -1); |
16514 | } |
16515 | |
16516 | size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) - |
16517 | generation_allocation_start (generation_of (max_generation - 1))); |
16518 | heap_segment_next (ephemeral_heap_segment) = new_seg; |
16519 | ephemeral_heap_segment = new_seg; |
16520 | uint8_t* start = heap_segment_mem (ephemeral_heap_segment); |
16521 | |
16522 | for (int i = condemned_gen_number; i >= 0; i--) |
16523 | { |
16524 | gen = generation_of (i); |
16525 | size_t gen_start_size = Align (min_obj_size); |
16526 | make_generation (generation_table[i], ephemeral_heap_segment, start, 0); |
16527 | generation_plan_allocation_start (gen) = start; |
16528 | generation_plan_allocation_start_size (gen) = gen_start_size; |
16529 | start += gen_start_size; |
16530 | } |
16531 | heap_segment_used (ephemeral_heap_segment) = start - plug_skew; |
16532 | heap_segment_plan_allocated (ephemeral_heap_segment) = start; |
16533 | |
16534 | fix_generation_bounds (condemned_gen_number, generation_of (0)); |
16535 | |
16536 | dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size; |
16537 | dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation)); |
16538 | |
16539 | adjust_ephemeral_limits(); |
16540 | return TRUE; |
16541 | } |
16542 | else |
16543 | return FALSE; |
16544 | } |
16545 | |
16546 | // Only to be done on the thread that calls restart in a join for server GC |
16547 | // and reset the oom status per heap. |
16548 | void gc_heap::check_and_set_no_gc_oom() |
16549 | { |
16550 | #ifdef MULTIPLE_HEAPS |
16551 | for (int i = 0; i < n_heaps; i++) |
16552 | { |
16553 | gc_heap* hp = g_heaps[i]; |
16554 | if (hp->no_gc_oom_p) |
16555 | { |
16556 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16557 | hp->no_gc_oom_p = false; |
16558 | } |
16559 | } |
16560 | #else |
16561 | if (no_gc_oom_p) |
16562 | { |
16563 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16564 | no_gc_oom_p = false; |
16565 | } |
16566 | #endif //MULTIPLE_HEAPS |
16567 | } |
16568 | |
16569 | void gc_heap::allocate_for_no_gc_after_gc() |
16570 | { |
16571 | if (current_no_gc_region_info.minimal_gc_p) |
16572 | repair_allocation_contexts (TRUE); |
16573 | |
16574 | no_gc_oom_p = false; |
16575 | |
16576 | if (current_no_gc_region_info.start_status != start_no_gc_no_memory) |
16577 | { |
16578 | if (current_no_gc_region_info.soh_allocation_size != 0) |
16579 | { |
16580 | if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) || |
16581 | (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc)))) |
16582 | { |
16583 | no_gc_oom_p = true; |
16584 | } |
16585 | |
16586 | #ifdef MULTIPLE_HEAPS |
16587 | gc_t_join.join(this, gc_join_after_commit_soh_no_gc); |
16588 | if (gc_t_join.joined()) |
16589 | { |
16590 | #endif //MULTIPLE_HEAPS |
16591 | |
16592 | check_and_set_no_gc_oom(); |
16593 | |
16594 | #ifdef MULTIPLE_HEAPS |
16595 | gc_t_join.restart(); |
16596 | } |
16597 | #endif //MULTIPLE_HEAPS |
16598 | } |
16599 | |
16600 | if ((current_no_gc_region_info.start_status == start_no_gc_success) && |
16601 | !(current_no_gc_region_info.minimal_gc_p) && |
16602 | (current_no_gc_region_info.loh_allocation_size != 0)) |
16603 | { |
16604 | gc_policy = policy_compact; |
16605 | saved_loh_segment_no_gc = 0; |
16606 | |
16607 | if (!find_loh_free_for_no_gc()) |
16608 | { |
16609 | heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1)); |
16610 | BOOL found_seg_p = FALSE; |
16611 | while (seg) |
16612 | { |
16613 | if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc) |
16614 | { |
16615 | found_seg_p = TRUE; |
16616 | if (!commit_loh_for_no_gc (seg)) |
16617 | { |
16618 | no_gc_oom_p = true; |
16619 | break; |
16620 | } |
16621 | } |
16622 | seg = heap_segment_next (seg); |
16623 | } |
16624 | |
16625 | if (!found_seg_p) |
16626 | gc_policy = policy_expand; |
16627 | } |
16628 | |
16629 | #ifdef MULTIPLE_HEAPS |
16630 | gc_t_join.join(this, gc_join_expand_loh_no_gc); |
16631 | if (gc_t_join.joined()) |
16632 | { |
16633 | check_and_set_no_gc_oom(); |
16634 | |
16635 | if (current_no_gc_region_info.start_status == start_no_gc_success) |
16636 | { |
16637 | for (int i = 0; i < n_heaps; i++) |
16638 | { |
16639 | gc_heap* hp = g_heaps[i]; |
16640 | if (hp->gc_policy == policy_expand) |
16641 | { |
16642 | hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp); |
16643 | if (!(hp->saved_loh_segment_no_gc)) |
16644 | { |
16645 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16646 | break; |
16647 | } |
16648 | } |
16649 | } |
16650 | } |
16651 | |
16652 | gc_t_join.restart(); |
16653 | } |
16654 | #else //MULTIPLE_HEAPS |
16655 | check_and_set_no_gc_oom(); |
16656 | |
16657 | if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand)) |
16658 | { |
16659 | saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)); |
16660 | if (!saved_loh_segment_no_gc) |
16661 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16662 | } |
16663 | #endif //MULTIPLE_HEAPS |
16664 | |
16665 | if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc) |
16666 | { |
16667 | if (!commit_loh_for_no_gc (saved_loh_segment_no_gc)) |
16668 | { |
16669 | no_gc_oom_p = true; |
16670 | } |
16671 | } |
16672 | } |
16673 | } |
16674 | |
16675 | #ifdef MULTIPLE_HEAPS |
16676 | gc_t_join.join(this, gc_join_final_no_gc); |
16677 | if (gc_t_join.joined()) |
16678 | { |
16679 | #endif //MULTIPLE_HEAPS |
16680 | |
16681 | check_and_set_no_gc_oom(); |
16682 | |
16683 | if (current_no_gc_region_info.start_status == start_no_gc_success) |
16684 | { |
16685 | set_allocations_for_no_gc(); |
16686 | current_no_gc_region_info.started = TRUE; |
16687 | } |
16688 | |
16689 | #ifdef MULTIPLE_HEAPS |
16690 | gc_t_join.restart(); |
16691 | } |
16692 | #endif //MULTIPLE_HEAPS |
16693 | } |
16694 | |
16695 | void gc_heap::init_records() |
16696 | { |
16697 | // An option is to move this to be after we figure out which gen to condemn so we don't |
16698 | // need to clear some generations' data 'cause we know they don't change, but that also means |
16699 | // we can't simply call memset here. |
16700 | memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap)); |
16701 | gc_data_per_heap.heap_index = heap_number; |
16702 | if (heap_number == 0) |
16703 | memset (&gc_data_global, 0, sizeof (gc_data_global)); |
16704 | |
16705 | #ifdef GC_CONFIG_DRIVEN |
16706 | memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc)); |
16707 | #endif //GC_CONFIG_DRIVEN |
16708 | memset (&fgm_result, 0, sizeof (fgm_result)); |
16709 | |
16710 | for (int i = 0; i <= (max_generation + 1); i++) |
16711 | { |
16712 | gc_data_per_heap.gen_data[i].size_before = generation_size (i); |
16713 | generation* gen = generation_of (i); |
16714 | gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen); |
16715 | gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen); |
16716 | } |
16717 | |
16718 | sufficient_gen0_space_p = FALSE; |
16719 | #if defined (_DEBUG) && defined (VERIFY_HEAP) |
16720 | verify_pinned_queue_p = FALSE; |
16721 | #endif // _DEBUG && VERIFY_HEAP |
16722 | } |
16723 | |
16724 | void gc_heap::pm_full_gc_init_or_clear() |
16725 | { |
16726 | // This means the next GC will be a full blocking GC and we need to init. |
16727 | if (settings.condemned_generation == (max_generation - 1)) |
16728 | { |
16729 | if (pm_trigger_full_gc) |
16730 | { |
16731 | #ifdef MULTIPLE_HEAPS |
16732 | do_post_gc(); |
16733 | #endif //MULTIPLE_HEAPS |
16734 | dprintf (GTC_LOG, ("init for PM triggered full GC" )); |
16735 | uint32_t saved_entry_memory_load = settings.entry_memory_load; |
16736 | settings.init_mechanisms(); |
16737 | settings.reason = reason_pm_full_gc; |
16738 | settings.condemned_generation = max_generation; |
16739 | settings.entry_memory_load = saved_entry_memory_load; |
16740 | // Can't assert this since we only check at the end of gen2 GCs, |
16741 | // during gen1 the memory load could have already dropped. |
16742 | // Although arguably we should just turn off PM then... |
16743 | //assert (settings.entry_memory_load >= high_memory_load_th); |
16744 | assert (settings.entry_memory_load > 0); |
16745 | settings.gc_index += 1; |
16746 | do_pre_gc(); |
16747 | } |
16748 | } |
16749 | // This means we are in the progress of a full blocking GC triggered by |
16750 | // this PM mode. |
16751 | else if (settings.reason == reason_pm_full_gc) |
16752 | { |
16753 | assert (settings.condemned_generation == max_generation); |
16754 | assert (pm_trigger_full_gc); |
16755 | pm_trigger_full_gc = false; |
16756 | |
16757 | dprintf (GTC_LOG, ("PM triggered full GC done" )); |
16758 | } |
16759 | } |
16760 | |
16761 | void gc_heap::garbage_collect_pm_full_gc() |
16762 | { |
16763 | assert (settings.condemned_generation == max_generation); |
16764 | assert (settings.reason == reason_pm_full_gc); |
16765 | assert (!settings.concurrent); |
16766 | gc1(); |
16767 | } |
16768 | |
16769 | void gc_heap::garbage_collect (int n) |
16770 | { |
16771 | //reset the number of alloc contexts |
16772 | alloc_contexts_used = 0; |
16773 | |
16774 | fix_allocation_contexts (TRUE); |
16775 | #ifdef MULTIPLE_HEAPS |
16776 | #ifdef JOIN_STATS |
16777 | gc_t_join.start_ts(this); |
16778 | #endif //JOIN_STATS |
16779 | clear_gen0_bricks(); |
16780 | #endif //MULTIPLE_HEAPS |
16781 | |
16782 | if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p) |
16783 | { |
16784 | #ifdef MULTIPLE_HEAPS |
16785 | gc_t_join.join(this, gc_join_minimal_gc); |
16786 | if (gc_t_join.joined()) |
16787 | { |
16788 | #endif //MULTIPLE_HEAPS |
16789 | |
16790 | #ifdef MULTIPLE_HEAPS |
16791 | // this is serialized because we need to get a segment |
16792 | for (int i = 0; i < n_heaps; i++) |
16793 | { |
16794 | if (!(g_heaps[i]->expand_soh_with_minimal_gc())) |
16795 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16796 | } |
16797 | #else |
16798 | if (!expand_soh_with_minimal_gc()) |
16799 | current_no_gc_region_info.start_status = start_no_gc_no_memory; |
16800 | #endif //MULTIPLE_HEAPS |
16801 | |
16802 | update_collection_counts_for_no_gc(); |
16803 | |
16804 | #ifdef MULTIPLE_HEAPS |
16805 | gc_t_join.restart(); |
16806 | } |
16807 | #endif //MULTIPLE_HEAPS |
16808 | |
16809 | goto done; |
16810 | } |
16811 | |
16812 | init_records(); |
16813 | |
16814 | settings.reason = gc_trigger_reason; |
16815 | #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE) |
16816 | num_pinned_objects = 0; |
16817 | #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE |
16818 | |
16819 | #ifdef STRESS_HEAP |
16820 | if (settings.reason == reason_gcstress) |
16821 | { |
16822 | settings.reason = reason_induced; |
16823 | settings.stress_induced = TRUE; |
16824 | } |
16825 | #endif // STRESS_HEAP |
16826 | |
16827 | #ifdef MULTIPLE_HEAPS |
16828 | //align all heaps on the max generation to condemn |
16829 | dprintf (3, ("Joining for max generation to condemn" )); |
16830 | condemned_generation_num = generation_to_condemn (n, |
16831 | &blocking_collection, |
16832 | &elevation_requested, |
16833 | FALSE); |
16834 | gc_t_join.join(this, gc_join_generation_determined); |
16835 | if (gc_t_join.joined()) |
16836 | #endif //MULTIPLE_HEAPS |
16837 | { |
16838 | #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE) |
16839 | //delete old slots from the segment table |
16840 | seg_table->delete_old_slots(); |
16841 | #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE |
16842 | |
16843 | #ifdef MULTIPLE_HEAPS |
16844 | for (int i = 0; i < n_heaps; i++) |
16845 | { |
16846 | gc_heap* hp = g_heaps[i]; |
16847 | // check for card table growth |
16848 | if (g_gc_card_table != hp->card_table) |
16849 | hp->copy_brick_card_table(); |
16850 | |
16851 | hp->rearrange_large_heap_segments(); |
16852 | #ifdef BACKGROUND_GC |
16853 | hp->background_delay_delete_loh_segments(); |
16854 | if (!recursive_gc_sync::background_running_p()) |
16855 | hp->rearrange_small_heap_segments(); |
16856 | #endif //BACKGROUND_GC |
16857 | } |
16858 | #else //MULTIPLE_HEAPS |
16859 | if (g_gc_card_table != card_table) |
16860 | copy_brick_card_table(); |
16861 | |
16862 | rearrange_large_heap_segments(); |
16863 | #ifdef BACKGROUND_GC |
16864 | background_delay_delete_loh_segments(); |
16865 | if (!recursive_gc_sync::background_running_p()) |
16866 | rearrange_small_heap_segments(); |
16867 | #endif //BACKGROUND_GC |
16868 | #endif //MULTIPLE_HEAPS |
16869 | |
16870 | BOOL should_evaluate_elevation = FALSE; |
16871 | BOOL should_do_blocking_collection = FALSE; |
16872 | |
16873 | #ifdef MULTIPLE_HEAPS |
16874 | int gen_max = condemned_generation_num; |
16875 | for (int i = 0; i < n_heaps; i++) |
16876 | { |
16877 | if (gen_max < g_heaps[i]->condemned_generation_num) |
16878 | gen_max = g_heaps[i]->condemned_generation_num; |
16879 | if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested)) |
16880 | should_evaluate_elevation = TRUE; |
16881 | if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection)) |
16882 | should_do_blocking_collection = TRUE; |
16883 | } |
16884 | |
16885 | settings.condemned_generation = gen_max; |
16886 | #else //MULTIPLE_HEAPS |
16887 | settings.condemned_generation = generation_to_condemn (n, |
16888 | &blocking_collection, |
16889 | &elevation_requested, |
16890 | FALSE); |
16891 | should_evaluate_elevation = elevation_requested; |
16892 | should_do_blocking_collection = blocking_collection; |
16893 | #endif //MULTIPLE_HEAPS |
16894 | |
16895 | settings.condemned_generation = joined_generation_to_condemn ( |
16896 | should_evaluate_elevation, |
16897 | n, |
16898 | settings.condemned_generation, |
16899 | &should_do_blocking_collection |
16900 | STRESS_HEAP_ARG(n) |
16901 | ); |
16902 | |
16903 | STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, |
16904 | "condemned generation num: %d\n" , settings.condemned_generation); |
16905 | |
16906 | record_gcs_during_no_gc(); |
16907 | |
16908 | if (settings.condemned_generation > 1) |
16909 | settings.promotion = TRUE; |
16910 | |
16911 | #ifdef HEAP_ANALYZE |
16912 | // At this point we've decided what generation is condemned |
16913 | // See if we've been requested to analyze survivors after the mark phase |
16914 | if (GCToEEInterface::AnalyzeSurvivorsRequested(settings.condemned_generation)) |
16915 | { |
16916 | heap_analyze_enabled = TRUE; |
16917 | } |
16918 | #endif // HEAP_ANALYZE |
16919 | |
16920 | GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced); |
16921 | |
16922 | #ifdef BACKGROUND_GC |
16923 | if ((settings.condemned_generation == max_generation) && |
16924 | (recursive_gc_sync::background_running_p())) |
16925 | { |
16926 | //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work |
16927 | // because we have to collect 0 and 1 properly |
16928 | // in particular, the allocation contexts are gone. |
16929 | // For now, it is simpler to collect max_generation-1 |
16930 | settings.condemned_generation = max_generation - 1; |
16931 | dprintf (GTC_LOG, ("bgc - 1 instead of 2" )); |
16932 | } |
16933 | |
16934 | if ((settings.condemned_generation == max_generation) && |
16935 | (should_do_blocking_collection == FALSE) && |
16936 | gc_can_use_concurrent && |
16937 | !temp_disable_concurrent_p && |
16938 | ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency))) |
16939 | { |
16940 | keep_bgc_threads_p = TRUE; |
16941 | c_write (settings.concurrent, TRUE); |
16942 | } |
16943 | #endif //BACKGROUND_GC |
16944 | |
16945 | settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1; |
16946 | |
16947 | // Call the EE for start of GC work |
16948 | // just one thread for MP GC |
16949 | GCToEEInterface::GcStartWork (settings.condemned_generation, |
16950 | max_generation); |
16951 | |
16952 | // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to |
16953 | // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire |
16954 | // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be |
16955 | // fired in gc1. |
16956 | do_pre_gc(); |
16957 | |
16958 | #ifdef MULTIPLE_HEAPS |
16959 | gc_start_event.Reset(); |
16960 | //start all threads on the roots. |
16961 | dprintf(3, ("Starting all gc threads for gc" )); |
16962 | gc_t_join.restart(); |
16963 | #endif //MULTIPLE_HEAPS |
16964 | } |
16965 | |
16966 | descr_generations (TRUE); |
16967 | |
16968 | #ifdef VERIFY_HEAP |
16969 | if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) && |
16970 | !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_POST_GC_ONLY)) |
16971 | { |
16972 | verify_heap (TRUE); |
16973 | } |
16974 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK) |
16975 | checkGCWriteBarrier(); |
16976 | |
16977 | #endif // VERIFY_HEAP |
16978 | |
16979 | #ifdef BACKGROUND_GC |
16980 | if (settings.concurrent) |
16981 | { |
16982 | // We need to save the settings because we'll need to restore it after each FGC. |
16983 | assert (settings.condemned_generation == max_generation); |
16984 | settings.compaction = FALSE; |
16985 | saved_bgc_settings = settings; |
16986 | |
16987 | #ifdef MULTIPLE_HEAPS |
16988 | if (heap_number == 0) |
16989 | { |
16990 | for (int i = 0; i < n_heaps; i++) |
16991 | { |
16992 | prepare_bgc_thread (g_heaps[i]); |
16993 | } |
16994 | dprintf (2, ("setting bgc_threads_sync_event" )); |
16995 | bgc_threads_sync_event.Set(); |
16996 | } |
16997 | else |
16998 | { |
16999 | bgc_threads_sync_event.Wait(INFINITE, FALSE); |
17000 | dprintf (2, ("bgc_threads_sync_event is signalled" )); |
17001 | } |
17002 | #else |
17003 | prepare_bgc_thread(0); |
17004 | #endif //MULTIPLE_HEAPS |
17005 | |
17006 | #ifdef MULTIPLE_HEAPS |
17007 | gc_t_join.join(this, gc_join_start_bgc); |
17008 | if (gc_t_join.joined()) |
17009 | #endif //MULTIPLE_HEAPS |
17010 | { |
17011 | do_concurrent_p = TRUE; |
17012 | do_ephemeral_gc_p = FALSE; |
17013 | #ifdef MULTIPLE_HEAPS |
17014 | dprintf(2, ("Joined to perform a background GC" )); |
17015 | |
17016 | for (int i = 0; i < n_heaps; i++) |
17017 | { |
17018 | gc_heap* hp = g_heaps[i]; |
17019 | if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array)) |
17020 | { |
17021 | do_concurrent_p = FALSE; |
17022 | break; |
17023 | } |
17024 | else |
17025 | { |
17026 | hp->background_saved_lowest_address = hp->lowest_address; |
17027 | hp->background_saved_highest_address = hp->highest_address; |
17028 | } |
17029 | } |
17030 | #else |
17031 | do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array)); |
17032 | if (do_concurrent_p) |
17033 | { |
17034 | background_saved_lowest_address = lowest_address; |
17035 | background_saved_highest_address = highest_address; |
17036 | } |
17037 | #endif //MULTIPLE_HEAPS |
17038 | |
17039 | if (do_concurrent_p) |
17040 | { |
17041 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
17042 | SoftwareWriteWatch::EnableForGCHeap(); |
17043 | #endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
17044 | |
17045 | #ifdef MULTIPLE_HEAPS |
17046 | for (int i = 0; i < n_heaps; i++) |
17047 | g_heaps[i]->current_bgc_state = bgc_initialized; |
17048 | #else |
17049 | current_bgc_state = bgc_initialized; |
17050 | #endif //MULTIPLE_HEAPS |
17051 | |
17052 | int gen = check_for_ephemeral_alloc(); |
17053 | // always do a gen1 GC before we start BGC. |
17054 | // This is temporary for testing purpose. |
17055 | //int gen = max_generation - 1; |
17056 | dont_restart_ee_p = TRUE; |
17057 | if (gen == -1) |
17058 | { |
17059 | // If we decide to not do a GC before the BGC we need to |
17060 | // restore the gen0 alloc context. |
17061 | #ifdef MULTIPLE_HEAPS |
17062 | for (int i = 0; i < n_heaps; i++) |
17063 | { |
17064 | generation_allocation_pointer (g_heaps[i]->generation_of (0)) = 0; |
17065 | generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0; |
17066 | } |
17067 | #else |
17068 | generation_allocation_pointer (youngest_generation) = 0; |
17069 | generation_allocation_limit (youngest_generation) = 0; |
17070 | #endif //MULTIPLE_HEAPS |
17071 | } |
17072 | else |
17073 | { |
17074 | do_ephemeral_gc_p = TRUE; |
17075 | |
17076 | settings.init_mechanisms(); |
17077 | settings.condemned_generation = gen; |
17078 | settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2; |
17079 | do_pre_gc(); |
17080 | |
17081 | // TODO BACKGROUND_GC need to add the profiling stuff here. |
17082 | dprintf (GTC_LOG, ("doing gen%d before doing a bgc" , gen)); |
17083 | } |
17084 | |
17085 | //clear the cards so they don't bleed in gen 1 during collection |
17086 | // shouldn't this always be done at the beginning of any GC? |
17087 | //clear_card_for_addresses ( |
17088 | // generation_allocation_start (generation_of (0)), |
17089 | // heap_segment_allocated (ephemeral_heap_segment)); |
17090 | |
17091 | if (!do_ephemeral_gc_p) |
17092 | { |
17093 | do_background_gc(); |
17094 | } |
17095 | } |
17096 | else |
17097 | { |
17098 | settings.compaction = TRUE; |
17099 | c_write (settings.concurrent, FALSE); |
17100 | } |
17101 | |
17102 | #ifdef MULTIPLE_HEAPS |
17103 | gc_t_join.restart(); |
17104 | #endif //MULTIPLE_HEAPS |
17105 | } |
17106 | |
17107 | if (do_concurrent_p) |
17108 | { |
17109 | // At this point we are sure we'll be starting a BGC, so save its per heap data here. |
17110 | // global data is only calculated at the end of the GC so we don't need to worry about |
17111 | // FGCs overwriting it. |
17112 | memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap)); |
17113 | memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap)); |
17114 | |
17115 | if (do_ephemeral_gc_p) |
17116 | { |
17117 | dprintf (2, ("GC threads running, doing gen%d GC" , settings.condemned_generation)); |
17118 | |
17119 | gen_to_condemn_reasons.init(); |
17120 | gen_to_condemn_reasons.set_condition (gen_before_bgc); |
17121 | gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons); |
17122 | gc1(); |
17123 | #ifdef MULTIPLE_HEAPS |
17124 | gc_t_join.join(this, gc_join_bgc_after_ephemeral); |
17125 | if (gc_t_join.joined()) |
17126 | #endif //MULTIPLE_HEAPS |
17127 | { |
17128 | #ifdef MULTIPLE_HEAPS |
17129 | do_post_gc(); |
17130 | #endif //MULTIPLE_HEAPS |
17131 | settings = saved_bgc_settings; |
17132 | assert (settings.concurrent); |
17133 | |
17134 | do_background_gc(); |
17135 | |
17136 | #ifdef MULTIPLE_HEAPS |
17137 | gc_t_join.restart(); |
17138 | #endif //MULTIPLE_HEAPS |
17139 | } |
17140 | } |
17141 | } |
17142 | else |
17143 | { |
17144 | dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC" )); |
17145 | gc1(); |
17146 | } |
17147 | } |
17148 | else |
17149 | #endif //BACKGROUND_GC |
17150 | { |
17151 | gc1(); |
17152 | } |
17153 | #ifndef MULTIPLE_HEAPS |
17154 | allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp(); |
17155 | allocation_running_amount = dd_new_allocation (dynamic_data_of (0)); |
17156 | fgn_last_alloc = dd_new_allocation (dynamic_data_of (0)); |
17157 | #endif //MULTIPLE_HEAPS |
17158 | |
17159 | done: |
17160 | if (settings.pause_mode == pause_no_gc) |
17161 | allocate_for_no_gc_after_gc(); |
17162 | |
17163 | } |
17164 | |
17165 | #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos) |
17166 | |
17167 | inline |
17168 | size_t& gc_heap::promoted_bytes(int thread) |
17169 | { |
17170 | #ifdef MULTIPLE_HEAPS |
17171 | return g_promoted [thread*16]; |
17172 | #else //MULTIPLE_HEAPS |
17173 | UNREFERENCED_PARAMETER(thread); |
17174 | return g_promoted; |
17175 | #endif //MULTIPLE_HEAPS |
17176 | } |
17177 | |
17178 | #ifdef INTERIOR_POINTERS |
17179 | heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p) |
17180 | { |
17181 | #ifdef SEG_MAPPING_TABLE |
17182 | heap_segment* seg = seg_mapping_table_segment_of (interior); |
17183 | if (seg) |
17184 | { |
17185 | if (small_segment_only_p && heap_segment_loh_p (seg)) |
17186 | return 0; |
17187 | } |
17188 | return seg; |
17189 | #else //SEG_MAPPING_TABLE |
17190 | #ifdef MULTIPLE_HEAPS |
17191 | for (int i = 0; i < gc_heap::n_heaps; i++) |
17192 | { |
17193 | gc_heap* h = gc_heap::g_heaps [i]; |
17194 | hs = h->find_segment_per_heap (o, small_segment_only_p); |
17195 | if (hs) |
17196 | { |
17197 | break; |
17198 | } |
17199 | } |
17200 | #else |
17201 | { |
17202 | gc_heap* h = pGenGCHeap; |
17203 | hs = h->find_segment_per_heap (o, small_segment_only_p); |
17204 | } |
17205 | #endif //MULTIPLE_HEAPS |
17206 | #endif //SEG_MAPPING_TABLE |
17207 | } |
17208 | |
17209 | heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p) |
17210 | { |
17211 | #ifdef SEG_MAPPING_TABLE |
17212 | return find_segment (interior, small_segment_only_p); |
17213 | #else //SEG_MAPPING_TABLE |
17214 | if (in_range_for_segment (interior, ephemeral_heap_segment)) |
17215 | { |
17216 | return ephemeral_heap_segment; |
17217 | } |
17218 | else |
17219 | { |
17220 | heap_segment* found_seg = 0; |
17221 | |
17222 | { |
17223 | heap_segment* seg = generation_start_segment (generation_of (max_generation)); |
17224 | do |
17225 | { |
17226 | if (in_range_for_segment (interior, seg)) |
17227 | { |
17228 | found_seg = seg; |
17229 | goto end_find_segment; |
17230 | } |
17231 | |
17232 | } while ((seg = heap_segment_next (seg)) != 0); |
17233 | } |
17234 | if (!small_segment_only_p) |
17235 | { |
17236 | #ifdef BACKGROUND_GC |
17237 | { |
17238 | ptrdiff_t delta = 0; |
17239 | heap_segment* seg = segment_of (interior, delta); |
17240 | if (seg && in_range_for_segment (interior, seg)) |
17241 | { |
17242 | found_seg = seg; |
17243 | } |
17244 | goto end_find_segment; |
17245 | } |
17246 | #else //BACKGROUND_GC |
17247 | heap_segment* seg = generation_start_segment (generation_of (max_generation+1)); |
17248 | do |
17249 | { |
17250 | if (in_range_for_segment(interior, seg)) |
17251 | { |
17252 | found_seg = seg; |
17253 | goto end_find_segment; |
17254 | } |
17255 | |
17256 | } while ((seg = heap_segment_next (seg)) != 0); |
17257 | #endif //BACKGROUND_GC |
17258 | } |
17259 | end_find_segment: |
17260 | |
17261 | return found_seg; |
17262 | } |
17263 | #endif //SEG_MAPPING_TABLE |
17264 | } |
17265 | #endif //INTERIOR_POINTERS |
17266 | |
17267 | #if !defined(_DEBUG) && !defined(__GNUC__) |
17268 | inline // This causes link errors if global optimization is off |
17269 | #endif //!_DEBUG && !__GNUC__ |
17270 | gc_heap* gc_heap::heap_of (uint8_t* o) |
17271 | { |
17272 | #ifdef MULTIPLE_HEAPS |
17273 | if (o == 0) |
17274 | return g_heaps [0]; |
17275 | #ifdef SEG_MAPPING_TABLE |
17276 | gc_heap* hp = seg_mapping_table_heap_of (o); |
17277 | return (hp ? hp : g_heaps[0]); |
17278 | #else //SEG_MAPPING_TABLE |
17279 | ptrdiff_t delta = 0; |
17280 | heap_segment* seg = segment_of (o, delta); |
17281 | return (seg ? heap_segment_heap (seg) : g_heaps [0]); |
17282 | #endif //SEG_MAPPING_TABLE |
17283 | #else //MULTIPLE_HEAPS |
17284 | UNREFERENCED_PARAMETER(o); |
17285 | return __this; |
17286 | #endif //MULTIPLE_HEAPS |
17287 | } |
17288 | |
17289 | inline |
17290 | gc_heap* gc_heap::heap_of_gc (uint8_t* o) |
17291 | { |
17292 | #ifdef MULTIPLE_HEAPS |
17293 | if (o == 0) |
17294 | return g_heaps [0]; |
17295 | #ifdef SEG_MAPPING_TABLE |
17296 | gc_heap* hp = seg_mapping_table_heap_of_gc (o); |
17297 | return (hp ? hp : g_heaps[0]); |
17298 | #else //SEG_MAPPING_TABLE |
17299 | ptrdiff_t delta = 0; |
17300 | heap_segment* seg = segment_of (o, delta); |
17301 | return (seg ? heap_segment_heap (seg) : g_heaps [0]); |
17302 | #endif //SEG_MAPPING_TABLE |
17303 | #else //MULTIPLE_HEAPS |
17304 | UNREFERENCED_PARAMETER(o); |
17305 | return __this; |
17306 | #endif //MULTIPLE_HEAPS |
17307 | } |
17308 | |
17309 | #ifdef INTERIOR_POINTERS |
17310 | // will find all heap objects (large and small) |
17311 | uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low) |
17312 | { |
17313 | if (!gen0_bricks_cleared) |
17314 | { |
17315 | #ifdef MULTIPLE_HEAPS |
17316 | assert (!"Should have already been done in server GC" ); |
17317 | #endif //MULTIPLE_HEAPS |
17318 | gen0_bricks_cleared = TRUE; |
17319 | //initialize brick table for gen 0 |
17320 | for (size_t b = brick_of (generation_allocation_start (generation_of (0))); |
17321 | b < brick_of (align_on_brick |
17322 | (heap_segment_allocated (ephemeral_heap_segment))); |
17323 | b++) |
17324 | { |
17325 | set_brick (b, -1); |
17326 | } |
17327 | } |
17328 | #ifdef FFIND_OBJECT |
17329 | //indicate that in the future this needs to be done during allocation |
17330 | #ifdef MULTIPLE_HEAPS |
17331 | gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps; |
17332 | #else |
17333 | gen0_must_clear_bricks = FFIND_DECAY; |
17334 | #endif //MULTIPLE_HEAPS |
17335 | #endif //FFIND_OBJECT |
17336 | |
17337 | int brick_entry = get_brick_entry(brick_of (interior)); |
17338 | if (brick_entry == 0) |
17339 | { |
17340 | // this is a pointer to a large object |
17341 | heap_segment* seg = find_segment_per_heap (interior, FALSE); |
17342 | if (seg |
17343 | #ifdef FEATURE_CONSERVATIVE_GC |
17344 | && (GCConfig::GetConservativeGC() || interior <= heap_segment_allocated(seg)) |
17345 | #endif |
17346 | ) |
17347 | { |
17348 | // If interior falls within the first free object at the beginning of a generation, |
17349 | // we don't have brick entry for it, and we may incorrectly treat it as on large object heap. |
17350 | int align_const = get_alignment_constant (heap_segment_read_only_p (seg) |
17351 | #ifdef FEATURE_CONSERVATIVE_GC |
17352 | || (GCConfig::GetConservativeGC() && !heap_segment_loh_p (seg)) |
17353 | #endif |
17354 | ); |
17355 | //int align_const = get_alignment_constant (heap_segment_read_only_p (seg)); |
17356 | assert (interior < heap_segment_allocated (seg)); |
17357 | |
17358 | uint8_t* o = heap_segment_mem (seg); |
17359 | while (o < heap_segment_allocated (seg)) |
17360 | { |
17361 | uint8_t* next_o = o + Align (size (o), align_const); |
17362 | assert (next_o > o); |
17363 | if ((o <= interior) && (interior < next_o)) |
17364 | return o; |
17365 | o = next_o; |
17366 | } |
17367 | return 0; |
17368 | } |
17369 | else |
17370 | { |
17371 | return 0; |
17372 | } |
17373 | } |
17374 | else if (interior >= low) |
17375 | { |
17376 | heap_segment* seg = find_segment_per_heap (interior, TRUE); |
17377 | if (seg) |
17378 | { |
17379 | #ifdef FEATURE_CONSERVATIVE_GC |
17380 | if (interior >= heap_segment_allocated (seg)) |
17381 | return 0; |
17382 | #else |
17383 | assert (interior < heap_segment_allocated (seg)); |
17384 | #endif |
17385 | uint8_t* o = find_first_object (interior, heap_segment_mem (seg)); |
17386 | return o; |
17387 | } |
17388 | else |
17389 | return 0; |
17390 | } |
17391 | else |
17392 | return 0; |
17393 | } |
17394 | |
17395 | uint8_t* |
17396 | gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high) |
17397 | { |
17398 | uint8_t* old_address = interior; |
17399 | if (!((old_address >= low) && (old_address < high))) |
17400 | return 0; |
17401 | uint8_t* plug = 0; |
17402 | size_t brick = brick_of (old_address); |
17403 | int brick_entry = brick_table [ brick ]; |
17404 | if (brick_entry != 0) |
17405 | { |
17406 | retry: |
17407 | { |
17408 | while (brick_entry < 0) |
17409 | { |
17410 | brick = (brick + brick_entry); |
17411 | brick_entry = brick_table [ brick ]; |
17412 | } |
17413 | uint8_t* old_loc = old_address; |
17414 | uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1), |
17415 | old_loc); |
17416 | if (node <= old_loc) |
17417 | plug = node; |
17418 | else |
17419 | { |
17420 | brick = brick - 1; |
17421 | brick_entry = brick_table [ brick ]; |
17422 | goto retry; |
17423 | } |
17424 | |
17425 | } |
17426 | assert (plug); |
17427 | //find the object by going along the plug |
17428 | uint8_t* o = plug; |
17429 | while (o <= interior) |
17430 | { |
17431 | uint8_t* next_o = o + Align (size (o)); |
17432 | assert (next_o > o); |
17433 | if (next_o > interior) |
17434 | { |
17435 | break; |
17436 | } |
17437 | o = next_o; |
17438 | } |
17439 | assert ((o <= interior) && ((o + Align (size (o))) > interior)); |
17440 | return o; |
17441 | } |
17442 | else |
17443 | { |
17444 | // this is a pointer to a large object |
17445 | heap_segment* seg = find_segment_per_heap (interior, FALSE); |
17446 | if (seg) |
17447 | { |
17448 | assert (interior < heap_segment_allocated (seg)); |
17449 | |
17450 | uint8_t* o = heap_segment_mem (seg); |
17451 | while (o < heap_segment_allocated (seg)) |
17452 | { |
17453 | uint8_t* next_o = o + Align (size (o)); |
17454 | assert (next_o > o); |
17455 | if ((o < interior) && (interior < next_o)) |
17456 | return o; |
17457 | o = next_o; |
17458 | } |
17459 | return 0; |
17460 | } |
17461 | else |
17462 | { |
17463 | return 0; |
17464 | } |
17465 | } |
17466 | } |
17467 | #else //INTERIOR_POINTERS |
17468 | inline |
17469 | uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low) |
17470 | { |
17471 | return o; |
17472 | } |
17473 | #endif //INTERIOR_POINTERS |
17474 | |
17475 | #ifdef MULTIPLE_HEAPS |
17476 | |
17477 | #ifdef MARK_LIST |
17478 | #ifdef GC_CONFIG_DRIVEN |
17479 | #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;} else {mark_list_index++;}} |
17480 | #else //GC_CONFIG_DRIVEN |
17481 | #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;}} |
17482 | #endif //GC_CONFIG_DRIVEN |
17483 | #else //MARK_LIST |
17484 | #define m_boundary(o) {} |
17485 | #endif //MARK_LIST |
17486 | |
17487 | #define m_boundary_fullgc(o) {} |
17488 | |
17489 | #else //MULTIPLE_HEAPS |
17490 | |
17491 | #ifdef MARK_LIST |
17492 | #ifdef GC_CONFIG_DRIVEN |
17493 | #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;} else {mark_list_index++;} if (slow > o) slow = o; if (shigh < o) shigh = o;} |
17494 | #else |
17495 | #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;} |
17496 | #endif //GC_CONFIG_DRIVEN |
17497 | #else //MARK_LIST |
17498 | #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;} |
17499 | #endif //MARK_LIST |
17500 | |
17501 | #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;} |
17502 | |
17503 | #endif //MULTIPLE_HEAPS |
17504 | |
17505 | #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable() |
17506 | |
17507 | inline |
17508 | BOOL gc_heap::gc_mark1 (uint8_t* o) |
17509 | { |
17510 | BOOL marked = !marked (o); |
17511 | set_marked (o); |
17512 | dprintf (3, ("*%Ix*, newly marked: %d" , (size_t)o, marked)); |
17513 | return marked; |
17514 | } |
17515 | |
17516 | inline |
17517 | BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high) |
17518 | { |
17519 | BOOL marked = FALSE; |
17520 | if ((o >= low) && (o < high)) |
17521 | marked = gc_mark1 (o); |
17522 | #ifdef MULTIPLE_HEAPS |
17523 | else if (o) |
17524 | { |
17525 | //find the heap |
17526 | gc_heap* hp = heap_of_gc (o); |
17527 | assert (hp); |
17528 | if ((o >= hp->gc_low) && (o < hp->gc_high)) |
17529 | marked = gc_mark1 (o); |
17530 | } |
17531 | #ifdef SNOOP_STATS |
17532 | snoop_stat.objects_checked_count++; |
17533 | |
17534 | if (marked) |
17535 | { |
17536 | snoop_stat.objects_marked_count++; |
17537 | } |
17538 | if (!o) |
17539 | { |
17540 | snoop_stat.zero_ref_count++; |
17541 | } |
17542 | |
17543 | #endif //SNOOP_STATS |
17544 | #endif //MULTIPLE_HEAPS |
17545 | return marked; |
17546 | } |
17547 | |
17548 | #ifdef BACKGROUND_GC |
17549 | |
17550 | inline |
17551 | BOOL gc_heap::background_marked (uint8_t* o) |
17552 | { |
17553 | return mark_array_marked (o); |
17554 | } |
17555 | inline |
17556 | BOOL gc_heap::background_mark1 (uint8_t* o) |
17557 | { |
17558 | BOOL to_mark = !mark_array_marked (o); |
17559 | |
17560 | dprintf (3, ("b*%Ix*b(%d)" , (size_t)o, (to_mark ? 1 : 0))); |
17561 | if (to_mark) |
17562 | { |
17563 | mark_array_set_marked (o); |
17564 | dprintf (4, ("n*%Ix*n" , (size_t)o)); |
17565 | return TRUE; |
17566 | } |
17567 | else |
17568 | return FALSE; |
17569 | } |
17570 | |
17571 | // TODO: we could consider filtering out NULL's here instead of going to |
17572 | // look for it on other heaps |
17573 | inline |
17574 | BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high) |
17575 | { |
17576 | BOOL marked = FALSE; |
17577 | if ((o >= low) && (o < high)) |
17578 | marked = background_mark1 (o); |
17579 | #ifdef MULTIPLE_HEAPS |
17580 | else if (o) |
17581 | { |
17582 | //find the heap |
17583 | gc_heap* hp = heap_of (o); |
17584 | assert (hp); |
17585 | if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address)) |
17586 | marked = background_mark1 (o); |
17587 | } |
17588 | #endif //MULTIPLE_HEAPS |
17589 | return marked; |
17590 | } |
17591 | |
17592 | #endif //BACKGROUND_GC |
17593 | |
17594 | inline |
17595 | uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f) |
17596 | { |
17597 | if (seg == ephemeral_heap_segment) |
17598 | return f; |
17599 | else |
17600 | return heap_segment_allocated (seg); |
17601 | } |
17602 | |
17603 | #define new_start() {if (ppstop <= start) {break;} else {parm = start}} |
17604 | #define ignore_start 0 |
17605 | #define use_start 1 |
17606 | |
17607 | #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp) \ |
17608 | { \ |
17609 | CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt)); \ |
17610 | CGCDescSeries* cur = map->GetHighestSeries(); \ |
17611 | ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries(); \ |
17612 | \ |
17613 | if (cnt >= 0) \ |
17614 | { \ |
17615 | CGCDescSeries* last = map->GetLowestSeries(); \ |
17616 | uint8_t** parm = 0; \ |
17617 | do \ |
17618 | { \ |
17619 | assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset())); \ |
17620 | parm = (uint8_t**)((o) + cur->GetSeriesOffset()); \ |
17621 | uint8_t** ppstop = \ |
17622 | (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\ |
17623 | if (!start_useful || (uint8_t*)ppstop > (start)) \ |
17624 | { \ |
17625 | if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\ |
17626 | while (parm < ppstop) \ |
17627 | { \ |
17628 | {exp} \ |
17629 | parm++; \ |
17630 | } \ |
17631 | } \ |
17632 | cur--; \ |
17633 | \ |
17634 | } while (cur >= last); \ |
17635 | } \ |
17636 | else \ |
17637 | { \ |
17638 | /* Handle the repeating case - array of valuetypes */ \ |
17639 | uint8_t** parm = (uint8_t**)((o) + cur->startoffset); \ |
17640 | if (start_useful && start > (uint8_t*)parm) \ |
17641 | { \ |
17642 | ptrdiff_t cs = mt->RawGetComponentSize(); \ |
17643 | parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \ |
17644 | } \ |
17645 | while ((uint8_t*)parm < ((o)+(size)-plug_skew)) \ |
17646 | { \ |
17647 | for (ptrdiff_t __i = 0; __i > cnt; __i--) \ |
17648 | { \ |
17649 | HALF_SIZE_T skip = cur->val_serie[__i].skip; \ |
17650 | HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs; \ |
17651 | uint8_t** ppstop = parm + nptrs; \ |
17652 | if (!start_useful || (uint8_t*)ppstop > (start)) \ |
17653 | { \ |
17654 | if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start); \ |
17655 | do \ |
17656 | { \ |
17657 | {exp} \ |
17658 | parm++; \ |
17659 | } while (parm < ppstop); \ |
17660 | } \ |
17661 | parm = (uint8_t**)((uint8_t*)ppstop + skip); \ |
17662 | } \ |
17663 | } \ |
17664 | } \ |
17665 | } |
17666 | |
17667 | #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); } |
17668 | |
17669 | // 1 thing to note about this macro: |
17670 | // 1) you can use *parm safely but in general you don't want to use parm |
17671 | // because for the collectible types it's not an address on the managed heap. |
17672 | #ifndef COLLECTIBLE_CLASS |
17673 | #define go_through_object_cl(mt,o,size,parm,exp) \ |
17674 | { \ |
17675 | if (header(o)->ContainsPointers()) \ |
17676 | { \ |
17677 | go_through_object_nostart(mt,o,size,parm,exp); \ |
17678 | } \ |
17679 | } |
17680 | #else //COLLECTIBLE_CLASS |
17681 | #define go_through_object_cl(mt,o,size,parm,exp) \ |
17682 | { \ |
17683 | if (header(o)->Collectible()) \ |
17684 | { \ |
17685 | uint8_t* class_obj = get_class_object (o); \ |
17686 | uint8_t** parm = &class_obj; \ |
17687 | do {exp} while (false); \ |
17688 | } \ |
17689 | if (header(o)->ContainsPointers()) \ |
17690 | { \ |
17691 | go_through_object_nostart(mt,o,size,parm,exp); \ |
17692 | } \ |
17693 | } |
17694 | #endif //COLLECTIBLE_CLASS |
17695 | |
17696 | // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called. |
17697 | void gc_heap::enque_pinned_plug (uint8_t* plug, |
17698 | BOOL save_pre_plug_info_p, |
17699 | uint8_t* last_object_in_last_plug) |
17700 | { |
17701 | if (mark_stack_array_length <= mark_stack_tos) |
17702 | { |
17703 | if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH)) |
17704 | { |
17705 | // we don't want to continue here due to security |
17706 | // risks. This happens very rarely and fixing it in the |
17707 | // way so that we can continue is a bit involved and will |
17708 | // not be done in Dev10. |
17709 | GCToEEInterface::HandleFatalError(CORINFO_EXCEPTION_GC); |
17710 | } |
17711 | } |
17712 | |
17713 | dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d" , |
17714 | 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))); |
17715 | mark& m = mark_stack_array[mark_stack_tos]; |
17716 | m.first = plug; |
17717 | // Must be set now because if we have a short object we'll need the value of saved_pre_p. |
17718 | m.saved_pre_p = save_pre_plug_info_p; |
17719 | |
17720 | if (save_pre_plug_info_p) |
17721 | { |
17722 | #ifdef SHORT_PLUGS |
17723 | BOOL is_padded = is_plug_padded (last_object_in_last_plug); |
17724 | if (is_padded) |
17725 | clear_plug_padded (last_object_in_last_plug); |
17726 | #endif //SHORT_PLUGS |
17727 | memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair)); |
17728 | #ifdef SHORT_PLUGS |
17729 | if (is_padded) |
17730 | set_plug_padded (last_object_in_last_plug); |
17731 | #endif //SHORT_PLUGS |
17732 | |
17733 | memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair)); |
17734 | |
17735 | // If the last object in the last plug is too short, it requires special handling. |
17736 | size_t last_obj_size = plug - last_object_in_last_plug; |
17737 | if (last_obj_size < min_pre_pin_obj_size) |
17738 | { |
17739 | record_interesting_data_point (idp_pre_short); |
17740 | #ifdef SHORT_PLUGS |
17741 | if (is_padded) |
17742 | record_interesting_data_point (idp_pre_short_padded); |
17743 | #endif //SHORT_PLUGS |
17744 | dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!" , |
17745 | last_object_in_last_plug, plug)); |
17746 | // Need to set the short bit regardless of having refs or not because we need to |
17747 | // indicate that this object is not walkable. |
17748 | m.set_pre_short(); |
17749 | |
17750 | #ifdef COLLECTIBLE_CLASS |
17751 | if (is_collectible (last_object_in_last_plug)) |
17752 | { |
17753 | m.set_pre_short_collectible(); |
17754 | } |
17755 | #endif //COLLECTIBLE_CLASS |
17756 | |
17757 | if (contain_pointers (last_object_in_last_plug)) |
17758 | { |
17759 | dprintf (3, ("short object: %Ix(%Ix)" , last_object_in_last_plug, last_obj_size)); |
17760 | |
17761 | go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval, |
17762 | { |
17763 | size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*); |
17764 | dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap" , (uint8_t*)pval, *pval, gap_offset)); |
17765 | m.set_pre_short_bit (gap_offset); |
17766 | } |
17767 | ); |
17768 | } |
17769 | } |
17770 | } |
17771 | |
17772 | m.saved_post_p = FALSE; |
17773 | } |
17774 | |
17775 | void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug) |
17776 | { |
17777 | UNREFERENCED_PARAMETER(last_pinned_plug); |
17778 | |
17779 | mark& m = mark_stack_array[mark_stack_tos - 1]; |
17780 | assert (last_pinned_plug == m.first); |
17781 | m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]); |
17782 | |
17783 | #ifdef SHORT_PLUGS |
17784 | BOOL is_padded = is_plug_padded (last_object_in_last_plug); |
17785 | if (is_padded) |
17786 | clear_plug_padded (last_object_in_last_plug); |
17787 | #endif //SHORT_PLUGS |
17788 | memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair)); |
17789 | #ifdef SHORT_PLUGS |
17790 | if (is_padded) |
17791 | set_plug_padded (last_object_in_last_plug); |
17792 | #endif //SHORT_PLUGS |
17793 | |
17794 | memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair)); |
17795 | |
17796 | // This is important - we need to clear all bits here except the last one. |
17797 | m.saved_post_p = TRUE; |
17798 | |
17799 | #ifdef _DEBUG |
17800 | m.saved_post_plug_debug.gap = 1; |
17801 | #endif //_DEBUG |
17802 | |
17803 | dprintf (3, ("PP %Ix has NP %Ix right after" , last_pinned_plug, post_plug)); |
17804 | |
17805 | size_t last_obj_size = post_plug - last_object_in_last_plug; |
17806 | if (last_obj_size < min_pre_pin_obj_size) |
17807 | { |
17808 | dprintf (3, ("PP %Ix last obj %Ix is too short" , last_pinned_plug, last_object_in_last_plug)); |
17809 | record_interesting_data_point (idp_post_short); |
17810 | #ifdef SHORT_PLUGS |
17811 | if (is_padded) |
17812 | record_interesting_data_point (idp_post_short_padded); |
17813 | #endif //SHORT_PLUGS |
17814 | m.set_post_short(); |
17815 | #if defined (_DEBUG) && defined (VERIFY_HEAP) |
17816 | verify_pinned_queue_p = TRUE; |
17817 | #endif // _DEBUG && VERIFY_HEAP |
17818 | |
17819 | #ifdef COLLECTIBLE_CLASS |
17820 | if (is_collectible (last_object_in_last_plug)) |
17821 | { |
17822 | m.set_post_short_collectible(); |
17823 | } |
17824 | #endif //COLLECTIBLE_CLASS |
17825 | |
17826 | if (contain_pointers (last_object_in_last_plug)) |
17827 | { |
17828 | dprintf (3, ("short object: %Ix(%Ix)" , last_object_in_last_plug, last_obj_size)); |
17829 | |
17830 | // TODO: since we won't be able to walk this object in relocation, we still need to |
17831 | // take care of collectible assemblies here. |
17832 | go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval, |
17833 | { |
17834 | size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*); |
17835 | dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap" , (uint8_t*)pval, *pval, gap_offset)); |
17836 | m.set_post_short_bit (gap_offset); |
17837 | } |
17838 | ); |
17839 | } |
17840 | } |
17841 | } |
17842 | |
17843 | //#define PREFETCH |
17844 | #ifdef PREFETCH |
17845 | __declspec(naked) void __fastcall Prefetch(void* addr) |
17846 | { |
17847 | __asm { |
17848 | PREFETCHT0 [ECX] |
17849 | ret |
17850 | }; |
17851 | } |
17852 | #else //PREFETCH |
17853 | inline void Prefetch (void* addr) |
17854 | { |
17855 | UNREFERENCED_PARAMETER(addr); |
17856 | } |
17857 | #endif //PREFETCH |
17858 | #ifdef MH_SC_MARK |
17859 | inline |
17860 | VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index) |
17861 | { |
17862 | return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index]; |
17863 | } |
17864 | |
17865 | #endif //MH_SC_MARK |
17866 | |
17867 | #define stolen 2 |
17868 | #define partial 1 |
17869 | #define partial_object 3 |
17870 | inline |
17871 | uint8_t* ref_from_slot (uint8_t* r) |
17872 | { |
17873 | return (uint8_t*)((size_t)r & ~(stolen | partial)); |
17874 | } |
17875 | inline |
17876 | BOOL stolen_p (uint8_t* r) |
17877 | { |
17878 | return (((size_t)r&2) && !((size_t)r&1)); |
17879 | } |
17880 | inline |
17881 | BOOL ready_p (uint8_t* r) |
17882 | { |
17883 | return ((size_t)r != 1); |
17884 | } |
17885 | inline |
17886 | BOOL partial_p (uint8_t* r) |
17887 | { |
17888 | return (((size_t)r&1) && !((size_t)r&2)); |
17889 | } |
17890 | inline |
17891 | BOOL straight_ref_p (uint8_t* r) |
17892 | { |
17893 | return (!stolen_p (r) && !partial_p (r)); |
17894 | } |
17895 | inline |
17896 | BOOL partial_object_p (uint8_t* r) |
17897 | { |
17898 | return (((size_t)r & partial_object) == partial_object); |
17899 | } |
17900 | inline |
17901 | BOOL ref_p (uint8_t* r) |
17902 | { |
17903 | return (straight_ref_p (r) || partial_object_p (r)); |
17904 | } |
17905 | |
17906 | void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL) |
17907 | { |
17908 | SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array; |
17909 | SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length]; |
17910 | SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos; |
17911 | #ifdef SORT_MARK_STACK |
17912 | SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base; |
17913 | #endif //SORT_MARK_STACK |
17914 | |
17915 | // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't |
17916 | // update mark list. |
17917 | BOOL full_p = (settings.condemned_generation == max_generation); |
17918 | |
17919 | assert ((start >= oo) && (start < oo+size(oo))); |
17920 | |
17921 | #ifndef MH_SC_MARK |
17922 | *mark_stack_tos = oo; |
17923 | #endif //!MH_SC_MARK |
17924 | |
17925 | while (1) |
17926 | { |
17927 | #ifdef MULTIPLE_HEAPS |
17928 | #else //MULTIPLE_HEAPS |
17929 | const int thread = 0; |
17930 | #endif //MULTIPLE_HEAPS |
17931 | |
17932 | if (oo && ((size_t)oo != 4)) |
17933 | { |
17934 | size_t s = 0; |
17935 | if (stolen_p (oo)) |
17936 | { |
17937 | --mark_stack_tos; |
17938 | goto next_level; |
17939 | } |
17940 | else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*)))) |
17941 | { |
17942 | BOOL overflow_p = FALSE; |
17943 | |
17944 | if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1)) |
17945 | { |
17946 | size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0); |
17947 | if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1)) |
17948 | { |
17949 | overflow_p = TRUE; |
17950 | } |
17951 | } |
17952 | |
17953 | if (overflow_p == FALSE) |
17954 | { |
17955 | dprintf(3,("pushing mark for %Ix " , (size_t)oo)); |
17956 | |
17957 | go_through_object_cl (method_table(oo), oo, s, ppslot, |
17958 | { |
17959 | uint8_t* o = *ppslot; |
17960 | Prefetch(o); |
17961 | if (gc_mark (o, gc_low, gc_high)) |
17962 | { |
17963 | if (full_p) |
17964 | { |
17965 | m_boundary_fullgc (o); |
17966 | } |
17967 | else |
17968 | { |
17969 | m_boundary (o); |
17970 | } |
17971 | size_t obj_size = size (o); |
17972 | promoted_bytes (thread) += obj_size; |
17973 | if (contain_pointers_or_collectible (o)) |
17974 | { |
17975 | *(mark_stack_tos++) = o; |
17976 | } |
17977 | } |
17978 | } |
17979 | ); |
17980 | } |
17981 | else |
17982 | { |
17983 | dprintf(3,("mark stack overflow for object %Ix " , (size_t)oo)); |
17984 | min_overflow_address = min (min_overflow_address, oo); |
17985 | max_overflow_address = max (max_overflow_address, oo); |
17986 | } |
17987 | } |
17988 | else |
17989 | { |
17990 | if (partial_p (oo)) |
17991 | { |
17992 | start = ref_from_slot (oo); |
17993 | oo = ref_from_slot (*(--mark_stack_tos)); |
17994 | dprintf (4, ("oo: %Ix, start: %Ix\n" , (size_t)oo, (size_t)start)); |
17995 | assert ((oo < start) && (start < (oo + size (oo)))); |
17996 | } |
17997 | #ifdef COLLECTIBLE_CLASS |
17998 | else |
17999 | { |
18000 | // If there's a class object, push it now. We are guaranteed to have the slot since |
18001 | // we just popped one object off. |
18002 | if (is_collectible (oo)) |
18003 | { |
18004 | uint8_t* class_obj = get_class_object (oo); |
18005 | if (gc_mark (class_obj, gc_low, gc_high)) |
18006 | { |
18007 | if (full_p) |
18008 | { |
18009 | m_boundary_fullgc (class_obj); |
18010 | } |
18011 | else |
18012 | { |
18013 | m_boundary (class_obj); |
18014 | } |
18015 | |
18016 | size_t obj_size = size (class_obj); |
18017 | promoted_bytes (thread) += obj_size; |
18018 | *(mark_stack_tos++) = class_obj; |
18019 | // The code below expects that the oo is still stored in the stack slot that was |
18020 | // just popped and it "pushes" it back just by incrementing the mark_stack_tos. |
18021 | // But the class_obj has just overwritten that stack slot and so the oo needs to |
18022 | // be stored to the new slot that's pointed to by the mark_stack_tos. |
18023 | *mark_stack_tos = oo; |
18024 | } |
18025 | } |
18026 | |
18027 | if (!contain_pointers (oo)) |
18028 | { |
18029 | goto next_level; |
18030 | } |
18031 | } |
18032 | #endif //COLLECTIBLE_CLASS |
18033 | |
18034 | s = size (oo); |
18035 | |
18036 | BOOL overflow_p = FALSE; |
18037 | |
18038 | if (mark_stack_tos + (num_partial_refs + 2) >= mark_stack_limit) |
18039 | { |
18040 | overflow_p = TRUE; |
18041 | } |
18042 | if (overflow_p == FALSE) |
18043 | { |
18044 | dprintf(3,("pushing mark for %Ix " , (size_t)oo)); |
18045 | |
18046 | //push the object and its current |
18047 | SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos; |
18048 | mark_stack_tos++; |
18049 | #ifdef MH_SC_MARK |
18050 | *(place-1) = 0; |
18051 | *(place) = (uint8_t*)partial; |
18052 | #endif //MH_SC_MARK |
18053 | int i = num_partial_refs; |
18054 | uint8_t* ref_to_continue = 0; |
18055 | |
18056 | go_through_object (method_table(oo), oo, s, ppslot, |
18057 | start, use_start, (oo + s), |
18058 | { |
18059 | uint8_t* o = *ppslot; |
18060 | Prefetch(o); |
18061 | if (gc_mark (o, gc_low, gc_high)) |
18062 | { |
18063 | if (full_p) |
18064 | { |
18065 | m_boundary_fullgc (o); |
18066 | } |
18067 | else |
18068 | { |
18069 | m_boundary (o); |
18070 | } |
18071 | size_t obj_size = size (o); |
18072 | promoted_bytes (thread) += obj_size; |
18073 | if (contain_pointers_or_collectible (o)) |
18074 | { |
18075 | *(mark_stack_tos++) = o; |
18076 | if (--i == 0) |
18077 | { |
18078 | ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial); |
18079 | goto more_to_do; |
18080 | } |
18081 | |
18082 | } |
18083 | } |
18084 | |
18085 | } |
18086 | ); |
18087 | //we are finished with this object |
18088 | assert (ref_to_continue == 0); |
18089 | #ifdef MH_SC_MARK |
18090 | assert ((*(place-1)) == (uint8_t*)0); |
18091 | #else //MH_SC_MARK |
18092 | *(place-1) = 0; |
18093 | #endif //MH_SC_MARK |
18094 | *place = 0; |
18095 | // shouldn't we decrease tos by 2 here?? |
18096 | |
18097 | more_to_do: |
18098 | if (ref_to_continue) |
18099 | { |
18100 | //update the start |
18101 | #ifdef MH_SC_MARK |
18102 | assert ((*(place-1)) == (uint8_t*)0); |
18103 | *(place-1) = (uint8_t*)((size_t)oo | partial_object); |
18104 | assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2)); |
18105 | #endif //MH_SC_MARK |
18106 | *place = ref_to_continue; |
18107 | } |
18108 | } |
18109 | else |
18110 | { |
18111 | dprintf(3,("mark stack overflow for object %Ix " , (size_t)oo)); |
18112 | min_overflow_address = min (min_overflow_address, oo); |
18113 | max_overflow_address = max (max_overflow_address, oo); |
18114 | } |
18115 | } |
18116 | #ifdef SORT_MARK_STACK |
18117 | if (mark_stack_tos > sorted_tos + mark_stack_array_length/8) |
18118 | { |
18119 | rqsort1 (sorted_tos, mark_stack_tos-1); |
18120 | sorted_tos = mark_stack_tos-1; |
18121 | } |
18122 | #endif //SORT_MARK_STACK |
18123 | } |
18124 | next_level: |
18125 | if (!(mark_stack_empty_p())) |
18126 | { |
18127 | oo = *(--mark_stack_tos); |
18128 | start = oo; |
18129 | |
18130 | #ifdef SORT_MARK_STACK |
18131 | sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos); |
18132 | #endif //SORT_MARK_STACK |
18133 | } |
18134 | else |
18135 | break; |
18136 | } |
18137 | } |
18138 | |
18139 | #ifdef MH_SC_MARK |
18140 | BOOL same_numa_node_p (int hn1, int hn2) |
18141 | { |
18142 | return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2)); |
18143 | } |
18144 | |
18145 | int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps) |
18146 | { |
18147 | int hn = (current_buddy+1)%n_heaps; |
18148 | while (hn != current_buddy) |
18149 | { |
18150 | if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn))) |
18151 | return hn; |
18152 | hn = (hn+1)%n_heaps; |
18153 | } |
18154 | return current_buddy; |
18155 | } |
18156 | |
18157 | void |
18158 | gc_heap::mark_steal() |
18159 | { |
18160 | mark_stack_busy() = 0; |
18161 | //clear the mark stack in the snooping range |
18162 | for (int i = 0; i < max_snoop_level; i++) |
18163 | { |
18164 | ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0; |
18165 | } |
18166 | |
18167 | //pick the next heap as our buddy |
18168 | int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps); |
18169 | |
18170 | #ifdef SNOOP_STATS |
18171 | dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d" , settings.gc_index, heap_number, (heap_number+1)%n_heaps)); |
18172 | uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp(); |
18173 | #endif //SNOOP_STATS |
18174 | |
18175 | int idle_loop_count = 0; |
18176 | int first_not_ready_level = 0; |
18177 | |
18178 | while (1) |
18179 | { |
18180 | gc_heap* hp = g_heaps [thpn]; |
18181 | int level = first_not_ready_level; |
18182 | first_not_ready_level = 0; |
18183 | |
18184 | while (check_next_mark_stack (hp) && (level < (max_snoop_level-1))) |
18185 | { |
18186 | idle_loop_count = 0; |
18187 | #ifdef SNOOP_STATS |
18188 | snoop_stat.busy_count++; |
18189 | dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix" , |
18190 | heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level])); |
18191 | #endif //SNOOP_STATS |
18192 | |
18193 | uint8_t* o = ref_mark_stack (hp, level); |
18194 | |
18195 | uint8_t* start = o; |
18196 | if (ref_p (o)) |
18197 | { |
18198 | mark_stack_busy() = 1; |
18199 | |
18200 | BOOL success = TRUE; |
18201 | uint8_t* next = (ref_mark_stack (hp, level+1)); |
18202 | if (ref_p (next)) |
18203 | { |
18204 | if (((size_t)o > 4) && !partial_object_p (o)) |
18205 | { |
18206 | //this is a normal object, not a partial mark tuple |
18207 | //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o); |
18208 | success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o); |
18209 | #ifdef SNOOP_STATS |
18210 | snoop_stat.interlocked_count++; |
18211 | if (success) |
18212 | snoop_stat.normal_count++; |
18213 | #endif //SNOOP_STATS |
18214 | } |
18215 | else |
18216 | { |
18217 | //it is a stolen entry, or beginning/ending of a partial mark |
18218 | level++; |
18219 | #ifdef SNOOP_STATS |
18220 | snoop_stat.stolen_or_pm_count++; |
18221 | #endif //SNOOP_STATS |
18222 | success = FALSE; |
18223 | } |
18224 | } |
18225 | else if (stolen_p (next)) |
18226 | { |
18227 | //ignore the stolen guy and go to the next level |
18228 | success = FALSE; |
18229 | level+=2; |
18230 | #ifdef SNOOP_STATS |
18231 | snoop_stat.stolen_entry_count++; |
18232 | #endif //SNOOP_STATS |
18233 | } |
18234 | else |
18235 | { |
18236 | assert (partial_p (next)); |
18237 | start = ref_from_slot (next); |
18238 | //re-read the object |
18239 | o = ref_from_slot (ref_mark_stack (hp, level)); |
18240 | if (o && start) |
18241 | { |
18242 | //steal the object |
18243 | success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next); |
18244 | #ifdef SNOOP_STATS |
18245 | snoop_stat.interlocked_count++; |
18246 | if (success) |
18247 | { |
18248 | snoop_stat.partial_mark_parent_count++; |
18249 | } |
18250 | #endif //SNOOP_STATS |
18251 | } |
18252 | else |
18253 | { |
18254 | // stack is not ready, or o is completely different from the last time we read from this stack level. |
18255 | // go up 2 levels to steal children or totally unrelated objects. |
18256 | success = FALSE; |
18257 | if (first_not_ready_level == 0) |
18258 | { |
18259 | first_not_ready_level = level; |
18260 | } |
18261 | level+=2; |
18262 | #ifdef SNOOP_STATS |
18263 | snoop_stat.pm_not_ready_count++; |
18264 | #endif //SNOOP_STATS |
18265 | } |
18266 | } |
18267 | if (success) |
18268 | { |
18269 | |
18270 | #ifdef SNOOP_STATS |
18271 | dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms" , |
18272 | heap_number, (size_t)o, (heap_number+1)%n_heaps, level, |
18273 | (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick))); |
18274 | uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp(); |
18275 | #endif //SNOOP_STATS |
18276 | |
18277 | mark_object_simple1 (o, start, heap_number); |
18278 | |
18279 | #ifdef SNOOP_STATS |
18280 | dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms" , |
18281 | heap_number, (size_t)o, (heap_number+1)%n_heaps, level, |
18282 | (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick))); |
18283 | #endif //SNOOP_STATS |
18284 | |
18285 | mark_stack_busy() = 0; |
18286 | |
18287 | //clear the mark stack in snooping range |
18288 | for (int i = 0; i < max_snoop_level; i++) |
18289 | { |
18290 | if (((uint8_t**)mark_stack_array)[i] != 0) |
18291 | { |
18292 | ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0; |
18293 | #ifdef SNOOP_STATS |
18294 | snoop_stat.stack_bottom_clear_count++; |
18295 | #endif //SNOOP_STATS |
18296 | } |
18297 | } |
18298 | |
18299 | level = 0; |
18300 | } |
18301 | mark_stack_busy() = 0; |
18302 | } |
18303 | else |
18304 | { |
18305 | //slot is either partial or stolen |
18306 | level++; |
18307 | } |
18308 | } |
18309 | if ((first_not_ready_level != 0) && hp->mark_stack_busy()) |
18310 | { |
18311 | continue; |
18312 | } |
18313 | if (!hp->mark_stack_busy()) |
18314 | { |
18315 | first_not_ready_level = 0; |
18316 | idle_loop_count++; |
18317 | |
18318 | if ((idle_loop_count % (6) )==1) |
18319 | { |
18320 | #ifdef SNOOP_STATS |
18321 | snoop_stat.switch_to_thread_count++; |
18322 | #endif //SNOOP_STATS |
18323 | GCToOSInterface::Sleep(1); |
18324 | } |
18325 | int free_count = 1; |
18326 | #ifdef SNOOP_STATS |
18327 | snoop_stat.stack_idle_count++; |
18328 | //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number)); |
18329 | #endif //SNOOP_STATS |
18330 | for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;) |
18331 | { |
18332 | if (!((g_heaps [hpn])->mark_stack_busy())) |
18333 | { |
18334 | free_count++; |
18335 | #ifdef SNOOP_STATS |
18336 | dprintf (SNOOP_LOG, ("heap%d: %d idle" , heap_number, free_count)); |
18337 | #endif //SNOOP_STATS |
18338 | } |
18339 | else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999) |
18340 | { |
18341 | thpn = hpn; |
18342 | break; |
18343 | } |
18344 | hpn = (hpn+1)%n_heaps; |
18345 | YieldProcessor(); |
18346 | } |
18347 | if (free_count == n_heaps) |
18348 | { |
18349 | break; |
18350 | } |
18351 | } |
18352 | } |
18353 | } |
18354 | |
18355 | inline |
18356 | BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap) |
18357 | { |
18358 | #ifdef SNOOP_STATS |
18359 | snoop_stat.check_level_count++; |
18360 | #endif //SNOOP_STATS |
18361 | return (next_heap->mark_stack_busy()>=1); |
18362 | } |
18363 | #endif //MH_SC_MARK |
18364 | |
18365 | #ifdef SNOOP_STATS |
18366 | void gc_heap::print_snoop_stat() |
18367 | { |
18368 | dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s" , |
18369 | "heap" , "check" , "zero" , "mark" , "stole" , "pstack" , "nstack" , "nonsk" )); |
18370 | dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d" , |
18371 | snoop_stat.heap_index, |
18372 | snoop_stat.objects_checked_count, |
18373 | snoop_stat.zero_ref_count, |
18374 | snoop_stat.objects_marked_count, |
18375 | snoop_stat.stolen_stack_count, |
18376 | snoop_stat.partial_stack_count, |
18377 | snoop_stat.normal_stack_count, |
18378 | snoop_stat.non_stack_count)); |
18379 | dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s" , |
18380 | "heap" , "level" , "busy" , "xchg" , "pmparent" , "s_pm" , "stolen" , "nready" , "clear" )); |
18381 | dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n" , |
18382 | snoop_stat.heap_index, |
18383 | snoop_stat.check_level_count, |
18384 | snoop_stat.busy_count, |
18385 | snoop_stat.interlocked_count, |
18386 | snoop_stat.partial_mark_parent_count, |
18387 | snoop_stat.stolen_or_pm_count, |
18388 | snoop_stat.stolen_entry_count, |
18389 | snoop_stat.pm_not_ready_count, |
18390 | snoop_stat.normal_count, |
18391 | snoop_stat.stack_bottom_clear_count)); |
18392 | |
18393 | printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n" , |
18394 | "heap" , "check" , "zero" , "mark" , "idle" , "switch" ); |
18395 | printf ("%4d | %8d | %8d | %8d | %8d | %8d\n" , |
18396 | snoop_stat.heap_index, |
18397 | snoop_stat.objects_checked_count, |
18398 | snoop_stat.zero_ref_count, |
18399 | snoop_stat.objects_marked_count, |
18400 | snoop_stat.stack_idle_count, |
18401 | snoop_stat.switch_to_thread_count); |
18402 | printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n" , |
18403 | "heap" , "level" , "busy" , "xchg" , "pmparent" , "s_pm" , "stolen" , "nready" , "normal" , "clear" ); |
18404 | printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n" , |
18405 | snoop_stat.heap_index, |
18406 | snoop_stat.check_level_count, |
18407 | snoop_stat.busy_count, |
18408 | snoop_stat.interlocked_count, |
18409 | snoop_stat.partial_mark_parent_count, |
18410 | snoop_stat.stolen_or_pm_count, |
18411 | snoop_stat.stolen_entry_count, |
18412 | snoop_stat.pm_not_ready_count, |
18413 | snoop_stat.normal_count, |
18414 | snoop_stat.stack_bottom_clear_count); |
18415 | } |
18416 | #endif //SNOOP_STATS |
18417 | |
18418 | #ifdef HEAP_ANALYZE |
18419 | void |
18420 | gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL) |
18421 | { |
18422 | if (!internal_root_array) |
18423 | { |
18424 | internal_root_array = new (nothrow) uint8_t* [internal_root_array_length]; |
18425 | if (!internal_root_array) |
18426 | { |
18427 | heap_analyze_success = FALSE; |
18428 | } |
18429 | } |
18430 | |
18431 | if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index)) |
18432 | { |
18433 | size_t new_size = 2*internal_root_array_length; |
18434 | |
18435 | uint64_t available_physical = 0; |
18436 | get_memory_info (NULL, &available_physical); |
18437 | if (new_size > (size_t)(available_physical / 10)) |
18438 | { |
18439 | heap_analyze_success = FALSE; |
18440 | } |
18441 | else |
18442 | { |
18443 | uint8_t** tmp = new (nothrow) uint8_t* [new_size]; |
18444 | if (tmp) |
18445 | { |
18446 | memcpy (tmp, internal_root_array, |
18447 | internal_root_array_length*sizeof (uint8_t*)); |
18448 | delete[] internal_root_array; |
18449 | internal_root_array = tmp; |
18450 | internal_root_array_length = new_size; |
18451 | } |
18452 | else |
18453 | { |
18454 | heap_analyze_success = FALSE; |
18455 | } |
18456 | } |
18457 | } |
18458 | |
18459 | if (heap_analyze_success) |
18460 | { |
18461 | PREFIX_ASSUME(internal_root_array_index < internal_root_array_length); |
18462 | |
18463 | uint8_t* ref = (uint8_t*)po; |
18464 | if (!current_obj || |
18465 | !((ref >= current_obj) && (ref < (current_obj + current_obj_size)))) |
18466 | { |
18467 | gc_heap* hp = gc_heap::heap_of (ref); |
18468 | current_obj = hp->find_object (ref, hp->lowest_address); |
18469 | current_obj_size = size (current_obj); |
18470 | |
18471 | internal_root_array[internal_root_array_index] = current_obj; |
18472 | internal_root_array_index++; |
18473 | } |
18474 | } |
18475 | |
18476 | mark_object_simple (po THREAD_NUMBER_ARG); |
18477 | } |
18478 | #endif //HEAP_ANALYZE |
18479 | |
18480 | //this method assumes that *po is in the [low. high[ range |
18481 | void |
18482 | gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL) |
18483 | { |
18484 | uint8_t* o = *po; |
18485 | #ifdef MULTIPLE_HEAPS |
18486 | #else //MULTIPLE_HEAPS |
18487 | const int thread = 0; |
18488 | #endif //MULTIPLE_HEAPS |
18489 | { |
18490 | #ifdef SNOOP_STATS |
18491 | snoop_stat.objects_checked_count++; |
18492 | #endif //SNOOP_STATS |
18493 | |
18494 | if (gc_mark1 (o)) |
18495 | { |
18496 | m_boundary (o); |
18497 | size_t s = size (o); |
18498 | promoted_bytes (thread) += s; |
18499 | { |
18500 | go_through_object_cl (method_table(o), o, s, poo, |
18501 | { |
18502 | uint8_t* oo = *poo; |
18503 | if (gc_mark (oo, gc_low, gc_high)) |
18504 | { |
18505 | m_boundary (oo); |
18506 | size_t obj_size = size (oo); |
18507 | promoted_bytes (thread) += obj_size; |
18508 | |
18509 | if (contain_pointers_or_collectible (oo)) |
18510 | mark_object_simple1 (oo, oo THREAD_NUMBER_ARG); |
18511 | } |
18512 | } |
18513 | ); |
18514 | } |
18515 | } |
18516 | } |
18517 | } |
18518 | |
18519 | inline |
18520 | uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL) |
18521 | { |
18522 | if ((o >= gc_low) && (o < gc_high)) |
18523 | mark_object_simple (&o THREAD_NUMBER_ARG); |
18524 | #ifdef MULTIPLE_HEAPS |
18525 | else if (o) |
18526 | { |
18527 | //find the heap |
18528 | gc_heap* hp = heap_of (o); |
18529 | assert (hp); |
18530 | if ((o >= hp->gc_low) && (o < hp->gc_high)) |
18531 | mark_object_simple (&o THREAD_NUMBER_ARG); |
18532 | } |
18533 | #endif //MULTIPLE_HEAPS |
18534 | |
18535 | return o; |
18536 | } |
18537 | |
18538 | #ifdef BACKGROUND_GC |
18539 | |
18540 | void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL) |
18541 | { |
18542 | uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length]; |
18543 | |
18544 | #ifdef SORT_MARK_STACK |
18545 | uint8_t** sorted_tos = background_mark_stack_array; |
18546 | #endif //SORT_MARK_STACK |
18547 | |
18548 | background_mark_stack_tos = background_mark_stack_array; |
18549 | |
18550 | while (1) |
18551 | { |
18552 | #ifdef MULTIPLE_HEAPS |
18553 | #else //MULTIPLE_HEAPS |
18554 | const int thread = 0; |
18555 | #endif //MULTIPLE_HEAPS |
18556 | if (oo) |
18557 | { |
18558 | size_t s = 0; |
18559 | if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*)))) |
18560 | { |
18561 | BOOL overflow_p = FALSE; |
18562 | |
18563 | if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1)) |
18564 | { |
18565 | size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0); |
18566 | size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components); |
18567 | if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1)) |
18568 | { |
18569 | dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs" , |
18570 | heap_number, |
18571 | (size_t)(mark_stack_limit - 1 - background_mark_stack_tos), |
18572 | method_table(oo), |
18573 | num_pointers)); |
18574 | |
18575 | bgc_overflow_count++; |
18576 | overflow_p = TRUE; |
18577 | } |
18578 | } |
18579 | |
18580 | if (overflow_p == FALSE) |
18581 | { |
18582 | dprintf(3,("pushing mark for %Ix " , (size_t)oo)); |
18583 | |
18584 | go_through_object_cl (method_table(oo), oo, s, ppslot, |
18585 | { |
18586 | uint8_t* o = *ppslot; |
18587 | Prefetch(o); |
18588 | if (background_mark (o, |
18589 | background_saved_lowest_address, |
18590 | background_saved_highest_address)) |
18591 | { |
18592 | //m_boundary (o); |
18593 | size_t obj_size = size (o); |
18594 | bpromoted_bytes (thread) += obj_size; |
18595 | if (contain_pointers_or_collectible (o)) |
18596 | { |
18597 | *(background_mark_stack_tos++) = o; |
18598 | |
18599 | } |
18600 | } |
18601 | } |
18602 | ); |
18603 | } |
18604 | else |
18605 | { |
18606 | dprintf (3,("mark stack overflow for object %Ix " , (size_t)oo)); |
18607 | background_min_overflow_address = min (background_min_overflow_address, oo); |
18608 | background_max_overflow_address = max (background_max_overflow_address, oo); |
18609 | } |
18610 | } |
18611 | else |
18612 | { |
18613 | uint8_t* start = oo; |
18614 | if ((size_t)oo & 1) |
18615 | { |
18616 | oo = (uint8_t*)((size_t)oo & ~1); |
18617 | start = *(--background_mark_stack_tos); |
18618 | dprintf (4, ("oo: %Ix, start: %Ix\n" , (size_t)oo, (size_t)start)); |
18619 | } |
18620 | #ifdef COLLECTIBLE_CLASS |
18621 | else |
18622 | { |
18623 | // If there's a class object, push it now. We are guaranteed to have the slot since |
18624 | // we just popped one object off. |
18625 | if (is_collectible (oo)) |
18626 | { |
18627 | uint8_t* class_obj = get_class_object (oo); |
18628 | if (background_mark (class_obj, |
18629 | background_saved_lowest_address, |
18630 | background_saved_highest_address)) |
18631 | { |
18632 | size_t obj_size = size (class_obj); |
18633 | bpromoted_bytes (thread) += obj_size; |
18634 | |
18635 | *(background_mark_stack_tos++) = class_obj; |
18636 | } |
18637 | } |
18638 | |
18639 | if (!contain_pointers (oo)) |
18640 | { |
18641 | goto next_level; |
18642 | } |
18643 | } |
18644 | #endif //COLLECTIBLE_CLASS |
18645 | |
18646 | s = size (oo); |
18647 | |
18648 | BOOL overflow_p = FALSE; |
18649 | |
18650 | if (background_mark_stack_tos + (num_partial_refs + 2) >= mark_stack_limit) |
18651 | { |
18652 | size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0); |
18653 | size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components); |
18654 | |
18655 | dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id" , |
18656 | heap_number, |
18657 | (size_t)(mark_stack_limit - background_mark_stack_tos), |
18658 | oo, |
18659 | method_table(oo), |
18660 | start, |
18661 | num_pointers)); |
18662 | |
18663 | bgc_overflow_count++; |
18664 | overflow_p = TRUE; |
18665 | } |
18666 | if (overflow_p == FALSE) |
18667 | { |
18668 | dprintf(3,("pushing mark for %Ix " , (size_t)oo)); |
18669 | |
18670 | //push the object and its current |
18671 | uint8_t** place = background_mark_stack_tos++; |
18672 | *(place) = start; |
18673 | *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1); |
18674 | |
18675 | int i = num_partial_refs; |
18676 | |
18677 | go_through_object (method_table(oo), oo, s, ppslot, |
18678 | start, use_start, (oo + s), |
18679 | { |
18680 | uint8_t* o = *ppslot; |
18681 | Prefetch(o); |
18682 | |
18683 | if (background_mark (o, |
18684 | background_saved_lowest_address, |
18685 | background_saved_highest_address)) |
18686 | { |
18687 | //m_boundary (o); |
18688 | size_t obj_size = size (o); |
18689 | bpromoted_bytes (thread) += obj_size; |
18690 | if (contain_pointers_or_collectible (o)) |
18691 | { |
18692 | *(background_mark_stack_tos++) = o; |
18693 | if (--i == 0) |
18694 | { |
18695 | //update the start |
18696 | *place = (uint8_t*)(ppslot+1); |
18697 | goto more_to_do; |
18698 | } |
18699 | |
18700 | } |
18701 | } |
18702 | |
18703 | } |
18704 | ); |
18705 | //we are finished with this object |
18706 | *place = 0; |
18707 | *(place+1) = 0; |
18708 | |
18709 | more_to_do:; |
18710 | } |
18711 | else |
18712 | { |
18713 | dprintf (3,("mark stack overflow for object %Ix " , (size_t)oo)); |
18714 | background_min_overflow_address = min (background_min_overflow_address, oo); |
18715 | background_max_overflow_address = max (background_max_overflow_address, oo); |
18716 | } |
18717 | } |
18718 | } |
18719 | #ifdef SORT_MARK_STACK |
18720 | if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8) |
18721 | { |
18722 | rqsort1 (sorted_tos, background_mark_stack_tos-1); |
18723 | sorted_tos = background_mark_stack_tos-1; |
18724 | } |
18725 | #endif //SORT_MARK_STACK |
18726 | |
18727 | #ifdef COLLECTIBLE_CLASS |
18728 | next_level: |
18729 | #endif // COLLECTIBLE_CLASS |
18730 | allow_fgc(); |
18731 | |
18732 | if (!(background_mark_stack_tos == background_mark_stack_array)) |
18733 | { |
18734 | oo = *(--background_mark_stack_tos); |
18735 | |
18736 | #ifdef SORT_MARK_STACK |
18737 | sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos); |
18738 | #endif //SORT_MARK_STACK |
18739 | } |
18740 | else |
18741 | break; |
18742 | } |
18743 | |
18744 | assert (background_mark_stack_tos == background_mark_stack_array); |
18745 | |
18746 | |
18747 | } |
18748 | |
18749 | //this version is different than the foreground GC because |
18750 | //it can't keep pointers to the inside of an object |
18751 | //while calling background_mark_simple1. The object could be moved |
18752 | //by an intervening foreground gc. |
18753 | //this method assumes that *po is in the [low. high[ range |
18754 | void |
18755 | gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL) |
18756 | { |
18757 | #ifdef MULTIPLE_HEAPS |
18758 | #else //MULTIPLE_HEAPS |
18759 | const int thread = 0; |
18760 | #endif //MULTIPLE_HEAPS |
18761 | { |
18762 | dprintf (3, ("bmarking %Ix" , o)); |
18763 | |
18764 | if (background_mark1 (o)) |
18765 | { |
18766 | //m_boundary (o); |
18767 | size_t s = size (o); |
18768 | bpromoted_bytes (thread) += s; |
18769 | |
18770 | if (contain_pointers_or_collectible (o)) |
18771 | { |
18772 | background_mark_simple1 (o THREAD_NUMBER_ARG); |
18773 | } |
18774 | } |
18775 | } |
18776 | } |
18777 | |
18778 | inline |
18779 | uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL) |
18780 | { |
18781 | if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address)) |
18782 | { |
18783 | background_mark_simple (o THREAD_NUMBER_ARG); |
18784 | } |
18785 | else |
18786 | { |
18787 | if (o) |
18788 | { |
18789 | dprintf (3, ("or-%Ix" , o)); |
18790 | } |
18791 | } |
18792 | return o; |
18793 | } |
18794 | |
18795 | void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags) |
18796 | { |
18797 | UNREFERENCED_PARAMETER(sc); |
18798 | |
18799 | assert (settings.concurrent); |
18800 | uint8_t* o = (uint8_t*)object; |
18801 | |
18802 | gc_heap* hp = gc_heap::heap_of (o); |
18803 | #ifdef INTERIOR_POINTERS |
18804 | if (flags & GC_CALL_INTERIOR) |
18805 | { |
18806 | o = hp->find_object (o, background_saved_lowest_address); |
18807 | } |
18808 | #endif //INTERIOR_POINTERS |
18809 | |
18810 | if (!background_object_marked (o, FALSE)) |
18811 | { |
18812 | FATAL_GC_ERROR(); |
18813 | } |
18814 | } |
18815 | |
18816 | void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags) |
18817 | { |
18818 | UNREFERENCED_PARAMETER(sc); |
18819 | //in order to save space on the array, mark the object, |
18820 | //knowing that it will be visited later |
18821 | assert (settings.concurrent); |
18822 | |
18823 | THREAD_NUMBER_FROM_CONTEXT; |
18824 | #ifndef MULTIPLE_HEAPS |
18825 | const int thread = 0; |
18826 | #endif //!MULTIPLE_HEAPS |
18827 | |
18828 | uint8_t* o = (uint8_t*)*ppObject; |
18829 | |
18830 | if (o == 0) |
18831 | return; |
18832 | |
18833 | #ifdef DEBUG_DestroyedHandleValue |
18834 | // we can race with destroy handle during concurrent scan |
18835 | if (o == (uint8_t*)DEBUG_DestroyedHandleValue) |
18836 | return; |
18837 | #endif //DEBUG_DestroyedHandleValue |
18838 | |
18839 | HEAP_FROM_THREAD; |
18840 | |
18841 | gc_heap* hp = gc_heap::heap_of (o); |
18842 | |
18843 | if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address)) |
18844 | { |
18845 | return; |
18846 | } |
18847 | |
18848 | #ifdef INTERIOR_POINTERS |
18849 | if (flags & GC_CALL_INTERIOR) |
18850 | { |
18851 | o = hp->find_object (o, hp->background_saved_lowest_address); |
18852 | if (o == 0) |
18853 | return; |
18854 | } |
18855 | #endif //INTERIOR_POINTERS |
18856 | |
18857 | #ifdef FEATURE_CONSERVATIVE_GC |
18858 | // For conservative GC, a value on stack may point to middle of a free object. |
18859 | // In this case, we don't need to promote the pointer. |
18860 | if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree()) |
18861 | { |
18862 | return; |
18863 | } |
18864 | #endif //FEATURE_CONSERVATIVE_GC |
18865 | |
18866 | #ifdef _DEBUG |
18867 | ((CObjectHeader*)o)->Validate(); |
18868 | #endif //_DEBUG |
18869 | |
18870 | dprintf (BGC_LOG, ("Background Promote %Ix" , (size_t)o)); |
18871 | |
18872 | //needs to be called before the marking because it is possible for a foreground |
18873 | //gc to take place during the mark and move the object |
18874 | STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, " GCHeap::Promote: Promote GC Root *%p = %p MT = %pT" , ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL); |
18875 | |
18876 | hpt->background_mark_simple (o THREAD_NUMBER_ARG); |
18877 | } |
18878 | |
18879 | //used by the ephemeral collection to scan the local background structures |
18880 | //containing references. |
18881 | void |
18882 | gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC) |
18883 | { |
18884 | ScanContext sc; |
18885 | if (pSC == 0) |
18886 | pSC = ≻ |
18887 | |
18888 | pSC->thread_number = hn; |
18889 | |
18890 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
18891 | pSC->pCurrentDomain = 0; |
18892 | #endif |
18893 | |
18894 | BOOL relocate_p = (fn == &GCHeap::Relocate); |
18895 | |
18896 | dprintf (3, ("Scanning background mark list" )); |
18897 | |
18898 | //scan mark_list |
18899 | size_t mark_list_finger = 0; |
18900 | while (mark_list_finger < c_mark_list_index) |
18901 | { |
18902 | uint8_t** o = &c_mark_list [mark_list_finger]; |
18903 | if (!relocate_p) |
18904 | { |
18905 | // We may not be able to calculate the size during relocate as POPO |
18906 | // may have written over the object. |
18907 | size_t s = size (*o); |
18908 | assert (Align (s) >= Align (min_obj_size)); |
18909 | dprintf(3,("background root %Ix" , (size_t)*o)); |
18910 | } |
18911 | (*fn) ((Object**)o, pSC, 0); |
18912 | mark_list_finger++; |
18913 | } |
18914 | |
18915 | //scan the mark stack |
18916 | dprintf (3, ("Scanning background mark stack" )); |
18917 | |
18918 | uint8_t** finger = background_mark_stack_array; |
18919 | while (finger < background_mark_stack_tos) |
18920 | { |
18921 | if ((finger + 1) < background_mark_stack_tos) |
18922 | { |
18923 | // We need to check for the partial mark case here. |
18924 | uint8_t* parent_obj = *(finger + 1); |
18925 | if ((size_t)parent_obj & 1) |
18926 | { |
18927 | uint8_t* place = *finger; |
18928 | size_t place_offset = 0; |
18929 | uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1); |
18930 | |
18931 | if (relocate_p) |
18932 | { |
18933 | *(finger + 1) = real_parent_obj; |
18934 | place_offset = place - real_parent_obj; |
18935 | dprintf(3,("relocating background root %Ix" , (size_t)real_parent_obj)); |
18936 | (*fn) ((Object**)(finger + 1), pSC, 0); |
18937 | real_parent_obj = *(finger + 1); |
18938 | *finger = real_parent_obj + place_offset; |
18939 | *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1); |
18940 | dprintf(3,("roots changed to %Ix, %Ix" , *finger, *(finger + 1))); |
18941 | } |
18942 | else |
18943 | { |
18944 | uint8_t** temp = &real_parent_obj; |
18945 | dprintf(3,("marking background root %Ix" , (size_t)real_parent_obj)); |
18946 | (*fn) ((Object**)temp, pSC, 0); |
18947 | } |
18948 | |
18949 | finger += 2; |
18950 | continue; |
18951 | } |
18952 | } |
18953 | dprintf(3,("background root %Ix" , (size_t)*finger)); |
18954 | (*fn) ((Object**)finger, pSC, 0); |
18955 | finger++; |
18956 | } |
18957 | } |
18958 | |
18959 | inline |
18960 | void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL) |
18961 | { |
18962 | if (contain_pointers (oo)) |
18963 | { |
18964 | size_t total_refs = 0; |
18965 | size_t s = size (oo); |
18966 | go_through_object_nostart (method_table(oo), oo, s, po, |
18967 | { |
18968 | uint8_t* o = *po; |
18969 | total_refs++; |
18970 | background_mark_object (o THREAD_NUMBER_ARG); |
18971 | } |
18972 | ); |
18973 | |
18974 | dprintf (3,("Background marking through %Ix went through %Id refs" , |
18975 | (size_t)oo, |
18976 | total_refs)); |
18977 | } |
18978 | } |
18979 | |
18980 | uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p) |
18981 | { |
18982 | if (concurrent_p && (seg == saved_overflow_ephemeral_seg)) |
18983 | { |
18984 | // for now we stop at where gen1 started when we started processing |
18985 | return background_min_soh_overflow_address; |
18986 | } |
18987 | else |
18988 | { |
18989 | return heap_segment_allocated (seg); |
18990 | } |
18991 | } |
18992 | |
18993 | uint8_t* gc_heap::background_first_overflow (uint8_t* min_add, |
18994 | heap_segment* seg, |
18995 | BOOL concurrent_p, |
18996 | BOOL small_object_p) |
18997 | { |
18998 | uint8_t* o = 0; |
18999 | |
19000 | if (small_object_p) |
19001 | { |
19002 | if (in_range_for_segment (min_add, seg)) |
19003 | { |
19004 | // min_add was the beginning of gen1 when we did the concurrent |
19005 | // overflow. Now we could be in a situation where min_add is |
19006 | // actually the same as allocated for that segment (because |
19007 | // we expanded heap), in which case we can not call |
19008 | // find first on this address or we will AV. |
19009 | if (min_add >= heap_segment_allocated (seg)) |
19010 | { |
19011 | return min_add; |
19012 | } |
19013 | else |
19014 | { |
19015 | if (concurrent_p && |
19016 | ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address))) |
19017 | { |
19018 | return background_min_soh_overflow_address; |
19019 | } |
19020 | else |
19021 | { |
19022 | o = find_first_object (min_add, heap_segment_mem (seg)); |
19023 | return o; |
19024 | } |
19025 | } |
19026 | } |
19027 | } |
19028 | |
19029 | o = max (heap_segment_mem (seg), min_add); |
19030 | return o; |
19031 | } |
19032 | |
19033 | void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number, |
19034 | uint8_t* min_add, uint8_t* max_add, |
19035 | BOOL concurrent_p) |
19036 | { |
19037 | if (concurrent_p) |
19038 | { |
19039 | current_bgc_state = bgc_overflow_soh; |
19040 | } |
19041 | |
19042 | size_t total_marked_objects = 0; |
19043 | |
19044 | #ifdef MULTIPLE_HEAPS |
19045 | int thread = heap_number; |
19046 | #endif //MULTIPLE_HEAPS |
19047 | |
19048 | exclusive_sync* loh_alloc_lock = 0; |
19049 | |
19050 | dprintf (2,("Processing Mark overflow [%Ix %Ix]" , (size_t)min_add, (size_t)max_add)); |
19051 | #ifdef MULTIPLE_HEAPS |
19052 | // We don't have each heap scan all heaps concurrently because we are worried about |
19053 | // multiple threads calling things like find_first_object. |
19054 | int h_start = (concurrent_p ? heap_number : 0); |
19055 | int h_end = (concurrent_p ? (heap_number + 1) : n_heaps); |
19056 | for (int hi = h_start; hi < h_end; hi++) |
19057 | { |
19058 | gc_heap* hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]); |
19059 | |
19060 | #else |
19061 | { |
19062 | gc_heap* hp = 0; |
19063 | |
19064 | #endif //MULTIPLE_HEAPS |
19065 | BOOL small_object_segments = TRUE; |
19066 | int align_const = get_alignment_constant (small_object_segments); |
19067 | generation* gen = hp->generation_of (condemned_gen_number); |
19068 | heap_segment* seg = heap_segment_in_range (generation_start_segment (gen)); |
19069 | PREFIX_ASSUME(seg != NULL); |
19070 | loh_alloc_lock = hp->bgc_alloc_lock; |
19071 | |
19072 | uint8_t* o = hp->background_first_overflow (min_add, |
19073 | seg, |
19074 | concurrent_p, |
19075 | small_object_segments); |
19076 | |
19077 | while (1) |
19078 | { |
19079 | while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add)) |
19080 | { |
19081 | dprintf (3, ("considering %Ix" , (size_t)o)); |
19082 | |
19083 | size_t s; |
19084 | |
19085 | if (concurrent_p && !small_object_segments) |
19086 | { |
19087 | loh_alloc_lock->bgc_mark_set (o); |
19088 | |
19089 | if (((CObjectHeader*)o)->IsFree()) |
19090 | { |
19091 | s = unused_array_size (o); |
19092 | } |
19093 | else |
19094 | { |
19095 | s = size (o); |
19096 | } |
19097 | } |
19098 | else |
19099 | { |
19100 | s = size (o); |
19101 | } |
19102 | |
19103 | if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o)) |
19104 | { |
19105 | total_marked_objects++; |
19106 | go_through_object_cl (method_table(o), o, s, poo, |
19107 | uint8_t* oo = *poo; |
19108 | background_mark_object (oo THREAD_NUMBER_ARG); |
19109 | ); |
19110 | } |
19111 | |
19112 | if (concurrent_p && !small_object_segments) |
19113 | { |
19114 | loh_alloc_lock->bgc_mark_done (); |
19115 | } |
19116 | |
19117 | o = o + Align (s, align_const); |
19118 | |
19119 | if (concurrent_p) |
19120 | { |
19121 | allow_fgc(); |
19122 | } |
19123 | } |
19124 | |
19125 | dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)" , |
19126 | heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects)); |
19127 | |
19128 | if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) || |
19129 | (seg = heap_segment_next_in_range (seg)) == 0) |
19130 | { |
19131 | if (small_object_segments) |
19132 | { |
19133 | if (concurrent_p) |
19134 | { |
19135 | current_bgc_state = bgc_overflow_loh; |
19136 | } |
19137 | |
19138 | dprintf (2, ("h%d: SOH: ov-mo: %Id" , heap_number, total_marked_objects)); |
19139 | fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments); |
19140 | concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH" ); |
19141 | total_marked_objects = 0; |
19142 | small_object_segments = FALSE; |
19143 | align_const = get_alignment_constant (small_object_segments); |
19144 | seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1))); |
19145 | |
19146 | PREFIX_ASSUME(seg != NULL); |
19147 | |
19148 | o = max (heap_segment_mem (seg), min_add); |
19149 | continue; |
19150 | } |
19151 | else |
19152 | { |
19153 | dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id" , heap_number, total_marked_objects)); |
19154 | fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments); |
19155 | break; |
19156 | } |
19157 | } |
19158 | else |
19159 | { |
19160 | o = hp->background_first_overflow (min_add, |
19161 | seg, |
19162 | concurrent_p, |
19163 | small_object_segments); |
19164 | continue; |
19165 | } |
19166 | } |
19167 | } |
19168 | } |
19169 | |
19170 | BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p) |
19171 | { |
19172 | BOOL grow_mark_array_p = TRUE; |
19173 | |
19174 | if (concurrent_p) |
19175 | { |
19176 | assert (!processed_soh_overflow_p); |
19177 | |
19178 | if ((background_max_overflow_address != 0) && |
19179 | (background_min_overflow_address != MAX_PTR)) |
19180 | { |
19181 | // We have overflow to process but we know we can't process the ephemeral generations |
19182 | // now (we actually could process till the current gen1 start but since we are going to |
19183 | // make overflow per segment, for now I'll just stop at the saved gen1 start. |
19184 | saved_overflow_ephemeral_seg = ephemeral_heap_segment; |
19185 | background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg); |
19186 | background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1)); |
19187 | } |
19188 | } |
19189 | else |
19190 | { |
19191 | assert ((saved_overflow_ephemeral_seg == 0) || |
19192 | ((background_max_soh_overflow_address != 0) && |
19193 | (background_min_soh_overflow_address != MAX_PTR))); |
19194 | |
19195 | if (!processed_soh_overflow_p) |
19196 | { |
19197 | // if there was no more overflow we just need to process what we didn't process |
19198 | // on the saved ephemeral segment. |
19199 | if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR)) |
19200 | { |
19201 | dprintf (2, ("final processing mark overflow - no more overflow since last time" )); |
19202 | grow_mark_array_p = FALSE; |
19203 | } |
19204 | |
19205 | background_min_overflow_address = min (background_min_overflow_address, |
19206 | background_min_soh_overflow_address); |
19207 | background_max_overflow_address = max (background_max_overflow_address, |
19208 | background_max_soh_overflow_address); |
19209 | processed_soh_overflow_p = TRUE; |
19210 | } |
19211 | } |
19212 | |
19213 | BOOL overflow_p = FALSE; |
19214 | recheck: |
19215 | if ((! ((background_max_overflow_address == 0)) || |
19216 | ! ((background_min_overflow_address == MAX_PTR)))) |
19217 | { |
19218 | overflow_p = TRUE; |
19219 | |
19220 | if (grow_mark_array_p) |
19221 | { |
19222 | // Try to grow the array. |
19223 | size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length); |
19224 | |
19225 | if ((new_size * sizeof(mark)) > 100*1024) |
19226 | { |
19227 | size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark); |
19228 | |
19229 | new_size = min(new_max_size, new_size); |
19230 | } |
19231 | |
19232 | if ((background_mark_stack_array_length < new_size) && |
19233 | ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2))) |
19234 | { |
19235 | dprintf (2, ("h%d: ov grow to %Id" , heap_number, new_size)); |
19236 | |
19237 | uint8_t** tmp = new (nothrow) uint8_t* [new_size]; |
19238 | if (tmp) |
19239 | { |
19240 | delete background_mark_stack_array; |
19241 | background_mark_stack_array = tmp; |
19242 | background_mark_stack_array_length = new_size; |
19243 | background_mark_stack_tos = background_mark_stack_array; |
19244 | } |
19245 | } |
19246 | } |
19247 | else |
19248 | { |
19249 | grow_mark_array_p = TRUE; |
19250 | } |
19251 | |
19252 | uint8_t* min_add = background_min_overflow_address; |
19253 | uint8_t* max_add = background_max_overflow_address; |
19254 | |
19255 | background_max_overflow_address = 0; |
19256 | background_min_overflow_address = MAX_PTR; |
19257 | |
19258 | background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p); |
19259 | if (!concurrent_p) |
19260 | { |
19261 | goto recheck; |
19262 | } |
19263 | } |
19264 | |
19265 | return overflow_p; |
19266 | } |
19267 | |
19268 | #endif //BACKGROUND_GC |
19269 | |
19270 | inline |
19271 | void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL) |
19272 | { |
19273 | #ifndef COLLECTIBLE_CLASS |
19274 | UNREFERENCED_PARAMETER(mark_class_object_p); |
19275 | BOOL to_mark_class_object = FALSE; |
19276 | #else //COLLECTIBLE_CLASS |
19277 | BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo))); |
19278 | #endif //COLLECTIBLE_CLASS |
19279 | if (contain_pointers (oo) || to_mark_class_object) |
19280 | { |
19281 | dprintf(3,( "Marking through %Ix" , (size_t)oo)); |
19282 | size_t s = size (oo); |
19283 | |
19284 | #ifdef COLLECTIBLE_CLASS |
19285 | if (to_mark_class_object) |
19286 | { |
19287 | uint8_t* class_obj = get_class_object (oo); |
19288 | mark_object (class_obj THREAD_NUMBER_ARG); |
19289 | } |
19290 | #endif //COLLECTIBLE_CLASS |
19291 | |
19292 | if (contain_pointers (oo)) |
19293 | { |
19294 | go_through_object_nostart (method_table(oo), oo, s, po, |
19295 | uint8_t* o = *po; |
19296 | mark_object (o THREAD_NUMBER_ARG); |
19297 | ); |
19298 | } |
19299 | } |
19300 | } |
19301 | |
19302 | size_t gc_heap::get_total_heap_size() |
19303 | { |
19304 | size_t total_heap_size = 0; |
19305 | |
19306 | #ifdef MULTIPLE_HEAPS |
19307 | int hn = 0; |
19308 | |
19309 | for (hn = 0; hn < gc_heap::n_heaps; hn++) |
19310 | { |
19311 | gc_heap* hp2 = gc_heap::g_heaps [hn]; |
19312 | total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation)); |
19313 | } |
19314 | #else |
19315 | total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation)); |
19316 | #endif //MULTIPLE_HEAPS |
19317 | |
19318 | return total_heap_size; |
19319 | } |
19320 | |
19321 | size_t gc_heap::get_total_fragmentation() |
19322 | { |
19323 | size_t total_fragmentation = 0; |
19324 | |
19325 | #ifdef MULTIPLE_HEAPS |
19326 | for (int i = 0; i < gc_heap::n_heaps; i++) |
19327 | { |
19328 | gc_heap* hp = gc_heap::g_heaps[i]; |
19329 | #else //MULTIPLE_HEAPS |
19330 | { |
19331 | gc_heap* hp = pGenGCHeap; |
19332 | #endif //MULTIPLE_HEAPS |
19333 | for (int i = 0; i <= (max_generation + 1); i++) |
19334 | { |
19335 | generation* gen = hp->generation_of (i); |
19336 | total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen)); |
19337 | } |
19338 | } |
19339 | |
19340 | return total_fragmentation; |
19341 | } |
19342 | |
19343 | size_t gc_heap::committed_size() |
19344 | { |
19345 | generation* gen = generation_of (max_generation); |
19346 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
19347 | size_t total_committed = 0; |
19348 | |
19349 | while (1) |
19350 | { |
19351 | total_committed += heap_segment_committed (seg) - (uint8_t*)seg; |
19352 | |
19353 | seg = heap_segment_next (seg); |
19354 | if (!seg) |
19355 | { |
19356 | if (gen != large_object_generation) |
19357 | { |
19358 | gen = generation_of (max_generation + 1); |
19359 | seg = generation_start_segment (gen); |
19360 | } |
19361 | else |
19362 | break; |
19363 | } |
19364 | } |
19365 | |
19366 | return total_committed; |
19367 | } |
19368 | |
19369 | size_t gc_heap::get_total_committed_size() |
19370 | { |
19371 | size_t total_committed = 0; |
19372 | |
19373 | #ifdef MULTIPLE_HEAPS |
19374 | int hn = 0; |
19375 | |
19376 | for (hn = 0; hn < gc_heap::n_heaps; hn++) |
19377 | { |
19378 | gc_heap* hp = gc_heap::g_heaps [hn]; |
19379 | total_committed += hp->committed_size(); |
19380 | } |
19381 | #else |
19382 | total_committed = committed_size(); |
19383 | #endif //MULTIPLE_HEAPS |
19384 | |
19385 | return total_committed; |
19386 | } |
19387 | |
19388 | void gc_heap::get_memory_info (uint32_t* memory_load, |
19389 | uint64_t* available_physical, |
19390 | uint64_t* available_page_file) |
19391 | { |
19392 | GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file); |
19393 | } |
19394 | |
19395 | void fire_mark_event (int heap_num, int root_type, size_t bytes_marked) |
19396 | { |
19397 | dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id" , heap_num, root_type, bytes_marked)); |
19398 | FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked); |
19399 | } |
19400 | |
19401 | //returns TRUE is an overflow happened. |
19402 | BOOL gc_heap::process_mark_overflow(int condemned_gen_number) |
19403 | { |
19404 | size_t last_promoted_bytes = promoted_bytes (heap_number); |
19405 | BOOL overflow_p = FALSE; |
19406 | recheck: |
19407 | if ((! (max_overflow_address == 0) || |
19408 | ! (min_overflow_address == MAX_PTR))) |
19409 | { |
19410 | overflow_p = TRUE; |
19411 | // Try to grow the array. |
19412 | size_t new_size = |
19413 | max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length); |
19414 | |
19415 | if ((new_size * sizeof(mark)) > 100*1024) |
19416 | { |
19417 | size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark); |
19418 | |
19419 | new_size = min(new_max_size, new_size); |
19420 | } |
19421 | |
19422 | if ((mark_stack_array_length < new_size) && |
19423 | ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2))) |
19424 | { |
19425 | mark* tmp = new (nothrow) mark [new_size]; |
19426 | if (tmp) |
19427 | { |
19428 | delete mark_stack_array; |
19429 | mark_stack_array = tmp; |
19430 | mark_stack_array_length = new_size; |
19431 | } |
19432 | } |
19433 | |
19434 | uint8_t* min_add = min_overflow_address; |
19435 | uint8_t* max_add = max_overflow_address; |
19436 | max_overflow_address = 0; |
19437 | min_overflow_address = MAX_PTR; |
19438 | process_mark_overflow_internal (condemned_gen_number, min_add, max_add); |
19439 | goto recheck; |
19440 | } |
19441 | |
19442 | size_t current_promoted_bytes = promoted_bytes (heap_number); |
19443 | |
19444 | if (current_promoted_bytes != last_promoted_bytes) |
19445 | fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes)); |
19446 | return overflow_p; |
19447 | } |
19448 | |
19449 | void gc_heap::process_mark_overflow_internal (int condemned_gen_number, |
19450 | uint8_t* min_add, uint8_t* max_add) |
19451 | { |
19452 | #ifdef MULTIPLE_HEAPS |
19453 | int thread = heap_number; |
19454 | #endif //MULTIPLE_HEAPS |
19455 | BOOL full_p = (condemned_gen_number == max_generation); |
19456 | |
19457 | dprintf(3,("Processing Mark overflow [%Ix %Ix]" , (size_t)min_add, (size_t)max_add)); |
19458 | #ifdef MULTIPLE_HEAPS |
19459 | for (int hi = 0; hi < n_heaps; hi++) |
19460 | { |
19461 | gc_heap* hp = g_heaps [(heap_number + hi) % n_heaps]; |
19462 | |
19463 | #else |
19464 | { |
19465 | gc_heap* hp = 0; |
19466 | |
19467 | #endif //MULTIPLE_HEAPS |
19468 | BOOL small_object_segments = TRUE; |
19469 | int align_const = get_alignment_constant (small_object_segments); |
19470 | generation* gen = hp->generation_of (condemned_gen_number); |
19471 | heap_segment* seg = heap_segment_in_range (generation_start_segment (gen)); |
19472 | |
19473 | PREFIX_ASSUME(seg != NULL); |
19474 | uint8_t* o = max (heap_segment_mem (seg), min_add); |
19475 | while (1) |
19476 | { |
19477 | uint8_t* end = heap_segment_allocated (seg); |
19478 | |
19479 | while ((o < end) && (o <= max_add)) |
19480 | { |
19481 | assert ((min_add <= o) && (max_add >= o)); |
19482 | dprintf (3, ("considering %Ix" , (size_t)o)); |
19483 | if (marked (o)) |
19484 | { |
19485 | mark_through_object (o, TRUE THREAD_NUMBER_ARG); |
19486 | } |
19487 | |
19488 | o = o + Align (size (o), align_const); |
19489 | } |
19490 | |
19491 | if (( seg = heap_segment_next_in_range (seg)) == 0) |
19492 | { |
19493 | if (small_object_segments && full_p) |
19494 | { |
19495 | small_object_segments = FALSE; |
19496 | align_const = get_alignment_constant (small_object_segments); |
19497 | seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1))); |
19498 | |
19499 | PREFIX_ASSUME(seg != NULL); |
19500 | |
19501 | o = max (heap_segment_mem (seg), min_add); |
19502 | continue; |
19503 | } |
19504 | else |
19505 | { |
19506 | break; |
19507 | } |
19508 | } |
19509 | else |
19510 | { |
19511 | o = max (heap_segment_mem (seg), min_add); |
19512 | continue; |
19513 | } |
19514 | } |
19515 | } |
19516 | } |
19517 | |
19518 | // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong |
19519 | // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of |
19520 | // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the |
19521 | // promotion scan multiple times. |
19522 | // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It |
19523 | // also has the effect of processing any mark stack overflow. |
19524 | |
19525 | #ifdef MULTIPLE_HEAPS |
19526 | // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC |
19527 | // worker threads synchronized. The algorithms are sufficiently divergent that we have different |
19528 | // implementations based on whether MULTIPLE_HEAPS is defined or not. |
19529 | // |
19530 | // Define some static variables used for synchronization in the method below. These should really be defined |
19531 | // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class. |
19532 | // |
19533 | // A note about the synchronization used within this method. Communication between the worker threads is |
19534 | // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from |
19535 | // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the |
19536 | // protection of a join. |
19537 | static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE; |
19538 | static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE; |
19539 | static VOLATILE(BOOL) s_fScanRequired; |
19540 | void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p) |
19541 | { |
19542 | // Whenever we call this method there may have been preceding object promotions. So set |
19543 | // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set |
19544 | // based on the how the scanning proceeded). |
19545 | s_fUnscannedPromotions = TRUE; |
19546 | |
19547 | // We don't know how many times we need to loop yet. In particular we can't base the loop condition on |
19548 | // the state of this thread's portion of the dependent handle table. That's because promotions on other |
19549 | // threads could cause handle promotions to become necessary here. Even if there are definitely no more |
19550 | // promotions possible in this thread's handles, we still have to stay in lock-step with those worker |
19551 | // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times |
19552 | // as all the others or they'll get out of step). |
19553 | while (true) |
19554 | { |
19555 | // The various worker threads are all currently racing in this code. We need to work out if at least |
19556 | // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the |
19557 | // dependent handle table when both of the following conditions apply: |
19558 | // 1) At least one (arbitrary) object might have been promoted since the last scan (because if this |
19559 | // object happens to correspond to a primary in one of our handles we might potentially have to |
19560 | // promote the associated secondary). |
19561 | // 2) The table for this thread has at least one handle with a secondary that isn't promoted yet. |
19562 | // |
19563 | // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first |
19564 | // iteration of this loop (see comment above) and in subsequent cycles each thread updates this |
19565 | // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary |
19566 | // being promoted. This value is cleared back to zero in a synchronized fashion in the join that |
19567 | // follows below. Note that we can't read this outside of the join since on any iteration apart from |
19568 | // the first threads will be racing between reading this value and completing their previous |
19569 | // iteration's table scan. |
19570 | // |
19571 | // The second condition is tracked by the dependent handle code itself on a per worker thread basis |
19572 | // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to |
19573 | // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is |
19574 | // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until |
19575 | // we're safely joined. |
19576 | if (GCScan::GcDhUnpromotedHandlesExist(sc)) |
19577 | s_fUnpromotedHandles = TRUE; |
19578 | |
19579 | // Synchronize all the threads so we can read our state variables safely. The shared variable |
19580 | // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by |
19581 | // a single thread inside the join. |
19582 | gc_t_join.join(this, gc_join_scan_dependent_handles); |
19583 | if (gc_t_join.joined()) |
19584 | { |
19585 | // We're synchronized so it's safe to read our shared state variables. We update another shared |
19586 | // variable to indicate to all threads whether we'll be scanning for another cycle or terminating |
19587 | // the loop. We scan if there has been at least one object promotion since last time and at least |
19588 | // one thread has a dependent handle table with a potential handle promotion possible. |
19589 | s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles; |
19590 | |
19591 | // Reset our shared state variables (ready to be set again on this scan or with a good initial |
19592 | // value for the next call if we're terminating the loop). |
19593 | s_fUnscannedPromotions = FALSE; |
19594 | s_fUnpromotedHandles = FALSE; |
19595 | |
19596 | if (!s_fScanRequired) |
19597 | { |
19598 | // We're terminating the loop. Perform any last operations that require single threaded access. |
19599 | if (!initial_scan_p) |
19600 | { |
19601 | // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help |
19602 | // load balance if some of the heaps have an abnormally large workload. |
19603 | uint8_t* all_heaps_max = 0; |
19604 | uint8_t* all_heaps_min = MAX_PTR; |
19605 | int i; |
19606 | for (i = 0; i < n_heaps; i++) |
19607 | { |
19608 | if (all_heaps_max < g_heaps[i]->max_overflow_address) |
19609 | all_heaps_max = g_heaps[i]->max_overflow_address; |
19610 | if (all_heaps_min > g_heaps[i]->min_overflow_address) |
19611 | all_heaps_min = g_heaps[i]->min_overflow_address; |
19612 | } |
19613 | for (i = 0; i < n_heaps; i++) |
19614 | { |
19615 | g_heaps[i]->max_overflow_address = all_heaps_max; |
19616 | g_heaps[i]->min_overflow_address = all_heaps_min; |
19617 | } |
19618 | } |
19619 | } |
19620 | |
19621 | // Restart all the workers. |
19622 | dprintf(3, ("Starting all gc thread mark stack overflow processing" )); |
19623 | gc_t_join.restart(); |
19624 | } |
19625 | |
19626 | // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions |
19627 | // being visible. If there really was an overflow (process_mark_overflow returns true) then set the |
19628 | // global flag indicating that at least one object promotion may have occurred (the usual comment |
19629 | // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and |
19630 | // exit the method since we unconditionally set this variable on method entry anyway). |
19631 | if (process_mark_overflow(condemned_gen_number)) |
19632 | s_fUnscannedPromotions = TRUE; |
19633 | |
19634 | // If we decided that no scan was required we can terminate the loop now. |
19635 | if (!s_fScanRequired) |
19636 | break; |
19637 | |
19638 | // Otherwise we must join with the other workers to ensure that all mark stack overflows have been |
19639 | // processed before we start scanning dependent handle tables (if overflows remain while we scan we |
19640 | // could miss noting the promotion of some primary objects). |
19641 | gc_t_join.join(this, gc_join_rescan_dependent_handles); |
19642 | if (gc_t_join.joined()) |
19643 | { |
19644 | // Restart all the workers. |
19645 | dprintf(3, ("Starting all gc thread for dependent handle promotion" )); |
19646 | gc_t_join.restart(); |
19647 | } |
19648 | |
19649 | // If the portion of the dependent handle table managed by this worker has handles that could still be |
19650 | // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it |
19651 | // could require a rescan of handles on this or other workers. |
19652 | if (GCScan::GcDhUnpromotedHandlesExist(sc)) |
19653 | if (GCScan::GcDhReScan(sc)) |
19654 | s_fUnscannedPromotions = TRUE; |
19655 | } |
19656 | } |
19657 | #else //MULTIPLE_HEAPS |
19658 | // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker |
19659 | // threads synchronized. |
19660 | void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p) |
19661 | { |
19662 | UNREFERENCED_PARAMETER(initial_scan_p); |
19663 | |
19664 | // Whenever we call this method there may have been preceding object promotions. So set |
19665 | // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set |
19666 | // based on the how the scanning proceeded). |
19667 | bool fUnscannedPromotions = true; |
19668 | |
19669 | // Loop until there are either no more dependent handles that can have their secondary promoted or we've |
19670 | // managed to perform a scan without promoting anything new. |
19671 | while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions) |
19672 | { |
19673 | // On each iteration of the loop start with the assumption that no further objects have been promoted. |
19674 | fUnscannedPromotions = false; |
19675 | |
19676 | // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions |
19677 | // being visible. If there was an overflow (process_mark_overflow returned true) then additional |
19678 | // objects now appear to be promoted and we should set the flag. |
19679 | if (process_mark_overflow(condemned_gen_number)) |
19680 | fUnscannedPromotions = true; |
19681 | |
19682 | // Perform the scan and set the flag if any promotions resulted. |
19683 | if (GCScan::GcDhReScan(sc)) |
19684 | fUnscannedPromotions = true; |
19685 | } |
19686 | |
19687 | // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to |
19688 | // scan any handles at all this is the processing of overflows that may have occurred prior to this method |
19689 | // invocation). |
19690 | process_mark_overflow(condemned_gen_number); |
19691 | } |
19692 | #endif //MULTIPLE_HEAPS |
19693 | |
19694 | void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p) |
19695 | { |
19696 | assert (settings.concurrent == FALSE); |
19697 | |
19698 | ScanContext sc; |
19699 | sc.thread_number = heap_number; |
19700 | sc.promotion = TRUE; |
19701 | sc.concurrent = FALSE; |
19702 | |
19703 | dprintf(2,("---- Mark Phase condemning %d ----" , condemned_gen_number)); |
19704 | BOOL full_p = (condemned_gen_number == max_generation); |
19705 | |
19706 | #ifdef TIME_GC |
19707 | unsigned start; |
19708 | unsigned finish; |
19709 | start = GetCycleCount32(); |
19710 | #endif //TIME_GC |
19711 | |
19712 | int gen_to_init = condemned_gen_number; |
19713 | if (condemned_gen_number == max_generation) |
19714 | { |
19715 | gen_to_init = max_generation + 1; |
19716 | } |
19717 | for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++) |
19718 | { |
19719 | dynamic_data* dd = dynamic_data_of (gen_idx); |
19720 | dd_begin_data_size (dd) = generation_size (gen_idx) - |
19721 | dd_fragmentation (dd) - |
19722 | Align (size (generation_allocation_start (generation_of (gen_idx)))); |
19723 | dprintf (2, ("begin data size for gen%d is %Id" , gen_idx, dd_begin_data_size (dd))); |
19724 | dd_survived_size (dd) = 0; |
19725 | dd_pinned_survived_size (dd) = 0; |
19726 | dd_artificial_pinned_survived_size (dd) = 0; |
19727 | dd_added_pinned_size (dd) = 0; |
19728 | #ifdef SHORT_PLUGS |
19729 | dd_padding_size (dd) = 0; |
19730 | #endif //SHORT_PLUGS |
19731 | #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN) |
19732 | dd_num_npinned_plugs (dd) = 0; |
19733 | #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN |
19734 | } |
19735 | |
19736 | #ifdef FFIND_OBJECT |
19737 | if (gen0_must_clear_bricks > 0) |
19738 | gen0_must_clear_bricks--; |
19739 | #endif //FFIND_OBJECT |
19740 | |
19741 | size_t last_promoted_bytes = 0; |
19742 | |
19743 | promoted_bytes (heap_number) = 0; |
19744 | reset_mark_stack(); |
19745 | |
19746 | #ifdef SNOOP_STATS |
19747 | memset (&snoop_stat, 0, sizeof(snoop_stat)); |
19748 | snoop_stat.heap_index = heap_number; |
19749 | #endif //SNOOP_STATS |
19750 | |
19751 | #ifdef MH_SC_MARK |
19752 | if (full_p) |
19753 | { |
19754 | //initialize the mark stack |
19755 | for (int i = 0; i < max_snoop_level; i++) |
19756 | { |
19757 | ((uint8_t**)(mark_stack_array))[i] = 0; |
19758 | } |
19759 | |
19760 | mark_stack_busy() = 1; |
19761 | } |
19762 | #endif //MH_SC_MARK |
19763 | |
19764 | static uint32_t num_sizedrefs = 0; |
19765 | |
19766 | #ifdef MH_SC_MARK |
19767 | static BOOL do_mark_steal_p = FALSE; |
19768 | #endif //MH_SC_MARK |
19769 | |
19770 | #ifdef MULTIPLE_HEAPS |
19771 | gc_t_join.join(this, gc_join_begin_mark_phase); |
19772 | if (gc_t_join.joined()) |
19773 | { |
19774 | #endif //MULTIPLE_HEAPS |
19775 | |
19776 | maxgen_size_inc_p = false; |
19777 | |
19778 | num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles(); |
19779 | |
19780 | #ifdef MULTIPLE_HEAPS |
19781 | |
19782 | #ifdef MH_SC_MARK |
19783 | if (full_p) |
19784 | { |
19785 | size_t total_heap_size = get_total_heap_size(); |
19786 | |
19787 | if (total_heap_size > (100 * 1024 * 1024)) |
19788 | { |
19789 | do_mark_steal_p = TRUE; |
19790 | } |
19791 | else |
19792 | { |
19793 | do_mark_steal_p = FALSE; |
19794 | } |
19795 | } |
19796 | else |
19797 | { |
19798 | do_mark_steal_p = FALSE; |
19799 | } |
19800 | #endif //MH_SC_MARK |
19801 | |
19802 | gc_t_join.restart(); |
19803 | } |
19804 | #endif //MULTIPLE_HEAPS |
19805 | |
19806 | { |
19807 | |
19808 | #ifdef MARK_LIST |
19809 | //set up the mark lists from g_mark_list |
19810 | assert (g_mark_list); |
19811 | #ifdef MULTIPLE_HEAPS |
19812 | mark_list = &g_mark_list [heap_number*mark_list_size]; |
19813 | #else |
19814 | mark_list = g_mark_list; |
19815 | #endif //MULTIPLE_HEAPS |
19816 | //dont use the mark list for full gc |
19817 | //because multiple segments are more complex to handle and the list |
19818 | //is likely to overflow |
19819 | if (condemned_gen_number != max_generation) |
19820 | mark_list_end = &mark_list [mark_list_size-1]; |
19821 | else |
19822 | mark_list_end = &mark_list [0]; |
19823 | mark_list_index = &mark_list [0]; |
19824 | #endif //MARK_LIST |
19825 | |
19826 | #ifndef MULTIPLE_HEAPS |
19827 | shigh = (uint8_t*) 0; |
19828 | slow = MAX_PTR; |
19829 | #endif //MULTIPLE_HEAPS |
19830 | |
19831 | //%type% category = quote (mark); |
19832 | |
19833 | if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0)) |
19834 | { |
19835 | GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc); |
19836 | fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes)); |
19837 | last_promoted_bytes = promoted_bytes (heap_number); |
19838 | |
19839 | #ifdef MULTIPLE_HEAPS |
19840 | gc_t_join.join(this, gc_join_scan_sizedref_done); |
19841 | if (gc_t_join.joined()) |
19842 | { |
19843 | dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots" )); |
19844 | gc_t_join.restart(); |
19845 | } |
19846 | #endif //MULTIPLE_HEAPS |
19847 | } |
19848 | |
19849 | dprintf(3,("Marking Roots" )); |
19850 | |
19851 | GCScan::GcScanRoots(GCHeap::Promote, |
19852 | condemned_gen_number, max_generation, |
19853 | &sc); |
19854 | |
19855 | fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes)); |
19856 | last_promoted_bytes = promoted_bytes (heap_number); |
19857 | |
19858 | #ifdef BACKGROUND_GC |
19859 | if (recursive_gc_sync::background_running_p()) |
19860 | { |
19861 | scan_background_roots (GCHeap::Promote, heap_number, &sc); |
19862 | } |
19863 | #endif //BACKGROUND_GC |
19864 | |
19865 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
19866 | dprintf(3, ("Marking finalization data" )); |
19867 | finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0); |
19868 | #endif // FEATURE_PREMORTEM_FINALIZATION |
19869 | |
19870 | fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes)); |
19871 | last_promoted_bytes = promoted_bytes (heap_number); |
19872 | |
19873 | // MTHTS |
19874 | { |
19875 | |
19876 | dprintf(3,("Marking handle table" )); |
19877 | GCScan::GcScanHandles(GCHeap::Promote, |
19878 | condemned_gen_number, max_generation, |
19879 | &sc); |
19880 | fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes)); |
19881 | last_promoted_bytes = promoted_bytes (heap_number); |
19882 | } |
19883 | |
19884 | #ifdef TRACE_GC |
19885 | size_t promoted_before_cards = promoted_bytes (heap_number); |
19886 | #endif //TRACE_GC |
19887 | |
19888 | dprintf (3, ("before cards: %Id" , promoted_before_cards)); |
19889 | if (!full_p) |
19890 | { |
19891 | #ifdef CARD_BUNDLE |
19892 | #ifdef MULTIPLE_HEAPS |
19893 | if (gc_t_join.r_join(this, gc_r_join_update_card_bundle)) |
19894 | { |
19895 | #endif //MULTIPLE_HEAPS |
19896 | |
19897 | #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
19898 | // If we are manually managing card bundles, every write to the card table should already be |
19899 | // accounted for in the card bundle table so there's nothing to update here. |
19900 | update_card_table_bundle(); |
19901 | #endif |
19902 | if (card_bundles_enabled()) |
19903 | { |
19904 | verify_card_bundles(); |
19905 | } |
19906 | |
19907 | #ifdef MULTIPLE_HEAPS |
19908 | gc_t_join.r_restart(); |
19909 | } |
19910 | #endif //MULTIPLE_HEAPS |
19911 | #endif //CARD_BUNDLE |
19912 | |
19913 | card_fn mark_object_fn = &gc_heap::mark_object_simple; |
19914 | #ifdef HEAP_ANALYZE |
19915 | heap_analyze_success = TRUE; |
19916 | if (heap_analyze_enabled) |
19917 | { |
19918 | internal_root_array_index = 0; |
19919 | current_obj = 0; |
19920 | current_obj_size = 0; |
19921 | mark_object_fn = &gc_heap::ha_mark_object_simple; |
19922 | } |
19923 | #endif //HEAP_ANALYZE |
19924 | |
19925 | dprintf(3,("Marking cross generation pointers" )); |
19926 | mark_through_cards_for_segments (mark_object_fn, FALSE); |
19927 | |
19928 | dprintf(3,("Marking cross generation pointers for large objects" )); |
19929 | mark_through_cards_for_large_objects (mark_object_fn, FALSE); |
19930 | |
19931 | dprintf (3, ("marked by cards: %Id" , |
19932 | (promoted_bytes (heap_number) - promoted_before_cards))); |
19933 | fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes)); |
19934 | last_promoted_bytes = promoted_bytes (heap_number); |
19935 | } |
19936 | } |
19937 | |
19938 | #ifdef MH_SC_MARK |
19939 | if (do_mark_steal_p) |
19940 | { |
19941 | mark_steal(); |
19942 | } |
19943 | #endif //MH_SC_MARK |
19944 | |
19945 | // Dependent handles need to be scanned with a special algorithm (see the header comment on |
19946 | // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other |
19947 | // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation |
19948 | // but in a common case (where there are no dependent handles that are due to be collected) it allows us |
19949 | // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more |
19950 | // iterations if required and will also perform processing of any mark stack overflow once the dependent |
19951 | // handle table has been fully promoted. |
19952 | GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc); |
19953 | scan_dependent_handles(condemned_gen_number, &sc, true); |
19954 | |
19955 | #ifdef MULTIPLE_HEAPS |
19956 | dprintf(3, ("Joining for short weak handle scan" )); |
19957 | gc_t_join.join(this, gc_join_null_dead_short_weak); |
19958 | if (gc_t_join.joined()) |
19959 | #endif //MULTIPLE_HEAPS |
19960 | { |
19961 | #ifdef HEAP_ANALYZE |
19962 | heap_analyze_enabled = FALSE; |
19963 | GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number); |
19964 | #endif // HEAP_ANALYZE |
19965 | GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc); |
19966 | |
19967 | #ifdef MULTIPLE_HEAPS |
19968 | if (!full_p) |
19969 | { |
19970 | // we used r_join and need to reinitialize states for it here. |
19971 | gc_t_join.r_init(); |
19972 | } |
19973 | |
19974 | //start all threads on the roots. |
19975 | dprintf(3, ("Starting all gc thread for short weak handle scan" )); |
19976 | gc_t_join.restart(); |
19977 | #endif //MULTIPLE_HEAPS |
19978 | |
19979 | } |
19980 | |
19981 | // null out the target of short weakref that were not promoted. |
19982 | GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc); |
19983 | |
19984 | // MTHTS: keep by single thread |
19985 | #ifdef MULTIPLE_HEAPS |
19986 | dprintf(3, ("Joining for finalization" )); |
19987 | gc_t_join.join(this, gc_join_scan_finalization); |
19988 | if (gc_t_join.joined()) |
19989 | #endif //MULTIPLE_HEAPS |
19990 | |
19991 | { |
19992 | #ifdef MULTIPLE_HEAPS |
19993 | //start all threads on the roots. |
19994 | dprintf(3, ("Starting all gc thread for Finalization" )); |
19995 | gc_t_join.restart(); |
19996 | #endif //MULTIPLE_HEAPS |
19997 | } |
19998 | |
19999 | //Handle finalization. |
20000 | size_t promoted_bytes_live = promoted_bytes (heap_number); |
20001 | |
20002 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
20003 | dprintf (3, ("Finalize marking" )); |
20004 | finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this); |
20005 | |
20006 | GCToEEInterface::DiagWalkFReachableObjects(__this); |
20007 | #endif // FEATURE_PREMORTEM_FINALIZATION |
20008 | |
20009 | // Scan dependent handles again to promote any secondaries associated with primaries that were promoted |
20010 | // for finalization. As before scan_dependent_handles will also process any mark stack overflow. |
20011 | scan_dependent_handles(condemned_gen_number, &sc, false); |
20012 | |
20013 | #ifdef MULTIPLE_HEAPS |
20014 | dprintf(3, ("Joining for weak pointer deletion" )); |
20015 | gc_t_join.join(this, gc_join_null_dead_long_weak); |
20016 | if (gc_t_join.joined()) |
20017 | { |
20018 | //start all threads on the roots. |
20019 | dprintf(3, ("Starting all gc thread for weak pointer deletion" )); |
20020 | gc_t_join.restart(); |
20021 | } |
20022 | #endif //MULTIPLE_HEAPS |
20023 | |
20024 | // null out the target of long weakref that were not promoted. |
20025 | GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc); |
20026 | |
20027 | // MTHTS: keep by single thread |
20028 | #ifdef MULTIPLE_HEAPS |
20029 | #ifdef MARK_LIST |
20030 | #ifdef PARALLEL_MARK_LIST_SORT |
20031 | // unsigned long start = GetCycleCount32(); |
20032 | sort_mark_list(); |
20033 | // printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start); |
20034 | #endif //PARALLEL_MARK_LIST_SORT |
20035 | #endif //MARK_LIST |
20036 | |
20037 | dprintf (3, ("Joining for sync block cache entry scanning" )); |
20038 | gc_t_join.join(this, gc_join_null_dead_syncblk); |
20039 | if (gc_t_join.joined()) |
20040 | #endif //MULTIPLE_HEAPS |
20041 | { |
20042 | // scan for deleted entries in the syncblk cache |
20043 | GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc); |
20044 | |
20045 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
20046 | if (g_fEnableAppDomainMonitoring) |
20047 | { |
20048 | size_t promoted_all_heaps = 0; |
20049 | #ifdef MULTIPLE_HEAPS |
20050 | for (int i = 0; i < n_heaps; i++) |
20051 | { |
20052 | promoted_all_heaps += promoted_bytes (i); |
20053 | } |
20054 | #else |
20055 | promoted_all_heaps = promoted_bytes (heap_number); |
20056 | #endif //MULTIPLE_HEAPS |
20057 | GCToEEInterface::RecordTotalSurvivedBytes(promoted_all_heaps); |
20058 | } |
20059 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
20060 | |
20061 | #ifdef MULTIPLE_HEAPS |
20062 | |
20063 | #ifdef MARK_LIST |
20064 | #ifndef PARALLEL_MARK_LIST_SORT |
20065 | //compact g_mark_list and sort it. |
20066 | combine_mark_lists(); |
20067 | #endif //PARALLEL_MARK_LIST_SORT |
20068 | #endif //MARK_LIST |
20069 | |
20070 | //decide on promotion |
20071 | if (!settings.promotion) |
20072 | { |
20073 | size_t m = 0; |
20074 | for (int n = 0; n <= condemned_gen_number;n++) |
20075 | { |
20076 | m += (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1); |
20077 | } |
20078 | |
20079 | for (int i = 0; i < n_heaps; i++) |
20080 | { |
20081 | dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1, |
20082 | max_generation)); |
20083 | size_t older_gen_size = (dd_current_size (dd) + |
20084 | (dd_desired_allocation (dd) - |
20085 | dd_new_allocation (dd))); |
20086 | |
20087 | if ((m > (older_gen_size)) || |
20088 | (promoted_bytes (i) > m)) |
20089 | { |
20090 | settings.promotion = TRUE; |
20091 | } |
20092 | } |
20093 | } |
20094 | |
20095 | #ifdef SNOOP_STATS |
20096 | if (do_mark_steal_p) |
20097 | { |
20098 | size_t objects_checked_count = 0; |
20099 | size_t zero_ref_count = 0; |
20100 | size_t objects_marked_count = 0; |
20101 | size_t check_level_count = 0; |
20102 | size_t busy_count = 0; |
20103 | size_t interlocked_count = 0; |
20104 | size_t partial_mark_parent_count = 0; |
20105 | size_t stolen_or_pm_count = 0; |
20106 | size_t stolen_entry_count = 0; |
20107 | size_t pm_not_ready_count = 0; |
20108 | size_t normal_count = 0; |
20109 | size_t stack_bottom_clear_count = 0; |
20110 | |
20111 | for (int i = 0; i < n_heaps; i++) |
20112 | { |
20113 | gc_heap* hp = g_heaps[i]; |
20114 | hp->print_snoop_stat(); |
20115 | objects_checked_count += hp->snoop_stat.objects_checked_count; |
20116 | zero_ref_count += hp->snoop_stat.zero_ref_count; |
20117 | objects_marked_count += hp->snoop_stat.objects_marked_count; |
20118 | check_level_count += hp->snoop_stat.check_level_count; |
20119 | busy_count += hp->snoop_stat.busy_count; |
20120 | interlocked_count += hp->snoop_stat.interlocked_count; |
20121 | partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count; |
20122 | stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count; |
20123 | stolen_entry_count += hp->snoop_stat.stolen_entry_count; |
20124 | pm_not_ready_count += hp->snoop_stat.pm_not_ready_count; |
20125 | normal_count += hp->snoop_stat.normal_count; |
20126 | stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count; |
20127 | } |
20128 | |
20129 | fflush (stdout); |
20130 | |
20131 | printf ("-------total stats-------\n" ); |
20132 | printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n" , |
20133 | "checked" , "zero" , "marked" , "level" , "busy" , "xchg" , "pmparent" , "s_pm" , "stolen" , "nready" , "normal" , "clear" ); |
20134 | printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n" , |
20135 | objects_checked_count, |
20136 | zero_ref_count, |
20137 | objects_marked_count, |
20138 | check_level_count, |
20139 | busy_count, |
20140 | interlocked_count, |
20141 | partial_mark_parent_count, |
20142 | stolen_or_pm_count, |
20143 | stolen_entry_count, |
20144 | pm_not_ready_count, |
20145 | normal_count, |
20146 | stack_bottom_clear_count); |
20147 | } |
20148 | #endif //SNOOP_STATS |
20149 | |
20150 | //start all threads. |
20151 | dprintf(3, ("Starting all threads for end of mark phase" )); |
20152 | gc_t_join.restart(); |
20153 | #else //MULTIPLE_HEAPS |
20154 | |
20155 | //decide on promotion |
20156 | if (!settings.promotion) |
20157 | { |
20158 | size_t m = 0; |
20159 | for (int n = 0; n <= condemned_gen_number;n++) |
20160 | { |
20161 | m += (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06); |
20162 | } |
20163 | dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1, |
20164 | max_generation)); |
20165 | size_t older_gen_size = (dd_current_size (dd) + |
20166 | (dd_desired_allocation (dd) - |
20167 | dd_new_allocation (dd))); |
20168 | |
20169 | dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id" , |
20170 | m, promoted_bytes (heap_number), older_gen_size)); |
20171 | |
20172 | if ((m > older_gen_size) || |
20173 | (promoted_bytes (heap_number) > m)) |
20174 | { |
20175 | settings.promotion = TRUE; |
20176 | } |
20177 | } |
20178 | |
20179 | #endif //MULTIPLE_HEAPS |
20180 | } |
20181 | |
20182 | #ifdef MULTIPLE_HEAPS |
20183 | #ifdef MARK_LIST |
20184 | #ifdef PARALLEL_MARK_LIST_SORT |
20185 | // start = GetCycleCount32(); |
20186 | merge_mark_lists(); |
20187 | // printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start); |
20188 | #endif //PARALLEL_MARK_LIST_SORT |
20189 | #endif //MARK_LIST |
20190 | #endif //MULTIPLE_HEAPS |
20191 | |
20192 | #ifdef BACKGROUND_GC |
20193 | total_promoted_bytes = promoted_bytes (heap_number); |
20194 | #endif //BACKGROUND_GC |
20195 | |
20196 | promoted_bytes (heap_number) -= promoted_bytes_live; |
20197 | |
20198 | #ifdef TIME_GC |
20199 | finish = GetCycleCount32(); |
20200 | mark_time = finish - start; |
20201 | #endif //TIME_GC |
20202 | |
20203 | dprintf(2,("---- End of mark phase ----" )); |
20204 | } |
20205 | |
20206 | inline |
20207 | void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high) |
20208 | { |
20209 | dprintf (3, ("Pinning %Ix" , (size_t)o)); |
20210 | if ((o >= low) && (o < high)) |
20211 | { |
20212 | dprintf(3,("^%Ix^" , (size_t)o)); |
20213 | set_pinned (o); |
20214 | |
20215 | #ifdef FEATURE_EVENT_TRACE |
20216 | if(EVENT_ENABLED(PinObjectAtGCTime)) |
20217 | { |
20218 | fire_etw_pin_object_event(o, ppObject); |
20219 | } |
20220 | #endif // FEATURE_EVENT_TRACE |
20221 | |
20222 | #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE) |
20223 | num_pinned_objects++; |
20224 | #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE |
20225 | } |
20226 | } |
20227 | |
20228 | #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE) |
20229 | size_t gc_heap::get_total_pinned_objects() |
20230 | { |
20231 | #ifdef MULTIPLE_HEAPS |
20232 | size_t total_num_pinned_objects = 0; |
20233 | for (int i = 0; i < gc_heap::n_heaps; i++) |
20234 | { |
20235 | gc_heap* hp = gc_heap::g_heaps[i]; |
20236 | total_num_pinned_objects += hp->num_pinned_objects; |
20237 | } |
20238 | return total_num_pinned_objects; |
20239 | #else //MULTIPLE_HEAPS |
20240 | return num_pinned_objects; |
20241 | #endif //MULTIPLE_HEAPS |
20242 | } |
20243 | #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE |
20244 | |
20245 | void gc_heap::reset_mark_stack () |
20246 | { |
20247 | reset_pinned_queue(); |
20248 | max_overflow_address = 0; |
20249 | min_overflow_address = MAX_PTR; |
20250 | } |
20251 | |
20252 | #ifdef FEATURE_STRUCTALIGN |
20253 | // |
20254 | // The word with left child, right child, and align info is laid out as follows: |
20255 | // |
20256 | // | upper short word | lower short word | |
20257 | // |<------------> <----->|<------------> <----->| |
20258 | // | left child info hi| right child info lo| |
20259 | // x86: | 10 bits 6 bits| 10 bits 6 bits| |
20260 | // |
20261 | // where left/right child are signed values and concat(info hi, info lo) is unsigned. |
20262 | // |
20263 | // The "align info" encodes two numbers: the required alignment (a power of two) |
20264 | // and the misalignment (the number of machine words the destination address needs |
20265 | // to be adjusted by to provide alignment - so this number is always smaller than |
20266 | // the required alignment). Thus, the two can be represented as the "logical or" |
20267 | // of the two numbers. Note that the actual pad is computed from the misalignment |
20268 | // by adding the alignment iff the misalignment is non-zero and less than min_obj_size. |
20269 | // |
20270 | |
20271 | // The number of bits in a brick. |
20272 | #if defined (_TARGET_AMD64_) |
20273 | #define brick_bits (12) |
20274 | #else |
20275 | #define brick_bits (11) |
20276 | #endif //_TARGET_AMD64_ |
20277 | C_ASSERT(brick_size == (1 << brick_bits)); |
20278 | |
20279 | // The number of bits needed to represent the offset to a child node. |
20280 | // "brick_bits + 1" allows us to represent a signed offset within a brick. |
20281 | #define child_bits (brick_bits + 1 - LOG2_PTRSIZE) |
20282 | |
20283 | // The number of bits in each of the pad hi, pad lo fields. |
20284 | #define pad_bits (sizeof(short) * 8 - child_bits) |
20285 | |
20286 | #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1)) |
20287 | #define pad_mask ((1 << pad_bits) - 1) |
20288 | #define pad_from_short(w) ((size_t)(w) & pad_mask) |
20289 | #else // FEATURE_STRUCTALIGN |
20290 | #define child_from_short(w) (w) |
20291 | #endif // FEATURE_STRUCTALIGN |
20292 | |
20293 | inline |
20294 | short node_left_child(uint8_t* node) |
20295 | { |
20296 | return child_from_short(((plug_and_pair*)node)[-1].m_pair.left); |
20297 | } |
20298 | |
20299 | inline |
20300 | void set_node_left_child(uint8_t* node, ptrdiff_t val) |
20301 | { |
20302 | assert (val > -(ptrdiff_t)brick_size); |
20303 | assert (val < (ptrdiff_t)brick_size); |
20304 | assert (Aligned (val)); |
20305 | #ifdef FEATURE_STRUCTALIGN |
20306 | size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left); |
20307 | ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad; |
20308 | #else // FEATURE_STRUCTALIGN |
20309 | ((plug_and_pair*)node)[-1].m_pair.left = (short)val; |
20310 | #endif // FEATURE_STRUCTALIGN |
20311 | assert (node_left_child (node) == val); |
20312 | } |
20313 | |
20314 | inline |
20315 | short node_right_child(uint8_t* node) |
20316 | { |
20317 | return child_from_short(((plug_and_pair*)node)[-1].m_pair.right); |
20318 | } |
20319 | |
20320 | inline |
20321 | void set_node_right_child(uint8_t* node, ptrdiff_t val) |
20322 | { |
20323 | assert (val > -(ptrdiff_t)brick_size); |
20324 | assert (val < (ptrdiff_t)brick_size); |
20325 | assert (Aligned (val)); |
20326 | #ifdef FEATURE_STRUCTALIGN |
20327 | size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right); |
20328 | ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad; |
20329 | #else // FEATURE_STRUCTALIGN |
20330 | ((plug_and_pair*)node)[-1].m_pair.right = (short)val; |
20331 | #endif // FEATURE_STRUCTALIGN |
20332 | assert (node_right_child (node) == val); |
20333 | } |
20334 | |
20335 | #ifdef FEATURE_STRUCTALIGN |
20336 | void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad) |
20337 | { |
20338 | // Extract the single-number aligninfo from the fields. |
20339 | short left = ((plug_and_pair*)node)[-1].m_pair.left; |
20340 | short right = ((plug_and_pair*)node)[-1].m_pair.right; |
20341 | ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right); |
20342 | ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT; |
20343 | |
20344 | // Replicate the topmost bit into all lower bits. |
20345 | ptrdiff_t x = aligninfo; |
20346 | x |= x >> 8; |
20347 | x |= x >> 4; |
20348 | x |= x >> 2; |
20349 | x |= x >> 1; |
20350 | |
20351 | // Clear all bits but the highest. |
20352 | requiredAlignment = (int)(x ^ (x >> 1)); |
20353 | pad = aligninfo - requiredAlignment; |
20354 | pad += AdjustmentForMinPadSize(pad, requiredAlignment); |
20355 | } |
20356 | |
20357 | inline |
20358 | ptrdiff_t node_alignpad (uint8_t* node) |
20359 | { |
20360 | int requiredAlignment; |
20361 | ptrdiff_t alignpad; |
20362 | node_aligninfo (node, requiredAlignment, alignpad); |
20363 | return alignpad; |
20364 | } |
20365 | |
20366 | void clear_node_aligninfo (uint8_t* node) |
20367 | { |
20368 | ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits; |
20369 | ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits; |
20370 | } |
20371 | |
20372 | void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad) |
20373 | { |
20374 | // Encode the alignment requirement and alignment offset as a single number |
20375 | // as described above. |
20376 | ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1)); |
20377 | assert (Aligned (aligninfo)); |
20378 | ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT; |
20379 | assert (aligninfo_shifted < (1 << (pad_bits + pad_bits))); |
20380 | |
20381 | ptrdiff_t hi = aligninfo_shifted >> pad_bits; |
20382 | assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0); |
20383 | ((plug_and_pair*)node)[-1].m_pair.left |= hi; |
20384 | |
20385 | ptrdiff_t lo = aligninfo_shifted & pad_mask; |
20386 | assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0); |
20387 | ((plug_and_pair*)node)[-1].m_pair.right |= lo; |
20388 | |
20389 | #ifdef _DEBUG |
20390 | int requiredAlignment2; |
20391 | ptrdiff_t pad2; |
20392 | node_aligninfo (node, requiredAlignment2, pad2); |
20393 | assert (requiredAlignment == requiredAlignment2); |
20394 | assert (pad == pad2); |
20395 | #endif // _DEBUG |
20396 | } |
20397 | #endif // FEATURE_STRUCTALIGN |
20398 | |
20399 | inline |
20400 | void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val) |
20401 | { |
20402 | ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc); |
20403 | *place = val; |
20404 | } |
20405 | |
20406 | inline |
20407 | ptrdiff_t loh_node_relocation_distance(uint8_t* node) |
20408 | { |
20409 | return (((loh_obj_and_pad*)node)[-1].reloc); |
20410 | } |
20411 | |
20412 | inline |
20413 | ptrdiff_t node_relocation_distance (uint8_t* node) |
20414 | { |
20415 | return (((plug_and_reloc*)(node))[-1].reloc & ~3); |
20416 | } |
20417 | |
20418 | inline |
20419 | void set_node_relocation_distance(uint8_t* node, ptrdiff_t val) |
20420 | { |
20421 | assert (val == (val & ~3)); |
20422 | ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc); |
20423 | //clear the left bit and the relocation field |
20424 | *place &= 1; |
20425 | // store the value |
20426 | *place |= val; |
20427 | } |
20428 | |
20429 | #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2) |
20430 | |
20431 | #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2; |
20432 | |
20433 | #ifndef FEATURE_STRUCTALIGN |
20434 | void set_node_realigned(uint8_t* node) |
20435 | { |
20436 | ((plug_and_reloc*)(node))[-1].reloc |= 1; |
20437 | } |
20438 | |
20439 | void clear_node_realigned(uint8_t* node) |
20440 | { |
20441 | #ifdef RESPECT_LARGE_ALIGNMENT |
20442 | ((plug_and_reloc*)(node))[-1].reloc &= ~1; |
20443 | #else |
20444 | UNREFERENCED_PARAMETER(node); |
20445 | #endif //RESPECT_LARGE_ALIGNMENT |
20446 | } |
20447 | #endif // FEATURE_STRUCTALIGN |
20448 | |
20449 | inline |
20450 | size_t node_gap_size (uint8_t* node) |
20451 | { |
20452 | return ((plug_and_gap *)node)[-1].gap; |
20453 | } |
20454 | |
20455 | void set_gap_size (uint8_t* node, size_t size) |
20456 | { |
20457 | assert (Aligned (size)); |
20458 | |
20459 | // clear the 2 uint32_t used by the node. |
20460 | ((plug_and_gap *)node)[-1].reloc = 0; |
20461 | ((plug_and_gap *)node)[-1].lr =0; |
20462 | ((plug_and_gap *)node)[-1].gap = size; |
20463 | |
20464 | assert ((size == 0 )||(size >= sizeof(plug_and_reloc))); |
20465 | |
20466 | } |
20467 | |
20468 | uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number, |
20469 | uint8_t* tree, uint8_t* last_node) |
20470 | { |
20471 | dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]" , |
20472 | (size_t)new_node, brick_of(new_node), |
20473 | (size_t)tree, brick_of(tree), |
20474 | (size_t)last_node, brick_of(last_node), |
20475 | sequence_number)); |
20476 | if (power_of_two_p (sequence_number)) |
20477 | { |
20478 | set_node_left_child (new_node, (tree - new_node)); |
20479 | dprintf (3, ("NT: %Ix, LC->%Ix" , (size_t)new_node, (tree - new_node))); |
20480 | tree = new_node; |
20481 | } |
20482 | else |
20483 | { |
20484 | if (oddp (sequence_number)) |
20485 | { |
20486 | set_node_right_child (last_node, (new_node - last_node)); |
20487 | dprintf (3, ("%Ix RC->%Ix" , last_node, (new_node - last_node))); |
20488 | } |
20489 | else |
20490 | { |
20491 | uint8_t* earlier_node = tree; |
20492 | size_t imax = logcount(sequence_number) - 2; |
20493 | for (size_t i = 0; i != imax; i++) |
20494 | { |
20495 | earlier_node = earlier_node + node_right_child (earlier_node); |
20496 | } |
20497 | int tmp_offset = node_right_child (earlier_node); |
20498 | assert (tmp_offset); // should never be empty |
20499 | set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node)); |
20500 | set_node_right_child (earlier_node, (new_node - earlier_node)); |
20501 | |
20502 | dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix" , |
20503 | new_node, ((earlier_node + tmp_offset ) - new_node), |
20504 | earlier_node, (new_node - earlier_node))); |
20505 | } |
20506 | } |
20507 | return tree; |
20508 | } |
20509 | |
20510 | size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick, |
20511 | uint8_t* x, uint8_t* plug_end) |
20512 | { |
20513 | dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix" , |
20514 | tree, current_brick, x, plug_end)); |
20515 | |
20516 | if (tree != NULL) |
20517 | { |
20518 | dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix" , |
20519 | current_brick, (size_t)(tree - brick_address (current_brick)), tree)); |
20520 | set_brick (current_brick, (tree - brick_address (current_brick))); |
20521 | } |
20522 | else |
20523 | { |
20524 | dprintf (3, ("b- %Ix->-1" , current_brick)); |
20525 | set_brick (current_brick, -1); |
20526 | } |
20527 | size_t b = 1 + current_brick; |
20528 | ptrdiff_t offset = 0; |
20529 | size_t last_br = brick_of (plug_end-1); |
20530 | current_brick = brick_of (x-1); |
20531 | dprintf (3, ("ubt: %Ix->%Ix]->%Ix]" , b, last_br, current_brick)); |
20532 | while (b <= current_brick) |
20533 | { |
20534 | if (b <= last_br) |
20535 | { |
20536 | set_brick (b, --offset); |
20537 | } |
20538 | else |
20539 | { |
20540 | set_brick (b,-1); |
20541 | } |
20542 | b++; |
20543 | } |
20544 | return brick_of (x); |
20545 | } |
20546 | |
20547 | void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate) |
20548 | { |
20549 | #ifdef BIT64 |
20550 | // We should never demote big plugs to gen0. |
20551 | if (gen == youngest_generation) |
20552 | { |
20553 | heap_segment* seg = ephemeral_heap_segment; |
20554 | size_t mark_stack_large_bos = mark_stack_bos; |
20555 | size_t large_plug_pos = 0; |
20556 | while (mark_stack_large_bos < mark_stack_tos) |
20557 | { |
20558 | if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th) |
20559 | { |
20560 | while (mark_stack_bos <= mark_stack_large_bos) |
20561 | { |
20562 | size_t entry = deque_pinned_plug(); |
20563 | size_t len = pinned_len (pinned_plug_of (entry)); |
20564 | uint8_t* plug = pinned_plug (pinned_plug_of(entry)); |
20565 | if (len > demotion_plug_len_th) |
20566 | { |
20567 | dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)" , gen->gen_num, plug, len, (plug+len))); |
20568 | } |
20569 | pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen); |
20570 | assert(mark_stack_array[entry].len == 0 || |
20571 | mark_stack_array[entry].len >= Align(min_obj_size)); |
20572 | generation_allocation_pointer (consing_gen) = plug + len; |
20573 | generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg); |
20574 | set_allocator_next_pin (consing_gen); |
20575 | } |
20576 | } |
20577 | |
20578 | mark_stack_large_bos++; |
20579 | } |
20580 | } |
20581 | #endif // BIT64 |
20582 | |
20583 | generation_plan_allocation_start (gen) = |
20584 | allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1); |
20585 | generation_plan_allocation_start_size (gen) = Align (min_obj_size); |
20586 | size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen)); |
20587 | if (next_plug_to_allocate) |
20588 | { |
20589 | size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen)); |
20590 | if (allocation_left > dist_to_next_plug) |
20591 | { |
20592 | allocation_left = dist_to_next_plug; |
20593 | } |
20594 | } |
20595 | if (allocation_left < Align (min_obj_size)) |
20596 | { |
20597 | generation_plan_allocation_start_size (gen) += allocation_left; |
20598 | generation_allocation_pointer (consing_gen) += allocation_left; |
20599 | } |
20600 | |
20601 | dprintf (1, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)" , gen->gen_num, |
20602 | generation_plan_allocation_start (gen), |
20603 | generation_plan_allocation_start_size (gen), |
20604 | generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen), |
20605 | next_plug_to_allocate)); |
20606 | } |
20607 | |
20608 | void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen) |
20609 | { |
20610 | BOOL adjacentp = FALSE; |
20611 | |
20612 | generation_plan_allocation_start (gen) = |
20613 | allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0, |
20614 | #ifdef SHORT_PLUGS |
20615 | FALSE, NULL, |
20616 | #endif //SHORT_PLUGS |
20617 | FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG); |
20618 | |
20619 | generation_plan_allocation_start_size (gen) = Align (min_obj_size); |
20620 | size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen)); |
20621 | if ((allocation_left < Align (min_obj_size)) && |
20622 | (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen)))) |
20623 | { |
20624 | generation_plan_allocation_start_size (gen) += allocation_left; |
20625 | generation_allocation_pointer (consing_gen) += allocation_left; |
20626 | } |
20627 | |
20628 | dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)" , gen->gen_num, |
20629 | generation_plan_allocation_start (consing_gen), |
20630 | generation_allocation_pointer (consing_gen), |
20631 | generation_allocation_limit (consing_gen))); |
20632 | } |
20633 | |
20634 | void gc_heap::plan_generation_starts (generation*& consing_gen) |
20635 | { |
20636 | //make sure that every generation has a planned allocation start |
20637 | int gen_number = settings.condemned_generation; |
20638 | while (gen_number >= 0) |
20639 | { |
20640 | if (gen_number < max_generation) |
20641 | { |
20642 | consing_gen = ensure_ephemeral_heap_segment (consing_gen); |
20643 | } |
20644 | generation* gen = generation_of (gen_number); |
20645 | if (0 == generation_plan_allocation_start (gen)) |
20646 | { |
20647 | plan_generation_start (gen, consing_gen, 0); |
20648 | assert (generation_plan_allocation_start (gen)); |
20649 | } |
20650 | gen_number--; |
20651 | } |
20652 | // now we know the planned allocation size |
20653 | heap_segment_plan_allocated (ephemeral_heap_segment) = |
20654 | generation_allocation_pointer (consing_gen); |
20655 | } |
20656 | |
20657 | void gc_heap::advance_pins_for_demotion (generation* gen) |
20658 | { |
20659 | uint8_t* original_youngest_start = generation_allocation_start (youngest_generation); |
20660 | heap_segment* seg = ephemeral_heap_segment; |
20661 | |
20662 | if ((!(pinned_plug_que_empty_p()))) |
20663 | { |
20664 | size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation)); |
20665 | size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted; |
20666 | size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen); |
20667 | float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip; |
20668 | float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1))); |
20669 | if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30)) |
20670 | { |
20671 | while (!pinned_plug_que_empty_p() && |
20672 | (pinned_plug (oldest_pin()) < original_youngest_start)) |
20673 | { |
20674 | size_t entry = deque_pinned_plug(); |
20675 | size_t len = pinned_len (pinned_plug_of (entry)); |
20676 | uint8_t* plug = pinned_plug (pinned_plug_of(entry)); |
20677 | pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen); |
20678 | assert(mark_stack_array[entry].len == 0 || |
20679 | mark_stack_array[entry].len >= Align(min_obj_size)); |
20680 | generation_allocation_pointer (gen) = plug + len; |
20681 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
20682 | set_allocator_next_pin (gen); |
20683 | |
20684 | //Add the size of the pinned plug to the right pinned allocations |
20685 | //find out which gen this pinned plug came from |
20686 | int frgn = object_gennum (plug); |
20687 | if ((frgn != (int)max_generation) && settings.promotion) |
20688 | { |
20689 | int togn = object_gennum_plan (plug); |
20690 | generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len; |
20691 | if (frgn < togn) |
20692 | { |
20693 | generation_pinned_allocation_compact_size (generation_of (togn)) += len; |
20694 | } |
20695 | } |
20696 | |
20697 | dprintf (2, ("skipping gap %d, pin %Ix (%Id)" , |
20698 | pinned_len (pinned_plug_of (entry)), plug, len)); |
20699 | } |
20700 | } |
20701 | dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d" , |
20702 | gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100))); |
20703 | } |
20704 | } |
20705 | |
20706 | void gc_heap::process_ephemeral_boundaries (uint8_t* x, |
20707 | int& active_new_gen_number, |
20708 | int& active_old_gen_number, |
20709 | generation*& consing_gen, |
20710 | BOOL& allocate_in_condemned) |
20711 | { |
20712 | retry: |
20713 | if ((active_old_gen_number > 0) && |
20714 | (x >= generation_allocation_start (generation_of (active_old_gen_number - 1)))) |
20715 | { |
20716 | dprintf (1, ("crossing gen%d, x is %Ix" , active_old_gen_number - 1, x)); |
20717 | |
20718 | if (!pinned_plug_que_empty_p()) |
20719 | { |
20720 | dprintf (1, ("oldest pin: %Ix(%Id)" , |
20721 | pinned_plug (oldest_pin()), |
20722 | (x - pinned_plug (oldest_pin())))); |
20723 | } |
20724 | |
20725 | if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation)) |
20726 | { |
20727 | active_new_gen_number--; |
20728 | } |
20729 | |
20730 | active_old_gen_number--; |
20731 | assert ((!settings.promotion) || (active_new_gen_number>0)); |
20732 | |
20733 | if (active_new_gen_number == (max_generation - 1)) |
20734 | { |
20735 | #ifdef FREE_USAGE_STATS |
20736 | if (settings.condemned_generation == max_generation) |
20737 | { |
20738 | // We need to do this before we skip the rest of the pinned plugs. |
20739 | generation* gen_2 = generation_of (max_generation); |
20740 | generation* gen_1 = generation_of (max_generation - 1); |
20741 | |
20742 | size_t total_num_pinned_free_spaces_left = 0; |
20743 | |
20744 | // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is. |
20745 | for (int j = 0; j < NUM_GEN_POWER2; j++) |
20746 | { |
20747 | dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)" , |
20748 | heap_number, |
20749 | settings.gc_index, |
20750 | (j + 10), |
20751 | gen_2->gen_current_pinned_free_spaces[j], |
20752 | gen_2->gen_plugs[j], gen_1->gen_plugs[j], |
20753 | (gen_2->gen_plugs[j] + gen_1->gen_plugs[j]))); |
20754 | |
20755 | total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j]; |
20756 | } |
20757 | |
20758 | float pinned_free_list_efficiency = 0; |
20759 | size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2); |
20760 | if (total_pinned_free_space != 0) |
20761 | { |
20762 | pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space; |
20763 | } |
20764 | |
20765 | dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left" , |
20766 | heap_number, |
20767 | generation_allocated_in_pinned_free (gen_2), |
20768 | total_pinned_free_space, |
20769 | (int)(pinned_free_list_efficiency * 100), |
20770 | generation_pinned_free_obj_space (gen_2), |
20771 | total_num_pinned_free_spaces_left)); |
20772 | } |
20773 | #endif //FREE_USAGE_STATS |
20774 | |
20775 | //Go past all of the pinned plugs for this generation. |
20776 | while (!pinned_plug_que_empty_p() && |
20777 | (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment))) |
20778 | { |
20779 | size_t entry = deque_pinned_plug(); |
20780 | mark* m = pinned_plug_of (entry); |
20781 | uint8_t* plug = pinned_plug (m); |
20782 | size_t len = pinned_len (m); |
20783 | // detect pinned block in different segment (later) than |
20784 | // allocation segment, skip those until the oldest pin is in the ephemeral seg. |
20785 | // adjust the allocation segment along the way (at the end it will |
20786 | // be the ephemeral segment. |
20787 | heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen)); |
20788 | |
20789 | PREFIX_ASSUME(nseg != NULL); |
20790 | |
20791 | while (!((plug >= generation_allocation_pointer (consing_gen))&& |
20792 | (plug < heap_segment_allocated (nseg)))) |
20793 | { |
20794 | //adjust the end of the segment to be the end of the plug |
20795 | assert (generation_allocation_pointer (consing_gen)>= |
20796 | heap_segment_mem (nseg)); |
20797 | assert (generation_allocation_pointer (consing_gen)<= |
20798 | heap_segment_committed (nseg)); |
20799 | |
20800 | heap_segment_plan_allocated (nseg) = |
20801 | generation_allocation_pointer (consing_gen); |
20802 | //switch allocation segment |
20803 | nseg = heap_segment_next_rw (nseg); |
20804 | generation_allocation_segment (consing_gen) = nseg; |
20805 | //reset the allocation pointer and limits |
20806 | generation_allocation_pointer (consing_gen) = |
20807 | heap_segment_mem (nseg); |
20808 | } |
20809 | set_new_pin_info (m, generation_allocation_pointer (consing_gen)); |
20810 | assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size)); |
20811 | generation_allocation_pointer (consing_gen) = plug + len; |
20812 | generation_allocation_limit (consing_gen) = |
20813 | generation_allocation_pointer (consing_gen); |
20814 | } |
20815 | allocate_in_condemned = TRUE; |
20816 | consing_gen = ensure_ephemeral_heap_segment (consing_gen); |
20817 | } |
20818 | |
20819 | if (active_new_gen_number != max_generation) |
20820 | { |
20821 | if (active_new_gen_number == (max_generation - 1)) |
20822 | { |
20823 | maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)); |
20824 | if (!demote_gen1_p) |
20825 | advance_pins_for_demotion (consing_gen); |
20826 | } |
20827 | |
20828 | plan_generation_start (generation_of (active_new_gen_number), consing_gen, x); |
20829 | |
20830 | dprintf (1, ("process eph: allocated gen%d start at %Ix" , |
20831 | active_new_gen_number, |
20832 | generation_plan_allocation_start (generation_of (active_new_gen_number)))); |
20833 | |
20834 | if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p()) |
20835 | { |
20836 | uint8_t* pplug = pinned_plug (oldest_pin()); |
20837 | if (object_gennum (pplug) > 0) |
20838 | { |
20839 | demotion_low = pplug; |
20840 | dprintf (3, ("process eph: dlow->%Ix" , demotion_low)); |
20841 | } |
20842 | } |
20843 | |
20844 | assert (generation_plan_allocation_start (generation_of (active_new_gen_number))); |
20845 | } |
20846 | |
20847 | goto retry; |
20848 | } |
20849 | } |
20850 | |
20851 | inline |
20852 | void gc_heap::seg_clear_mark_bits (heap_segment* seg) |
20853 | { |
20854 | uint8_t* o = heap_segment_mem (seg); |
20855 | while (o < heap_segment_allocated (seg)) |
20856 | { |
20857 | if (marked (o)) |
20858 | { |
20859 | clear_marked (o); |
20860 | } |
20861 | o = o + Align (size (o)); |
20862 | } |
20863 | } |
20864 | |
20865 | #ifdef FEATURE_BASICFREEZE |
20866 | void gc_heap::sweep_ro_segments (heap_segment* start_seg) |
20867 | { |
20868 | //go through all of the segment in range and reset the mark bit |
20869 | //TODO works only on small object segments |
20870 | |
20871 | heap_segment* seg = start_seg; |
20872 | |
20873 | while (seg) |
20874 | { |
20875 | if (heap_segment_read_only_p (seg) && |
20876 | heap_segment_in_range_p (seg)) |
20877 | { |
20878 | #ifdef BACKGROUND_GC |
20879 | if (settings.concurrent) |
20880 | { |
20881 | seg_clear_mark_array_bits_soh (seg); |
20882 | } |
20883 | else |
20884 | { |
20885 | seg_clear_mark_bits (seg); |
20886 | } |
20887 | #else //BACKGROUND_GC |
20888 | |
20889 | #ifdef MARK_ARRAY |
20890 | if(gc_can_use_concurrent) |
20891 | { |
20892 | clear_mark_array (max (heap_segment_mem (seg), lowest_address), |
20893 | min (heap_segment_allocated (seg), highest_address), |
20894 | FALSE); // read_only segments need the mark clear |
20895 | } |
20896 | #else //MARK_ARRAY |
20897 | seg_clear_mark_bits (seg); |
20898 | #endif //MARK_ARRAY |
20899 | |
20900 | #endif //BACKGROUND_GC |
20901 | } |
20902 | seg = heap_segment_next (seg); |
20903 | } |
20904 | } |
20905 | #endif // FEATURE_BASICFREEZE |
20906 | |
20907 | #ifdef FEATURE_LOH_COMPACTION |
20908 | inline |
20909 | BOOL gc_heap::loh_pinned_plug_que_empty_p() |
20910 | { |
20911 | return (loh_pinned_queue_bos == loh_pinned_queue_tos); |
20912 | } |
20913 | |
20914 | void gc_heap::loh_set_allocator_next_pin() |
20915 | { |
20916 | if (!(loh_pinned_plug_que_empty_p())) |
20917 | { |
20918 | mark* oldest_entry = loh_oldest_pin(); |
20919 | uint8_t* plug = pinned_plug (oldest_entry); |
20920 | generation* gen = large_object_generation; |
20921 | if ((plug >= generation_allocation_pointer (gen)) && |
20922 | (plug < generation_allocation_limit (gen))) |
20923 | { |
20924 | generation_allocation_limit (gen) = pinned_plug (oldest_entry); |
20925 | } |
20926 | else |
20927 | assert (!((plug < generation_allocation_pointer (gen)) && |
20928 | (plug >= heap_segment_mem (generation_allocation_segment (gen))))); |
20929 | } |
20930 | } |
20931 | |
20932 | size_t gc_heap::loh_deque_pinned_plug () |
20933 | { |
20934 | size_t m = loh_pinned_queue_bos; |
20935 | loh_pinned_queue_bos++; |
20936 | return m; |
20937 | } |
20938 | |
20939 | inline |
20940 | mark* gc_heap::loh_pinned_plug_of (size_t bos) |
20941 | { |
20942 | return &loh_pinned_queue[bos]; |
20943 | } |
20944 | |
20945 | inline |
20946 | mark* gc_heap::loh_oldest_pin() |
20947 | { |
20948 | return loh_pinned_plug_of (loh_pinned_queue_bos); |
20949 | } |
20950 | |
20951 | // If we can't grow the queue, then don't compact. |
20952 | BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len) |
20953 | { |
20954 | assert(len >= Align(min_obj_size, get_alignment_constant (FALSE))); |
20955 | |
20956 | if (loh_pinned_queue_length <= loh_pinned_queue_tos) |
20957 | { |
20958 | if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH)) |
20959 | { |
20960 | return FALSE; |
20961 | } |
20962 | } |
20963 | dprintf (3, (" P: %Ix(%Id)" , plug, len)); |
20964 | mark& m = loh_pinned_queue[loh_pinned_queue_tos]; |
20965 | m.first = plug; |
20966 | m.len = len; |
20967 | loh_pinned_queue_tos++; |
20968 | loh_set_allocator_next_pin(); |
20969 | return TRUE; |
20970 | } |
20971 | |
20972 | inline |
20973 | BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit) |
20974 | { |
20975 | dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)" , |
20976 | size, |
20977 | (2* AlignQword (loh_padding_obj_size) + size), |
20978 | alloc_pointer, |
20979 | alloc_limit, |
20980 | (alloc_limit - alloc_pointer))); |
20981 | |
20982 | return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) + size) <= alloc_limit); |
20983 | } |
20984 | |
20985 | uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size) |
20986 | { |
20987 | UNREFERENCED_PARAMETER(old_loc); |
20988 | |
20989 | generation* gen = large_object_generation; |
20990 | dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id" , |
20991 | generation_allocation_pointer (gen), |
20992 | generation_allocation_limit (gen), |
20993 | size)); |
20994 | |
20995 | retry: |
20996 | { |
20997 | heap_segment* seg = generation_allocation_segment (gen); |
20998 | if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen)))) |
20999 | { |
21000 | if ((!(loh_pinned_plug_que_empty_p()) && |
21001 | (generation_allocation_limit (gen) == |
21002 | pinned_plug (loh_oldest_pin())))) |
21003 | { |
21004 | mark* m = loh_pinned_plug_of (loh_deque_pinned_plug()); |
21005 | size_t len = pinned_len (m); |
21006 | uint8_t* plug = pinned_plug (m); |
21007 | dprintf (1235, ("AIC: %Ix->%Ix(%Id)" , generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen))); |
21008 | pinned_len (m) = plug - generation_allocation_pointer (gen); |
21009 | generation_allocation_pointer (gen) = plug + len; |
21010 | |
21011 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
21012 | loh_set_allocator_next_pin(); |
21013 | dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)" , |
21014 | generation_allocation_pointer (gen), |
21015 | generation_allocation_limit (gen), |
21016 | (generation_allocation_limit (gen) - generation_allocation_pointer (gen)))); |
21017 | |
21018 | goto retry; |
21019 | } |
21020 | |
21021 | if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg)) |
21022 | { |
21023 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
21024 | dprintf (1235, ("l->pa(%Ix)" , generation_allocation_limit (gen))); |
21025 | } |
21026 | else |
21027 | { |
21028 | if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg)) |
21029 | { |
21030 | heap_segment_plan_allocated (seg) = heap_segment_committed (seg); |
21031 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
21032 | dprintf (1235, ("l->c(%Ix)" , generation_allocation_limit (gen))); |
21033 | } |
21034 | else |
21035 | { |
21036 | if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) && |
21037 | (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size))))) |
21038 | { |
21039 | dprintf (1235, ("growing seg from %Ix to %Ix\n" , heap_segment_committed (seg), |
21040 | (generation_allocation_pointer (gen) + size))); |
21041 | |
21042 | heap_segment_plan_allocated (seg) = heap_segment_committed (seg); |
21043 | generation_allocation_limit (gen) = heap_segment_plan_allocated (seg); |
21044 | |
21045 | dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)" , |
21046 | generation_allocation_pointer (gen), |
21047 | generation_allocation_limit (gen), |
21048 | (generation_allocation_limit (gen) - generation_allocation_pointer (gen)))); |
21049 | } |
21050 | else |
21051 | { |
21052 | heap_segment* next_seg = heap_segment_next (seg); |
21053 | assert (generation_allocation_pointer (gen)>= |
21054 | heap_segment_mem (seg)); |
21055 | // Verify that all pinned plugs for this segment are consumed |
21056 | if (!loh_pinned_plug_que_empty_p() && |
21057 | ((pinned_plug (loh_oldest_pin()) < |
21058 | heap_segment_allocated (seg)) && |
21059 | (pinned_plug (loh_oldest_pin()) >= |
21060 | generation_allocation_pointer (gen)))) |
21061 | { |
21062 | LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation" , |
21063 | pinned_plug (loh_oldest_pin()))); |
21064 | dprintf (1236, ("queue empty: %d" , loh_pinned_plug_que_empty_p())); |
21065 | FATAL_GC_ERROR(); |
21066 | } |
21067 | assert (generation_allocation_pointer (gen)>= |
21068 | heap_segment_mem (seg)); |
21069 | assert (generation_allocation_pointer (gen)<= |
21070 | heap_segment_committed (seg)); |
21071 | heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen); |
21072 | |
21073 | if (next_seg) |
21074 | { |
21075 | // for LOH do we want to try starting from the first LOH every time though? |
21076 | generation_allocation_segment (gen) = next_seg; |
21077 | generation_allocation_pointer (gen) = heap_segment_mem (next_seg); |
21078 | generation_allocation_limit (gen) = generation_allocation_pointer (gen); |
21079 | |
21080 | dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)" , |
21081 | generation_allocation_pointer (gen), |
21082 | generation_allocation_limit (gen), |
21083 | (generation_allocation_limit (gen) - generation_allocation_pointer (gen)))); |
21084 | } |
21085 | else |
21086 | { |
21087 | dprintf (1, ("We ran out of space compacting, shouldn't happen" )); |
21088 | FATAL_GC_ERROR(); |
21089 | } |
21090 | } |
21091 | } |
21092 | } |
21093 | loh_set_allocator_next_pin(); |
21094 | |
21095 | dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)" , |
21096 | generation_allocation_pointer (gen), |
21097 | generation_allocation_limit (gen), |
21098 | (generation_allocation_limit (gen) - generation_allocation_pointer (gen)))); |
21099 | |
21100 | goto retry; |
21101 | } |
21102 | } |
21103 | |
21104 | { |
21105 | assert (generation_allocation_pointer (gen)>= |
21106 | heap_segment_mem (generation_allocation_segment (gen))); |
21107 | uint8_t* result = generation_allocation_pointer (gen); |
21108 | size_t loh_pad = AlignQword (loh_padding_obj_size); |
21109 | |
21110 | generation_allocation_pointer (gen) += size + loh_pad; |
21111 | assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen)); |
21112 | |
21113 | dprintf (1235, ("p: %Ix, l: %Ix (%Id)" , |
21114 | generation_allocation_pointer (gen), |
21115 | generation_allocation_limit (gen), |
21116 | (generation_allocation_limit (gen) - generation_allocation_pointer (gen)))); |
21117 | |
21118 | assert (result + loh_pad); |
21119 | return result + loh_pad; |
21120 | } |
21121 | } |
21122 | |
21123 | BOOL gc_heap::should_compact_loh() |
21124 | { |
21125 | return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default)); |
21126 | } |
21127 | |
21128 | inline |
21129 | void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p) |
21130 | { |
21131 | if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once)) |
21132 | { |
21133 | if (all_heaps_compacted_p) |
21134 | { |
21135 | // If the compaction mode says to compact once and we are going to compact LOH, |
21136 | // we need to revert it back to no compaction. |
21137 | loh_compaction_mode = loh_compaction_default; |
21138 | } |
21139 | } |
21140 | } |
21141 | |
21142 | BOOL gc_heap::plan_loh() |
21143 | { |
21144 | if (!loh_pinned_queue) |
21145 | { |
21146 | loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]); |
21147 | if (!loh_pinned_queue) |
21148 | { |
21149 | dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction" , |
21150 | LOH_PIN_QUEUE_LENGTH * sizeof (mark))); |
21151 | return FALSE; |
21152 | } |
21153 | |
21154 | loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH; |
21155 | } |
21156 | |
21157 | if (heap_number == 0) |
21158 | loh_pinned_queue_decay = LOH_PIN_DECAY; |
21159 | |
21160 | loh_pinned_queue_tos = 0; |
21161 | loh_pinned_queue_bos = 0; |
21162 | |
21163 | generation* gen = large_object_generation; |
21164 | heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen)); |
21165 | PREFIX_ASSUME(start_seg != NULL); |
21166 | heap_segment* seg = start_seg; |
21167 | uint8_t* o = generation_allocation_start (gen); |
21168 | |
21169 | dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n" , |
21170 | generation_size (max_generation + 1), |
21171 | generation_free_list_space (gen), |
21172 | generation_free_obj_space (gen))); |
21173 | |
21174 | while (seg) |
21175 | { |
21176 | heap_segment_plan_allocated (seg) = heap_segment_mem (seg); |
21177 | seg = heap_segment_next (seg); |
21178 | } |
21179 | |
21180 | seg = start_seg; |
21181 | |
21182 | //Skip the generation gap object |
21183 | o = o + AlignQword (size (o)); |
21184 | // We don't need to ever realloc gen3 start so don't touch it. |
21185 | heap_segment_plan_allocated (seg) = o; |
21186 | generation_allocation_pointer (gen) = o; |
21187 | generation_allocation_limit (gen) = generation_allocation_pointer (gen); |
21188 | generation_allocation_segment (gen) = start_seg; |
21189 | |
21190 | uint8_t* free_space_start = o; |
21191 | uint8_t* free_space_end = o; |
21192 | uint8_t* new_address = 0; |
21193 | |
21194 | while (1) |
21195 | { |
21196 | if (o >= heap_segment_allocated (seg)) |
21197 | { |
21198 | seg = heap_segment_next (seg); |
21199 | if (seg == 0) |
21200 | { |
21201 | break; |
21202 | } |
21203 | |
21204 | o = heap_segment_mem (seg); |
21205 | } |
21206 | |
21207 | if (marked (o)) |
21208 | { |
21209 | free_space_end = o; |
21210 | size_t size = AlignQword (size (o)); |
21211 | dprintf (1235, ("%Ix(%Id) M" , o, size)); |
21212 | |
21213 | if (pinned (o)) |
21214 | { |
21215 | // We don't clear the pinned bit yet so we can check in |
21216 | // compact phase how big a free object we should allocate |
21217 | // in front of the pinned object. We use the reloc address |
21218 | // field to store this. |
21219 | if (!loh_enque_pinned_plug (o, size)) |
21220 | { |
21221 | return FALSE; |
21222 | } |
21223 | new_address = o; |
21224 | } |
21225 | else |
21226 | { |
21227 | new_address = loh_allocate_in_condemned (o, size); |
21228 | } |
21229 | |
21230 | loh_set_node_relocation_distance (o, (new_address - o)); |
21231 | dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)" , o, (o + size), new_address, (new_address + size), (new_address - o))); |
21232 | |
21233 | o = o + size; |
21234 | free_space_start = o; |
21235 | if (o < heap_segment_allocated (seg)) |
21236 | { |
21237 | assert (!marked (o)); |
21238 | } |
21239 | } |
21240 | else |
21241 | { |
21242 | while (o < heap_segment_allocated (seg) && !marked (o)) |
21243 | { |
21244 | dprintf (1235, ("%Ix(%Id) F (%d)" , o, AlignQword (size (o)), ((method_table (o) == g_gc_pFreeObjectMethodTable) ? 1 : 0))); |
21245 | o = o + AlignQword (size (o)); |
21246 | } |
21247 | } |
21248 | } |
21249 | |
21250 | while (!loh_pinned_plug_que_empty_p()) |
21251 | { |
21252 | mark* m = loh_pinned_plug_of (loh_deque_pinned_plug()); |
21253 | size_t len = pinned_len (m); |
21254 | uint8_t* plug = pinned_plug (m); |
21255 | |
21256 | // detect pinned block in different segment (later) than |
21257 | // allocation segment |
21258 | heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen)); |
21259 | |
21260 | while ((plug < generation_allocation_pointer (gen)) || |
21261 | (plug >= heap_segment_allocated (nseg))) |
21262 | { |
21263 | assert ((plug < heap_segment_mem (nseg)) || |
21264 | (plug > heap_segment_reserved (nseg))); |
21265 | //adjust the end of the segment to be the end of the plug |
21266 | assert (generation_allocation_pointer (gen)>= |
21267 | heap_segment_mem (nseg)); |
21268 | assert (generation_allocation_pointer (gen)<= |
21269 | heap_segment_committed (nseg)); |
21270 | |
21271 | heap_segment_plan_allocated (nseg) = |
21272 | generation_allocation_pointer (gen); |
21273 | //switch allocation segment |
21274 | nseg = heap_segment_next_rw (nseg); |
21275 | generation_allocation_segment (gen) = nseg; |
21276 | //reset the allocation pointer and limits |
21277 | generation_allocation_pointer (gen) = |
21278 | heap_segment_mem (nseg); |
21279 | } |
21280 | |
21281 | dprintf (1235, ("SP: %Ix->%Ix(%Id)" , generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen))); |
21282 | pinned_len (m) = plug - generation_allocation_pointer (gen); |
21283 | generation_allocation_pointer (gen) = plug + len; |
21284 | } |
21285 | |
21286 | heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen); |
21287 | generation_allocation_pointer (gen) = 0; |
21288 | generation_allocation_limit (gen) = 0; |
21289 | |
21290 | return TRUE; |
21291 | } |
21292 | |
21293 | void gc_heap::compact_loh() |
21294 | { |
21295 | assert (should_compact_loh()); |
21296 | |
21297 | generation* gen = large_object_generation; |
21298 | heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen)); |
21299 | PREFIX_ASSUME(start_seg != NULL); |
21300 | heap_segment* seg = start_seg; |
21301 | heap_segment* prev_seg = 0; |
21302 | uint8_t* o = generation_allocation_start (gen); |
21303 | |
21304 | //Skip the generation gap object |
21305 | o = o + AlignQword (size (o)); |
21306 | // We don't need to ever realloc gen3 start so don't touch it. |
21307 | uint8_t* free_space_start = o; |
21308 | uint8_t* free_space_end = o; |
21309 | generation_allocator (gen)->clear(); |
21310 | generation_free_list_space (gen) = 0; |
21311 | generation_free_obj_space (gen) = 0; |
21312 | |
21313 | loh_pinned_queue_bos = 0; |
21314 | |
21315 | while (1) |
21316 | { |
21317 | if (o >= heap_segment_allocated (seg)) |
21318 | { |
21319 | heap_segment* next_seg = heap_segment_next (seg); |
21320 | |
21321 | if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) && |
21322 | (seg != start_seg) && !heap_segment_read_only_p (seg)) |
21323 | { |
21324 | dprintf (3, ("Preparing empty large segment %Ix" , (size_t)seg)); |
21325 | assert (prev_seg); |
21326 | heap_segment_next (prev_seg) = next_seg; |
21327 | heap_segment_next (seg) = freeable_large_heap_segment; |
21328 | freeable_large_heap_segment = seg; |
21329 | } |
21330 | else |
21331 | { |
21332 | if (!heap_segment_read_only_p (seg)) |
21333 | { |
21334 | // We grew the segment to accommodate allocations. |
21335 | if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg)) |
21336 | { |
21337 | if ((heap_segment_plan_allocated (seg) - plug_skew) > heap_segment_used (seg)) |
21338 | { |
21339 | heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew; |
21340 | } |
21341 | } |
21342 | |
21343 | heap_segment_allocated (seg) = heap_segment_plan_allocated (seg); |
21344 | dprintf (3, ("Trimming seg to %Ix[" , heap_segment_allocated (seg))); |
21345 | decommit_heap_segment_pages (seg, 0); |
21346 | dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix" , |
21347 | seg, |
21348 | heap_segment_allocated (seg), |
21349 | heap_segment_used (seg), |
21350 | heap_segment_committed (seg))); |
21351 | //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew; |
21352 | dprintf (1236, ("CLOH: used is set to %Ix" , heap_segment_used (seg))); |
21353 | } |
21354 | prev_seg = seg; |
21355 | } |
21356 | |
21357 | seg = next_seg; |
21358 | if (seg == 0) |
21359 | break; |
21360 | else |
21361 | { |
21362 | o = heap_segment_mem (seg); |
21363 | } |
21364 | } |
21365 | |
21366 | if (marked (o)) |
21367 | { |
21368 | free_space_end = o; |
21369 | size_t size = AlignQword (size (o)); |
21370 | |
21371 | size_t loh_pad; |
21372 | uint8_t* reloc = o; |
21373 | clear_marked (o); |
21374 | |
21375 | if (pinned (o)) |
21376 | { |
21377 | // We are relying on the fact the pinned objects are always looked at in the same order |
21378 | // in plan phase and in compact phase. |
21379 | mark* m = loh_pinned_plug_of (loh_deque_pinned_plug()); |
21380 | uint8_t* plug = pinned_plug (m); |
21381 | assert (plug == o); |
21382 | |
21383 | loh_pad = pinned_len (m); |
21384 | clear_pinned (o); |
21385 | } |
21386 | else |
21387 | { |
21388 | loh_pad = AlignQword (loh_padding_obj_size); |
21389 | |
21390 | reloc += loh_node_relocation_distance (o); |
21391 | gcmemcopy (reloc, o, size, TRUE); |
21392 | } |
21393 | |
21394 | thread_gap ((reloc - loh_pad), loh_pad, gen); |
21395 | |
21396 | o = o + size; |
21397 | free_space_start = o; |
21398 | if (o < heap_segment_allocated (seg)) |
21399 | { |
21400 | assert (!marked (o)); |
21401 | } |
21402 | } |
21403 | else |
21404 | { |
21405 | while (o < heap_segment_allocated (seg) && !marked (o)) |
21406 | { |
21407 | o = o + AlignQword (size (o)); |
21408 | } |
21409 | } |
21410 | } |
21411 | |
21412 | assert (loh_pinned_plug_que_empty_p()); |
21413 | |
21414 | dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n" , |
21415 | generation_size (max_generation + 1), |
21416 | generation_free_list_space (gen), |
21417 | generation_free_obj_space (gen))); |
21418 | } |
21419 | |
21420 | void gc_heap::relocate_in_loh_compact() |
21421 | { |
21422 | generation* gen = large_object_generation; |
21423 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
21424 | uint8_t* o = generation_allocation_start (gen); |
21425 | |
21426 | //Skip the generation gap object |
21427 | o = o + AlignQword (size (o)); |
21428 | |
21429 | relocate_args args; |
21430 | args.low = gc_low; |
21431 | args.high = gc_high; |
21432 | args.last_plug = 0; |
21433 | |
21434 | while (1) |
21435 | { |
21436 | if (o >= heap_segment_allocated (seg)) |
21437 | { |
21438 | seg = heap_segment_next (seg); |
21439 | if (seg == 0) |
21440 | { |
21441 | break; |
21442 | } |
21443 | |
21444 | o = heap_segment_mem (seg); |
21445 | } |
21446 | |
21447 | if (marked (o)) |
21448 | { |
21449 | size_t size = AlignQword (size (o)); |
21450 | |
21451 | check_class_object_demotion (o); |
21452 | if (contain_pointers (o)) |
21453 | { |
21454 | go_through_object_nostart (method_table (o), o, size(o), pval, |
21455 | { |
21456 | reloc_survivor_helper (pval); |
21457 | }); |
21458 | } |
21459 | |
21460 | o = o + size; |
21461 | if (o < heap_segment_allocated (seg)) |
21462 | { |
21463 | assert (!marked (o)); |
21464 | } |
21465 | } |
21466 | else |
21467 | { |
21468 | while (o < heap_segment_allocated (seg) && !marked (o)) |
21469 | { |
21470 | o = o + AlignQword (size (o)); |
21471 | } |
21472 | } |
21473 | } |
21474 | |
21475 | dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n" , |
21476 | generation_size (max_generation + 1), |
21477 | generation_free_list_space (gen), |
21478 | generation_free_obj_space (gen))); |
21479 | } |
21480 | |
21481 | void gc_heap::walk_relocation_for_loh (void* profiling_context, record_surv_fn fn) |
21482 | { |
21483 | generation* gen = large_object_generation; |
21484 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
21485 | uint8_t* o = generation_allocation_start (gen); |
21486 | |
21487 | //Skip the generation gap object |
21488 | o = o + AlignQword (size (o)); |
21489 | |
21490 | while (1) |
21491 | { |
21492 | if (o >= heap_segment_allocated (seg)) |
21493 | { |
21494 | seg = heap_segment_next (seg); |
21495 | if (seg == 0) |
21496 | { |
21497 | break; |
21498 | } |
21499 | |
21500 | o = heap_segment_mem (seg); |
21501 | } |
21502 | |
21503 | if (marked (o)) |
21504 | { |
21505 | size_t size = AlignQword (size (o)); |
21506 | |
21507 | ptrdiff_t reloc = loh_node_relocation_distance (o); |
21508 | |
21509 | STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc); |
21510 | |
21511 | fn (o, (o + size), reloc, profiling_context, !!settings.compaction, false); |
21512 | |
21513 | o = o + size; |
21514 | if (o < heap_segment_allocated (seg)) |
21515 | { |
21516 | assert (!marked (o)); |
21517 | } |
21518 | } |
21519 | else |
21520 | { |
21521 | while (o < heap_segment_allocated (seg) && !marked (o)) |
21522 | { |
21523 | o = o + AlignQword (size (o)); |
21524 | } |
21525 | } |
21526 | } |
21527 | } |
21528 | |
21529 | BOOL gc_heap::loh_object_p (uint8_t* o) |
21530 | { |
21531 | #ifdef MULTIPLE_HEAPS |
21532 | gc_heap* hp = gc_heap::g_heaps [0]; |
21533 | int brick_entry = hp->brick_table[hp->brick_of (o)]; |
21534 | #else //MULTIPLE_HEAPS |
21535 | int brick_entry = brick_table[brick_of (o)]; |
21536 | #endif //MULTIPLE_HEAPS |
21537 | |
21538 | return (brick_entry == 0); |
21539 | } |
21540 | #endif //FEATURE_LOH_COMPACTION |
21541 | |
21542 | void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p, |
21543 | BOOL& last_pinned_plug_p, |
21544 | BOOL& pinned_plug_p, |
21545 | size_t ps, |
21546 | size_t& artificial_pinned_size) |
21547 | { |
21548 | last_npinned_plug_p = FALSE; |
21549 | last_pinned_plug_p = TRUE; |
21550 | pinned_plug_p = TRUE; |
21551 | artificial_pinned_size = ps; |
21552 | } |
21553 | |
21554 | // Because we have the artificial pinning, we can't guarantee that pinned and npinned |
21555 | // plugs are always interleaved. |
21556 | void gc_heap::store_plug_gap_info (uint8_t* plug_start, |
21557 | uint8_t* plug_end, |
21558 | BOOL& last_npinned_plug_p, |
21559 | BOOL& last_pinned_plug_p, |
21560 | uint8_t*& last_pinned_plug, |
21561 | BOOL& pinned_plug_p, |
21562 | uint8_t* last_object_in_last_plug, |
21563 | BOOL& merge_with_last_pin_p, |
21564 | // this is only for verification purpose |
21565 | size_t last_plug_len) |
21566 | { |
21567 | UNREFERENCED_PARAMETER(last_plug_len); |
21568 | |
21569 | if (!last_npinned_plug_p && !last_pinned_plug_p) |
21570 | { |
21571 | //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start)); |
21572 | dprintf (3, ("Free: %Ix" , (plug_start - plug_end))); |
21573 | assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size))); |
21574 | set_gap_size (plug_start, plug_start - plug_end); |
21575 | } |
21576 | |
21577 | if (pinned (plug_start)) |
21578 | { |
21579 | BOOL save_pre_plug_info_p = FALSE; |
21580 | |
21581 | if (last_npinned_plug_p || last_pinned_plug_p) |
21582 | { |
21583 | //if (last_plug_len == Align (min_obj_size)) |
21584 | //{ |
21585 | // dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct")); |
21586 | // GCToOSInterface::DebugBreak(); |
21587 | //} |
21588 | save_pre_plug_info_p = TRUE; |
21589 | } |
21590 | |
21591 | pinned_plug_p = TRUE; |
21592 | last_npinned_plug_p = FALSE; |
21593 | |
21594 | if (last_pinned_plug_p) |
21595 | { |
21596 | dprintf (3, ("last plug %Ix was also pinned, should merge" , last_pinned_plug)); |
21597 | merge_with_last_pin_p = TRUE; |
21598 | } |
21599 | else |
21600 | { |
21601 | last_pinned_plug_p = TRUE; |
21602 | last_pinned_plug = plug_start; |
21603 | |
21604 | enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug); |
21605 | |
21606 | if (save_pre_plug_info_p) |
21607 | { |
21608 | set_gap_size (plug_start, sizeof (gap_reloc_pair)); |
21609 | } |
21610 | } |
21611 | } |
21612 | else |
21613 | { |
21614 | if (last_pinned_plug_p) |
21615 | { |
21616 | //if (Align (last_plug_len) < min_pre_pin_obj_size) |
21617 | //{ |
21618 | // dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct")); |
21619 | // GCToOSInterface::DebugBreak(); |
21620 | //} |
21621 | |
21622 | save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start); |
21623 | set_gap_size (plug_start, sizeof (gap_reloc_pair)); |
21624 | |
21625 | verify_pins_with_post_plug_info("after saving post plug info" ); |
21626 | } |
21627 | last_npinned_plug_p = TRUE; |
21628 | last_pinned_plug_p = FALSE; |
21629 | } |
21630 | } |
21631 | |
21632 | void gc_heap::record_interesting_data_point (interesting_data_point idp) |
21633 | { |
21634 | #ifdef GC_CONFIG_DRIVEN |
21635 | (interesting_data_per_gc[idp])++; |
21636 | #else |
21637 | UNREFERENCED_PARAMETER(idp); |
21638 | #endif //GC_CONFIG_DRIVEN |
21639 | } |
21640 | |
21641 | #ifdef _PREFAST_ |
21642 | #pragma warning(push) |
21643 | #pragma warning(disable:21000) // Suppress PREFast warning about overly large function |
21644 | #endif //_PREFAST_ |
21645 | void gc_heap::plan_phase (int condemned_gen_number) |
21646 | { |
21647 | size_t old_gen2_allocated = 0; |
21648 | size_t old_gen2_size = 0; |
21649 | |
21650 | if (condemned_gen_number == (max_generation - 1)) |
21651 | { |
21652 | old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation)); |
21653 | old_gen2_size = generation_size (max_generation); |
21654 | } |
21655 | |
21656 | assert (settings.concurrent == FALSE); |
21657 | |
21658 | // %type% category = quote (plan); |
21659 | #ifdef TIME_GC |
21660 | unsigned start; |
21661 | unsigned finish; |
21662 | start = GetCycleCount32(); |
21663 | #endif //TIME_GC |
21664 | |
21665 | dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d" , |
21666 | condemned_gen_number, settings.promotion ? 1 : 0)); |
21667 | |
21668 | generation* condemned_gen1 = generation_of (condemned_gen_number); |
21669 | |
21670 | #ifdef MARK_LIST |
21671 | BOOL use_mark_list = FALSE; |
21672 | uint8_t** mark_list_next = &mark_list[0]; |
21673 | #ifdef GC_CONFIG_DRIVEN |
21674 | dprintf (3, ("total number of marked objects: %Id (%Id)" , |
21675 | (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0])))); |
21676 | |
21677 | if (mark_list_index >= (mark_list_end + 1)) |
21678 | mark_list_index = mark_list_end + 1; |
21679 | #else |
21680 | dprintf (3, ("mark_list length: %Id" , |
21681 | (mark_list_index - &mark_list[0]))); |
21682 | #endif //GC_CONFIG_DRIVEN |
21683 | |
21684 | if ((condemned_gen_number < max_generation) && |
21685 | (mark_list_index <= mark_list_end) |
21686 | #ifdef BACKGROUND_GC |
21687 | && (!recursive_gc_sync::background_running_p()) |
21688 | #endif //BACKGROUND_GC |
21689 | ) |
21690 | { |
21691 | #ifndef MULTIPLE_HEAPS |
21692 | _sort (&mark_list[0], mark_list_index-1, 0); |
21693 | //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0))); |
21694 | //verify_qsort_array (&mark_list[0], mark_list_index-1); |
21695 | #endif //!MULTIPLE_HEAPS |
21696 | use_mark_list = TRUE; |
21697 | get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit); |
21698 | } |
21699 | else |
21700 | { |
21701 | dprintf (3, ("mark_list not used" )); |
21702 | } |
21703 | |
21704 | #endif //MARK_LIST |
21705 | |
21706 | #ifdef FEATURE_BASICFREEZE |
21707 | if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) && |
21708 | ro_segments_in_range) |
21709 | { |
21710 | sweep_ro_segments (generation_start_segment (condemned_gen1)); |
21711 | } |
21712 | #endif // FEATURE_BASICFREEZE |
21713 | |
21714 | #ifndef MULTIPLE_HEAPS |
21715 | if (shigh != (uint8_t*)0) |
21716 | { |
21717 | heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1)); |
21718 | |
21719 | PREFIX_ASSUME(seg != NULL); |
21720 | |
21721 | heap_segment* fseg = seg; |
21722 | do |
21723 | { |
21724 | if (slow > heap_segment_mem (seg) && |
21725 | slow < heap_segment_reserved (seg)) |
21726 | { |
21727 | if (seg == fseg) |
21728 | { |
21729 | uint8_t* o = generation_allocation_start (condemned_gen1) + |
21730 | Align (size (generation_allocation_start (condemned_gen1))); |
21731 | if (slow > o) |
21732 | { |
21733 | assert ((slow - o) >= (int)Align (min_obj_size)); |
21734 | #ifdef BACKGROUND_GC |
21735 | if (current_c_gc_state == c_gc_state_marking) |
21736 | { |
21737 | bgc_clear_batch_mark_array_bits (o, slow); |
21738 | } |
21739 | #endif //BACKGROUND_GC |
21740 | make_unused_array (o, slow - o); |
21741 | } |
21742 | } |
21743 | else |
21744 | { |
21745 | assert (condemned_gen_number == max_generation); |
21746 | make_unused_array (heap_segment_mem (seg), |
21747 | slow - heap_segment_mem (seg)); |
21748 | } |
21749 | } |
21750 | if (in_range_for_segment (shigh, seg)) |
21751 | { |
21752 | #ifdef BACKGROUND_GC |
21753 | if (current_c_gc_state == c_gc_state_marking) |
21754 | { |
21755 | bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg)); |
21756 | } |
21757 | #endif //BACKGROUND_GC |
21758 | heap_segment_allocated (seg) = shigh + Align (size (shigh)); |
21759 | } |
21760 | // test if the segment is in the range of [slow, shigh] |
21761 | if (!((heap_segment_reserved (seg) >= slow) && |
21762 | (heap_segment_mem (seg) <= shigh))) |
21763 | { |
21764 | // shorten it to minimum |
21765 | heap_segment_allocated (seg) = heap_segment_mem (seg); |
21766 | } |
21767 | seg = heap_segment_next_rw (seg); |
21768 | } while (seg); |
21769 | } |
21770 | else |
21771 | { |
21772 | heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1)); |
21773 | |
21774 | PREFIX_ASSUME(seg != NULL); |
21775 | |
21776 | heap_segment* sseg = seg; |
21777 | do |
21778 | { |
21779 | // shorten it to minimum |
21780 | if (seg == sseg) |
21781 | { |
21782 | // no survivors make all generations look empty |
21783 | uint8_t* o = generation_allocation_start (condemned_gen1) + |
21784 | Align (size (generation_allocation_start (condemned_gen1))); |
21785 | #ifdef BACKGROUND_GC |
21786 | if (current_c_gc_state == c_gc_state_marking) |
21787 | { |
21788 | bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg)); |
21789 | } |
21790 | #endif //BACKGROUND_GC |
21791 | heap_segment_allocated (seg) = o; |
21792 | } |
21793 | else |
21794 | { |
21795 | assert (condemned_gen_number == max_generation); |
21796 | #ifdef BACKGROUND_GC |
21797 | if (current_c_gc_state == c_gc_state_marking) |
21798 | { |
21799 | bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg)); |
21800 | } |
21801 | #endif //BACKGROUND_GC |
21802 | heap_segment_allocated (seg) = heap_segment_mem (seg); |
21803 | } |
21804 | seg = heap_segment_next_rw (seg); |
21805 | } while (seg); |
21806 | } |
21807 | |
21808 | #endif //MULTIPLE_HEAPS |
21809 | |
21810 | heap_segment* seg1 = heap_segment_rw (generation_start_segment (condemned_gen1)); |
21811 | |
21812 | PREFIX_ASSUME(seg1 != NULL); |
21813 | |
21814 | uint8_t* end = heap_segment_allocated (seg1); |
21815 | uint8_t* first_condemned_address = generation_allocation_start (condemned_gen1); |
21816 | uint8_t* x = first_condemned_address; |
21817 | |
21818 | assert (!marked (x)); |
21819 | uint8_t* plug_end = x; |
21820 | uint8_t* tree = 0; |
21821 | size_t sequence_number = 0; |
21822 | uint8_t* last_node = 0; |
21823 | size_t current_brick = brick_of (x); |
21824 | BOOL allocate_in_condemned = ((condemned_gen_number == max_generation)|| |
21825 | (settings.promotion == FALSE)); |
21826 | int active_old_gen_number = condemned_gen_number; |
21827 | int active_new_gen_number = (allocate_in_condemned ? condemned_gen_number: |
21828 | (1 + condemned_gen_number)); |
21829 | generation* older_gen = 0; |
21830 | generation* consing_gen = condemned_gen1; |
21831 | alloc_list r_free_list [MAX_BUCKET_COUNT]; |
21832 | |
21833 | size_t r_free_list_space = 0; |
21834 | size_t r_free_obj_space = 0; |
21835 | size_t r_older_gen_free_list_allocated = 0; |
21836 | size_t r_older_gen_condemned_allocated = 0; |
21837 | size_t r_older_gen_end_seg_allocated = 0; |
21838 | uint8_t* r_allocation_pointer = 0; |
21839 | uint8_t* r_allocation_limit = 0; |
21840 | uint8_t* r_allocation_start_region = 0; |
21841 | heap_segment* r_allocation_segment = 0; |
21842 | #ifdef FREE_USAGE_STATS |
21843 | size_t r_older_gen_free_space[NUM_GEN_POWER2]; |
21844 | #endif //FREE_USAGE_STATS |
21845 | |
21846 | if ((condemned_gen_number < max_generation)) |
21847 | { |
21848 | older_gen = generation_of (min (max_generation, 1 + condemned_gen_number)); |
21849 | generation_allocator (older_gen)->copy_to_alloc_list (r_free_list); |
21850 | |
21851 | r_free_list_space = generation_free_list_space (older_gen); |
21852 | r_free_obj_space = generation_free_obj_space (older_gen); |
21853 | #ifdef FREE_USAGE_STATS |
21854 | memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space)); |
21855 | #endif //FREE_USAGE_STATS |
21856 | generation_allocate_end_seg_p (older_gen) = FALSE; |
21857 | r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen); |
21858 | r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen); |
21859 | r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen); |
21860 | r_allocation_limit = generation_allocation_limit (older_gen); |
21861 | r_allocation_pointer = generation_allocation_pointer (older_gen); |
21862 | r_allocation_start_region = generation_allocation_context_start_region (older_gen); |
21863 | r_allocation_segment = generation_allocation_segment (older_gen); |
21864 | heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen)); |
21865 | |
21866 | PREFIX_ASSUME(start_seg != NULL); |
21867 | |
21868 | if (start_seg != ephemeral_heap_segment) |
21869 | { |
21870 | assert (condemned_gen_number == (max_generation - 1)); |
21871 | while (start_seg && (start_seg != ephemeral_heap_segment)) |
21872 | { |
21873 | assert (heap_segment_allocated (start_seg) >= |
21874 | heap_segment_mem (start_seg)); |
21875 | assert (heap_segment_allocated (start_seg) <= |
21876 | heap_segment_reserved (start_seg)); |
21877 | heap_segment_plan_allocated (start_seg) = |
21878 | heap_segment_allocated (start_seg); |
21879 | start_seg = heap_segment_next_rw (start_seg); |
21880 | } |
21881 | } |
21882 | } |
21883 | |
21884 | //reset all of the segment allocated sizes |
21885 | { |
21886 | heap_segment* seg2 = heap_segment_rw (generation_start_segment (condemned_gen1)); |
21887 | |
21888 | PREFIX_ASSUME(seg2 != NULL); |
21889 | |
21890 | while (seg2) |
21891 | { |
21892 | heap_segment_plan_allocated (seg2) = |
21893 | heap_segment_mem (seg2); |
21894 | seg2 = heap_segment_next_rw (seg2); |
21895 | } |
21896 | } |
21897 | int condemned_gn = condemned_gen_number; |
21898 | |
21899 | int bottom_gen = 0; |
21900 | init_free_and_plug(); |
21901 | |
21902 | while (condemned_gn >= bottom_gen) |
21903 | { |
21904 | generation* condemned_gen2 = generation_of (condemned_gn); |
21905 | generation_allocator (condemned_gen2)->clear(); |
21906 | generation_free_list_space (condemned_gen2) = 0; |
21907 | generation_free_obj_space (condemned_gen2) = 0; |
21908 | generation_allocation_size (condemned_gen2) = 0; |
21909 | generation_condemned_allocated (condemned_gen2) = 0; |
21910 | generation_pinned_allocated (condemned_gen2) = 0; |
21911 | generation_free_list_allocated(condemned_gen2) = 0; |
21912 | generation_end_seg_allocated (condemned_gen2) = 0; |
21913 | generation_pinned_allocation_sweep_size (condemned_gen2) = 0; |
21914 | generation_pinned_allocation_compact_size (condemned_gen2) = 0; |
21915 | #ifdef FREE_USAGE_STATS |
21916 | generation_pinned_free_obj_space (condemned_gen2) = 0; |
21917 | generation_allocated_in_pinned_free (condemned_gen2) = 0; |
21918 | generation_allocated_since_last_pin (condemned_gen2) = 0; |
21919 | #endif //FREE_USAGE_STATS |
21920 | generation_plan_allocation_start (condemned_gen2) = 0; |
21921 | generation_allocation_segment (condemned_gen2) = |
21922 | heap_segment_rw (generation_start_segment (condemned_gen2)); |
21923 | |
21924 | PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL); |
21925 | |
21926 | if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment) |
21927 | { |
21928 | generation_allocation_pointer (condemned_gen2) = |
21929 | heap_segment_mem (generation_allocation_segment (condemned_gen2)); |
21930 | } |
21931 | else |
21932 | { |
21933 | generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2); |
21934 | } |
21935 | |
21936 | generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2); |
21937 | generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2); |
21938 | |
21939 | condemned_gn--; |
21940 | } |
21941 | |
21942 | BOOL allocate_first_generation_start = FALSE; |
21943 | |
21944 | if (allocate_in_condemned) |
21945 | { |
21946 | allocate_first_generation_start = TRUE; |
21947 | } |
21948 | |
21949 | dprintf(3,( " From %Ix to %Ix" , (size_t)x, (size_t)end)); |
21950 | |
21951 | demotion_low = MAX_PTR; |
21952 | demotion_high = heap_segment_allocated (ephemeral_heap_segment); |
21953 | |
21954 | // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs |
21955 | // from gen1. They should get promoted to gen2. |
21956 | demote_gen1_p = !(settings.promotion && |
21957 | (settings.condemned_generation == (max_generation - 1)) && |
21958 | gen_to_condemn_reasons.is_only_condition (gen_low_card_p)); |
21959 | |
21960 | total_ephemeral_size = 0; |
21961 | |
21962 | print_free_and_plug ("BP" ); |
21963 | |
21964 | for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++) |
21965 | { |
21966 | generation* temp_gen = generation_of (gen_idx); |
21967 | |
21968 | dprintf (2, ("gen%d start %Ix, plan start %Ix" , |
21969 | gen_idx, |
21970 | generation_allocation_start (temp_gen), |
21971 | generation_plan_allocation_start (temp_gen))); |
21972 | } |
21973 | |
21974 | BOOL fire_pinned_plug_events_p = EVENT_ENABLED(PinPlugAtGCTime); |
21975 | size_t last_plug_len = 0; |
21976 | |
21977 | while (1) |
21978 | { |
21979 | if (x >= end) |
21980 | { |
21981 | assert (x == end); |
21982 | assert (heap_segment_allocated (seg1) == end); |
21983 | heap_segment_allocated (seg1) = plug_end; |
21984 | |
21985 | current_brick = update_brick_table (tree, current_brick, x, plug_end); |
21986 | dprintf (3, ("end of seg: new tree, sequence# 0" )); |
21987 | sequence_number = 0; |
21988 | tree = 0; |
21989 | |
21990 | if (heap_segment_next_rw (seg1)) |
21991 | { |
21992 | seg1 = heap_segment_next_rw (seg1); |
21993 | end = heap_segment_allocated (seg1); |
21994 | plug_end = x = heap_segment_mem (seg1); |
21995 | current_brick = brick_of (x); |
21996 | dprintf(3,( " From %Ix to %Ix" , (size_t)x, (size_t)end)); |
21997 | continue; |
21998 | } |
21999 | else |
22000 | { |
22001 | break; |
22002 | } |
22003 | } |
22004 | |
22005 | BOOL last_npinned_plug_p = FALSE; |
22006 | BOOL last_pinned_plug_p = FALSE; |
22007 | |
22008 | // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned |
22009 | // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs - |
22010 | // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it. |
22011 | uint8_t* last_pinned_plug = 0; |
22012 | size_t num_pinned_plugs_in_plug = 0; |
22013 | |
22014 | uint8_t* last_object_in_plug = 0; |
22015 | |
22016 | while ((x < end) && marked (x)) |
22017 | { |
22018 | uint8_t* plug_start = x; |
22019 | uint8_t* saved_plug_end = plug_end; |
22020 | BOOL pinned_plug_p = FALSE; |
22021 | BOOL npin_before_pin_p = FALSE; |
22022 | BOOL saved_last_npinned_plug_p = last_npinned_plug_p; |
22023 | uint8_t* saved_last_object_in_plug = last_object_in_plug; |
22024 | BOOL merge_with_last_pin_p = FALSE; |
22025 | |
22026 | size_t added_pinning_size = 0; |
22027 | size_t artificial_pinned_size = 0; |
22028 | |
22029 | store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, |
22030 | last_pinned_plug, pinned_plug_p, last_object_in_plug, |
22031 | merge_with_last_pin_p, last_plug_len); |
22032 | |
22033 | #ifdef FEATURE_STRUCTALIGN |
22034 | int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment(); |
22035 | size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET; |
22036 | #endif // FEATURE_STRUCTALIGN |
22037 | |
22038 | { |
22039 | uint8_t* xl = x; |
22040 | while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p)) |
22041 | { |
22042 | assert (xl < end); |
22043 | if (pinned(xl)) |
22044 | { |
22045 | clear_pinned (xl); |
22046 | } |
22047 | #ifdef FEATURE_STRUCTALIGN |
22048 | else |
22049 | { |
22050 | int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment(); |
22051 | if (obj_requiredAlignment > requiredAlignment) |
22052 | { |
22053 | requiredAlignment = obj_requiredAlignment; |
22054 | alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET; |
22055 | } |
22056 | } |
22057 | #endif // FEATURE_STRUCTALIGN |
22058 | |
22059 | clear_marked (xl); |
22060 | |
22061 | dprintf(4, ("+%Ix+" , (size_t)xl)); |
22062 | assert ((size (xl) > 0)); |
22063 | assert ((size (xl) <= loh_size_threshold)); |
22064 | |
22065 | last_object_in_plug = xl; |
22066 | |
22067 | xl = xl + Align (size (xl)); |
22068 | Prefetch (xl); |
22069 | } |
22070 | |
22071 | BOOL next_object_marked_p = ((xl < end) && marked (xl)); |
22072 | |
22073 | if (pinned_plug_p) |
22074 | { |
22075 | // If it is pinned we need to extend to the next marked object as we can't use part of |
22076 | // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all |
22077 | // references but for now I am just using the next non pinned object for that). |
22078 | if (next_object_marked_p) |
22079 | { |
22080 | clear_marked (xl); |
22081 | last_object_in_plug = xl; |
22082 | size_t = Align (size (xl)); |
22083 | xl = xl + extra_size; |
22084 | added_pinning_size = extra_size; |
22085 | } |
22086 | } |
22087 | else |
22088 | { |
22089 | if (next_object_marked_p) |
22090 | npin_before_pin_p = TRUE; |
22091 | } |
22092 | |
22093 | assert (xl <= end); |
22094 | x = xl; |
22095 | } |
22096 | dprintf (3, ( "%Ix[" , (size_t)x)); |
22097 | plug_end = x; |
22098 | size_t ps = plug_end - plug_start; |
22099 | last_plug_len = ps; |
22100 | dprintf (3, ( "%Ix[(%Ix)" , (size_t)x, ps)); |
22101 | uint8_t* new_address = 0; |
22102 | |
22103 | if (!pinned_plug_p) |
22104 | { |
22105 | if (allocate_in_condemned && |
22106 | (settings.condemned_generation == max_generation) && |
22107 | (ps > OS_PAGE_SIZE)) |
22108 | { |
22109 | ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen); |
22110 | //reloc should >=0 except when we relocate |
22111 | //across segments and the dest seg is higher then the src |
22112 | |
22113 | if ((ps > (8*OS_PAGE_SIZE)) && |
22114 | (reloc > 0) && |
22115 | ((size_t)reloc < (ps/16))) |
22116 | { |
22117 | dprintf (3, ("Pinning %Ix; reloc would have been: %Ix" , |
22118 | (size_t)plug_start, reloc)); |
22119 | // The last plug couldn't have been a npinned plug or it would have |
22120 | // included this plug. |
22121 | assert (!saved_last_npinned_plug_p); |
22122 | |
22123 | if (last_pinned_plug) |
22124 | { |
22125 | dprintf (3, ("artificially pinned plug merged with last pinned plug" )); |
22126 | merge_with_last_pin_p = TRUE; |
22127 | } |
22128 | else |
22129 | { |
22130 | enque_pinned_plug (plug_start, FALSE, 0); |
22131 | last_pinned_plug = plug_start; |
22132 | } |
22133 | |
22134 | convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p, |
22135 | ps, artificial_pinned_size); |
22136 | } |
22137 | } |
22138 | } |
22139 | |
22140 | if (allocate_first_generation_start) |
22141 | { |
22142 | allocate_first_generation_start = FALSE; |
22143 | plan_generation_start (condemned_gen1, consing_gen, plug_start); |
22144 | assert (generation_plan_allocation_start (condemned_gen1)); |
22145 | } |
22146 | |
22147 | if (seg1 == ephemeral_heap_segment) |
22148 | { |
22149 | process_ephemeral_boundaries (plug_start, active_new_gen_number, |
22150 | active_old_gen_number, |
22151 | consing_gen, |
22152 | allocate_in_condemned); |
22153 | } |
22154 | |
22155 | dprintf (3, ("adding %Id to gen%d surv" , ps, active_old_gen_number)); |
22156 | |
22157 | dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number); |
22158 | dd_survived_size (dd_active_old) += ps; |
22159 | |
22160 | BOOL convert_to_pinned_p = FALSE; |
22161 | |
22162 | if (!pinned_plug_p) |
22163 | { |
22164 | #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN) |
22165 | dd_num_npinned_plugs (dd_active_old)++; |
22166 | #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN |
22167 | |
22168 | add_gen_plug (active_old_gen_number, ps); |
22169 | |
22170 | if (allocate_in_condemned) |
22171 | { |
22172 | verify_pins_with_post_plug_info("before aic" ); |
22173 | |
22174 | new_address = |
22175 | allocate_in_condemned_generations (consing_gen, |
22176 | ps, |
22177 | active_old_gen_number, |
22178 | #ifdef SHORT_PLUGS |
22179 | &convert_to_pinned_p, |
22180 | (npin_before_pin_p ? plug_end : 0), |
22181 | seg1, |
22182 | #endif //SHORT_PLUGS |
22183 | plug_start REQD_ALIGN_AND_OFFSET_ARG); |
22184 | verify_pins_with_post_plug_info("after aic" ); |
22185 | } |
22186 | else |
22187 | { |
22188 | new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG); |
22189 | |
22190 | if (new_address != 0) |
22191 | { |
22192 | if (settings.condemned_generation == (max_generation - 1)) |
22193 | { |
22194 | dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)" , |
22195 | plug_start, plug_end, |
22196 | (size_t)new_address, (size_t)new_address + (plug_end - plug_start), |
22197 | (size_t)(plug_end - plug_start))); |
22198 | } |
22199 | } |
22200 | else |
22201 | { |
22202 | if (generation_allocator(older_gen)->discard_if_no_fit_p()) |
22203 | { |
22204 | allocate_in_condemned = TRUE; |
22205 | } |
22206 | |
22207 | new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, |
22208 | #ifdef SHORT_PLUGS |
22209 | &convert_to_pinned_p, |
22210 | (npin_before_pin_p ? plug_end : 0), |
22211 | seg1, |
22212 | #endif //SHORT_PLUGS |
22213 | plug_start REQD_ALIGN_AND_OFFSET_ARG); |
22214 | } |
22215 | } |
22216 | |
22217 | if (convert_to_pinned_p) |
22218 | { |
22219 | assert (last_npinned_plug_p != FALSE); |
22220 | assert (last_pinned_plug_p == FALSE); |
22221 | convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p, |
22222 | ps, artificial_pinned_size); |
22223 | enque_pinned_plug (plug_start, FALSE, 0); |
22224 | last_pinned_plug = plug_start; |
22225 | } |
22226 | else |
22227 | { |
22228 | if (!new_address) |
22229 | { |
22230 | //verify that we are at then end of the ephemeral segment |
22231 | assert (generation_allocation_segment (consing_gen) == |
22232 | ephemeral_heap_segment); |
22233 | //verify that we are near the end |
22234 | assert ((generation_allocation_pointer (consing_gen) + Align (ps)) < |
22235 | heap_segment_allocated (ephemeral_heap_segment)); |
22236 | assert ((generation_allocation_pointer (consing_gen) + Align (ps)) > |
22237 | (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size))); |
22238 | } |
22239 | else |
22240 | { |
22241 | #ifdef SIMPLE_DPRINTF |
22242 | dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)" , |
22243 | (size_t)(node_gap_size (plug_start)), |
22244 | plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address), |
22245 | (size_t)new_address + ps, ps, |
22246 | (is_plug_padded (plug_start) ? 1 : 0))); |
22247 | #endif //SIMPLE_DPRINTF |
22248 | |
22249 | #ifdef SHORT_PLUGS |
22250 | if (is_plug_padded (plug_start)) |
22251 | { |
22252 | dprintf (3, ("%Ix was padded" , plug_start)); |
22253 | dd_padding_size (dd_active_old) += Align (min_obj_size); |
22254 | } |
22255 | #endif //SHORT_PLUGS |
22256 | } |
22257 | } |
22258 | } |
22259 | |
22260 | if (pinned_plug_p) |
22261 | { |
22262 | if (fire_pinned_plug_events_p) |
22263 | { |
22264 | FIRE_EVENT(PinPlugAtGCTime, plug_start, plug_end, |
22265 | (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start))); |
22266 | } |
22267 | |
22268 | if (merge_with_last_pin_p) |
22269 | { |
22270 | merge_with_last_pinned_plug (last_pinned_plug, ps); |
22271 | } |
22272 | else |
22273 | { |
22274 | assert (last_pinned_plug == plug_start); |
22275 | set_pinned_info (plug_start, ps, consing_gen); |
22276 | } |
22277 | |
22278 | new_address = plug_start; |
22279 | |
22280 | dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)" , |
22281 | (size_t)(node_gap_size (plug_start)), (size_t)plug_start, |
22282 | (size_t)plug_end, ps, |
22283 | (merge_with_last_pin_p ? 1 : 0))); |
22284 | |
22285 | dprintf (3, ("adding %Id to gen%d pinned surv" , plug_end - plug_start, active_old_gen_number)); |
22286 | dd_pinned_survived_size (dd_active_old) += plug_end - plug_start; |
22287 | dd_added_pinned_size (dd_active_old) += added_pinning_size; |
22288 | dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size; |
22289 | |
22290 | if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1))) |
22291 | { |
22292 | last_gen1_pin_end = plug_end; |
22293 | } |
22294 | } |
22295 | |
22296 | #ifdef _DEBUG |
22297 | // detect forward allocation in the same segment |
22298 | assert (!((new_address > plug_start) && |
22299 | (new_address < heap_segment_reserved (seg1)))); |
22300 | #endif //_DEBUG |
22301 | |
22302 | if (!merge_with_last_pin_p) |
22303 | { |
22304 | if (current_brick != brick_of (plug_start)) |
22305 | { |
22306 | current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end); |
22307 | sequence_number = 0; |
22308 | tree = 0; |
22309 | } |
22310 | |
22311 | set_node_relocation_distance (plug_start, (new_address - plug_start)); |
22312 | if (last_node && (node_relocation_distance (last_node) == |
22313 | (node_relocation_distance (plug_start) + |
22314 | (ptrdiff_t)node_gap_size (plug_start)))) |
22315 | { |
22316 | //dprintf(3,( " Lb")); |
22317 | dprintf (3, ("%Ix Lb" , plug_start)); |
22318 | set_node_left (plug_start); |
22319 | } |
22320 | if (0 == sequence_number) |
22321 | { |
22322 | dprintf (2, ("sn: 0, tree is set to %Ix" , plug_start)); |
22323 | tree = plug_start; |
22324 | } |
22325 | |
22326 | verify_pins_with_post_plug_info("before insert node" ); |
22327 | |
22328 | tree = insert_node (plug_start, ++sequence_number, tree, last_node); |
22329 | dprintf (3, ("tree is %Ix (b: %Ix) after insert_node" , tree, brick_of (tree))); |
22330 | last_node = plug_start; |
22331 | |
22332 | #ifdef _DEBUG |
22333 | // If we detect if the last plug is pinned plug right before us, we should save this gap info |
22334 | if (!pinned_plug_p) |
22335 | { |
22336 | if (mark_stack_tos > 0) |
22337 | { |
22338 | mark& m = mark_stack_array[mark_stack_tos - 1]; |
22339 | if (m.has_post_plug_info()) |
22340 | { |
22341 | uint8_t* post_plug_info_start = m.saved_post_plug_info_start; |
22342 | size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap)); |
22343 | if ((uint8_t*)current_plug_gap_start == post_plug_info_start) |
22344 | { |
22345 | dprintf (3, ("Ginfo: %Ix, %Ix, %Ix" , |
22346 | *current_plug_gap_start, *(current_plug_gap_start + 1), |
22347 | *(current_plug_gap_start + 2))); |
22348 | memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair)); |
22349 | } |
22350 | } |
22351 | } |
22352 | } |
22353 | #endif //_DEBUG |
22354 | |
22355 | verify_pins_with_post_plug_info("after insert node" ); |
22356 | } |
22357 | } |
22358 | |
22359 | if (num_pinned_plugs_in_plug > 1) |
22360 | { |
22361 | dprintf (3, ("more than %Id pinned plugs in this plug" , num_pinned_plugs_in_plug)); |
22362 | } |
22363 | |
22364 | { |
22365 | #ifdef MARK_LIST |
22366 | if (use_mark_list) |
22367 | { |
22368 | while ((mark_list_next < mark_list_index) && |
22369 | (*mark_list_next <= x)) |
22370 | { |
22371 | mark_list_next++; |
22372 | } |
22373 | if ((mark_list_next < mark_list_index) |
22374 | #ifdef MULTIPLE_HEAPS |
22375 | && (*mark_list_next < end) //for multiple segments |
22376 | #endif //MULTIPLE_HEAPS |
22377 | ) |
22378 | x = *mark_list_next; |
22379 | else |
22380 | x = end; |
22381 | } |
22382 | else |
22383 | #endif //MARK_LIST |
22384 | { |
22385 | uint8_t* xl = x; |
22386 | #ifdef BACKGROUND_GC |
22387 | if (current_c_gc_state == c_gc_state_marking) |
22388 | { |
22389 | assert (recursive_gc_sync::background_running_p()); |
22390 | while ((xl < end) && !marked (xl)) |
22391 | { |
22392 | dprintf (4, ("-%Ix-" , (size_t)xl)); |
22393 | assert ((size (xl) > 0)); |
22394 | background_object_marked (xl, TRUE); |
22395 | xl = xl + Align (size (xl)); |
22396 | Prefetch (xl); |
22397 | } |
22398 | } |
22399 | else |
22400 | #endif //BACKGROUND_GC |
22401 | { |
22402 | while ((xl < end) && !marked (xl)) |
22403 | { |
22404 | dprintf (4, ("-%Ix-" , (size_t)xl)); |
22405 | assert ((size (xl) > 0)); |
22406 | xl = xl + Align (size (xl)); |
22407 | Prefetch (xl); |
22408 | } |
22409 | } |
22410 | assert (xl <= end); |
22411 | x = xl; |
22412 | } |
22413 | } |
22414 | } |
22415 | |
22416 | while (!pinned_plug_que_empty_p()) |
22417 | { |
22418 | if (settings.promotion) |
22419 | { |
22420 | uint8_t* pplug = pinned_plug (oldest_pin()); |
22421 | if (in_range_for_segment (pplug, ephemeral_heap_segment)) |
22422 | { |
22423 | consing_gen = ensure_ephemeral_heap_segment (consing_gen); |
22424 | //allocate all of the generation gaps |
22425 | while (active_new_gen_number > 0) |
22426 | { |
22427 | active_new_gen_number--; |
22428 | |
22429 | if (active_new_gen_number == (max_generation - 1)) |
22430 | { |
22431 | maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)); |
22432 | if (!demote_gen1_p) |
22433 | advance_pins_for_demotion (consing_gen); |
22434 | } |
22435 | |
22436 | generation* gen = generation_of (active_new_gen_number); |
22437 | plan_generation_start (gen, consing_gen, 0); |
22438 | |
22439 | if (demotion_low == MAX_PTR) |
22440 | { |
22441 | demotion_low = pplug; |
22442 | dprintf (3, ("end plan: dlow->%Ix" , demotion_low)); |
22443 | } |
22444 | |
22445 | dprintf (2, ("(%d)gen%d plan start: %Ix" , |
22446 | heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen))); |
22447 | assert (generation_plan_allocation_start (gen)); |
22448 | } |
22449 | } |
22450 | } |
22451 | |
22452 | if (pinned_plug_que_empty_p()) |
22453 | break; |
22454 | |
22455 | size_t entry = deque_pinned_plug(); |
22456 | mark* m = pinned_plug_of (entry); |
22457 | uint8_t* plug = pinned_plug (m); |
22458 | size_t len = pinned_len (m); |
22459 | |
22460 | // detect pinned block in different segment (later) than |
22461 | // allocation segment |
22462 | heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen)); |
22463 | |
22464 | while ((plug < generation_allocation_pointer (consing_gen)) || |
22465 | (plug >= heap_segment_allocated (nseg))) |
22466 | { |
22467 | assert ((plug < heap_segment_mem (nseg)) || |
22468 | (plug > heap_segment_reserved (nseg))); |
22469 | //adjust the end of the segment to be the end of the plug |
22470 | assert (generation_allocation_pointer (consing_gen)>= |
22471 | heap_segment_mem (nseg)); |
22472 | assert (generation_allocation_pointer (consing_gen)<= |
22473 | heap_segment_committed (nseg)); |
22474 | |
22475 | heap_segment_plan_allocated (nseg) = |
22476 | generation_allocation_pointer (consing_gen); |
22477 | //switch allocation segment |
22478 | nseg = heap_segment_next_rw (nseg); |
22479 | generation_allocation_segment (consing_gen) = nseg; |
22480 | //reset the allocation pointer and limits |
22481 | generation_allocation_pointer (consing_gen) = |
22482 | heap_segment_mem (nseg); |
22483 | } |
22484 | |
22485 | set_new_pin_info (m, generation_allocation_pointer (consing_gen)); |
22486 | dprintf (2, ("pin %Ix b: %Ix->%Ix" , plug, brick_of (plug), |
22487 | (size_t)(brick_table[brick_of (plug)]))); |
22488 | |
22489 | generation_allocation_pointer (consing_gen) = plug + len; |
22490 | generation_allocation_limit (consing_gen) = |
22491 | generation_allocation_pointer (consing_gen); |
22492 | //Add the size of the pinned plug to the right pinned allocations |
22493 | //find out which gen this pinned plug came from |
22494 | int frgn = object_gennum (plug); |
22495 | if ((frgn != (int)max_generation) && settings.promotion) |
22496 | { |
22497 | generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len; |
22498 | } |
22499 | |
22500 | } |
22501 | |
22502 | plan_generation_starts (consing_gen); |
22503 | print_free_and_plug ("AP" ); |
22504 | |
22505 | { |
22506 | #ifdef SIMPLE_DPRINTF |
22507 | for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++) |
22508 | { |
22509 | generation* temp_gen = generation_of (gen_idx); |
22510 | dynamic_data* temp_dd = dynamic_data_of (gen_idx); |
22511 | |
22512 | int added_pinning_ratio = 0; |
22513 | int artificial_pinned_ratio = 0; |
22514 | |
22515 | if (dd_pinned_survived_size (temp_dd) != 0) |
22516 | { |
22517 | added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd)); |
22518 | artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd)); |
22519 | } |
22520 | |
22521 | size_t padding_size = |
22522 | #ifdef SHORT_PLUGS |
22523 | dd_padding_size (temp_dd); |
22524 | #else |
22525 | 0; |
22526 | #endif //SHORT_PLUGS |
22527 | 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" , |
22528 | gen_idx, |
22529 | generation_allocation_start (temp_gen), |
22530 | generation_plan_allocation_start (temp_gen), |
22531 | (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)), |
22532 | generation_allocation_size (temp_gen), |
22533 | generation_pinned_allocation_compact_size (temp_gen), |
22534 | generation_pinned_allocation_sweep_size (temp_gen), |
22535 | dd_survived_size (temp_dd), |
22536 | dd_pinned_survived_size (temp_dd), |
22537 | added_pinning_ratio, |
22538 | artificial_pinned_ratio, |
22539 | (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)), |
22540 | padding_size)); |
22541 | } |
22542 | #endif //SIMPLE_DPRINTF |
22543 | } |
22544 | |
22545 | if (settings.condemned_generation == (max_generation - 1 )) |
22546 | { |
22547 | size_t plan_gen2_size = generation_plan_size (max_generation); |
22548 | size_t growth = plan_gen2_size - old_gen2_size; |
22549 | |
22550 | if (growth > 0) |
22551 | { |
22552 | dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, condemned alloc: %Id" , |
22553 | growth, end_seg_allocated, condemned_allocated)); |
22554 | |
22555 | maxgen_size_inc_p = true; |
22556 | } |
22557 | else |
22558 | { |
22559 | dprintf (2, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id" , |
22560 | (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)), |
22561 | generation_condemned_allocated (generation_of (max_generation - 1)))); |
22562 | } |
22563 | |
22564 | generation* older_gen = generation_of (settings.condemned_generation + 1); |
22565 | size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space; |
22566 | size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated; |
22567 | size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated; |
22568 | size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated; |
22569 | |
22570 | dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id" , |
22571 | r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen), |
22572 | r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), |
22573 | r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen))); |
22574 | |
22575 | 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" , |
22576 | free_list_allocated, rejected_free_space, end_seg_allocated, |
22577 | condemned_allocated, generation_condemned_allocated (generation_of (settings.condemned_generation)))); |
22578 | |
22579 | maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info); |
22580 | maxgen_size_info->free_list_allocated = free_list_allocated; |
22581 | maxgen_size_info->free_list_rejected = rejected_free_space; |
22582 | maxgen_size_info->end_seg_allocated = end_seg_allocated; |
22583 | maxgen_size_info->condemned_allocated = condemned_allocated; |
22584 | maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance; |
22585 | maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance; |
22586 | |
22587 | #ifdef FREE_USAGE_STATS |
22588 | int free_list_efficiency = 0; |
22589 | if ((free_list_allocated + rejected_free_space) != 0) |
22590 | free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100); |
22591 | |
22592 | int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100); |
22593 | |
22594 | dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%" , |
22595 | older_gen->gen_num, |
22596 | free_list_efficiency, running_free_list_efficiency)); |
22597 | |
22598 | dprintf (1, ("gen2 free list change" )); |
22599 | for (int j = 0; j < NUM_GEN_POWER2; j++) |
22600 | { |
22601 | dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id" , |
22602 | heap_number, |
22603 | settings.gc_index, |
22604 | (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], |
22605 | (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]), |
22606 | (generation_of(max_generation - 1))->gen_plugs[j])); |
22607 | } |
22608 | #endif //FREE_USAGE_STATS |
22609 | } |
22610 | |
22611 | size_t fragmentation = |
22612 | generation_fragmentation (generation_of (condemned_gen_number), |
22613 | consing_gen, |
22614 | heap_segment_allocated (ephemeral_heap_segment)); |
22615 | |
22616 | dprintf (2,("Fragmentation: %Id" , fragmentation)); |
22617 | dprintf (2,("---- End of Plan phase ----" )); |
22618 | |
22619 | #ifdef TIME_GC |
22620 | finish = GetCycleCount32(); |
22621 | plan_time = finish - start; |
22622 | #endif //TIME_GC |
22623 | |
22624 | // We may update write barrier code. We assume here EE has been suspended if we are on a GC thread. |
22625 | assert(IsGCInProgress()); |
22626 | |
22627 | BOOL should_expand = FALSE; |
22628 | BOOL should_compact= FALSE; |
22629 | ephemeral_promotion = FALSE; |
22630 | |
22631 | #ifdef BIT64 |
22632 | if ((!settings.concurrent) && |
22633 | !provisional_mode_triggered && |
22634 | ((condemned_gen_number < max_generation) && |
22635 | ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95)))) |
22636 | { |
22637 | dprintf (GTC_LOG, ("gen0 reduction count is %d, condemning %d, mem load %d" , |
22638 | settings.gen0_reduction_count, |
22639 | condemned_gen_number, |
22640 | settings.entry_memory_load)); |
22641 | should_compact = TRUE; |
22642 | |
22643 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, |
22644 | ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load)); |
22645 | |
22646 | if ((condemned_gen_number >= (max_generation - 1)) && |
22647 | dt_low_ephemeral_space_p (tuning_deciding_expansion)) |
22648 | { |
22649 | dprintf (GTC_LOG, ("Not enough space for all ephemeral generations with compaction" )); |
22650 | should_expand = TRUE; |
22651 | } |
22652 | } |
22653 | else |
22654 | { |
22655 | #endif // BIT64 |
22656 | should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand); |
22657 | #ifdef BIT64 |
22658 | } |
22659 | #endif // BIT64 |
22660 | |
22661 | #ifdef FEATURE_LOH_COMPACTION |
22662 | loh_compacted_p = FALSE; |
22663 | #endif //FEATURE_LOH_COMPACTION |
22664 | |
22665 | if (condemned_gen_number == max_generation) |
22666 | { |
22667 | #ifdef FEATURE_LOH_COMPACTION |
22668 | if (settings.loh_compaction) |
22669 | { |
22670 | if (plan_loh()) |
22671 | { |
22672 | should_compact = TRUE; |
22673 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced); |
22674 | loh_compacted_p = TRUE; |
22675 | } |
22676 | } |
22677 | else |
22678 | { |
22679 | if ((heap_number == 0) && (loh_pinned_queue)) |
22680 | { |
22681 | loh_pinned_queue_decay--; |
22682 | |
22683 | if (!loh_pinned_queue_decay) |
22684 | { |
22685 | delete loh_pinned_queue; |
22686 | loh_pinned_queue = 0; |
22687 | } |
22688 | } |
22689 | } |
22690 | |
22691 | if (!loh_compacted_p) |
22692 | #endif //FEATURE_LOH_COMPACTION |
22693 | { |
22694 | GCToEEInterface::DiagWalkLOHSurvivors(__this); |
22695 | sweep_large_objects(); |
22696 | } |
22697 | } |
22698 | else |
22699 | { |
22700 | settings.loh_compaction = FALSE; |
22701 | } |
22702 | |
22703 | #ifdef MULTIPLE_HEAPS |
22704 | |
22705 | new_heap_segment = NULL; |
22706 | |
22707 | if (should_compact && should_expand) |
22708 | gc_policy = policy_expand; |
22709 | else if (should_compact) |
22710 | gc_policy = policy_compact; |
22711 | else |
22712 | gc_policy = policy_sweep; |
22713 | |
22714 | //vote for result of should_compact |
22715 | dprintf (3, ("Joining for compaction decision" )); |
22716 | gc_t_join.join(this, gc_join_decide_on_compaction); |
22717 | if (gc_t_join.joined()) |
22718 | { |
22719 | //safe place to delete large heap segments |
22720 | if (condemned_gen_number == max_generation) |
22721 | { |
22722 | for (int i = 0; i < n_heaps; i++) |
22723 | { |
22724 | g_heaps [i]->rearrange_large_heap_segments (); |
22725 | } |
22726 | } |
22727 | |
22728 | if (maxgen_size_inc_p && provisional_mode_triggered) |
22729 | { |
22730 | pm_trigger_full_gc = true; |
22731 | dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2" )); |
22732 | } |
22733 | else |
22734 | { |
22735 | settings.demotion = FALSE; |
22736 | int pol_max = policy_sweep; |
22737 | #ifdef GC_CONFIG_DRIVEN |
22738 | BOOL is_compaction_mandatory = FALSE; |
22739 | #endif //GC_CONFIG_DRIVEN |
22740 | |
22741 | int i; |
22742 | for (i = 0; i < n_heaps; i++) |
22743 | { |
22744 | if (pol_max < g_heaps[i]->gc_policy) |
22745 | pol_max = policy_compact; |
22746 | // set the demotion flag is any of the heap has demotion |
22747 | if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low) |
22748 | { |
22749 | (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit); |
22750 | settings.demotion = TRUE; |
22751 | } |
22752 | |
22753 | #ifdef GC_CONFIG_DRIVEN |
22754 | if (!is_compaction_mandatory) |
22755 | { |
22756 | int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact); |
22757 | if (compact_reason >= 0) |
22758 | { |
22759 | if (gc_heap_compact_reason_mandatory_p[compact_reason]) |
22760 | is_compaction_mandatory = TRUE; |
22761 | } |
22762 | } |
22763 | #endif //GC_CONFIG_DRIVEN |
22764 | } |
22765 | |
22766 | #ifdef GC_CONFIG_DRIVEN |
22767 | if (!is_compaction_mandatory) |
22768 | { |
22769 | // If compaction is not mandatory we can feel free to change it to a sweeping GC. |
22770 | // Note that we may want to change this to only checking every so often instead of every single GC. |
22771 | if (should_do_sweeping_gc (pol_max >= policy_compact)) |
22772 | { |
22773 | pol_max = policy_sweep; |
22774 | } |
22775 | else |
22776 | { |
22777 | if (pol_max == policy_sweep) |
22778 | pol_max = policy_compact; |
22779 | } |
22780 | } |
22781 | #endif //GC_CONFIG_DRIVEN |
22782 | |
22783 | for (i = 0; i < n_heaps; i++) |
22784 | { |
22785 | if (pol_max > g_heaps[i]->gc_policy) |
22786 | g_heaps[i]->gc_policy = pol_max; |
22787 | //get the segment while we are serialized |
22788 | if (g_heaps[i]->gc_policy == policy_expand) |
22789 | { |
22790 | g_heaps[i]->new_heap_segment = |
22791 | g_heaps[i]->soh_get_segment_to_expand(); |
22792 | if (!g_heaps[i]->new_heap_segment) |
22793 | { |
22794 | set_expand_in_full_gc (condemned_gen_number); |
22795 | //we are out of memory, cancel the expansion |
22796 | g_heaps[i]->gc_policy = policy_compact; |
22797 | } |
22798 | } |
22799 | } |
22800 | |
22801 | BOOL is_full_compacting_gc = FALSE; |
22802 | |
22803 | if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation)) |
22804 | { |
22805 | full_gc_counts[gc_type_compacting]++; |
22806 | is_full_compacting_gc = TRUE; |
22807 | } |
22808 | |
22809 | for (i = 0; i < n_heaps; i++) |
22810 | { |
22811 | //copy the card and brick tables |
22812 | if (g_gc_card_table!= g_heaps[i]->card_table) |
22813 | { |
22814 | g_heaps[i]->copy_brick_card_table(); |
22815 | } |
22816 | |
22817 | if (is_full_compacting_gc) |
22818 | { |
22819 | g_heaps[i]->loh_alloc_since_cg = 0; |
22820 | } |
22821 | } |
22822 | } |
22823 | |
22824 | //start all threads on the roots. |
22825 | dprintf(3, ("Starting all gc threads after compaction decision" )); |
22826 | gc_t_join.restart(); |
22827 | } |
22828 | |
22829 | //reset the local variable accordingly |
22830 | should_compact = (gc_policy >= policy_compact); |
22831 | should_expand = (gc_policy >= policy_expand); |
22832 | |
22833 | #else //MULTIPLE_HEAPS |
22834 | |
22835 | //safe place to delete large heap segments |
22836 | if (condemned_gen_number == max_generation) |
22837 | { |
22838 | rearrange_large_heap_segments (); |
22839 | } |
22840 | |
22841 | if (maxgen_size_inc_p && provisional_mode_triggered) |
22842 | { |
22843 | pm_trigger_full_gc = true; |
22844 | dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2" )); |
22845 | } |
22846 | else |
22847 | { |
22848 | settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE); |
22849 | if (settings.demotion) |
22850 | get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit); |
22851 | |
22852 | #ifdef GC_CONFIG_DRIVEN |
22853 | BOOL is_compaction_mandatory = FALSE; |
22854 | int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact); |
22855 | if (compact_reason >= 0) |
22856 | is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason]; |
22857 | |
22858 | if (!is_compaction_mandatory) |
22859 | { |
22860 | if (should_do_sweeping_gc (should_compact)) |
22861 | should_compact = FALSE; |
22862 | else |
22863 | should_compact = TRUE; |
22864 | } |
22865 | #endif //GC_CONFIG_DRIVEN |
22866 | |
22867 | if (should_compact && (condemned_gen_number == max_generation)) |
22868 | { |
22869 | full_gc_counts[gc_type_compacting]++; |
22870 | loh_alloc_since_cg = 0; |
22871 | } |
22872 | } |
22873 | #endif //MULTIPLE_HEAPS |
22874 | |
22875 | if (!pm_trigger_full_gc && pm_stress_on && provisional_mode_triggered) |
22876 | { |
22877 | if ((settings.condemned_generation == (max_generation - 1)) && |
22878 | ((settings.gc_index % 5) == 0)) |
22879 | { |
22880 | pm_trigger_full_gc = true; |
22881 | } |
22882 | } |
22883 | |
22884 | if (settings.condemned_generation == (max_generation - 1)) |
22885 | { |
22886 | if (provisional_mode_triggered) |
22887 | { |
22888 | if (should_expand) |
22889 | { |
22890 | should_expand = FALSE; |
22891 | dprintf (GTC_LOG, ("h%d in PM cannot expand" , heap_number)); |
22892 | } |
22893 | } |
22894 | |
22895 | if (pm_trigger_full_gc) |
22896 | { |
22897 | should_compact = FALSE; |
22898 | dprintf (GTC_LOG, ("h%d PM doing sweeping" , heap_number)); |
22899 | } |
22900 | } |
22901 | |
22902 | if (should_compact) |
22903 | { |
22904 | dprintf (2,( "**** Doing Compacting GC ****" )); |
22905 | |
22906 | if (should_expand) |
22907 | { |
22908 | #ifndef MULTIPLE_HEAPS |
22909 | heap_segment* new_heap_segment = soh_get_segment_to_expand(); |
22910 | #endif //!MULTIPLE_HEAPS |
22911 | if (new_heap_segment) |
22912 | { |
22913 | consing_gen = expand_heap(condemned_gen_number, |
22914 | consing_gen, |
22915 | new_heap_segment); |
22916 | } |
22917 | |
22918 | // If we couldn't get a new segment, or we were able to |
22919 | // reserve one but no space to commit, we couldn't |
22920 | // expand heap. |
22921 | if (ephemeral_heap_segment != new_heap_segment) |
22922 | { |
22923 | set_expand_in_full_gc (condemned_gen_number); |
22924 | should_expand = FALSE; |
22925 | } |
22926 | } |
22927 | generation_allocation_limit (condemned_gen1) = |
22928 | generation_allocation_pointer (condemned_gen1); |
22929 | if ((condemned_gen_number < max_generation)) |
22930 | { |
22931 | generation_allocator (older_gen)->commit_alloc_list_changes(); |
22932 | |
22933 | // Fix the allocation area of the older generation |
22934 | fix_older_allocation_area (older_gen); |
22935 | } |
22936 | assert (generation_allocation_segment (consing_gen) == |
22937 | ephemeral_heap_segment); |
22938 | |
22939 | GCToEEInterface::DiagWalkSurvivors(__this); |
22940 | |
22941 | relocate_phase (condemned_gen_number, first_condemned_address); |
22942 | compact_phase (condemned_gen_number, first_condemned_address, |
22943 | (!settings.demotion && settings.promotion)); |
22944 | fix_generation_bounds (condemned_gen_number, consing_gen); |
22945 | assert (generation_allocation_limit (youngest_generation) == |
22946 | generation_allocation_pointer (youngest_generation)); |
22947 | if (condemned_gen_number >= (max_generation -1)) |
22948 | { |
22949 | #ifdef MULTIPLE_HEAPS |
22950 | // this needs be serialized just because we have one |
22951 | // segment_standby_list/seg_table for all heaps. We should make it at least |
22952 | // so that when hoarding is not on we don't need this join because |
22953 | // decommitting memory can take a long time. |
22954 | //must serialize on deleting segments |
22955 | gc_t_join.join(this, gc_join_rearrange_segs_compaction); |
22956 | if (gc_t_join.joined()) |
22957 | { |
22958 | for (int i = 0; i < n_heaps; i++) |
22959 | { |
22960 | g_heaps[i]->rearrange_heap_segments(TRUE); |
22961 | } |
22962 | gc_t_join.restart(); |
22963 | } |
22964 | #else |
22965 | rearrange_heap_segments(TRUE); |
22966 | #endif //MULTIPLE_HEAPS |
22967 | |
22968 | if (should_expand) |
22969 | { |
22970 | //fix the start_segment for the ephemeral generations |
22971 | for (int i = 0; i < max_generation; i++) |
22972 | { |
22973 | generation* gen = generation_of (i); |
22974 | generation_start_segment (gen) = ephemeral_heap_segment; |
22975 | generation_allocation_segment (gen) = ephemeral_heap_segment; |
22976 | } |
22977 | } |
22978 | } |
22979 | |
22980 | { |
22981 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
22982 | finalize_queue->UpdatePromotedGenerations (condemned_gen_number, |
22983 | (!settings.demotion && settings.promotion)); |
22984 | #endif // FEATURE_PREMORTEM_FINALIZATION |
22985 | |
22986 | #ifdef MULTIPLE_HEAPS |
22987 | dprintf(3, ("Joining after end of compaction" )); |
22988 | gc_t_join.join(this, gc_join_adjust_handle_age_compact); |
22989 | if (gc_t_join.joined()) |
22990 | #endif //MULTIPLE_HEAPS |
22991 | { |
22992 | #ifdef MULTIPLE_HEAPS |
22993 | //join all threads to make sure they are synchronized |
22994 | dprintf(3, ("Restarting after Promotion granted" )); |
22995 | gc_t_join.restart(); |
22996 | #endif //MULTIPLE_HEAPS |
22997 | } |
22998 | |
22999 | ScanContext sc; |
23000 | sc.thread_number = heap_number; |
23001 | sc.promotion = FALSE; |
23002 | sc.concurrent = FALSE; |
23003 | // new generations bounds are set can call this guy |
23004 | if (settings.promotion && !settings.demotion) |
23005 | { |
23006 | dprintf (2, ("Promoting EE roots for gen %d" , |
23007 | condemned_gen_number)); |
23008 | GCScan::GcPromotionsGranted(condemned_gen_number, |
23009 | max_generation, &sc); |
23010 | } |
23011 | else if (settings.demotion) |
23012 | { |
23013 | dprintf (2, ("Demoting EE roots for gen %d" , |
23014 | condemned_gen_number)); |
23015 | GCScan::GcDemote (condemned_gen_number, max_generation, &sc); |
23016 | } |
23017 | } |
23018 | |
23019 | { |
23020 | gen0_big_free_spaces = 0; |
23021 | |
23022 | reset_pinned_queue_bos(); |
23023 | unsigned int gen_number = min (max_generation, 1 + condemned_gen_number); |
23024 | generation* gen = generation_of (gen_number); |
23025 | uint8_t* low = generation_allocation_start (generation_of (gen_number-1)); |
23026 | uint8_t* high = heap_segment_allocated (ephemeral_heap_segment); |
23027 | |
23028 | while (!pinned_plug_que_empty_p()) |
23029 | { |
23030 | mark* m = pinned_plug_of (deque_pinned_plug()); |
23031 | size_t len = pinned_len (m); |
23032 | uint8_t* arr = (pinned_plug (m) - len); |
23033 | dprintf(3,("free [%Ix %Ix[ pin" , |
23034 | (size_t)arr, (size_t)arr + len)); |
23035 | if (len != 0) |
23036 | { |
23037 | assert (len >= Align (min_obj_size)); |
23038 | make_unused_array (arr, len); |
23039 | // fix fully contained bricks + first one |
23040 | // if the array goes beyond the first brick |
23041 | size_t start_brick = brick_of (arr); |
23042 | size_t end_brick = brick_of (arr + len); |
23043 | if (end_brick != start_brick) |
23044 | { |
23045 | dprintf (3, |
23046 | ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix" , |
23047 | start_brick, end_brick, (size_t)arr)); |
23048 | set_brick (start_brick, |
23049 | arr - brick_address (start_brick)); |
23050 | size_t brick = start_brick+1; |
23051 | while (brick < end_brick) |
23052 | { |
23053 | set_brick (brick, start_brick - brick); |
23054 | brick++; |
23055 | } |
23056 | } |
23057 | |
23058 | //when we take an old segment to make the new |
23059 | //ephemeral segment. we can have a bunch of |
23060 | //pinned plugs out of order going to the new ephemeral seg |
23061 | //and then the next plugs go back to max_generation |
23062 | if ((heap_segment_mem (ephemeral_heap_segment) <= arr) && |
23063 | (heap_segment_reserved (ephemeral_heap_segment) > arr)) |
23064 | { |
23065 | |
23066 | while ((low <= arr) && (high > arr)) |
23067 | { |
23068 | gen_number--; |
23069 | assert ((gen_number >= 1) || (demotion_low != MAX_PTR) || |
23070 | settings.demotion || !settings.promotion); |
23071 | dprintf (3, ("new free list generation %d" , gen_number)); |
23072 | |
23073 | gen = generation_of (gen_number); |
23074 | if (gen_number >= 1) |
23075 | low = generation_allocation_start (generation_of (gen_number-1)); |
23076 | else |
23077 | low = high; |
23078 | } |
23079 | } |
23080 | else |
23081 | { |
23082 | dprintf (3, ("new free list generation %d" , max_generation)); |
23083 | gen_number = max_generation; |
23084 | gen = generation_of (gen_number); |
23085 | } |
23086 | |
23087 | dprintf(3,("threading it into generation %d" , gen_number)); |
23088 | thread_gap (arr, len, gen); |
23089 | add_gen_free (gen_number, len); |
23090 | } |
23091 | } |
23092 | } |
23093 | |
23094 | #ifdef _DEBUG |
23095 | for (int x = 0; x <= max_generation; x++) |
23096 | { |
23097 | assert (generation_allocation_start (generation_of (x))); |
23098 | } |
23099 | #endif //_DEBUG |
23100 | |
23101 | if (!settings.demotion && settings.promotion) |
23102 | { |
23103 | //clear card for generation 1. generation 0 is empty |
23104 | clear_card_for_addresses ( |
23105 | generation_allocation_start (generation_of (1)), |
23106 | generation_allocation_start (generation_of (0))); |
23107 | } |
23108 | if (settings.promotion && !settings.demotion) |
23109 | { |
23110 | uint8_t* start = generation_allocation_start (youngest_generation); |
23111 | MAYBE_UNUSED_VAR(start); |
23112 | assert (heap_segment_allocated (ephemeral_heap_segment) == |
23113 | (start + Align (size (start)))); |
23114 | } |
23115 | } |
23116 | else |
23117 | { |
23118 | //force promotion for sweep |
23119 | settings.promotion = TRUE; |
23120 | settings.compaction = FALSE; |
23121 | |
23122 | ScanContext sc; |
23123 | sc.thread_number = heap_number; |
23124 | sc.promotion = FALSE; |
23125 | sc.concurrent = FALSE; |
23126 | |
23127 | dprintf (2, ("**** Doing Mark and Sweep GC****" )); |
23128 | |
23129 | if ((condemned_gen_number < max_generation)) |
23130 | { |
23131 | generation_allocator (older_gen)->copy_from_alloc_list (r_free_list); |
23132 | generation_free_list_space (older_gen) = r_free_list_space; |
23133 | generation_free_obj_space (older_gen) = r_free_obj_space; |
23134 | generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated; |
23135 | generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated; |
23136 | generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated; |
23137 | generation_allocation_limit (older_gen) = r_allocation_limit; |
23138 | generation_allocation_pointer (older_gen) = r_allocation_pointer; |
23139 | generation_allocation_context_start_region (older_gen) = r_allocation_start_region; |
23140 | generation_allocation_segment (older_gen) = r_allocation_segment; |
23141 | } |
23142 | |
23143 | if ((condemned_gen_number < max_generation)) |
23144 | { |
23145 | // Fix the allocation area of the older generation |
23146 | fix_older_allocation_area (older_gen); |
23147 | } |
23148 | |
23149 | GCToEEInterface::DiagWalkSurvivors(__this); |
23150 | |
23151 | gen0_big_free_spaces = 0; |
23152 | make_free_lists (condemned_gen_number); |
23153 | recover_saved_pinned_info(); |
23154 | |
23155 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
23156 | finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE); |
23157 | #endif // FEATURE_PREMORTEM_FINALIZATION |
23158 | // MTHTS: leave single thread for HT processing on plan_phase |
23159 | #ifdef MULTIPLE_HEAPS |
23160 | dprintf(3, ("Joining after end of sweep" )); |
23161 | gc_t_join.join(this, gc_join_adjust_handle_age_sweep); |
23162 | if (gc_t_join.joined()) |
23163 | #endif //MULTIPLE_HEAPS |
23164 | { |
23165 | GCScan::GcPromotionsGranted(condemned_gen_number, |
23166 | max_generation, &sc); |
23167 | if (condemned_gen_number >= (max_generation -1)) |
23168 | { |
23169 | #ifdef MULTIPLE_HEAPS |
23170 | for (int i = 0; i < n_heaps; i++) |
23171 | { |
23172 | g_heaps[i]->rearrange_heap_segments(FALSE); |
23173 | } |
23174 | #else |
23175 | rearrange_heap_segments(FALSE); |
23176 | #endif //MULTIPLE_HEAPS |
23177 | } |
23178 | |
23179 | #ifdef MULTIPLE_HEAPS |
23180 | //join all threads to make sure they are synchronized |
23181 | dprintf(3, ("Restarting after Promotion granted" )); |
23182 | gc_t_join.restart(); |
23183 | #endif //MULTIPLE_HEAPS |
23184 | } |
23185 | |
23186 | #ifdef _DEBUG |
23187 | for (int x = 0; x <= max_generation; x++) |
23188 | { |
23189 | assert (generation_allocation_start (generation_of (x))); |
23190 | } |
23191 | #endif //_DEBUG |
23192 | |
23193 | //clear card for generation 1. generation 0 is empty |
23194 | clear_card_for_addresses ( |
23195 | generation_allocation_start (generation_of (1)), |
23196 | generation_allocation_start (generation_of (0))); |
23197 | assert ((heap_segment_allocated (ephemeral_heap_segment) == |
23198 | (generation_allocation_start (youngest_generation) + |
23199 | Align (min_obj_size)))); |
23200 | } |
23201 | |
23202 | //verify_partial(); |
23203 | } |
23204 | #ifdef _PREFAST_ |
23205 | #pragma warning(pop) |
23206 | #endif //_PREFAST_ |
23207 | |
23208 | |
23209 | /***************************** |
23210 | Called after compact phase to fix all generation gaps |
23211 | ********************************/ |
23212 | void gc_heap::fix_generation_bounds (int condemned_gen_number, |
23213 | generation* consing_gen) |
23214 | { |
23215 | UNREFERENCED_PARAMETER(consing_gen); |
23216 | |
23217 | assert (generation_allocation_segment (consing_gen) == |
23218 | ephemeral_heap_segment); |
23219 | |
23220 | //assign the planned allocation start to the generation |
23221 | int gen_number = condemned_gen_number; |
23222 | int bottom_gen = 0; |
23223 | |
23224 | while (gen_number >= bottom_gen) |
23225 | { |
23226 | generation* gen = generation_of (gen_number); |
23227 | dprintf(3,("Fixing generation pointers for %Ix" , gen_number)); |
23228 | if ((gen_number < max_generation) && ephemeral_promotion) |
23229 | { |
23230 | make_unused_array (saved_ephemeral_plan_start[gen_number], |
23231 | saved_ephemeral_plan_start_size[gen_number]); |
23232 | } |
23233 | reset_allocation_pointers (gen, generation_plan_allocation_start (gen)); |
23234 | make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen)); |
23235 | dprintf(3,(" start %Ix" , (size_t)generation_allocation_start (gen))); |
23236 | gen_number--; |
23237 | } |
23238 | #ifdef MULTIPLE_HEAPS |
23239 | if (ephemeral_promotion) |
23240 | { |
23241 | //we are creating a generation fault. set the cards. |
23242 | // and we are only doing this for multiple heaps because in the single heap scenario the |
23243 | // new ephemeral generations will be empty and there'll be no need to set cards for the |
23244 | // old ephemeral generations that got promoted into max_generation. |
23245 | ptrdiff_t delta = 0; |
23246 | #ifdef SEG_MAPPING_TABLE |
23247 | heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]); |
23248 | #else //SEG_MAPPING_TABLE |
23249 | heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta); |
23250 | #endif //SEG_MAPPING_TABLE |
23251 | |
23252 | assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg)); |
23253 | size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg))); |
23254 | size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]); |
23255 | while (card != end_card) |
23256 | { |
23257 | set_card (card); |
23258 | card++; |
23259 | } |
23260 | } |
23261 | #endif //MULTIPLE_HEAPS |
23262 | { |
23263 | alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment); |
23264 | //reset the allocated size |
23265 | uint8_t* start = generation_allocation_start (youngest_generation); |
23266 | MAYBE_UNUSED_VAR(start); |
23267 | if (settings.promotion && !settings.demotion) |
23268 | { |
23269 | assert ((start + Align (size (start))) == |
23270 | heap_segment_plan_allocated(ephemeral_heap_segment)); |
23271 | } |
23272 | |
23273 | heap_segment_allocated(ephemeral_heap_segment)= |
23274 | heap_segment_plan_allocated(ephemeral_heap_segment); |
23275 | } |
23276 | } |
23277 | |
23278 | uint8_t* gc_heap::generation_limit (int gen_number) |
23279 | { |
23280 | if (settings.promotion) |
23281 | { |
23282 | if (gen_number <= 1) |
23283 | return heap_segment_reserved (ephemeral_heap_segment); |
23284 | else |
23285 | return generation_allocation_start (generation_of ((gen_number - 2))); |
23286 | } |
23287 | else |
23288 | { |
23289 | if (gen_number <= 0) |
23290 | return heap_segment_reserved (ephemeral_heap_segment); |
23291 | else |
23292 | return generation_allocation_start (generation_of ((gen_number - 1))); |
23293 | } |
23294 | } |
23295 | |
23296 | BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number) |
23297 | { |
23298 | uint8_t* start = heap_segment_allocated (ephemeral_heap_segment); |
23299 | size_t size = Align (min_obj_size)*(condemned_gen_number+1); |
23300 | assert ((start + size) <= |
23301 | heap_segment_reserved (ephemeral_heap_segment)); |
23302 | if ((start + size) > |
23303 | heap_segment_committed (ephemeral_heap_segment)) |
23304 | { |
23305 | if (!grow_heap_segment (ephemeral_heap_segment, start + size)) |
23306 | { |
23307 | return FALSE; |
23308 | } |
23309 | } |
23310 | return TRUE; |
23311 | } |
23312 | |
23313 | uint8_t* gc_heap::allocate_at_end (size_t size) |
23314 | { |
23315 | uint8_t* start = heap_segment_allocated (ephemeral_heap_segment); |
23316 | size = Align (size); |
23317 | uint8_t* result = start; |
23318 | // only called to allocate a min obj so can't overflow here. |
23319 | assert ((start + size) <= |
23320 | heap_segment_reserved (ephemeral_heap_segment)); |
23321 | //ensure_gap_allocation took care of it |
23322 | assert ((start + size) <= |
23323 | heap_segment_committed (ephemeral_heap_segment)); |
23324 | heap_segment_allocated (ephemeral_heap_segment) += size; |
23325 | return result; |
23326 | } |
23327 | |
23328 | |
23329 | void gc_heap::make_free_lists (int condemned_gen_number) |
23330 | { |
23331 | #ifdef TIME_GC |
23332 | unsigned start; |
23333 | unsigned finish; |
23334 | start = GetCycleCount32(); |
23335 | #endif //TIME_GC |
23336 | |
23337 | //Promotion has to happen in sweep case. |
23338 | assert (settings.promotion); |
23339 | |
23340 | generation* condemned_gen = generation_of (condemned_gen_number); |
23341 | uint8_t* start_address = generation_allocation_start (condemned_gen); |
23342 | |
23343 | size_t current_brick = brick_of (start_address); |
23344 | heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen)); |
23345 | |
23346 | PREFIX_ASSUME(current_heap_segment != NULL); |
23347 | |
23348 | uint8_t* end_address = heap_segment_allocated (current_heap_segment); |
23349 | size_t end_brick = brick_of (end_address-1); |
23350 | make_free_args args; |
23351 | args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number); |
23352 | args.current_gen_limit = (((condemned_gen_number == max_generation)) ? |
23353 | MAX_PTR : |
23354 | (generation_limit (args.free_list_gen_number))); |
23355 | args.free_list_gen = generation_of (args.free_list_gen_number); |
23356 | args.highest_plug = 0; |
23357 | |
23358 | if ((start_address < end_address) || |
23359 | (condemned_gen_number == max_generation)) |
23360 | { |
23361 | while (1) |
23362 | { |
23363 | if ((current_brick > end_brick)) |
23364 | { |
23365 | if (args.current_gen_limit == MAX_PTR) |
23366 | { |
23367 | //We had an empty segment |
23368 | //need to allocate the generation start |
23369 | |
23370 | generation* gen = generation_of (max_generation); |
23371 | |
23372 | heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen)); |
23373 | |
23374 | PREFIX_ASSUME(start_seg != NULL); |
23375 | |
23376 | uint8_t* gap = heap_segment_mem (start_seg); |
23377 | |
23378 | generation_allocation_start (gen) = gap; |
23379 | heap_segment_allocated (start_seg) = gap + Align (min_obj_size); |
23380 | make_unused_array (gap, Align (min_obj_size)); |
23381 | reset_allocation_pointers (gen, gap); |
23382 | dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix" , |
23383 | max_generation, (size_t)gap)); |
23384 | args.current_gen_limit = generation_limit (args.free_list_gen_number); |
23385 | } |
23386 | if (heap_segment_next_rw (current_heap_segment)) |
23387 | { |
23388 | current_heap_segment = heap_segment_next_rw (current_heap_segment); |
23389 | current_brick = brick_of (heap_segment_mem (current_heap_segment)); |
23390 | end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1); |
23391 | |
23392 | continue; |
23393 | } |
23394 | else |
23395 | { |
23396 | break; |
23397 | } |
23398 | } |
23399 | { |
23400 | int brick_entry = brick_table [ current_brick ]; |
23401 | if ((brick_entry >= 0)) |
23402 | { |
23403 | make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args); |
23404 | dprintf(3,("Fixing brick entry %Ix to %Ix" , |
23405 | current_brick, (size_t)args.highest_plug)); |
23406 | set_brick (current_brick, |
23407 | (args.highest_plug - brick_address (current_brick))); |
23408 | } |
23409 | else |
23410 | { |
23411 | if ((brick_entry > -32768)) |
23412 | { |
23413 | |
23414 | #ifdef _DEBUG |
23415 | ptrdiff_t offset = brick_of (args.highest_plug) - current_brick; |
23416 | if ((brick_entry != -32767) && (! ((offset == brick_entry)))) |
23417 | { |
23418 | assert ((brick_entry == -1)); |
23419 | } |
23420 | #endif //_DEBUG |
23421 | //init to -1 for faster find_first_object |
23422 | set_brick (current_brick, -1); |
23423 | } |
23424 | } |
23425 | } |
23426 | current_brick++; |
23427 | } |
23428 | } |
23429 | { |
23430 | int bottom_gen = 0; |
23431 | args.free_list_gen_number--; |
23432 | while (args.free_list_gen_number >= bottom_gen) |
23433 | { |
23434 | uint8_t* gap = 0; |
23435 | generation* gen2 = generation_of (args.free_list_gen_number); |
23436 | gap = allocate_at_end (Align(min_obj_size)); |
23437 | generation_allocation_start (gen2) = gap; |
23438 | reset_allocation_pointers (gen2, gap); |
23439 | dprintf(3,("Fixing generation start of %d to: %Ix" , |
23440 | args.free_list_gen_number, (size_t)gap)); |
23441 | PREFIX_ASSUME(gap != NULL); |
23442 | make_unused_array (gap, Align (min_obj_size)); |
23443 | |
23444 | args.free_list_gen_number--; |
23445 | } |
23446 | |
23447 | //reset the allocated size |
23448 | uint8_t* start2 = generation_allocation_start (youngest_generation); |
23449 | alloc_allocated = start2 + Align (size (start2)); |
23450 | } |
23451 | |
23452 | #ifdef TIME_GC |
23453 | finish = GetCycleCount32(); |
23454 | sweep_time = finish - start; |
23455 | #endif //TIME_GC |
23456 | } |
23457 | |
23458 | void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args) |
23459 | { |
23460 | assert ((tree != NULL)); |
23461 | { |
23462 | int right_node = node_right_child (tree); |
23463 | int left_node = node_left_child (tree); |
23464 | args->highest_plug = 0; |
23465 | if (! (0 == tree)) |
23466 | { |
23467 | if (! (0 == left_node)) |
23468 | { |
23469 | make_free_list_in_brick (tree + left_node, args); |
23470 | |
23471 | } |
23472 | { |
23473 | uint8_t* plug = tree; |
23474 | size_t gap_size = node_gap_size (tree); |
23475 | uint8_t* gap = (plug - gap_size); |
23476 | dprintf (3,("Making free list %Ix len %d in %d" , |
23477 | //dprintf (3,("F: %Ix len %Ix in %d", |
23478 | (size_t)gap, gap_size, args->free_list_gen_number)); |
23479 | args->highest_plug = tree; |
23480 | #ifdef SHORT_PLUGS |
23481 | if (is_plug_padded (plug)) |
23482 | { |
23483 | dprintf (3, ("%Ix padded" , plug)); |
23484 | clear_plug_padded (plug); |
23485 | } |
23486 | #endif //SHORT_PLUGS |
23487 | gen_crossing: |
23488 | { |
23489 | if ((args->current_gen_limit == MAX_PTR) || |
23490 | ((plug >= args->current_gen_limit) && |
23491 | ephemeral_pointer_p (plug))) |
23492 | { |
23493 | dprintf(3,(" Crossing Generation boundary at %Ix" , |
23494 | (size_t)args->current_gen_limit)); |
23495 | if (!(args->current_gen_limit == MAX_PTR)) |
23496 | { |
23497 | args->free_list_gen_number--; |
23498 | args->free_list_gen = generation_of (args->free_list_gen_number); |
23499 | } |
23500 | dprintf(3,( " Fixing generation start of %d to: %Ix" , |
23501 | args->free_list_gen_number, (size_t)gap)); |
23502 | |
23503 | reset_allocation_pointers (args->free_list_gen, gap); |
23504 | args->current_gen_limit = generation_limit (args->free_list_gen_number); |
23505 | |
23506 | if ((gap_size >= (2*Align (min_obj_size)))) |
23507 | { |
23508 | dprintf(3,(" Splitting the gap in two %Id left" , |
23509 | gap_size)); |
23510 | make_unused_array (gap, Align(min_obj_size)); |
23511 | gap_size = (gap_size - Align(min_obj_size)); |
23512 | gap = (gap + Align(min_obj_size)); |
23513 | } |
23514 | else |
23515 | { |
23516 | make_unused_array (gap, gap_size); |
23517 | gap_size = 0; |
23518 | } |
23519 | goto gen_crossing; |
23520 | } |
23521 | } |
23522 | |
23523 | thread_gap (gap, gap_size, args->free_list_gen); |
23524 | add_gen_free (args->free_list_gen->gen_num, gap_size); |
23525 | } |
23526 | if (! (0 == right_node)) |
23527 | { |
23528 | make_free_list_in_brick (tree + right_node, args); |
23529 | } |
23530 | } |
23531 | } |
23532 | } |
23533 | |
23534 | void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation* gen) |
23535 | { |
23536 | assert (generation_allocation_start (gen)); |
23537 | if ((size > 0)) |
23538 | { |
23539 | if ((gen->gen_num == 0) && (size > CLR_SIZE)) |
23540 | { |
23541 | gen0_big_free_spaces += size; |
23542 | } |
23543 | |
23544 | assert ((heap_segment_rw (generation_start_segment (gen))!= |
23545 | ephemeral_heap_segment) || |
23546 | (gap_start > generation_allocation_start (gen))); |
23547 | // The beginning of a segment gap is not aligned |
23548 | assert (size >= Align (min_obj_size)); |
23549 | make_unused_array (gap_start, size, |
23550 | (!settings.concurrent && (gen != youngest_generation)), |
23551 | (gen->gen_num == max_generation)); |
23552 | dprintf (3, ("fr: [%Ix, %Ix[" , (size_t)gap_start, (size_t)gap_start+size)); |
23553 | |
23554 | if ((size >= min_free_list)) |
23555 | { |
23556 | generation_free_list_space (gen) += size; |
23557 | generation_allocator (gen)->thread_item (gap_start, size); |
23558 | } |
23559 | else |
23560 | { |
23561 | generation_free_obj_space (gen) += size; |
23562 | } |
23563 | } |
23564 | } |
23565 | |
23566 | void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation* gen) |
23567 | { |
23568 | assert (generation_allocation_start (gen)); |
23569 | if (size >= min_free_list) |
23570 | { |
23571 | generation_free_list_space (gen) += size; |
23572 | generation_allocator (gen)->thread_item_front (gap_start, size); |
23573 | } |
23574 | } |
23575 | |
23576 | void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp) |
23577 | { |
23578 | dprintf (3, ("Making unused array [%Ix, %Ix[" , |
23579 | (size_t)x, (size_t)(x+size))); |
23580 | assert (size >= Align (min_obj_size)); |
23581 | |
23582 | //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC) |
23583 | // check_batch_mark_array_bits (x, x+size); |
23584 | //#endif //VERIFY_HEAP && BACKGROUND_GC |
23585 | |
23586 | if (resetp) |
23587 | reset_memory (x, size); |
23588 | |
23589 | ((CObjectHeader*)x)->SetFree(size); |
23590 | |
23591 | #ifdef BIT64 |
23592 | |
23593 | #if BIGENDIAN |
23594 | #error "This won't work on big endian platforms" |
23595 | #endif |
23596 | |
23597 | size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size; |
23598 | |
23599 | if (size_as_object < size) |
23600 | { |
23601 | // |
23602 | // If the size is more than 4GB, we need to create multiple objects because of |
23603 | // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array |
23604 | // size is ignored in regular object size computation. |
23605 | // |
23606 | uint8_t * tmp = x + size_as_object; |
23607 | size_t remaining_size = size - size_as_object; |
23608 | |
23609 | while (remaining_size > UINT32_MAX) |
23610 | { |
23611 | // Make sure that there will be at least Align(min_obj_size) left |
23612 | size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) |
23613 | - Align (min_obj_size, get_alignment_constant (FALSE)); |
23614 | |
23615 | ((CObjectHeader*)tmp)->SetFree(current_size); |
23616 | |
23617 | remaining_size -= current_size; |
23618 | tmp += current_size; |
23619 | } |
23620 | |
23621 | ((CObjectHeader*)tmp)->SetFree(remaining_size); |
23622 | } |
23623 | #endif |
23624 | |
23625 | if (clearp) |
23626 | clear_card_for_addresses (x, x + Align(size)); |
23627 | } |
23628 | |
23629 | // Clear memory set by make_unused_array. |
23630 | void gc_heap::clear_unused_array (uint8_t* x, size_t size) |
23631 | { |
23632 | // Also clear the sync block |
23633 | *(((PTR_PTR)x)-1) = 0; |
23634 | |
23635 | ((CObjectHeader*)x)->UnsetFree(); |
23636 | |
23637 | #ifdef BIT64 |
23638 | |
23639 | #if BIGENDIAN |
23640 | #error "This won't work on big endian platforms" |
23641 | #endif |
23642 | |
23643 | // The memory could have been cleared in the meantime. We have to mirror the algorithm |
23644 | // from make_unused_array since we cannot depend on the object sizes in memory. |
23645 | size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size; |
23646 | |
23647 | if (size_as_object < size) |
23648 | { |
23649 | uint8_t * tmp = x + size_as_object; |
23650 | size_t remaining_size = size - size_as_object; |
23651 | |
23652 | while (remaining_size > UINT32_MAX) |
23653 | { |
23654 | size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) |
23655 | - Align (min_obj_size, get_alignment_constant (FALSE)); |
23656 | |
23657 | ((CObjectHeader*)tmp)->UnsetFree(); |
23658 | |
23659 | remaining_size -= current_size; |
23660 | tmp += current_size; |
23661 | } |
23662 | |
23663 | ((CObjectHeader*)tmp)->UnsetFree(); |
23664 | } |
23665 | #else |
23666 | UNREFERENCED_PARAMETER(size); |
23667 | #endif |
23668 | } |
23669 | |
23670 | inline |
23671 | uint8_t* tree_search (uint8_t* tree, uint8_t* old_address) |
23672 | { |
23673 | uint8_t* candidate = 0; |
23674 | int cn; |
23675 | while (1) |
23676 | { |
23677 | if (tree < old_address) |
23678 | { |
23679 | if ((cn = node_right_child (tree)) != 0) |
23680 | { |
23681 | assert (candidate < tree); |
23682 | candidate = tree; |
23683 | tree = tree + cn; |
23684 | Prefetch (tree - 8); |
23685 | continue; |
23686 | } |
23687 | else |
23688 | break; |
23689 | } |
23690 | else if (tree > old_address) |
23691 | { |
23692 | if ((cn = node_left_child (tree)) != 0) |
23693 | { |
23694 | tree = tree + cn; |
23695 | Prefetch (tree - 8); |
23696 | continue; |
23697 | } |
23698 | else |
23699 | break; |
23700 | } else |
23701 | break; |
23702 | } |
23703 | if (tree <= old_address) |
23704 | return tree; |
23705 | else if (candidate) |
23706 | return candidate; |
23707 | else |
23708 | return tree; |
23709 | } |
23710 | |
23711 | #ifdef FEATURE_BASICFREEZE |
23712 | bool gc_heap::frozen_object_p (Object* obj) |
23713 | { |
23714 | #ifdef MULTIPLE_HEAPS |
23715 | ptrdiff_t delta = 0; |
23716 | heap_segment* pSegment = segment_of ((uint8_t*)obj, delta); |
23717 | #else //MULTIPLE_HEAPS |
23718 | heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE); |
23719 | _ASSERTE(pSegment); |
23720 | #endif //MULTIPLE_HEAPS |
23721 | |
23722 | return heap_segment_read_only_p(pSegment); |
23723 | } |
23724 | #endif // FEATURE_BASICFREEZE |
23725 | |
23726 | #ifdef FEATURE_REDHAWK |
23727 | // TODO: this was added on RH, we have not done perf runs to see if this is the right |
23728 | // thing to do for other versions of the CLR. |
23729 | inline |
23730 | #endif // FEATURE_REDHAWK |
23731 | void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL) |
23732 | { |
23733 | uint8_t* old_address = *pold_address; |
23734 | if (!((old_address >= gc_low) && (old_address < gc_high))) |
23735 | #ifdef MULTIPLE_HEAPS |
23736 | { |
23737 | UNREFERENCED_PARAMETER(thread); |
23738 | if (old_address == 0) |
23739 | return; |
23740 | gc_heap* hp = heap_of (old_address); |
23741 | if ((hp == this) || |
23742 | !((old_address >= hp->gc_low) && (old_address < hp->gc_high))) |
23743 | return; |
23744 | } |
23745 | #else //MULTIPLE_HEAPS |
23746 | return ; |
23747 | #endif //MULTIPLE_HEAPS |
23748 | // delta translates old_address into address_gc (old_address); |
23749 | size_t brick = brick_of (old_address); |
23750 | int brick_entry = brick_table [ brick ]; |
23751 | uint8_t* new_address = old_address; |
23752 | if (! ((brick_entry == 0))) |
23753 | { |
23754 | retry: |
23755 | { |
23756 | while (brick_entry < 0) |
23757 | { |
23758 | brick = (brick + brick_entry); |
23759 | brick_entry = brick_table [ brick ]; |
23760 | } |
23761 | uint8_t* old_loc = old_address; |
23762 | |
23763 | uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1), |
23764 | old_loc); |
23765 | if ((node <= old_loc)) |
23766 | new_address = (old_address + node_relocation_distance (node)); |
23767 | else |
23768 | { |
23769 | if (node_left_p (node)) |
23770 | { |
23771 | dprintf(3,(" L: %Ix" , (size_t)node)); |
23772 | new_address = (old_address + |
23773 | (node_relocation_distance (node) + |
23774 | node_gap_size (node))); |
23775 | } |
23776 | else |
23777 | { |
23778 | brick = brick - 1; |
23779 | brick_entry = brick_table [ brick ]; |
23780 | goto retry; |
23781 | } |
23782 | } |
23783 | } |
23784 | |
23785 | *pold_address = new_address; |
23786 | return; |
23787 | } |
23788 | |
23789 | #ifdef FEATURE_LOH_COMPACTION |
23790 | if (loh_compacted_p |
23791 | #ifdef FEATURE_BASICFREEZE |
23792 | && !frozen_object_p((Object*)old_address) |
23793 | #endif // FEATURE_BASICFREEZE |
23794 | ) |
23795 | { |
23796 | *pold_address = old_address + loh_node_relocation_distance (old_address); |
23797 | } |
23798 | else |
23799 | #endif //FEATURE_LOH_COMPACTION |
23800 | { |
23801 | *pold_address = new_address; |
23802 | } |
23803 | } |
23804 | |
23805 | inline void |
23806 | gc_heap::check_class_object_demotion (uint8_t* obj) |
23807 | { |
23808 | #ifdef COLLECTIBLE_CLASS |
23809 | if (is_collectible(obj)) |
23810 | { |
23811 | check_class_object_demotion_internal (obj); |
23812 | } |
23813 | #else |
23814 | UNREFERENCED_PARAMETER(obj); |
23815 | #endif //COLLECTIBLE_CLASS |
23816 | } |
23817 | |
23818 | #ifdef COLLECTIBLE_CLASS |
23819 | NOINLINE void |
23820 | gc_heap::check_class_object_demotion_internal (uint8_t* obj) |
23821 | { |
23822 | if (settings.demotion) |
23823 | { |
23824 | #ifdef MULTIPLE_HEAPS |
23825 | // We set the card without checking the demotion range 'cause at this point |
23826 | // the handle that points to the loader allocator object may or may not have |
23827 | // been relocated by other GC threads. |
23828 | set_card (card_of (obj)); |
23829 | #else |
23830 | THREAD_FROM_HEAP; |
23831 | uint8_t* class_obj = get_class_object (obj); |
23832 | dprintf (3, ("%Ix: got classobj %Ix" , obj, class_obj)); |
23833 | uint8_t* temp_class_obj = class_obj; |
23834 | uint8_t** temp = &temp_class_obj; |
23835 | relocate_address (temp THREAD_NUMBER_ARG); |
23836 | |
23837 | check_demotion_helper (temp, obj); |
23838 | #endif //MULTIPLE_HEAPS |
23839 | } |
23840 | } |
23841 | |
23842 | #endif //COLLECTIBLE_CLASS |
23843 | |
23844 | inline void |
23845 | gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj) |
23846 | { |
23847 | // detect if we are demoting an object |
23848 | if ((*pval < demotion_high) && |
23849 | (*pval >= demotion_low)) |
23850 | { |
23851 | dprintf(3, ("setting card %Ix:%Ix" , |
23852 | card_of((uint8_t*)pval), |
23853 | (size_t)pval)); |
23854 | |
23855 | set_card (card_of (parent_obj)); |
23856 | } |
23857 | #ifdef MULTIPLE_HEAPS |
23858 | else if (settings.demotion) |
23859 | { |
23860 | dprintf (4, ("Demotion active, computing heap_of object" )); |
23861 | gc_heap* hp = heap_of (*pval); |
23862 | if ((*pval < hp->demotion_high) && |
23863 | (*pval >= hp->demotion_low)) |
23864 | { |
23865 | dprintf(3, ("setting card %Ix:%Ix" , |
23866 | card_of((uint8_t*)pval), |
23867 | (size_t)pval)); |
23868 | |
23869 | set_card (card_of (parent_obj)); |
23870 | } |
23871 | } |
23872 | #endif //MULTIPLE_HEAPS |
23873 | } |
23874 | |
23875 | inline void |
23876 | gc_heap::reloc_survivor_helper (uint8_t** pval) |
23877 | { |
23878 | THREAD_FROM_HEAP; |
23879 | relocate_address (pval THREAD_NUMBER_ARG); |
23880 | |
23881 | check_demotion_helper (pval, (uint8_t*)pval); |
23882 | } |
23883 | |
23884 | inline void |
23885 | gc_heap::relocate_obj_helper (uint8_t* x, size_t s) |
23886 | { |
23887 | THREAD_FROM_HEAP; |
23888 | if (contain_pointers (x)) |
23889 | { |
23890 | dprintf (3, ("$%Ix$" , (size_t)x)); |
23891 | |
23892 | go_through_object_nostart (method_table(x), x, s, pval, |
23893 | { |
23894 | uint8_t* child = *pval; |
23895 | reloc_survivor_helper (pval); |
23896 | if (child) |
23897 | { |
23898 | dprintf (3, ("%Ix->%Ix->%Ix" , (uint8_t*)pval, child, *pval)); |
23899 | } |
23900 | }); |
23901 | |
23902 | } |
23903 | check_class_object_demotion (x); |
23904 | } |
23905 | |
23906 | inline |
23907 | void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc) |
23908 | { |
23909 | THREAD_FROM_HEAP; |
23910 | |
23911 | uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0); |
23912 | relocate_address (address_to_reloc THREAD_NUMBER_ARG); |
23913 | if (address_to_reloc) |
23914 | { |
23915 | dprintf (3, ("SR %Ix: %Ix->%Ix" , (uint8_t*)address_to_reloc, old_val, *address_to_reloc)); |
23916 | } |
23917 | |
23918 | //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval); |
23919 | uint8_t* relocated_addr = *address_to_reloc; |
23920 | if ((relocated_addr < demotion_high) && |
23921 | (relocated_addr >= demotion_low)) |
23922 | { |
23923 | dprintf (3, ("set card for location %Ix(%Ix)" , |
23924 | (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card))); |
23925 | |
23926 | set_card (card_of ((uint8_t*)address_to_set_card)); |
23927 | } |
23928 | #ifdef MULTIPLE_HEAPS |
23929 | else if (settings.demotion) |
23930 | { |
23931 | gc_heap* hp = heap_of (relocated_addr); |
23932 | if ((relocated_addr < hp->demotion_high) && |
23933 | (relocated_addr >= hp->demotion_low)) |
23934 | { |
23935 | dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)" , |
23936 | relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card))); |
23937 | |
23938 | set_card (card_of ((uint8_t*)address_to_set_card)); |
23939 | } |
23940 | } |
23941 | #endif //MULTIPLE_HEAPS |
23942 | } |
23943 | |
23944 | void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry) |
23945 | { |
23946 | THREAD_FROM_HEAP; |
23947 | uint8_t* plug = pinned_plug (pinned_plug_entry); |
23948 | uint8_t* pre_plug_start = plug - sizeof (plug_and_gap); |
23949 | // Note that we need to add one ptr size here otherwise we may not be able to find the relocated |
23950 | // address. Consider this scenario: |
23951 | // gen1 start | 3-ptr sized NP | PP |
23952 | // 0 | 0x18 | 0x30 |
23953 | // If we are asking for the reloc address of 0x10 we will AV in relocate_address because |
23954 | // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick |
23955 | // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree). |
23956 | pre_plug_start += sizeof (uint8_t*); |
23957 | uint8_t** old_address = &pre_plug_start; |
23958 | |
23959 | uint8_t* old_val = (old_address ? *old_address : 0); |
23960 | relocate_address (old_address THREAD_NUMBER_ARG); |
23961 | if (old_address) |
23962 | { |
23963 | dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix" , |
23964 | (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*)))); |
23965 | } |
23966 | |
23967 | pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*)); |
23968 | } |
23969 | |
23970 | inline |
23971 | void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned) |
23972 | { |
23973 | THREAD_FROM_HEAP; |
23974 | uint8_t* plug = pinned_plug (pinned_plug_entry); |
23975 | |
23976 | if (!is_pinned) |
23977 | { |
23978 | //// Temporary - we just wanna make sure we are doing things right when padding is needed. |
23979 | //if ((x + s) < plug) |
23980 | //{ |
23981 | // dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", |
23982 | // x, (x + s), (plug- (x + s)), plug)); |
23983 | // GCToOSInterface::DebugBreak(); |
23984 | //} |
23985 | |
23986 | relocate_pre_plug_info (pinned_plug_entry); |
23987 | } |
23988 | |
23989 | verify_pins_with_post_plug_info("after relocate_pre_plug_info" ); |
23990 | |
23991 | uint8_t* saved_plug_info_start = 0; |
23992 | uint8_t** saved_info_to_relocate = 0; |
23993 | |
23994 | if (is_pinned) |
23995 | { |
23996 | saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start()); |
23997 | saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info()); |
23998 | } |
23999 | else |
24000 | { |
24001 | saved_plug_info_start = (plug - sizeof (plug_and_gap)); |
24002 | saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info()); |
24003 | } |
24004 | |
24005 | uint8_t** current_saved_info_to_relocate = 0; |
24006 | uint8_t* child = 0; |
24007 | |
24008 | dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix" , x, plug, end)); |
24009 | |
24010 | if (contain_pointers (x)) |
24011 | { |
24012 | dprintf (3,("$%Ix$" , (size_t)x)); |
24013 | |
24014 | go_through_object_nostart (method_table(x), x, s, pval, |
24015 | { |
24016 | dprintf (3, ("obj %Ix, member: %Ix->%Ix" , x, (uint8_t*)pval, *pval)); |
24017 | |
24018 | if ((uint8_t*)pval >= end) |
24019 | { |
24020 | current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**); |
24021 | child = *current_saved_info_to_relocate; |
24022 | reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate); |
24023 | dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix" , |
24024 | (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate)); |
24025 | } |
24026 | else |
24027 | { |
24028 | reloc_survivor_helper (pval); |
24029 | } |
24030 | }); |
24031 | } |
24032 | |
24033 | check_class_object_demotion (x); |
24034 | } |
24035 | |
24036 | void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end) |
24037 | { |
24038 | uint8_t* x = plug; |
24039 | while (x < plug_end) |
24040 | { |
24041 | size_t s = size (x); |
24042 | uint8_t* next_obj = x + Align (s); |
24043 | Prefetch (next_obj); |
24044 | relocate_obj_helper (x, s); |
24045 | assert (s > 0); |
24046 | x = next_obj; |
24047 | } |
24048 | } |
24049 | |
24050 | // if we expanded, right now we are not handling it as We are not saving the new reloc info. |
24051 | void gc_heap::verify_pins_with_post_plug_info (const char* msg) |
24052 | { |
24053 | #if defined (_DEBUG) && defined (VERIFY_HEAP) |
24054 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
24055 | { |
24056 | if (!verify_pinned_queue_p) |
24057 | return; |
24058 | |
24059 | if (settings.heap_expansion) |
24060 | return; |
24061 | |
24062 | for (size_t i = 0; i < mark_stack_tos; i++) |
24063 | { |
24064 | mark& m = mark_stack_array[i]; |
24065 | |
24066 | mark* pinned_plug_entry = pinned_plug_of(i); |
24067 | |
24068 | if (pinned_plug_entry->has_post_plug_info() && |
24069 | pinned_plug_entry->post_short_p() && |
24070 | (pinned_plug_entry->saved_post_plug_debug.gap != 1)) |
24071 | { |
24072 | uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap); |
24073 | // object after pin |
24074 | dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d" , |
24075 | next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj), |
24076 | (int)node_left_child (next_obj), (int)node_right_child (next_obj))); |
24077 | |
24078 | size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug); |
24079 | |
24080 | if (node_gap_size (next_obj) != *post_plug_debug) |
24081 | { |
24082 | dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix" , |
24083 | next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj)))); |
24084 | FATAL_GC_ERROR(); |
24085 | } |
24086 | post_plug_debug++; |
24087 | // can't do node_relocation_distance here as it clears the left bit. |
24088 | //if (node_relocation_distance (next_obj) != *post_plug_debug) |
24089 | if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug) |
24090 | { |
24091 | dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix" , |
24092 | next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj)))); |
24093 | FATAL_GC_ERROR(); |
24094 | } |
24095 | if (node_left_child (next_obj) > 0) |
24096 | { |
24097 | dprintf (3, ("obj: %Ix, vLC: %d\n" , next_obj, (int)(node_left_child (next_obj)))); |
24098 | FATAL_GC_ERROR(); |
24099 | } |
24100 | } |
24101 | } |
24102 | |
24103 | dprintf (3, ("%s verified" , msg)); |
24104 | } |
24105 | #else // _DEBUG && VERIFY_HEAP |
24106 | UNREFERENCED_PARAMETER(msg); |
24107 | #endif // _DEBUG && VERIFY_HEAP |
24108 | } |
24109 | |
24110 | #ifdef COLLECTIBLE_CLASS |
24111 | // We don't want to burn another ptr size space for pinned plugs to record this so just |
24112 | // set the card unconditionally for collectible objects if we are demoting. |
24113 | inline void |
24114 | gc_heap::unconditional_set_card_collectible (uint8_t* obj) |
24115 | { |
24116 | if (settings.demotion) |
24117 | { |
24118 | set_card (card_of (obj)); |
24119 | } |
24120 | } |
24121 | #endif //COLLECTIBLE_CLASS |
24122 | |
24123 | void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry) |
24124 | { |
24125 | uint8_t* x = plug; |
24126 | uint8_t* p_plug = pinned_plug (pinned_plug_entry); |
24127 | BOOL is_pinned = (plug == p_plug); |
24128 | BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p()); |
24129 | |
24130 | plug_end += sizeof (gap_reloc_pair); |
24131 | |
24132 | //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not"))); |
24133 | dprintf (3, ("%s %Ix-%Ix short, LO: %s OW" , (is_pinned ? "PP" : "NP" ), plug, plug_end, (check_short_obj_p ? "is" : "is not" ))); |
24134 | |
24135 | verify_pins_with_post_plug_info("begin reloc short surv" ); |
24136 | |
24137 | while (x < plug_end) |
24138 | { |
24139 | if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size)) |
24140 | { |
24141 | dprintf (3, ("last obj %Ix is short" , x)); |
24142 | |
24143 | if (is_pinned) |
24144 | { |
24145 | #ifdef COLLECTIBLE_CLASS |
24146 | if (pinned_plug_entry->post_short_collectible_p()) |
24147 | unconditional_set_card_collectible (x); |
24148 | #endif //COLLECTIBLE_CLASS |
24149 | |
24150 | // Relocate the saved references based on bits set. |
24151 | uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start()); |
24152 | uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info()); |
24153 | for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++) |
24154 | { |
24155 | if (pinned_plug_entry->post_short_bit_p (i)) |
24156 | { |
24157 | reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i)); |
24158 | } |
24159 | } |
24160 | } |
24161 | else |
24162 | { |
24163 | #ifdef COLLECTIBLE_CLASS |
24164 | if (pinned_plug_entry->pre_short_collectible_p()) |
24165 | unconditional_set_card_collectible (x); |
24166 | #endif //COLLECTIBLE_CLASS |
24167 | |
24168 | relocate_pre_plug_info (pinned_plug_entry); |
24169 | |
24170 | // Relocate the saved references based on bits set. |
24171 | uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap)); |
24172 | uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info()); |
24173 | for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++) |
24174 | { |
24175 | if (pinned_plug_entry->pre_short_bit_p (i)) |
24176 | { |
24177 | reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i)); |
24178 | } |
24179 | } |
24180 | } |
24181 | |
24182 | break; |
24183 | } |
24184 | |
24185 | size_t s = size (x); |
24186 | uint8_t* next_obj = x + Align (s); |
24187 | Prefetch (next_obj); |
24188 | |
24189 | if (next_obj >= plug_end) |
24190 | { |
24191 | dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix" , |
24192 | next_obj, plug, plug_end)); |
24193 | |
24194 | verify_pins_with_post_plug_info("before reloc short obj" ); |
24195 | |
24196 | relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned); |
24197 | } |
24198 | else |
24199 | { |
24200 | relocate_obj_helper (x, s); |
24201 | } |
24202 | |
24203 | assert (s > 0); |
24204 | x = next_obj; |
24205 | } |
24206 | |
24207 | verify_pins_with_post_plug_info("end reloc short surv" ); |
24208 | } |
24209 | |
24210 | void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end, |
24211 | BOOL check_last_object_p, |
24212 | mark* pinned_plug_entry) |
24213 | { |
24214 | //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end)); |
24215 | dprintf (3,("RP: [%Ix,%Ix[" , (size_t)plug, (size_t)plug_end)); |
24216 | |
24217 | if (check_last_object_p) |
24218 | { |
24219 | relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry); |
24220 | } |
24221 | else |
24222 | { |
24223 | relocate_survivor_helper (plug, plug_end); |
24224 | } |
24225 | } |
24226 | |
24227 | void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args) |
24228 | { |
24229 | assert ((tree != NULL)); |
24230 | |
24231 | dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix" , |
24232 | tree, args->last_plug, |
24233 | (tree + node_left_child (tree)), |
24234 | (tree + node_right_child (tree)), |
24235 | node_gap_size (tree))); |
24236 | |
24237 | if (node_left_child (tree)) |
24238 | { |
24239 | relocate_survivors_in_brick (tree + node_left_child (tree), args); |
24240 | } |
24241 | { |
24242 | uint8_t* plug = tree; |
24243 | BOOL has_post_plug_info_p = FALSE; |
24244 | BOOL has_pre_plug_info_p = FALSE; |
24245 | |
24246 | if (tree == oldest_pinned_plug) |
24247 | { |
24248 | args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p, |
24249 | &has_post_plug_info_p); |
24250 | assert (tree == pinned_plug (args->pinned_plug_entry)); |
24251 | |
24252 | dprintf (3, ("tree is the oldest pin: %Ix" , tree)); |
24253 | } |
24254 | if (args->last_plug) |
24255 | { |
24256 | size_t gap_size = node_gap_size (tree); |
24257 | uint8_t* gap = (plug - gap_size); |
24258 | dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)" , tree, gap, gap_size)); |
24259 | assert (gap_size >= Align (min_obj_size)); |
24260 | uint8_t* last_plug_end = gap; |
24261 | |
24262 | BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p); |
24263 | |
24264 | { |
24265 | relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry); |
24266 | } |
24267 | } |
24268 | else |
24269 | { |
24270 | assert (!has_pre_plug_info_p); |
24271 | } |
24272 | |
24273 | args->last_plug = plug; |
24274 | args->is_shortened = has_post_plug_info_p; |
24275 | if (has_post_plug_info_p) |
24276 | { |
24277 | dprintf (3, ("setting %Ix as shortened" , plug)); |
24278 | } |
24279 | dprintf (3, ("last_plug: %Ix(shortened: %d)" , plug, (args->is_shortened ? 1 : 0))); |
24280 | } |
24281 | if (node_right_child (tree)) |
24282 | { |
24283 | relocate_survivors_in_brick (tree + node_right_child (tree), args); |
24284 | } |
24285 | } |
24286 | |
24287 | inline |
24288 | void gc_heap::update_oldest_pinned_plug() |
24289 | { |
24290 | oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin())); |
24291 | } |
24292 | |
24293 | void gc_heap::relocate_survivors (int condemned_gen_number, |
24294 | uint8_t* first_condemned_address) |
24295 | { |
24296 | generation* condemned_gen = generation_of (condemned_gen_number); |
24297 | uint8_t* start_address = first_condemned_address; |
24298 | size_t current_brick = brick_of (start_address); |
24299 | heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen)); |
24300 | |
24301 | PREFIX_ASSUME(current_heap_segment != NULL); |
24302 | |
24303 | uint8_t* end_address = 0; |
24304 | |
24305 | reset_pinned_queue_bos(); |
24306 | update_oldest_pinned_plug(); |
24307 | |
24308 | end_address = heap_segment_allocated (current_heap_segment); |
24309 | |
24310 | size_t end_brick = brick_of (end_address - 1); |
24311 | relocate_args args; |
24312 | args.low = gc_low; |
24313 | args.high = gc_high; |
24314 | args.is_shortened = FALSE; |
24315 | args.pinned_plug_entry = 0; |
24316 | args.last_plug = 0; |
24317 | while (1) |
24318 | { |
24319 | if (current_brick > end_brick) |
24320 | { |
24321 | if (args.last_plug) |
24322 | { |
24323 | { |
24324 | assert (!(args.is_shortened)); |
24325 | relocate_survivors_in_plug (args.last_plug, |
24326 | heap_segment_allocated (current_heap_segment), |
24327 | args.is_shortened, |
24328 | args.pinned_plug_entry); |
24329 | } |
24330 | |
24331 | args.last_plug = 0; |
24332 | } |
24333 | |
24334 | if (heap_segment_next_rw (current_heap_segment)) |
24335 | { |
24336 | current_heap_segment = heap_segment_next_rw (current_heap_segment); |
24337 | current_brick = brick_of (heap_segment_mem (current_heap_segment)); |
24338 | end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1); |
24339 | continue; |
24340 | } |
24341 | else |
24342 | { |
24343 | break; |
24344 | } |
24345 | } |
24346 | { |
24347 | int brick_entry = brick_table [ current_brick ]; |
24348 | |
24349 | if (brick_entry >= 0) |
24350 | { |
24351 | relocate_survivors_in_brick (brick_address (current_brick) + |
24352 | brick_entry -1, |
24353 | &args); |
24354 | } |
24355 | } |
24356 | current_brick++; |
24357 | } |
24358 | } |
24359 | |
24360 | void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args) |
24361 | { |
24362 | if (check_last_object_p) |
24363 | { |
24364 | size += sizeof (gap_reloc_pair); |
24365 | mark* entry = args->pinned_plug_entry; |
24366 | |
24367 | if (args->is_shortened) |
24368 | { |
24369 | assert (entry->has_post_plug_info()); |
24370 | entry->swap_post_plug_and_saved_for_profiler(); |
24371 | } |
24372 | else |
24373 | { |
24374 | assert (entry->has_pre_plug_info()); |
24375 | entry->swap_pre_plug_and_saved_for_profiler(); |
24376 | } |
24377 | } |
24378 | |
24379 | ptrdiff_t last_plug_relocation = node_relocation_distance (plug); |
24380 | STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation); |
24381 | ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0; |
24382 | |
24383 | (args->fn) (plug, (plug + size), reloc, args->profiling_context, !!settings.compaction, false); |
24384 | |
24385 | if (check_last_object_p) |
24386 | { |
24387 | mark* entry = args->pinned_plug_entry; |
24388 | |
24389 | if (args->is_shortened) |
24390 | { |
24391 | entry->swap_post_plug_and_saved_for_profiler(); |
24392 | } |
24393 | else |
24394 | { |
24395 | entry->swap_pre_plug_and_saved_for_profiler(); |
24396 | } |
24397 | } |
24398 | } |
24399 | |
24400 | void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args) |
24401 | { |
24402 | assert ((tree != NULL)); |
24403 | if (node_left_child (tree)) |
24404 | { |
24405 | walk_relocation_in_brick (tree + node_left_child (tree), args); |
24406 | } |
24407 | |
24408 | uint8_t* plug = tree; |
24409 | BOOL has_pre_plug_info_p = FALSE; |
24410 | BOOL has_post_plug_info_p = FALSE; |
24411 | |
24412 | if (tree == oldest_pinned_plug) |
24413 | { |
24414 | args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p, |
24415 | &has_post_plug_info_p); |
24416 | assert (tree == pinned_plug (args->pinned_plug_entry)); |
24417 | } |
24418 | |
24419 | if (args->last_plug != 0) |
24420 | { |
24421 | size_t gap_size = node_gap_size (tree); |
24422 | uint8_t* gap = (plug - gap_size); |
24423 | uint8_t* last_plug_end = gap; |
24424 | size_t last_plug_size = (last_plug_end - args->last_plug); |
24425 | dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix" , |
24426 | tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size)); |
24427 | |
24428 | BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p); |
24429 | if (!check_last_object_p) |
24430 | { |
24431 | assert (last_plug_size >= Align (min_obj_size)); |
24432 | } |
24433 | |
24434 | walk_plug (args->last_plug, last_plug_size, check_last_object_p, args); |
24435 | } |
24436 | else |
24437 | { |
24438 | assert (!has_pre_plug_info_p); |
24439 | } |
24440 | |
24441 | dprintf (3, ("set args last plug to plug: %Ix" , plug)); |
24442 | args->last_plug = plug; |
24443 | args->is_shortened = has_post_plug_info_p; |
24444 | |
24445 | if (node_right_child (tree)) |
24446 | { |
24447 | walk_relocation_in_brick (tree + node_right_child (tree), args); |
24448 | } |
24449 | } |
24450 | |
24451 | void gc_heap::walk_relocation (void* profiling_context, record_surv_fn fn) |
24452 | { |
24453 | generation* condemned_gen = generation_of (settings.condemned_generation); |
24454 | uint8_t* start_address = generation_allocation_start (condemned_gen); |
24455 | size_t current_brick = brick_of (start_address); |
24456 | heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen)); |
24457 | |
24458 | PREFIX_ASSUME(current_heap_segment != NULL); |
24459 | |
24460 | reset_pinned_queue_bos(); |
24461 | update_oldest_pinned_plug(); |
24462 | size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1); |
24463 | walk_relocate_args args; |
24464 | args.is_shortened = FALSE; |
24465 | args.pinned_plug_entry = 0; |
24466 | args.last_plug = 0; |
24467 | args.profiling_context = profiling_context; |
24468 | args.fn = fn; |
24469 | |
24470 | while (1) |
24471 | { |
24472 | if (current_brick > end_brick) |
24473 | { |
24474 | if (args.last_plug) |
24475 | { |
24476 | walk_plug (args.last_plug, |
24477 | (heap_segment_allocated (current_heap_segment) - args.last_plug), |
24478 | args.is_shortened, |
24479 | &args); |
24480 | args.last_plug = 0; |
24481 | } |
24482 | if (heap_segment_next_rw (current_heap_segment)) |
24483 | { |
24484 | current_heap_segment = heap_segment_next_rw (current_heap_segment); |
24485 | current_brick = brick_of (heap_segment_mem (current_heap_segment)); |
24486 | end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1); |
24487 | continue; |
24488 | } |
24489 | else |
24490 | { |
24491 | break; |
24492 | } |
24493 | } |
24494 | { |
24495 | int brick_entry = brick_table [ current_brick ]; |
24496 | if (brick_entry >= 0) |
24497 | { |
24498 | walk_relocation_in_brick (brick_address (current_brick) + |
24499 | brick_entry - 1, |
24500 | &args); |
24501 | } |
24502 | } |
24503 | current_brick++; |
24504 | } |
24505 | } |
24506 | |
24507 | void gc_heap::walk_survivors (record_surv_fn fn, void* context, walk_surv_type type) |
24508 | { |
24509 | if (type == walk_for_gc) |
24510 | walk_survivors_relocation (context, fn); |
24511 | #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) |
24512 | else if (type == walk_for_bgc) |
24513 | walk_survivors_for_bgc (context, fn); |
24514 | #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE |
24515 | else if (type == walk_for_loh) |
24516 | walk_survivors_for_loh (context, fn); |
24517 | else |
24518 | assert (!"unknown type!" ); |
24519 | } |
24520 | |
24521 | #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) |
24522 | void gc_heap::walk_survivors_for_bgc (void* profiling_context, record_surv_fn fn) |
24523 | { |
24524 | // This should only be called for BGCs |
24525 | assert(settings.concurrent); |
24526 | |
24527 | heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
24528 | |
24529 | BOOL small_object_segments = TRUE; |
24530 | int align_const = get_alignment_constant (small_object_segments); |
24531 | |
24532 | while (1) |
24533 | { |
24534 | if (seg == 0) |
24535 | { |
24536 | if (small_object_segments) |
24537 | { |
24538 | //switch to large segment |
24539 | small_object_segments = FALSE; |
24540 | |
24541 | align_const = get_alignment_constant (small_object_segments); |
24542 | seg = heap_segment_rw (generation_start_segment (large_object_generation)); |
24543 | |
24544 | PREFIX_ASSUME(seg != NULL); |
24545 | |
24546 | continue; |
24547 | } |
24548 | else |
24549 | break; |
24550 | } |
24551 | |
24552 | uint8_t* o = heap_segment_mem (seg); |
24553 | uint8_t* end = heap_segment_allocated (seg); |
24554 | |
24555 | while (o < end) |
24556 | { |
24557 | if (method_table(o) == g_gc_pFreeObjectMethodTable) |
24558 | { |
24559 | o += Align (size (o), align_const); |
24560 | continue; |
24561 | } |
24562 | |
24563 | // It's survived. Make a fake plug, starting at o, |
24564 | // and send the event |
24565 | |
24566 | uint8_t* plug_start = o; |
24567 | |
24568 | while (method_table(o) != g_gc_pFreeObjectMethodTable) |
24569 | { |
24570 | o += Align (size (o), align_const); |
24571 | if (o >= end) |
24572 | { |
24573 | break; |
24574 | } |
24575 | } |
24576 | |
24577 | uint8_t* plug_end = o; |
24578 | |
24579 | fn (plug_start, |
24580 | plug_end, |
24581 | 0, // Reloc distance == 0 as this is non-compacting |
24582 | profiling_context, |
24583 | false, // Non-compacting |
24584 | true); // BGC |
24585 | } |
24586 | |
24587 | seg = heap_segment_next (seg); |
24588 | } |
24589 | } |
24590 | #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE) |
24591 | |
24592 | void gc_heap::relocate_phase (int condemned_gen_number, |
24593 | uint8_t* first_condemned_address) |
24594 | { |
24595 | ScanContext sc; |
24596 | sc.thread_number = heap_number; |
24597 | sc.promotion = FALSE; |
24598 | sc.concurrent = FALSE; |
24599 | |
24600 | |
24601 | #ifdef TIME_GC |
24602 | unsigned start; |
24603 | unsigned finish; |
24604 | start = GetCycleCount32(); |
24605 | #endif //TIME_GC |
24606 | |
24607 | // %type% category = quote (relocate); |
24608 | dprintf (2,("---- Relocate phase -----" )); |
24609 | |
24610 | #ifdef MULTIPLE_HEAPS |
24611 | //join all threads to make sure they are synchronized |
24612 | dprintf(3, ("Joining after end of plan" )); |
24613 | gc_t_join.join(this, gc_join_begin_relocate_phase); |
24614 | if (gc_t_join.joined()) |
24615 | #endif //MULTIPLE_HEAPS |
24616 | |
24617 | { |
24618 | #ifdef MULTIPLE_HEAPS |
24619 | |
24620 | //join all threads to make sure they are synchronized |
24621 | dprintf(3, ("Restarting for relocation" )); |
24622 | gc_t_join.restart(); |
24623 | #endif //MULTIPLE_HEAPS |
24624 | } |
24625 | |
24626 | dprintf(3,("Relocating roots" )); |
24627 | GCScan::GcScanRoots(GCHeap::Relocate, |
24628 | condemned_gen_number, max_generation, &sc); |
24629 | |
24630 | verify_pins_with_post_plug_info("after reloc stack" ); |
24631 | |
24632 | #ifdef BACKGROUND_GC |
24633 | if (recursive_gc_sync::background_running_p()) |
24634 | { |
24635 | scan_background_roots (GCHeap::Relocate, heap_number, &sc); |
24636 | } |
24637 | #endif //BACKGROUND_GC |
24638 | |
24639 | if (condemned_gen_number != max_generation) |
24640 | { |
24641 | dprintf(3,("Relocating cross generation pointers" )); |
24642 | mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE); |
24643 | verify_pins_with_post_plug_info("after reloc cards" ); |
24644 | } |
24645 | if (condemned_gen_number != max_generation) |
24646 | { |
24647 | dprintf(3,("Relocating cross generation pointers for large objects" )); |
24648 | mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE); |
24649 | } |
24650 | else |
24651 | { |
24652 | #ifdef FEATURE_LOH_COMPACTION |
24653 | if (loh_compacted_p) |
24654 | { |
24655 | assert (settings.condemned_generation == max_generation); |
24656 | relocate_in_loh_compact(); |
24657 | } |
24658 | else |
24659 | #endif //FEATURE_LOH_COMPACTION |
24660 | { |
24661 | relocate_in_large_objects (); |
24662 | } |
24663 | } |
24664 | { |
24665 | dprintf(3,("Relocating survivors" )); |
24666 | relocate_survivors (condemned_gen_number, |
24667 | first_condemned_address); |
24668 | } |
24669 | |
24670 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
24671 | dprintf(3,("Relocating finalization data" )); |
24672 | finalize_queue->RelocateFinalizationData (condemned_gen_number, |
24673 | __this); |
24674 | #endif // FEATURE_PREMORTEM_FINALIZATION |
24675 | |
24676 | |
24677 | // MTHTS |
24678 | { |
24679 | dprintf(3,("Relocating handle table" )); |
24680 | GCScan::GcScanHandles(GCHeap::Relocate, |
24681 | condemned_gen_number, max_generation, &sc); |
24682 | } |
24683 | |
24684 | #ifdef MULTIPLE_HEAPS |
24685 | //join all threads to make sure they are synchronized |
24686 | dprintf(3, ("Joining after end of relocation" )); |
24687 | gc_t_join.join(this, gc_join_relocate_phase_done); |
24688 | |
24689 | #endif //MULTIPLE_HEAPS |
24690 | |
24691 | #ifdef TIME_GC |
24692 | finish = GetCycleCount32(); |
24693 | reloc_time = finish - start; |
24694 | #endif //TIME_GC |
24695 | |
24696 | dprintf(2,( "---- End of Relocate phase ----" )); |
24697 | } |
24698 | |
24699 | // This compares to see if tree is the current pinned plug and returns info |
24700 | // for this pinned plug. Also advances the pinned queue if that's the case. |
24701 | // |
24702 | // We don't change the values of the plug info if tree is not the same as |
24703 | // the current pinned plug - the caller is responsible for setting the right |
24704 | // values to begin with. |
24705 | // |
24706 | // POPO TODO: We are keeping this temporarily as this is also used by realloc |
24707 | // where it passes FALSE to deque_p, change it to use the same optimization |
24708 | // as relocate. Not as essential since realloc is already a slow path. |
24709 | mark* gc_heap::get_next_pinned_entry (uint8_t* tree, |
24710 | BOOL* has_pre_plug_info_p, |
24711 | BOOL* has_post_plug_info_p, |
24712 | BOOL deque_p) |
24713 | { |
24714 | if (!pinned_plug_que_empty_p()) |
24715 | { |
24716 | mark* oldest_entry = oldest_pin(); |
24717 | uint8_t* oldest_plug = pinned_plug (oldest_entry); |
24718 | if (tree == oldest_plug) |
24719 | { |
24720 | *has_pre_plug_info_p = oldest_entry->has_pre_plug_info(); |
24721 | *has_post_plug_info_p = oldest_entry->has_post_plug_info(); |
24722 | |
24723 | if (deque_p) |
24724 | { |
24725 | deque_pinned_plug(); |
24726 | } |
24727 | |
24728 | dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d" , |
24729 | tree, |
24730 | (*has_pre_plug_info_p ? 1 : 0), |
24731 | (*has_post_plug_info_p ? 1 : 0))); |
24732 | |
24733 | return oldest_entry; |
24734 | } |
24735 | } |
24736 | |
24737 | return NULL; |
24738 | } |
24739 | |
24740 | // This also deques the oldest entry and update the oldest plug |
24741 | mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p, |
24742 | BOOL* has_post_plug_info_p) |
24743 | { |
24744 | mark* oldest_entry = oldest_pin(); |
24745 | *has_pre_plug_info_p = oldest_entry->has_pre_plug_info(); |
24746 | *has_post_plug_info_p = oldest_entry->has_post_plug_info(); |
24747 | |
24748 | deque_pinned_plug(); |
24749 | update_oldest_pinned_plug(); |
24750 | return oldest_entry; |
24751 | } |
24752 | |
24753 | inline |
24754 | void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p) |
24755 | { |
24756 | if (copy_cards_p) |
24757 | copy_cards_for_addresses (dest, src, len); |
24758 | else |
24759 | clear_card_for_addresses (dest, dest + len); |
24760 | } |
24761 | |
24762 | // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy |
24763 | // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way |
24764 | // we won't need to individually recover each overwritten part of plugs. |
24765 | inline |
24766 | void gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p) |
24767 | { |
24768 | if (dest != src) |
24769 | { |
24770 | #ifdef BACKGROUND_GC |
24771 | if (current_c_gc_state == c_gc_state_marking) |
24772 | { |
24773 | //TODO: should look to see whether we should consider changing this |
24774 | // to copy a consecutive region of the mark array instead. |
24775 | copy_mark_bits_for_addresses (dest, src, len); |
24776 | } |
24777 | #endif //BACKGROUND_GC |
24778 | //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len)); |
24779 | dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[" , (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len)); |
24780 | memcopy (dest - plug_skew, src - plug_skew, len); |
24781 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
24782 | if (SoftwareWriteWatch::IsEnabledForGCHeap()) |
24783 | { |
24784 | // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC |
24785 | // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the |
24786 | // object at (src + len), so it can be ignored anyway. |
24787 | SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew); |
24788 | } |
24789 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
24790 | copy_cards_range (dest, src, len, copy_cards_p); |
24791 | } |
24792 | } |
24793 | |
24794 | void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args) |
24795 | { |
24796 | args->print(); |
24797 | uint8_t* reloc_plug = plug + args->last_plug_relocation; |
24798 | |
24799 | if (check_last_object_p) |
24800 | { |
24801 | size += sizeof (gap_reloc_pair); |
24802 | mark* entry = args->pinned_plug_entry; |
24803 | |
24804 | if (args->is_shortened) |
24805 | { |
24806 | assert (entry->has_post_plug_info()); |
24807 | entry->swap_post_plug_and_saved(); |
24808 | } |
24809 | else |
24810 | { |
24811 | assert (entry->has_pre_plug_info()); |
24812 | entry->swap_pre_plug_and_saved(); |
24813 | } |
24814 | } |
24815 | |
24816 | int old_brick_entry = brick_table [brick_of (plug)]; |
24817 | |
24818 | assert (node_relocation_distance (plug) == args->last_plug_relocation); |
24819 | |
24820 | #ifdef FEATURE_STRUCTALIGN |
24821 | ptrdiff_t alignpad = node_alignpad(plug); |
24822 | if (alignpad) |
24823 | { |
24824 | make_unused_array (reloc_plug - alignpad, alignpad); |
24825 | if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug)) |
24826 | { |
24827 | // The alignment padding is straddling one or more bricks; |
24828 | // it has to be the last "object" of its first brick. |
24829 | fix_brick_to_highest (reloc_plug - alignpad, reloc_plug); |
24830 | } |
24831 | } |
24832 | #else // FEATURE_STRUCTALIGN |
24833 | size_t unused_arr_size = 0; |
24834 | BOOL already_padded_p = FALSE; |
24835 | #ifdef SHORT_PLUGS |
24836 | if (is_plug_padded (plug)) |
24837 | { |
24838 | already_padded_p = TRUE; |
24839 | clear_plug_padded (plug); |
24840 | unused_arr_size = Align (min_obj_size); |
24841 | } |
24842 | #endif //SHORT_PLUGS |
24843 | if (node_realigned (plug)) |
24844 | { |
24845 | unused_arr_size += switch_alignment_size (already_padded_p); |
24846 | } |
24847 | |
24848 | if (unused_arr_size != 0) |
24849 | { |
24850 | make_unused_array (reloc_plug - unused_arr_size, unused_arr_size); |
24851 | |
24852 | if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug)) |
24853 | { |
24854 | dprintf (3, ("fix B for padding: %Id: %Ix->%Ix" , |
24855 | unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug)); |
24856 | // The alignment padding is straddling one or more bricks; |
24857 | // it has to be the last "object" of its first brick. |
24858 | fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug); |
24859 | } |
24860 | } |
24861 | #endif // FEATURE_STRUCTALIGN |
24862 | |
24863 | #ifdef SHORT_PLUGS |
24864 | if (is_plug_padded (plug)) |
24865 | { |
24866 | make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size)); |
24867 | |
24868 | if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug)) |
24869 | { |
24870 | // The alignment padding is straddling one or more bricks; |
24871 | // it has to be the last "object" of its first brick. |
24872 | fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug); |
24873 | } |
24874 | } |
24875 | #endif //SHORT_PLUGS |
24876 | |
24877 | gcmemcopy (reloc_plug, plug, size, args->copy_cards_p); |
24878 | |
24879 | if (args->check_gennum_p) |
24880 | { |
24881 | int src_gennum = args->src_gennum; |
24882 | if (src_gennum == -1) |
24883 | { |
24884 | src_gennum = object_gennum (plug); |
24885 | } |
24886 | |
24887 | int dest_gennum = object_gennum_plan (reloc_plug); |
24888 | |
24889 | if (src_gennum < dest_gennum) |
24890 | { |
24891 | generation_allocation_size (generation_of (dest_gennum)) += size; |
24892 | } |
24893 | } |
24894 | |
24895 | size_t current_reloc_brick = args->current_compacted_brick; |
24896 | |
24897 | if (brick_of (reloc_plug) != current_reloc_brick) |
24898 | { |
24899 | dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix" , |
24900 | current_reloc_brick, brick_of (reloc_plug))); |
24901 | |
24902 | if (args->before_last_plug) |
24903 | { |
24904 | dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)" , |
24905 | current_reloc_brick, |
24906 | args->before_last_plug, |
24907 | (args->before_last_plug - brick_address (current_reloc_brick)))); |
24908 | |
24909 | { |
24910 | set_brick (current_reloc_brick, |
24911 | args->before_last_plug - brick_address (current_reloc_brick)); |
24912 | } |
24913 | } |
24914 | current_reloc_brick = brick_of (reloc_plug); |
24915 | } |
24916 | size_t end_brick = brick_of (reloc_plug + size-1); |
24917 | if (end_brick != current_reloc_brick) |
24918 | { |
24919 | // The plug is straddling one or more bricks |
24920 | // It has to be the last plug of its first brick |
24921 | dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)" , |
24922 | current_reloc_brick, (size_t)reloc_plug, |
24923 | (reloc_plug - brick_address (current_reloc_brick)))); |
24924 | |
24925 | { |
24926 | set_brick (current_reloc_brick, |
24927 | reloc_plug - brick_address (current_reloc_brick)); |
24928 | } |
24929 | // update all intervening brick |
24930 | size_t brick = current_reloc_brick + 1; |
24931 | dprintf (3,("setting intervening bricks %Ix->%Ix to -1" , |
24932 | brick, (end_brick - 1))); |
24933 | while (brick < end_brick) |
24934 | { |
24935 | set_brick (brick, -1); |
24936 | brick++; |
24937 | } |
24938 | // code last brick offset as a plug address |
24939 | args->before_last_plug = brick_address (end_brick) -1; |
24940 | current_reloc_brick = end_brick; |
24941 | dprintf (3, ("setting before last to %Ix, last brick to %Ix" , |
24942 | args->before_last_plug, current_reloc_brick)); |
24943 | } |
24944 | else |
24945 | { |
24946 | dprintf (3, ("still in the same brick: %Ix" , end_brick)); |
24947 | args->before_last_plug = reloc_plug; |
24948 | } |
24949 | args->current_compacted_brick = current_reloc_brick; |
24950 | |
24951 | if (check_last_object_p) |
24952 | { |
24953 | mark* entry = args->pinned_plug_entry; |
24954 | |
24955 | if (args->is_shortened) |
24956 | { |
24957 | entry->swap_post_plug_and_saved(); |
24958 | } |
24959 | else |
24960 | { |
24961 | entry->swap_pre_plug_and_saved(); |
24962 | } |
24963 | } |
24964 | } |
24965 | |
24966 | void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args) |
24967 | { |
24968 | assert (tree != NULL); |
24969 | int left_node = node_left_child (tree); |
24970 | int right_node = node_right_child (tree); |
24971 | ptrdiff_t relocation = node_relocation_distance (tree); |
24972 | |
24973 | args->print(); |
24974 | |
24975 | if (left_node) |
24976 | { |
24977 | dprintf (3, ("B: L: %d->%Ix" , left_node, (tree + left_node))); |
24978 | compact_in_brick ((tree + left_node), args); |
24979 | } |
24980 | |
24981 | uint8_t* plug = tree; |
24982 | BOOL has_pre_plug_info_p = FALSE; |
24983 | BOOL has_post_plug_info_p = FALSE; |
24984 | |
24985 | if (tree == oldest_pinned_plug) |
24986 | { |
24987 | args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p, |
24988 | &has_post_plug_info_p); |
24989 | assert (tree == pinned_plug (args->pinned_plug_entry)); |
24990 | } |
24991 | |
24992 | if (args->last_plug != 0) |
24993 | { |
24994 | size_t gap_size = node_gap_size (tree); |
24995 | uint8_t* gap = (plug - gap_size); |
24996 | uint8_t* last_plug_end = gap; |
24997 | size_t last_plug_size = (last_plug_end - args->last_plug); |
24998 | dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix" , |
24999 | tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size)); |
25000 | |
25001 | BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p); |
25002 | if (!check_last_object_p) |
25003 | { |
25004 | assert (last_plug_size >= Align (min_obj_size)); |
25005 | } |
25006 | |
25007 | compact_plug (args->last_plug, last_plug_size, check_last_object_p, args); |
25008 | } |
25009 | else |
25010 | { |
25011 | assert (!has_pre_plug_info_p); |
25012 | } |
25013 | |
25014 | dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix" , plug, relocation)); |
25015 | args->last_plug = plug; |
25016 | args->last_plug_relocation = relocation; |
25017 | args->is_shortened = has_post_plug_info_p; |
25018 | |
25019 | if (right_node) |
25020 | { |
25021 | dprintf (3, ("B: R: %d->%Ix" , right_node, (tree + right_node))); |
25022 | compact_in_brick ((tree + right_node), args); |
25023 | } |
25024 | } |
25025 | |
25026 | void gc_heap::recover_saved_pinned_info() |
25027 | { |
25028 | reset_pinned_queue_bos(); |
25029 | |
25030 | while (!(pinned_plug_que_empty_p())) |
25031 | { |
25032 | mark* oldest_entry = oldest_pin(); |
25033 | oldest_entry->recover_plug_info(); |
25034 | #ifdef GC_CONFIG_DRIVEN |
25035 | if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info()) |
25036 | record_interesting_data_point (idp_pre_and_post_pin); |
25037 | else if (oldest_entry->has_pre_plug_info()) |
25038 | record_interesting_data_point (idp_pre_pin); |
25039 | else if (oldest_entry->has_post_plug_info()) |
25040 | record_interesting_data_point (idp_post_pin); |
25041 | #endif //GC_CONFIG_DRIVEN |
25042 | |
25043 | deque_pinned_plug(); |
25044 | } |
25045 | } |
25046 | |
25047 | void gc_heap::compact_phase (int condemned_gen_number, |
25048 | uint8_t* first_condemned_address, |
25049 | BOOL clear_cards) |
25050 | { |
25051 | // %type% category = quote (compact); |
25052 | #ifdef TIME_GC |
25053 | unsigned start; |
25054 | unsigned finish; |
25055 | start = GetCycleCount32(); |
25056 | #endif //TIME_GC |
25057 | generation* condemned_gen = generation_of (condemned_gen_number); |
25058 | uint8_t* start_address = first_condemned_address; |
25059 | size_t current_brick = brick_of (start_address); |
25060 | heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen)); |
25061 | |
25062 | PREFIX_ASSUME(current_heap_segment != NULL); |
25063 | |
25064 | reset_pinned_queue_bos(); |
25065 | update_oldest_pinned_plug(); |
25066 | |
25067 | BOOL reused_seg = expand_reused_seg_p(); |
25068 | if (reused_seg) |
25069 | { |
25070 | for (int i = 1; i <= max_generation; i++) |
25071 | { |
25072 | generation_allocation_size (generation_of (i)) = 0; |
25073 | } |
25074 | } |
25075 | |
25076 | uint8_t* end_address = heap_segment_allocated (current_heap_segment); |
25077 | |
25078 | size_t end_brick = brick_of (end_address-1); |
25079 | compact_args args; |
25080 | args.last_plug = 0; |
25081 | args.before_last_plug = 0; |
25082 | args.current_compacted_brick = ~((size_t)1); |
25083 | args.is_shortened = FALSE; |
25084 | args.pinned_plug_entry = 0; |
25085 | args.copy_cards_p = (condemned_gen_number >= 1) || !clear_cards; |
25086 | args.check_gennum_p = reused_seg; |
25087 | if (args.check_gennum_p) |
25088 | { |
25089 | args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2); |
25090 | } |
25091 | |
25092 | dprintf (2,("---- Compact Phase: %Ix(%Ix)----" , |
25093 | first_condemned_address, brick_of (first_condemned_address))); |
25094 | |
25095 | #ifdef MULTIPLE_HEAPS |
25096 | //restart |
25097 | if (gc_t_join.joined()) |
25098 | { |
25099 | #endif //MULTIPLE_HEAPS |
25100 | |
25101 | #ifdef MULTIPLE_HEAPS |
25102 | dprintf(3, ("Restarting for compaction" )); |
25103 | gc_t_join.restart(); |
25104 | } |
25105 | #endif //MULTIPLE_HEAPS |
25106 | |
25107 | reset_pinned_queue_bos(); |
25108 | |
25109 | #ifdef FEATURE_LOH_COMPACTION |
25110 | if (loh_compacted_p) |
25111 | { |
25112 | compact_loh(); |
25113 | } |
25114 | #endif //FEATURE_LOH_COMPACTION |
25115 | |
25116 | if ((start_address < end_address) || |
25117 | (condemned_gen_number == max_generation)) |
25118 | { |
25119 | while (1) |
25120 | { |
25121 | if (current_brick > end_brick) |
25122 | { |
25123 | if (args.last_plug != 0) |
25124 | { |
25125 | dprintf (3, ("compacting last plug: %Ix" , args.last_plug)) |
25126 | compact_plug (args.last_plug, |
25127 | (heap_segment_allocated (current_heap_segment) - args.last_plug), |
25128 | args.is_shortened, |
25129 | &args); |
25130 | } |
25131 | |
25132 | if (heap_segment_next_rw (current_heap_segment)) |
25133 | { |
25134 | current_heap_segment = heap_segment_next_rw (current_heap_segment); |
25135 | current_brick = brick_of (heap_segment_mem (current_heap_segment)); |
25136 | end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1); |
25137 | args.last_plug = 0; |
25138 | if (args.check_gennum_p) |
25139 | { |
25140 | args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2); |
25141 | } |
25142 | continue; |
25143 | } |
25144 | else |
25145 | { |
25146 | if (args.before_last_plug !=0) |
25147 | { |
25148 | dprintf (3, ("Fixing last brick %Ix to point to plug %Ix" , |
25149 | args.current_compacted_brick, (size_t)args.before_last_plug)); |
25150 | assert (args.current_compacted_brick != ~1u); |
25151 | set_brick (args.current_compacted_brick, |
25152 | args.before_last_plug - brick_address (args.current_compacted_brick)); |
25153 | } |
25154 | break; |
25155 | } |
25156 | } |
25157 | { |
25158 | int brick_entry = brick_table [ current_brick ]; |
25159 | dprintf (3, ("B: %Ix(%Ix)->%Ix" , |
25160 | current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1))); |
25161 | |
25162 | if (brick_entry >= 0) |
25163 | { |
25164 | compact_in_brick ((brick_address (current_brick) + brick_entry -1), |
25165 | &args); |
25166 | |
25167 | } |
25168 | } |
25169 | current_brick++; |
25170 | } |
25171 | } |
25172 | |
25173 | recover_saved_pinned_info(); |
25174 | |
25175 | #ifdef TIME_GC |
25176 | finish = GetCycleCount32(); |
25177 | compact_time = finish - start; |
25178 | #endif //TIME_GC |
25179 | |
25180 | concurrent_print_time_delta ("compact end" ); |
25181 | |
25182 | dprintf(2,("---- End of Compact phase ----" )); |
25183 | } |
25184 | |
25185 | #ifdef MULTIPLE_HEAPS |
25186 | |
25187 | #ifdef _MSC_VER |
25188 | #pragma warning(push) |
25189 | #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return |
25190 | #endif //_MSC_VER |
25191 | void gc_heap::gc_thread_stub (void* arg) |
25192 | { |
25193 | gc_heap* heap = (gc_heap*)arg; |
25194 | if (!gc_thread_no_affinitize_p) |
25195 | { |
25196 | GCThreadAffinity affinity; |
25197 | affinity.Group = GCThreadAffinity::None; |
25198 | affinity.Processor = GCThreadAffinity::None; |
25199 | |
25200 | // We are about to set affinity for GC threads. It is a good place to set up NUMA and |
25201 | // CPU groups because the process mask, processor number, and group number are all |
25202 | // readily available. |
25203 | if (GCToOSInterface::CanEnableGCCPUGroups()) |
25204 | set_thread_group_affinity_for_heap(heap->heap_number, &affinity); |
25205 | else |
25206 | set_thread_affinity_mask_for_heap(heap->heap_number, &affinity); |
25207 | |
25208 | if (!GCToOSInterface::SetThreadAffinity(&affinity)) |
25209 | { |
25210 | dprintf(1, ("Failed to set thread affinity for server GC thread" )); |
25211 | } |
25212 | } |
25213 | |
25214 | // server GC threads run at a higher priority than normal. |
25215 | GCToOSInterface::BoostThreadPriority(); |
25216 | _alloca (256*heap->heap_number); |
25217 | heap->gc_thread_function(); |
25218 | } |
25219 | #ifdef _MSC_VER |
25220 | #pragma warning(pop) |
25221 | #endif //_MSC_VER |
25222 | |
25223 | #endif //MULTIPLE_HEAPS |
25224 | |
25225 | #ifdef BACKGROUND_GC |
25226 | |
25227 | #ifdef _MSC_VER |
25228 | #pragma warning(push) |
25229 | #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return |
25230 | #endif //_MSC_VER |
25231 | void gc_heap::bgc_thread_stub (void* arg) |
25232 | { |
25233 | gc_heap* heap = (gc_heap*)arg; |
25234 | heap->bgc_thread = GCToEEInterface::GetThread(); |
25235 | assert(heap->bgc_thread != nullptr); |
25236 | heap->bgc_thread_function(); |
25237 | } |
25238 | #ifdef _MSC_VER |
25239 | #pragma warning(pop) |
25240 | #endif //_MSC_VER |
25241 | |
25242 | #endif //BACKGROUND_GC |
25243 | |
25244 | /*------------------ Background GC ----------------------------*/ |
25245 | |
25246 | #ifdef BACKGROUND_GC |
25247 | |
25248 | void gc_heap::background_drain_mark_list (int thread) |
25249 | { |
25250 | UNREFERENCED_PARAMETER(thread); |
25251 | |
25252 | size_t saved_c_mark_list_index = c_mark_list_index; |
25253 | |
25254 | if (saved_c_mark_list_index) |
25255 | { |
25256 | concurrent_print_time_delta ("SML" ); |
25257 | } |
25258 | while (c_mark_list_index != 0) |
25259 | { |
25260 | size_t current_index = c_mark_list_index - 1; |
25261 | uint8_t* o = c_mark_list [current_index]; |
25262 | background_mark_object (o THREAD_NUMBER_ARG); |
25263 | c_mark_list_index--; |
25264 | } |
25265 | if (saved_c_mark_list_index) |
25266 | { |
25267 | |
25268 | concurrent_print_time_delta ("EML" ); |
25269 | } |
25270 | |
25271 | fire_drain_mark_list_event (saved_c_mark_list_index); |
25272 | } |
25273 | |
25274 | |
25275 | // The background GC version of scan_dependent_handles (see that method for a more in-depth comment). |
25276 | #ifdef MULTIPLE_HEAPS |
25277 | // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning |
25278 | // them. So we can use the same static variables. |
25279 | void gc_heap::background_scan_dependent_handles (ScanContext *sc) |
25280 | { |
25281 | // Whenever we call this method there may have been preceding object promotions. So set |
25282 | // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set |
25283 | // based on the how the scanning proceeded). |
25284 | s_fUnscannedPromotions = TRUE; |
25285 | |
25286 | // We don't know how many times we need to loop yet. In particular we can't base the loop condition on |
25287 | // the state of this thread's portion of the dependent handle table. That's because promotions on other |
25288 | // threads could cause handle promotions to become necessary here. Even if there are definitely no more |
25289 | // promotions possible in this thread's handles, we still have to stay in lock-step with those worker |
25290 | // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times |
25291 | // as all the others or they'll get out of step). |
25292 | while (true) |
25293 | { |
25294 | // The various worker threads are all currently racing in this code. We need to work out if at least |
25295 | // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the |
25296 | // dependent handle table when both of the following conditions apply: |
25297 | // 1) At least one (arbitrary) object might have been promoted since the last scan (because if this |
25298 | // object happens to correspond to a primary in one of our handles we might potentially have to |
25299 | // promote the associated secondary). |
25300 | // 2) The table for this thread has at least one handle with a secondary that isn't promoted yet. |
25301 | // |
25302 | // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first |
25303 | // iteration of this loop (see comment above) and in subsequent cycles each thread updates this |
25304 | // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary |
25305 | // being promoted. This value is cleared back to zero in a synchronized fashion in the join that |
25306 | // follows below. Note that we can't read this outside of the join since on any iteration apart from |
25307 | // the first threads will be racing between reading this value and completing their previous |
25308 | // iteration's table scan. |
25309 | // |
25310 | // The second condition is tracked by the dependent handle code itself on a per worker thread basis |
25311 | // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to |
25312 | // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is |
25313 | // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until |
25314 | // we're safely joined. |
25315 | if (GCScan::GcDhUnpromotedHandlesExist(sc)) |
25316 | s_fUnpromotedHandles = TRUE; |
25317 | |
25318 | // Synchronize all the threads so we can read our state variables safely. The following shared |
25319 | // variable (indicating whether we should scan the tables or terminate the loop) will be set by a |
25320 | // single thread inside the join. |
25321 | bgc_t_join.join(this, gc_join_scan_dependent_handles); |
25322 | if (bgc_t_join.joined()) |
25323 | { |
25324 | // We're synchronized so it's safe to read our shared state variables. We update another shared |
25325 | // variable to indicate to all threads whether we'll be scanning for another cycle or terminating |
25326 | // the loop. We scan if there has been at least one object promotion since last time and at least |
25327 | // one thread has a dependent handle table with a potential handle promotion possible. |
25328 | s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles; |
25329 | |
25330 | // Reset our shared state variables (ready to be set again on this scan or with a good initial |
25331 | // value for the next call if we're terminating the loop). |
25332 | s_fUnscannedPromotions = FALSE; |
25333 | s_fUnpromotedHandles = FALSE; |
25334 | |
25335 | if (!s_fScanRequired) |
25336 | { |
25337 | uint8_t* all_heaps_max = 0; |
25338 | uint8_t* all_heaps_min = MAX_PTR; |
25339 | int i; |
25340 | for (i = 0; i < n_heaps; i++) |
25341 | { |
25342 | if (all_heaps_max < g_heaps[i]->background_max_overflow_address) |
25343 | all_heaps_max = g_heaps[i]->background_max_overflow_address; |
25344 | if (all_heaps_min > g_heaps[i]->background_min_overflow_address) |
25345 | all_heaps_min = g_heaps[i]->background_min_overflow_address; |
25346 | } |
25347 | for (i = 0; i < n_heaps; i++) |
25348 | { |
25349 | g_heaps[i]->background_max_overflow_address = all_heaps_max; |
25350 | g_heaps[i]->background_min_overflow_address = all_heaps_min; |
25351 | } |
25352 | } |
25353 | |
25354 | // Restart all the workers. |
25355 | dprintf(2, ("Starting all gc thread mark stack overflow processing" )); |
25356 | bgc_t_join.restart(); |
25357 | } |
25358 | |
25359 | // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions |
25360 | // being visible. If there really was an overflow (process_mark_overflow returns true) then set the |
25361 | // global flag indicating that at least one object promotion may have occurred (the usual comment |
25362 | // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and |
25363 | // exit the method since we unconditionally set this variable on method entry anyway). |
25364 | if (background_process_mark_overflow (sc->concurrent)) |
25365 | s_fUnscannedPromotions = TRUE; |
25366 | |
25367 | // If we decided that no scan was required we can terminate the loop now. |
25368 | if (!s_fScanRequired) |
25369 | break; |
25370 | |
25371 | // Otherwise we must join with the other workers to ensure that all mark stack overflows have been |
25372 | // processed before we start scanning dependent handle tables (if overflows remain while we scan we |
25373 | // could miss noting the promotion of some primary objects). |
25374 | bgc_t_join.join(this, gc_join_rescan_dependent_handles); |
25375 | if (bgc_t_join.joined()) |
25376 | { |
25377 | // Restart all the workers. |
25378 | dprintf(3, ("Starting all gc thread for dependent handle promotion" )); |
25379 | bgc_t_join.restart(); |
25380 | } |
25381 | |
25382 | // If the portion of the dependent handle table managed by this worker has handles that could still be |
25383 | // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it |
25384 | // could require a rescan of handles on this or other workers. |
25385 | if (GCScan::GcDhUnpromotedHandlesExist(sc)) |
25386 | if (GCScan::GcDhReScan(sc)) |
25387 | s_fUnscannedPromotions = TRUE; |
25388 | } |
25389 | } |
25390 | #else |
25391 | void gc_heap::background_scan_dependent_handles (ScanContext *sc) |
25392 | { |
25393 | // Whenever we call this method there may have been preceding object promotions. So set |
25394 | // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set |
25395 | // based on the how the scanning proceeded). |
25396 | bool fUnscannedPromotions = true; |
25397 | |
25398 | // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a |
25399 | // scan without performing any new promotions. |
25400 | while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions) |
25401 | { |
25402 | // On each iteration of the loop start with the assumption that no further objects have been promoted. |
25403 | fUnscannedPromotions = false; |
25404 | |
25405 | // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions |
25406 | // being visible. If there was an overflow (background_process_mark_overflow returned true) then |
25407 | // additional objects now appear to be promoted and we should set the flag. |
25408 | if (background_process_mark_overflow (sc->concurrent)) |
25409 | fUnscannedPromotions = true; |
25410 | |
25411 | // Perform the scan and set the flag if any promotions resulted. |
25412 | if (GCScan::GcDhReScan (sc)) |
25413 | fUnscannedPromotions = true; |
25414 | } |
25415 | |
25416 | // Perform a last processing of any overflowed mark stack. |
25417 | background_process_mark_overflow (sc->concurrent); |
25418 | } |
25419 | #endif //MULTIPLE_HEAPS |
25420 | |
25421 | void gc_heap::recover_bgc_settings() |
25422 | { |
25423 | if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p()) |
25424 | { |
25425 | dprintf (2, ("restoring bgc settings" )); |
25426 | settings = saved_bgc_settings; |
25427 | GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation; |
25428 | } |
25429 | } |
25430 | |
25431 | void gc_heap::allow_fgc() |
25432 | { |
25433 | assert (bgc_thread == GCToEEInterface::GetThread()); |
25434 | bool bToggleGC = false; |
25435 | |
25436 | if (g_fSuspensionPending > 0) |
25437 | { |
25438 | bToggleGC = GCToEEInterface::EnablePreemptiveGC(); |
25439 | if (bToggleGC) |
25440 | { |
25441 | GCToEEInterface::DisablePreemptiveGC(); |
25442 | } |
25443 | } |
25444 | } |
25445 | |
25446 | BOOL gc_heap::should_commit_mark_array() |
25447 | { |
25448 | return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized)); |
25449 | } |
25450 | |
25451 | void gc_heap::clear_commit_flag() |
25452 | { |
25453 | generation* gen = generation_of (max_generation); |
25454 | heap_segment* seg = heap_segment_in_range (generation_start_segment (gen)); |
25455 | while (1) |
25456 | { |
25457 | if (seg == 0) |
25458 | { |
25459 | if (gen != large_object_generation) |
25460 | { |
25461 | gen = large_object_generation; |
25462 | seg = heap_segment_in_range (generation_start_segment (gen)); |
25463 | } |
25464 | else |
25465 | { |
25466 | break; |
25467 | } |
25468 | } |
25469 | |
25470 | if (seg->flags & heap_segment_flags_ma_committed) |
25471 | { |
25472 | seg->flags &= ~heap_segment_flags_ma_committed; |
25473 | } |
25474 | |
25475 | if (seg->flags & heap_segment_flags_ma_pcommitted) |
25476 | { |
25477 | seg->flags &= ~heap_segment_flags_ma_pcommitted; |
25478 | } |
25479 | |
25480 | seg = heap_segment_next (seg); |
25481 | } |
25482 | } |
25483 | |
25484 | void gc_heap::clear_commit_flag_global() |
25485 | { |
25486 | #ifdef MULTIPLE_HEAPS |
25487 | for (int i = 0; i < n_heaps; i++) |
25488 | { |
25489 | g_heaps[i]->clear_commit_flag(); |
25490 | } |
25491 | #else |
25492 | clear_commit_flag(); |
25493 | #endif //MULTIPLE_HEAPS |
25494 | } |
25495 | |
25496 | void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr) |
25497 | { |
25498 | #ifdef _DEBUG |
25499 | size_t markw = mark_word_of (begin); |
25500 | size_t markw_end = mark_word_of (end); |
25501 | |
25502 | while (markw < markw_end) |
25503 | { |
25504 | if (mark_array_addr[markw]) |
25505 | { |
25506 | dprintf (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
25507 | markw, mark_array_addr[markw], mark_word_address (markw))); |
25508 | FATAL_GC_ERROR(); |
25509 | } |
25510 | markw++; |
25511 | } |
25512 | #else // _DEBUG |
25513 | UNREFERENCED_PARAMETER(begin); |
25514 | UNREFERENCED_PARAMETER(end); |
25515 | UNREFERENCED_PARAMETER(mark_array_addr); |
25516 | #endif //_DEBUG |
25517 | } |
25518 | |
25519 | void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr) |
25520 | { |
25521 | verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr); |
25522 | } |
25523 | |
25524 | BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, |
25525 | heap_segment* seg, |
25526 | uint32_t* new_card_table, |
25527 | uint8_t* new_lowest_address) |
25528 | { |
25529 | UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced |
25530 | |
25531 | uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg); |
25532 | uint8_t* end = heap_segment_reserved (seg); |
25533 | |
25534 | uint8_t* lowest = hp->background_saved_lowest_address; |
25535 | uint8_t* highest = hp->background_saved_highest_address; |
25536 | |
25537 | uint8_t* commit_start = NULL; |
25538 | uint8_t* commit_end = NULL; |
25539 | size_t commit_flag = 0; |
25540 | |
25541 | if ((highest >= start) && |
25542 | (lowest <= end)) |
25543 | { |
25544 | if ((start >= lowest) && (end <= highest)) |
25545 | { |
25546 | dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix" , |
25547 | start, end, lowest, highest)); |
25548 | commit_flag = heap_segment_flags_ma_committed; |
25549 | } |
25550 | else |
25551 | { |
25552 | dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix" , |
25553 | start, end, lowest, highest)); |
25554 | commit_flag = heap_segment_flags_ma_pcommitted; |
25555 | } |
25556 | |
25557 | commit_start = max (lowest, start); |
25558 | commit_end = min (highest, end); |
25559 | |
25560 | if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array)) |
25561 | { |
25562 | return FALSE; |
25563 | } |
25564 | |
25565 | if (new_card_table == 0) |
25566 | { |
25567 | new_card_table = g_gc_card_table; |
25568 | } |
25569 | |
25570 | if (hp->card_table != new_card_table) |
25571 | { |
25572 | if (new_lowest_address == 0) |
25573 | { |
25574 | new_lowest_address = g_gc_lowest_address; |
25575 | } |
25576 | |
25577 | uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))]; |
25578 | uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address)); |
25579 | |
25580 | dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix" , |
25581 | hp->card_table, new_card_table, |
25582 | hp->mark_array, ma)); |
25583 | |
25584 | if (!commit_mark_array_by_range (commit_start, commit_end, ma)) |
25585 | { |
25586 | return FALSE; |
25587 | } |
25588 | } |
25589 | |
25590 | seg->flags |= commit_flag; |
25591 | } |
25592 | |
25593 | return TRUE; |
25594 | } |
25595 | |
25596 | BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr) |
25597 | { |
25598 | size_t beg_word = mark_word_of (begin); |
25599 | size_t end_word = mark_word_of (align_on_mark_word (end)); |
25600 | uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]); |
25601 | uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]); |
25602 | size_t size = (size_t)(commit_end - commit_start); |
25603 | |
25604 | #ifdef SIMPLE_DPRINTF |
25605 | dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)" , |
25606 | begin, end, |
25607 | beg_word, end_word, |
25608 | (end_word - beg_word) * sizeof (uint32_t), |
25609 | &mark_array_addr[beg_word], |
25610 | &mark_array_addr[end_word], |
25611 | (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]), |
25612 | commit_start, commit_end, |
25613 | size)); |
25614 | #endif //SIMPLE_DPRINTF |
25615 | |
25616 | if (GCToOSInterface::VirtualCommit (commit_start, size)) |
25617 | { |
25618 | // We can only verify the mark array is cleared from begin to end, the first and the last |
25619 | // page aren't necessarily all cleared 'cause they could be used by other segments or |
25620 | // card bundle. |
25621 | verify_mark_array_cleared (begin, end, mark_array_addr); |
25622 | return TRUE; |
25623 | } |
25624 | else |
25625 | { |
25626 | dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes" , (end_word - beg_word) * sizeof (uint32_t))); |
25627 | return FALSE; |
25628 | } |
25629 | } |
25630 | |
25631 | BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr) |
25632 | { |
25633 | uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg); |
25634 | uint8_t* end = heap_segment_reserved (seg); |
25635 | |
25636 | #ifdef MULTIPLE_HEAPS |
25637 | uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address; |
25638 | uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address; |
25639 | #else |
25640 | uint8_t* lowest = background_saved_lowest_address; |
25641 | uint8_t* highest = background_saved_highest_address; |
25642 | #endif //MULTIPLE_HEAPS |
25643 | |
25644 | if ((highest >= start) && |
25645 | (lowest <= end)) |
25646 | { |
25647 | start = max (lowest, start); |
25648 | end = min (highest, end); |
25649 | if (!commit_mark_array_by_range (start, end, new_mark_array_addr)) |
25650 | { |
25651 | return FALSE; |
25652 | } |
25653 | } |
25654 | |
25655 | return TRUE; |
25656 | } |
25657 | |
25658 | BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr) |
25659 | { |
25660 | dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix" , |
25661 | seg, |
25662 | heap_segment_reserved (seg), |
25663 | mark_array_addr)); |
25664 | uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg); |
25665 | |
25666 | return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr); |
25667 | } |
25668 | |
25669 | BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr) |
25670 | { |
25671 | UNREFERENCED_PARAMETER(mark_array_addr); |
25672 | |
25673 | dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix" , |
25674 | lowest_address, highest_address, mark_array)); |
25675 | |
25676 | generation* gen = generation_of (max_generation); |
25677 | heap_segment* seg = heap_segment_in_range (generation_start_segment (gen)); |
25678 | while (1) |
25679 | { |
25680 | if (seg == 0) |
25681 | { |
25682 | if (gen != large_object_generation) |
25683 | { |
25684 | gen = large_object_generation; |
25685 | seg = heap_segment_in_range (generation_start_segment (gen)); |
25686 | } |
25687 | else |
25688 | { |
25689 | break; |
25690 | } |
25691 | } |
25692 | |
25693 | dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id" , seg, seg->flags)); |
25694 | |
25695 | if (!(seg->flags & heap_segment_flags_ma_committed)) |
25696 | { |
25697 | // For ro segments they could always be only partially in range so we'd |
25698 | // be calling this at the beginning of every BGC. We are not making this |
25699 | // more efficient right now - ro segments are currently only used by redhawk. |
25700 | if (heap_segment_read_only_p (seg)) |
25701 | { |
25702 | if ((heap_segment_mem (seg) >= lowest_address) && |
25703 | (heap_segment_reserved (seg) <= highest_address)) |
25704 | { |
25705 | if (commit_mark_array_by_seg (seg, mark_array)) |
25706 | { |
25707 | seg->flags |= heap_segment_flags_ma_committed; |
25708 | } |
25709 | else |
25710 | { |
25711 | return FALSE; |
25712 | } |
25713 | } |
25714 | else |
25715 | { |
25716 | uint8_t* start = max (lowest_address, heap_segment_mem (seg)); |
25717 | uint8_t* end = min (highest_address, heap_segment_reserved (seg)); |
25718 | if (commit_mark_array_by_range (start, end, mark_array)) |
25719 | { |
25720 | seg->flags |= heap_segment_flags_ma_pcommitted; |
25721 | } |
25722 | else |
25723 | { |
25724 | return FALSE; |
25725 | } |
25726 | } |
25727 | } |
25728 | else |
25729 | { |
25730 | // For normal segments they are by design completely in range so just |
25731 | // commit the whole mark array for each seg. |
25732 | if (commit_mark_array_by_seg (seg, mark_array)) |
25733 | { |
25734 | if (seg->flags & heap_segment_flags_ma_pcommitted) |
25735 | { |
25736 | seg->flags &= ~heap_segment_flags_ma_pcommitted; |
25737 | } |
25738 | seg->flags |= heap_segment_flags_ma_committed; |
25739 | } |
25740 | else |
25741 | { |
25742 | return FALSE; |
25743 | } |
25744 | } |
25745 | } |
25746 | |
25747 | seg = heap_segment_next (seg); |
25748 | } |
25749 | |
25750 | return TRUE; |
25751 | } |
25752 | |
25753 | // This function doesn't check the commit flag since it's for a new array - |
25754 | // the mark_array flag for these segments will remain the same. |
25755 | BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr) |
25756 | { |
25757 | dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix" , new_mark_array_addr)); |
25758 | generation* gen = generation_of (max_generation); |
25759 | heap_segment* seg = heap_segment_in_range (generation_start_segment (gen)); |
25760 | while (1) |
25761 | { |
25762 | if (seg == 0) |
25763 | { |
25764 | if (gen != large_object_generation) |
25765 | { |
25766 | gen = large_object_generation; |
25767 | seg = heap_segment_in_range (generation_start_segment (gen)); |
25768 | } |
25769 | else |
25770 | { |
25771 | break; |
25772 | } |
25773 | } |
25774 | |
25775 | if (!commit_mark_array_with_check (seg, new_mark_array_addr)) |
25776 | { |
25777 | return FALSE; |
25778 | } |
25779 | |
25780 | seg = heap_segment_next (seg); |
25781 | } |
25782 | |
25783 | #ifdef MULTIPLE_HEAPS |
25784 | if (new_heap_segment) |
25785 | { |
25786 | if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr)) |
25787 | { |
25788 | return FALSE; |
25789 | } |
25790 | } |
25791 | #endif //MULTIPLE_HEAPS |
25792 | |
25793 | return TRUE; |
25794 | } |
25795 | |
25796 | BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array) |
25797 | { |
25798 | #ifdef MULTIPLE_HEAPS |
25799 | for (int i = 0; i < n_heaps; i++) |
25800 | { |
25801 | if (!g_heaps[i]->commit_new_mark_array (new_mark_array)) |
25802 | { |
25803 | return FALSE; |
25804 | } |
25805 | } |
25806 | #else |
25807 | if (!commit_new_mark_array (new_mark_array)) |
25808 | { |
25809 | return FALSE; |
25810 | } |
25811 | #endif //MULTIPLE_HEAPS |
25812 | |
25813 | return TRUE; |
25814 | } |
25815 | |
25816 | void gc_heap::decommit_mark_array_by_seg (heap_segment* seg) |
25817 | { |
25818 | // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have |
25819 | // been set to NULL. |
25820 | if (mark_array == NULL) |
25821 | { |
25822 | return; |
25823 | } |
25824 | |
25825 | dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix" , seg, seg->flags, mark_array)); |
25826 | |
25827 | size_t flags = seg->flags; |
25828 | |
25829 | if ((flags & heap_segment_flags_ma_committed) || |
25830 | (flags & heap_segment_flags_ma_pcommitted)) |
25831 | { |
25832 | uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg); |
25833 | uint8_t* end = heap_segment_reserved (seg); |
25834 | |
25835 | if (flags & heap_segment_flags_ma_pcommitted) |
25836 | { |
25837 | start = max (lowest_address, start); |
25838 | end = min (highest_address, end); |
25839 | } |
25840 | |
25841 | size_t beg_word = mark_word_of (start); |
25842 | size_t end_word = mark_word_of (align_on_mark_word (end)); |
25843 | uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]); |
25844 | uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]); |
25845 | size_t size = (size_t)(decommit_end - decommit_start); |
25846 | |
25847 | #ifdef SIMPLE_DPRINTF |
25848 | dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)" , |
25849 | seg, |
25850 | beg_word, end_word, |
25851 | (end_word - beg_word) * sizeof (uint32_t), |
25852 | &mark_array[beg_word], |
25853 | &mark_array[end_word], |
25854 | (size_t)(&mark_array[end_word] - &mark_array[beg_word]), |
25855 | decommit_start, decommit_end, |
25856 | size)); |
25857 | #endif //SIMPLE_DPRINTF |
25858 | |
25859 | if (decommit_start < decommit_end) |
25860 | { |
25861 | if (!GCToOSInterface::VirtualDecommit (decommit_start, size)) |
25862 | { |
25863 | dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualDecommit on %Ix for %Id bytes failed" , |
25864 | decommit_start, size)); |
25865 | assert (!"decommit failed" ); |
25866 | } |
25867 | } |
25868 | |
25869 | dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix" , beg_word, seg)); |
25870 | } |
25871 | } |
25872 | |
25873 | void gc_heap::background_mark_phase () |
25874 | { |
25875 | verify_mark_array_cleared(); |
25876 | |
25877 | ScanContext sc; |
25878 | sc.thread_number = heap_number; |
25879 | sc.promotion = TRUE; |
25880 | sc.concurrent = FALSE; |
25881 | |
25882 | THREAD_FROM_HEAP; |
25883 | BOOL cooperative_mode = TRUE; |
25884 | #ifndef MULTIPLE_HEAPS |
25885 | const int thread = heap_number; |
25886 | #endif //!MULTIPLE_HEAPS |
25887 | |
25888 | dprintf(2,("-(GC%d)BMark-" , VolatileLoad(&settings.gc_index))); |
25889 | |
25890 | assert (settings.concurrent); |
25891 | |
25892 | #ifdef TIME_GC |
25893 | unsigned start; |
25894 | unsigned finish; |
25895 | start = GetCycleCount32(); |
25896 | #endif //TIME_GC |
25897 | |
25898 | #ifdef FFIND_OBJECT |
25899 | if (gen0_must_clear_bricks > 0) |
25900 | gen0_must_clear_bricks--; |
25901 | #endif //FFIND_OBJECT |
25902 | |
25903 | background_soh_alloc_count = 0; |
25904 | background_loh_alloc_count = 0; |
25905 | bgc_overflow_count = 0; |
25906 | |
25907 | bpromoted_bytes (heap_number) = 0; |
25908 | static uint32_t num_sizedrefs = 0; |
25909 | |
25910 | background_min_overflow_address = MAX_PTR; |
25911 | background_max_overflow_address = 0; |
25912 | background_min_soh_overflow_address = MAX_PTR; |
25913 | background_max_soh_overflow_address = 0; |
25914 | processed_soh_overflow_p = FALSE; |
25915 | |
25916 | { |
25917 | //set up the mark lists from g_mark_list |
25918 | assert (g_mark_list); |
25919 | mark_list = g_mark_list; |
25920 | //dont use the mark list for full gc |
25921 | //because multiple segments are more complex to handle and the list |
25922 | //is likely to overflow |
25923 | mark_list_end = &mark_list [0]; |
25924 | mark_list_index = &mark_list [0]; |
25925 | |
25926 | c_mark_list_index = 0; |
25927 | |
25928 | #ifndef MULTIPLE_HEAPS |
25929 | shigh = (uint8_t*) 0; |
25930 | slow = MAX_PTR; |
25931 | #endif //MULTIPLE_HEAPS |
25932 | |
25933 | generation* gen = generation_of (max_generation); |
25934 | |
25935 | dprintf(3,("BGC: stack marking" )); |
25936 | sc.concurrent = TRUE; |
25937 | |
25938 | GCScan::GcScanRoots(background_promote_callback, |
25939 | max_generation, max_generation, |
25940 | &sc); |
25941 | } |
25942 | |
25943 | { |
25944 | dprintf(3,("BGC: finalization marking" )); |
25945 | finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0); |
25946 | } |
25947 | |
25948 | size_t total_loh_size = generation_size (max_generation + 1); |
25949 | bgc_begin_loh_size = total_loh_size; |
25950 | bgc_alloc_spin_loh = 0; |
25951 | bgc_loh_size_increased = 0; |
25952 | bgc_loh_allocated_in_free = 0; |
25953 | size_t total_soh_size = generation_sizes (generation_of (max_generation)); |
25954 | |
25955 | dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id" , heap_number, total_loh_size, total_soh_size)); |
25956 | |
25957 | { |
25958 | //concurrent_print_time_delta ("copying stack roots"); |
25959 | concurrent_print_time_delta ("CS" ); |
25960 | |
25961 | FIRE_EVENT(BGC1stNonConEnd); |
25962 | |
25963 | expanded_in_fgc = FALSE; |
25964 | saved_overflow_ephemeral_seg = 0; |
25965 | current_bgc_state = bgc_reset_ww; |
25966 | |
25967 | // we don't need a join here - just whichever thread that gets here |
25968 | // first can change the states and call restart_vm. |
25969 | // this is not true - we can't let the EE run when we are scanning stack. |
25970 | // since we now allow reset ww to run concurrently and have a join for it, |
25971 | // we can do restart ee on the 1st thread that got here. Make sure we handle the |
25972 | // sizedref handles correctly. |
25973 | #ifdef MULTIPLE_HEAPS |
25974 | bgc_t_join.join(this, gc_join_restart_ee); |
25975 | if (bgc_t_join.joined()) |
25976 | #endif //MULTIPLE_HEAPS |
25977 | { |
25978 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
25979 | // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset |
25980 | // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while |
25981 | // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below. |
25982 | #ifdef WRITE_WATCH |
25983 | concurrent_print_time_delta ("CRWW begin" ); |
25984 | |
25985 | #ifdef MULTIPLE_HEAPS |
25986 | for (int i = 0; i < n_heaps; i++) |
25987 | { |
25988 | g_heaps[i]->reset_write_watch (FALSE); |
25989 | } |
25990 | #else |
25991 | reset_write_watch (FALSE); |
25992 | #endif //MULTIPLE_HEAPS |
25993 | |
25994 | concurrent_print_time_delta ("CRWW" ); |
25995 | #endif //WRITE_WATCH |
25996 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
25997 | |
25998 | num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles(); |
25999 | |
26000 | // this c_write is not really necessary because restart_vm |
26001 | // has an instruction that will flush the cpu cache (interlocked |
26002 | // or whatever) but we don't want to rely on that. |
26003 | dprintf (BGC_LOG, ("setting cm_in_progress" )); |
26004 | c_write (cm_in_progress, TRUE); |
26005 | |
26006 | //restart all thread, doing the marking from the array |
26007 | assert (dont_restart_ee_p); |
26008 | dont_restart_ee_p = FALSE; |
26009 | |
26010 | restart_vm(); |
26011 | GCToOSInterface::YieldThread (0); |
26012 | #ifdef MULTIPLE_HEAPS |
26013 | dprintf(3, ("Starting all gc threads for gc" )); |
26014 | bgc_t_join.restart(); |
26015 | #endif //MULTIPLE_HEAPS |
26016 | } |
26017 | |
26018 | #ifdef MULTIPLE_HEAPS |
26019 | bgc_t_join.join(this, gc_join_after_reset); |
26020 | if (bgc_t_join.joined()) |
26021 | #endif //MULTIPLE_HEAPS |
26022 | { |
26023 | disable_preemptive (true); |
26024 | |
26025 | #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26026 | // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The |
26027 | // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied |
26028 | // pages during the concurrent reset. |
26029 | |
26030 | #ifdef WRITE_WATCH |
26031 | concurrent_print_time_delta ("CRWW begin" ); |
26032 | |
26033 | #ifdef MULTIPLE_HEAPS |
26034 | for (int i = 0; i < n_heaps; i++) |
26035 | { |
26036 | g_heaps[i]->reset_write_watch (TRUE); |
26037 | } |
26038 | #else |
26039 | reset_write_watch (TRUE); |
26040 | #endif //MULTIPLE_HEAPS |
26041 | |
26042 | concurrent_print_time_delta ("CRWW" ); |
26043 | #endif //WRITE_WATCH |
26044 | |
26045 | #ifdef MULTIPLE_HEAPS |
26046 | for (int i = 0; i < n_heaps; i++) |
26047 | { |
26048 | g_heaps[i]->revisit_written_pages (TRUE, TRUE); |
26049 | } |
26050 | #else |
26051 | revisit_written_pages (TRUE, TRUE); |
26052 | #endif //MULTIPLE_HEAPS |
26053 | |
26054 | concurrent_print_time_delta ("CRW" ); |
26055 | #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26056 | |
26057 | #ifdef MULTIPLE_HEAPS |
26058 | for (int i = 0; i < n_heaps; i++) |
26059 | { |
26060 | g_heaps[i]->current_bgc_state = bgc_mark_handles; |
26061 | } |
26062 | #else |
26063 | current_bgc_state = bgc_mark_handles; |
26064 | #endif //MULTIPLE_HEAPS |
26065 | |
26066 | current_c_gc_state = c_gc_state_marking; |
26067 | |
26068 | enable_preemptive (); |
26069 | |
26070 | #ifdef MULTIPLE_HEAPS |
26071 | dprintf(3, ("Joining BGC threads after resetting writewatch" )); |
26072 | bgc_t_join.restart(); |
26073 | #endif //MULTIPLE_HEAPS |
26074 | } |
26075 | |
26076 | disable_preemptive (true); |
26077 | |
26078 | if (num_sizedrefs > 0) |
26079 | { |
26080 | GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc); |
26081 | |
26082 | enable_preemptive (); |
26083 | |
26084 | #ifdef MULTIPLE_HEAPS |
26085 | bgc_t_join.join(this, gc_join_scan_sizedref_done); |
26086 | if (bgc_t_join.joined()) |
26087 | { |
26088 | dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots" )); |
26089 | bgc_t_join.restart(); |
26090 | } |
26091 | #endif //MULTIPLE_HEAPS |
26092 | |
26093 | disable_preemptive (true); |
26094 | } |
26095 | |
26096 | dprintf (3,("BGC: handle table marking" )); |
26097 | GCScan::GcScanHandles(background_promote, |
26098 | max_generation, max_generation, |
26099 | &sc); |
26100 | //concurrent_print_time_delta ("concurrent marking handle table"); |
26101 | concurrent_print_time_delta ("CRH" ); |
26102 | |
26103 | current_bgc_state = bgc_mark_stack; |
26104 | dprintf (2,("concurrent draining mark list" )); |
26105 | background_drain_mark_list (thread); |
26106 | //concurrent_print_time_delta ("concurrent marking stack roots"); |
26107 | concurrent_print_time_delta ("CRS" ); |
26108 | |
26109 | dprintf (2,("concurrent revisiting dirtied pages" )); |
26110 | revisit_written_pages (TRUE); |
26111 | revisit_written_pages (TRUE); |
26112 | //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH"); |
26113 | concurrent_print_time_delta ("CRre" ); |
26114 | |
26115 | enable_preemptive (); |
26116 | |
26117 | #ifdef MULTIPLE_HEAPS |
26118 | bgc_t_join.join(this, gc_join_concurrent_overflow); |
26119 | if (bgc_t_join.joined()) |
26120 | { |
26121 | uint8_t* all_heaps_max = 0; |
26122 | uint8_t* all_heaps_min = MAX_PTR; |
26123 | int i; |
26124 | for (i = 0; i < n_heaps; i++) |
26125 | { |
26126 | dprintf (3, ("heap %d overflow max is %Ix, min is %Ix" , |
26127 | i, |
26128 | g_heaps[i]->background_max_overflow_address, |
26129 | g_heaps[i]->background_min_overflow_address)); |
26130 | if (all_heaps_max < g_heaps[i]->background_max_overflow_address) |
26131 | all_heaps_max = g_heaps[i]->background_max_overflow_address; |
26132 | if (all_heaps_min > g_heaps[i]->background_min_overflow_address) |
26133 | all_heaps_min = g_heaps[i]->background_min_overflow_address; |
26134 | } |
26135 | for (i = 0; i < n_heaps; i++) |
26136 | { |
26137 | g_heaps[i]->background_max_overflow_address = all_heaps_max; |
26138 | g_heaps[i]->background_min_overflow_address = all_heaps_min; |
26139 | } |
26140 | dprintf(3, ("Starting all bgc threads after updating the overflow info" )); |
26141 | bgc_t_join.restart(); |
26142 | } |
26143 | #endif //MULTIPLE_HEAPS |
26144 | |
26145 | disable_preemptive (true); |
26146 | |
26147 | dprintf (2, ("before CRov count: %d" , bgc_overflow_count)); |
26148 | bgc_overflow_count = 0; |
26149 | background_process_mark_overflow (TRUE); |
26150 | dprintf (2, ("after CRov count: %d" , bgc_overflow_count)); |
26151 | bgc_overflow_count = 0; |
26152 | //concurrent_print_time_delta ("concurrent processing mark overflow"); |
26153 | concurrent_print_time_delta ("CRov" ); |
26154 | |
26155 | // Stop all threads, crawl all stacks and revisit changed pages. |
26156 | FIRE_EVENT(BGC1stConEnd); |
26157 | |
26158 | dprintf (2, ("Stopping the EE" )); |
26159 | |
26160 | enable_preemptive (); |
26161 | |
26162 | #ifdef MULTIPLE_HEAPS |
26163 | bgc_t_join.join(this, gc_join_suspend_ee); |
26164 | if (bgc_t_join.joined()) |
26165 | { |
26166 | bgc_threads_sync_event.Reset(); |
26167 | |
26168 | dprintf(3, ("Joining BGC threads for non concurrent final marking" )); |
26169 | bgc_t_join.restart(); |
26170 | } |
26171 | #endif //MULTIPLE_HEAPS |
26172 | |
26173 | if (heap_number == 0) |
26174 | { |
26175 | enter_spin_lock (&gc_lock); |
26176 | |
26177 | bgc_suspend_EE (); |
26178 | //suspend_EE (); |
26179 | bgc_threads_sync_event.Set(); |
26180 | } |
26181 | else |
26182 | { |
26183 | bgc_threads_sync_event.Wait(INFINITE, FALSE); |
26184 | dprintf (2, ("bgc_threads_sync_event is signalled" )); |
26185 | } |
26186 | |
26187 | assert (settings.concurrent); |
26188 | assert (settings.condemned_generation == max_generation); |
26189 | |
26190 | dprintf (2, ("clearing cm_in_progress" )); |
26191 | c_write (cm_in_progress, FALSE); |
26192 | |
26193 | bgc_alloc_lock->check(); |
26194 | |
26195 | current_bgc_state = bgc_final_marking; |
26196 | |
26197 | //concurrent_print_time_delta ("concurrent marking ended"); |
26198 | concurrent_print_time_delta ("CR" ); |
26199 | |
26200 | FIRE_EVENT(BGC2ndNonConBegin); |
26201 | |
26202 | mark_absorb_new_alloc(); |
26203 | |
26204 | // We need a join here 'cause find_object would complain if the gen0 |
26205 | // bricks of another heap haven't been fixed up. So we need to make sure |
26206 | // that every heap's gen0 bricks are fixed up before we proceed. |
26207 | #ifdef MULTIPLE_HEAPS |
26208 | bgc_t_join.join(this, gc_join_after_absorb); |
26209 | if (bgc_t_join.joined()) |
26210 | { |
26211 | dprintf(3, ("Joining BGC threads after absorb" )); |
26212 | bgc_t_join.restart(); |
26213 | } |
26214 | #endif //MULTIPLE_HEAPS |
26215 | |
26216 | // give VM a chance to do work |
26217 | GCToEEInterface::GcBeforeBGCSweepWork(); |
26218 | |
26219 | //reset the flag, indicating that the EE no longer expect concurrent |
26220 | //marking |
26221 | sc.concurrent = FALSE; |
26222 | |
26223 | total_loh_size = generation_size (max_generation + 1); |
26224 | total_soh_size = generation_sizes (generation_of (max_generation)); |
26225 | |
26226 | dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id" , heap_number, total_loh_size, total_soh_size)); |
26227 | |
26228 | dprintf (2, ("nonconcurrent marking stack roots" )); |
26229 | GCScan::GcScanRoots(background_promote, |
26230 | max_generation, max_generation, |
26231 | &sc); |
26232 | //concurrent_print_time_delta ("nonconcurrent marking stack roots"); |
26233 | concurrent_print_time_delta ("NRS" ); |
26234 | |
26235 | // finalize_queue->EnterFinalizeLock(); |
26236 | finalize_queue->GcScanRoots(background_promote, heap_number, 0); |
26237 | // finalize_queue->LeaveFinalizeLock(); |
26238 | |
26239 | dprintf (2, ("nonconcurrent marking handle table" )); |
26240 | GCScan::GcScanHandles(background_promote, |
26241 | max_generation, max_generation, |
26242 | &sc); |
26243 | //concurrent_print_time_delta ("nonconcurrent marking handle table"); |
26244 | concurrent_print_time_delta ("NRH" ); |
26245 | |
26246 | dprintf (2,("---- (GC%d)final going through written pages ----" , VolatileLoad(&settings.gc_index))); |
26247 | revisit_written_pages (FALSE); |
26248 | //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH"); |
26249 | concurrent_print_time_delta ("NRre LOH" ); |
26250 | |
26251 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26252 | #ifdef MULTIPLE_HEAPS |
26253 | bgc_t_join.join(this, gc_join_disable_software_write_watch); |
26254 | if (bgc_t_join.joined()) |
26255 | #endif // MULTIPLE_HEAPS |
26256 | { |
26257 | // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to |
26258 | // avoid further perf penalty after the runtime is restarted |
26259 | SoftwareWriteWatch::DisableForGCHeap(); |
26260 | |
26261 | #ifdef MULTIPLE_HEAPS |
26262 | dprintf(3, ("Restarting BGC threads after disabling software write watch" )); |
26263 | bgc_t_join.restart(); |
26264 | #endif // MULTIPLE_HEAPS |
26265 | } |
26266 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26267 | |
26268 | dprintf (2, ("before NR 1st Hov count: %d" , bgc_overflow_count)); |
26269 | bgc_overflow_count = 0; |
26270 | |
26271 | // Dependent handles need to be scanned with a special algorithm (see the header comment on |
26272 | // scan_dependent_handles for more detail). We perform an initial scan without processing any mark |
26273 | // stack overflow. This is not guaranteed to complete the operation but in a common case (where there |
26274 | // are no dependent handles that are due to be collected) it allows us to optimize away further scans. |
26275 | // The call to background_scan_dependent_handles is what will cycle through more iterations if |
26276 | // required and will also perform processing of any mark stack overflow once the dependent handle |
26277 | // table has been fully promoted. |
26278 | dprintf (2, ("1st dependent handle scan and process mark overflow" )); |
26279 | GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc); |
26280 | background_scan_dependent_handles (&sc); |
26281 | //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow"); |
26282 | concurrent_print_time_delta ("NR 1st Hov" ); |
26283 | |
26284 | dprintf (2, ("after NR 1st Hov count: %d" , bgc_overflow_count)); |
26285 | bgc_overflow_count = 0; |
26286 | |
26287 | #ifdef MULTIPLE_HEAPS |
26288 | bgc_t_join.join(this, gc_join_null_dead_short_weak); |
26289 | if (bgc_t_join.joined()) |
26290 | #endif //MULTIPLE_HEAPS |
26291 | { |
26292 | GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc); |
26293 | |
26294 | #ifdef MULTIPLE_HEAPS |
26295 | dprintf(3, ("Joining BGC threads for short weak handle scan" )); |
26296 | bgc_t_join.restart(); |
26297 | #endif //MULTIPLE_HEAPS |
26298 | } |
26299 | |
26300 | // null out the target of short weakref that were not promoted. |
26301 | GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc); |
26302 | |
26303 | //concurrent_print_time_delta ("bgc GcShortWeakPtrScan"); |
26304 | concurrent_print_time_delta ("NR GcShortWeakPtrScan" ); |
26305 | } |
26306 | |
26307 | { |
26308 | #ifdef MULTIPLE_HEAPS |
26309 | bgc_t_join.join(this, gc_join_scan_finalization); |
26310 | if (bgc_t_join.joined()) |
26311 | { |
26312 | dprintf(3, ("Joining BGC threads for finalization" )); |
26313 | bgc_t_join.restart(); |
26314 | } |
26315 | #endif //MULTIPLE_HEAPS |
26316 | |
26317 | //Handle finalization. |
26318 | dprintf(3,("Marking finalization data" )); |
26319 | //concurrent_print_time_delta ("bgc joined to mark finalization"); |
26320 | concurrent_print_time_delta ("NRj" ); |
26321 | |
26322 | // finalize_queue->EnterFinalizeLock(); |
26323 | finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this); |
26324 | // finalize_queue->LeaveFinalizeLock(); |
26325 | |
26326 | concurrent_print_time_delta ("NRF" ); |
26327 | } |
26328 | |
26329 | dprintf (2, ("before NR 2nd Hov count: %d" , bgc_overflow_count)); |
26330 | bgc_overflow_count = 0; |
26331 | |
26332 | // Scan dependent handles again to promote any secondaries associated with primaries that were promoted |
26333 | // for finalization. As before background_scan_dependent_handles will also process any mark stack |
26334 | // overflow. |
26335 | dprintf (2, ("2nd dependent handle scan and process mark overflow" )); |
26336 | background_scan_dependent_handles (&sc); |
26337 | //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow"); |
26338 | concurrent_print_time_delta ("NR 2nd Hov" ); |
26339 | |
26340 | #ifdef MULTIPLE_HEAPS |
26341 | bgc_t_join.join(this, gc_join_null_dead_long_weak); |
26342 | if (bgc_t_join.joined()) |
26343 | { |
26344 | dprintf(2, ("Joining BGC threads for weak pointer deletion" )); |
26345 | bgc_t_join.restart(); |
26346 | } |
26347 | #endif //MULTIPLE_HEAPS |
26348 | |
26349 | // null out the target of long weakref that were not promoted. |
26350 | GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc); |
26351 | concurrent_print_time_delta ("NR GcWeakPtrScan" ); |
26352 | |
26353 | #ifdef MULTIPLE_HEAPS |
26354 | bgc_t_join.join(this, gc_join_null_dead_syncblk); |
26355 | if (bgc_t_join.joined()) |
26356 | #endif //MULTIPLE_HEAPS |
26357 | { |
26358 | dprintf (2, ("calling GcWeakPtrScanBySingleThread" )); |
26359 | // scan for deleted entries in the syncblk cache |
26360 | GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc); |
26361 | concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread" ); |
26362 | #ifdef MULTIPLE_HEAPS |
26363 | dprintf(2, ("Starting BGC threads for end of background mark phase" )); |
26364 | bgc_t_join.restart(); |
26365 | #endif //MULTIPLE_HEAPS |
26366 | } |
26367 | |
26368 | gen0_bricks_cleared = FALSE; |
26369 | |
26370 | dprintf (2, ("end of bgc mark: loh: %d, soh: %d" , |
26371 | generation_size (max_generation + 1), |
26372 | generation_sizes (generation_of (max_generation)))); |
26373 | |
26374 | for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++) |
26375 | { |
26376 | generation* gen = generation_of (gen_idx); |
26377 | dynamic_data* dd = dynamic_data_of (gen_idx); |
26378 | dd_begin_data_size (dd) = generation_size (gen_idx) - |
26379 | (generation_free_list_space (gen) + generation_free_obj_space (gen)) - |
26380 | Align (size (generation_allocation_start (gen))); |
26381 | dd_survived_size (dd) = 0; |
26382 | dd_pinned_survived_size (dd) = 0; |
26383 | dd_artificial_pinned_survived_size (dd) = 0; |
26384 | dd_added_pinned_size (dd) = 0; |
26385 | } |
26386 | |
26387 | heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
26388 | PREFIX_ASSUME(seg != NULL); |
26389 | |
26390 | while (seg) |
26391 | { |
26392 | seg->flags &= ~heap_segment_flags_swept; |
26393 | |
26394 | if (heap_segment_allocated (seg) == heap_segment_mem (seg)) |
26395 | { |
26396 | // This can't happen... |
26397 | FATAL_GC_ERROR(); |
26398 | } |
26399 | |
26400 | if (seg == ephemeral_heap_segment) |
26401 | { |
26402 | heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1)); |
26403 | } |
26404 | else |
26405 | { |
26406 | heap_segment_background_allocated (seg) = heap_segment_allocated (seg); |
26407 | } |
26408 | |
26409 | dprintf (2, ("seg %Ix background allocated is %Ix" , |
26410 | heap_segment_mem (seg), |
26411 | heap_segment_background_allocated (seg))); |
26412 | seg = heap_segment_next_rw (seg); |
26413 | } |
26414 | |
26415 | // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running |
26416 | // we can't let the user code consume the left over parts in these alloc contexts. |
26417 | repair_allocation_contexts (FALSE); |
26418 | |
26419 | #ifdef TIME_GC |
26420 | finish = GetCycleCount32(); |
26421 | mark_time = finish - start; |
26422 | #endif //TIME_GC |
26423 | |
26424 | dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d" , |
26425 | generation_free_list_space (generation_of (max_generation)), |
26426 | generation_free_obj_space (generation_of (max_generation)))); |
26427 | |
26428 | dprintf(2,("---- (GC%d)End of background mark phase ----" , VolatileLoad(&settings.gc_index))); |
26429 | } |
26430 | |
26431 | void |
26432 | gc_heap::suspend_EE () |
26433 | { |
26434 | dprintf (2, ("suspend_EE" )); |
26435 | #ifdef MULTIPLE_HEAPS |
26436 | gc_heap* hp = gc_heap::g_heaps[0]; |
26437 | GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP); |
26438 | #else |
26439 | GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP); |
26440 | #endif //MULTIPLE_HEAPS |
26441 | } |
26442 | |
26443 | #ifdef MULTIPLE_HEAPS |
26444 | void |
26445 | gc_heap::bgc_suspend_EE () |
26446 | { |
26447 | for (int i = 0; i < n_heaps; i++) |
26448 | { |
26449 | gc_heap::g_heaps[i]->reset_gc_done(); |
26450 | } |
26451 | gc_started = TRUE; |
26452 | dprintf (2, ("bgc_suspend_EE" )); |
26453 | GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP); |
26454 | |
26455 | gc_started = FALSE; |
26456 | for (int i = 0; i < n_heaps; i++) |
26457 | { |
26458 | gc_heap::g_heaps[i]->set_gc_done(); |
26459 | } |
26460 | } |
26461 | #else |
26462 | void |
26463 | gc_heap::bgc_suspend_EE () |
26464 | { |
26465 | reset_gc_done(); |
26466 | gc_started = TRUE; |
26467 | dprintf (2, ("bgc_suspend_EE" )); |
26468 | GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP); |
26469 | gc_started = FALSE; |
26470 | set_gc_done(); |
26471 | } |
26472 | #endif //MULTIPLE_HEAPS |
26473 | |
26474 | void |
26475 | gc_heap::restart_EE () |
26476 | { |
26477 | dprintf (2, ("restart_EE" )); |
26478 | #ifdef MULTIPLE_HEAPS |
26479 | GCToEEInterface::RestartEE(FALSE); |
26480 | #else |
26481 | GCToEEInterface::RestartEE(FALSE); |
26482 | #endif //MULTIPLE_HEAPS |
26483 | } |
26484 | |
26485 | inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p) |
26486 | { |
26487 | if (concurrent_p) |
26488 | { |
26489 | uint8_t* end = ((seg == ephemeral_heap_segment) ? |
26490 | generation_allocation_start (generation_of (max_generation-1)) : |
26491 | heap_segment_allocated (seg)); |
26492 | return align_lower_page (end); |
26493 | } |
26494 | else |
26495 | { |
26496 | return heap_segment_allocated (seg); |
26497 | } |
26498 | } |
26499 | |
26500 | void gc_heap::revisit_written_page (uint8_t* page, |
26501 | uint8_t* end, |
26502 | BOOL concurrent_p, |
26503 | heap_segment* seg, |
26504 | uint8_t*& last_page, |
26505 | uint8_t*& last_object, |
26506 | BOOL large_objects_p, |
26507 | size_t& num_marked_objects) |
26508 | { |
26509 | UNREFERENCED_PARAMETER(seg); |
26510 | |
26511 | uint8_t* start_address = page; |
26512 | uint8_t* o = 0; |
26513 | int align_const = get_alignment_constant (!large_objects_p); |
26514 | uint8_t* high_address = end; |
26515 | uint8_t* current_lowest_address = background_saved_lowest_address; |
26516 | uint8_t* current_highest_address = background_saved_highest_address; |
26517 | BOOL no_more_loop_p = FALSE; |
26518 | |
26519 | THREAD_FROM_HEAP; |
26520 | #ifndef MULTIPLE_HEAPS |
26521 | const int thread = heap_number; |
26522 | #endif //!MULTIPLE_HEAPS |
26523 | |
26524 | if (large_objects_p) |
26525 | { |
26526 | o = last_object; |
26527 | } |
26528 | else |
26529 | { |
26530 | if (((last_page + WRITE_WATCH_UNIT_SIZE) == page) |
26531 | || (start_address <= last_object)) |
26532 | { |
26533 | o = last_object; |
26534 | } |
26535 | else |
26536 | { |
26537 | o = find_first_object (start_address, last_object); |
26538 | // We can visit the same object again, but on a different page. |
26539 | assert (o >= last_object); |
26540 | } |
26541 | } |
26542 | |
26543 | dprintf (3,("page %Ix start: %Ix, %Ix[ " , |
26544 | (size_t)page, (size_t)o, |
26545 | (size_t)(min (high_address, page + WRITE_WATCH_UNIT_SIZE)))); |
26546 | |
26547 | while (o < (min (high_address, page + WRITE_WATCH_UNIT_SIZE))) |
26548 | { |
26549 | size_t s; |
26550 | |
26551 | if (concurrent_p && large_objects_p) |
26552 | { |
26553 | bgc_alloc_lock->bgc_mark_set (o); |
26554 | |
26555 | if (((CObjectHeader*)o)->IsFree()) |
26556 | { |
26557 | s = unused_array_size (o); |
26558 | } |
26559 | else |
26560 | { |
26561 | s = size (o); |
26562 | } |
26563 | } |
26564 | else |
26565 | { |
26566 | s = size (o); |
26567 | } |
26568 | |
26569 | dprintf (3,("Considering object %Ix(%s)" , (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm" ))); |
26570 | |
26571 | assert (Align (s) >= Align (min_obj_size)); |
26572 | |
26573 | uint8_t* next_o = o + Align (s, align_const); |
26574 | |
26575 | if (next_o >= start_address) |
26576 | { |
26577 | #ifdef MULTIPLE_HEAPS |
26578 | if (concurrent_p) |
26579 | { |
26580 | // We set last_object here for SVR BGC here because SVR BGC has more than |
26581 | // one GC thread. When we have more than one GC thread we would run into this |
26582 | // situation if we skipped unmarked objects: |
26583 | // bgc thread 1 calls GWW, and detect object X not marked so it would skip it |
26584 | // for revisit. |
26585 | // bgc thread 2 marks X and all its current children. |
26586 | // user thread comes along and dirties more (and later) pages in X. |
26587 | // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything |
26588 | // on them because it had already skipped X. We need to detect that this object is now |
26589 | // marked and mark the children on the dirtied pages. |
26590 | // In the future if we have less BGC threads than we have heaps we should add |
26591 | // the check to the number of BGC threads. |
26592 | last_object = o; |
26593 | } |
26594 | #endif //MULTIPLE_HEAPS |
26595 | |
26596 | if (contain_pointers (o) && |
26597 | (!((o >= current_lowest_address) && (o < current_highest_address)) || |
26598 | background_marked (o))) |
26599 | { |
26600 | dprintf (3, ("going through %Ix" , (size_t)o)); |
26601 | go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s), |
26602 | if ((uint8_t*)poo >= min (high_address, page + WRITE_WATCH_UNIT_SIZE)) |
26603 | { |
26604 | no_more_loop_p = TRUE; |
26605 | goto end_limit; |
26606 | } |
26607 | uint8_t* oo = *poo; |
26608 | |
26609 | num_marked_objects++; |
26610 | background_mark_object (oo THREAD_NUMBER_ARG); |
26611 | ); |
26612 | } |
26613 | else if ( |
26614 | concurrent_p && |
26615 | #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below |
26616 | large_objects_p && |
26617 | #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26618 | ((CObjectHeader*)o)->IsFree() && |
26619 | (next_o > min (high_address, page + WRITE_WATCH_UNIT_SIZE))) |
26620 | { |
26621 | // We need to not skip the object here because of this corner scenario: |
26622 | // A large object was being allocated during BGC mark so we first made it |
26623 | // into a free object, then cleared its memory. In this loop we would detect |
26624 | // that it's a free object which normally we would skip. But by the next time |
26625 | // we call GetWriteWatch we could still be on this object and the object had |
26626 | // been made into a valid object and some of its memory was changed. We need |
26627 | // to be sure to process those written pages so we can't skip the object just |
26628 | // yet. |
26629 | // |
26630 | // Similarly, when using software write watch, don't advance last_object when |
26631 | // the current object is a free object that spans beyond the current page or |
26632 | // high_address. Software write watch acquires gc_lock before the concurrent |
26633 | // GetWriteWatch() call during revisit_written_pages(). A foreground GC may |
26634 | // happen at that point and allocate from this free region, so when |
26635 | // revisit_written_pages() continues, it cannot skip now-valid objects in this |
26636 | // region. |
26637 | no_more_loop_p = TRUE; |
26638 | goto end_limit; |
26639 | } |
26640 | } |
26641 | end_limit: |
26642 | if (concurrent_p && large_objects_p) |
26643 | { |
26644 | bgc_alloc_lock->bgc_mark_done (); |
26645 | } |
26646 | if (no_more_loop_p) |
26647 | { |
26648 | break; |
26649 | } |
26650 | o = next_o; |
26651 | } |
26652 | |
26653 | #ifdef MULTIPLE_HEAPS |
26654 | if (concurrent_p) |
26655 | { |
26656 | assert (last_object < (min (high_address, page + WRITE_WATCH_UNIT_SIZE))); |
26657 | } |
26658 | else |
26659 | #endif //MULTIPLE_HEAPS |
26660 | { |
26661 | last_object = o; |
26662 | } |
26663 | |
26664 | dprintf (3,("Last object: %Ix" , (size_t)last_object)); |
26665 | last_page = align_write_watch_lower_page (o); |
26666 | } |
26667 | |
26668 | // When reset_only_p is TRUE, we should only reset pages that are in range |
26669 | // because we need to consider the segments or part of segments that were |
26670 | // allocated out of range all live. |
26671 | void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p) |
26672 | { |
26673 | #ifdef WRITE_WATCH |
26674 | if (concurrent_p && !reset_only_p) |
26675 | { |
26676 | current_bgc_state = bgc_revisit_soh; |
26677 | } |
26678 | |
26679 | size_t total_dirtied_pages = 0; |
26680 | size_t total_marked_objects = 0; |
26681 | |
26682 | heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
26683 | |
26684 | PREFIX_ASSUME(seg != NULL); |
26685 | |
26686 | bool reset_watch_state = !!concurrent_p; |
26687 | bool is_runtime_suspended = !concurrent_p; |
26688 | BOOL small_object_segments = TRUE; |
26689 | int align_const = get_alignment_constant (small_object_segments); |
26690 | |
26691 | while (1) |
26692 | { |
26693 | if (seg == 0) |
26694 | { |
26695 | if (small_object_segments) |
26696 | { |
26697 | //switch to large segment |
26698 | if (concurrent_p && !reset_only_p) |
26699 | { |
26700 | current_bgc_state = bgc_revisit_loh; |
26701 | } |
26702 | |
26703 | if (!reset_only_p) |
26704 | { |
26705 | dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id" , heap_number, total_dirtied_pages, total_marked_objects)); |
26706 | fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments); |
26707 | concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH" ); |
26708 | total_dirtied_pages = 0; |
26709 | total_marked_objects = 0; |
26710 | } |
26711 | |
26712 | small_object_segments = FALSE; |
26713 | //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH"); |
26714 | |
26715 | dprintf (3, ("now revisiting large object segments" )); |
26716 | align_const = get_alignment_constant (small_object_segments); |
26717 | seg = heap_segment_rw (generation_start_segment (large_object_generation)); |
26718 | |
26719 | PREFIX_ASSUME(seg != NULL); |
26720 | |
26721 | continue; |
26722 | } |
26723 | else |
26724 | { |
26725 | if (reset_only_p) |
26726 | { |
26727 | dprintf (GTC_LOG, ("h%d: tdp: %Id" , heap_number, total_dirtied_pages)); |
26728 | } |
26729 | else |
26730 | { |
26731 | dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id" , heap_number, total_dirtied_pages, total_marked_objects)); |
26732 | fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments); |
26733 | } |
26734 | break; |
26735 | } |
26736 | } |
26737 | uint8_t* base_address = (uint8_t*)heap_segment_mem (seg); |
26738 | //we need to truncate to the base of the page because |
26739 | //some newly allocated could exist beyond heap_segment_allocated |
26740 | //and if we reset the last page write watch status, |
26741 | // they wouldn't be guaranteed to be visited -> gc hole. |
26742 | uintptr_t bcount = array_size; |
26743 | uint8_t* last_page = 0; |
26744 | uint8_t* last_object = heap_segment_mem (seg); |
26745 | uint8_t* high_address = 0; |
26746 | |
26747 | BOOL skip_seg_p = FALSE; |
26748 | |
26749 | if (reset_only_p) |
26750 | { |
26751 | if ((heap_segment_mem (seg) >= background_saved_lowest_address) || |
26752 | (heap_segment_reserved (seg) <= background_saved_highest_address)) |
26753 | { |
26754 | dprintf (3, ("h%d: sseg: %Ix(-%Ix)" , heap_number, |
26755 | heap_segment_mem (seg), heap_segment_reserved (seg))); |
26756 | skip_seg_p = TRUE; |
26757 | } |
26758 | } |
26759 | |
26760 | if (!skip_seg_p) |
26761 | { |
26762 | dprintf (3, ("looking at seg %Ix" , (size_t)last_object)); |
26763 | |
26764 | if (reset_only_p) |
26765 | { |
26766 | base_address = max (base_address, background_saved_lowest_address); |
26767 | dprintf (3, ("h%d: reset only starting %Ix" , heap_number, base_address)); |
26768 | } |
26769 | |
26770 | dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix" , heap_number, base_address, |
26771 | heap_segment_mem (seg), heap_segment_reserved (seg))); |
26772 | |
26773 | |
26774 | while (1) |
26775 | { |
26776 | if (reset_only_p) |
26777 | { |
26778 | high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg)); |
26779 | high_address = min (high_address, background_saved_highest_address); |
26780 | } |
26781 | else |
26782 | { |
26783 | high_address = high_page (seg, concurrent_p); |
26784 | } |
26785 | |
26786 | if ((base_address < high_address) && |
26787 | (bcount >= array_size)) |
26788 | { |
26789 | ptrdiff_t region_size = high_address - base_address; |
26790 | dprintf (3, ("h%d: gw: [%Ix(%Id)" , heap_number, (size_t)base_address, (size_t)region_size)); |
26791 | |
26792 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26793 | // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan |
26794 | // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is |
26795 | // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint |
26796 | // memory regions. |
26797 | if (!is_runtime_suspended) |
26798 | { |
26799 | enter_spin_lock(&gc_lock); |
26800 | } |
26801 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26802 | |
26803 | get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size, |
26804 | (void**)background_written_addresses, |
26805 | &bcount, is_runtime_suspended); |
26806 | |
26807 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26808 | if (!is_runtime_suspended) |
26809 | { |
26810 | leave_spin_lock(&gc_lock); |
26811 | } |
26812 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
26813 | |
26814 | if (bcount != 0) |
26815 | { |
26816 | total_dirtied_pages += bcount; |
26817 | |
26818 | dprintf (3, ("Found %d pages [%Ix, %Ix[" , |
26819 | bcount, (size_t)base_address, (size_t)high_address)); |
26820 | } |
26821 | |
26822 | if (!reset_only_p) |
26823 | { |
26824 | for (unsigned i = 0; i < bcount; i++) |
26825 | { |
26826 | uint8_t* page = (uint8_t*)background_written_addresses[i]; |
26827 | dprintf (3, ("looking at page %d at %Ix(h: %Ix)" , i, |
26828 | (size_t)page, (size_t)high_address)); |
26829 | if (page < high_address) |
26830 | { |
26831 | //search for marked objects in the page |
26832 | revisit_written_page (page, high_address, concurrent_p, |
26833 | seg, last_page, last_object, |
26834 | !small_object_segments, |
26835 | total_marked_objects); |
26836 | } |
26837 | else |
26838 | { |
26839 | dprintf (3, ("page %d at %Ix is >= %Ix!" , i, (size_t)page, (size_t)high_address)); |
26840 | assert (!"page shouldn't have exceeded limit" ); |
26841 | } |
26842 | } |
26843 | } |
26844 | |
26845 | if (bcount >= array_size){ |
26846 | base_address = background_written_addresses [array_size-1] + WRITE_WATCH_UNIT_SIZE; |
26847 | bcount = array_size; |
26848 | } |
26849 | } |
26850 | else |
26851 | { |
26852 | break; |
26853 | } |
26854 | } |
26855 | } |
26856 | |
26857 | seg = heap_segment_next_rw (seg); |
26858 | } |
26859 | |
26860 | #endif //WRITE_WATCH |
26861 | } |
26862 | |
26863 | void gc_heap::background_grow_c_mark_list() |
26864 | { |
26865 | assert (c_mark_list_index >= c_mark_list_length); |
26866 | BOOL should_drain_p = FALSE; |
26867 | THREAD_FROM_HEAP; |
26868 | #ifndef MULTIPLE_HEAPS |
26869 | const int thread = heap_number; |
26870 | #endif //!MULTIPLE_HEAPS |
26871 | |
26872 | dprintf (2, ("stack copy buffer overflow" )); |
26873 | uint8_t** new_c_mark_list = 0; |
26874 | { |
26875 | FAULT_NOT_FATAL(); |
26876 | if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*)))) |
26877 | { |
26878 | should_drain_p = TRUE; |
26879 | } |
26880 | else |
26881 | { |
26882 | new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2]; |
26883 | if (new_c_mark_list == 0) |
26884 | { |
26885 | should_drain_p = TRUE; |
26886 | } |
26887 | } |
26888 | } |
26889 | if (should_drain_p) |
26890 | |
26891 | { |
26892 | dprintf (2, ("No more memory for the stacks copy, draining.." )); |
26893 | //drain the list by marking its elements |
26894 | background_drain_mark_list (thread); |
26895 | } |
26896 | else |
26897 | { |
26898 | assert (new_c_mark_list); |
26899 | memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*)); |
26900 | c_mark_list_length = c_mark_list_length*2; |
26901 | delete c_mark_list; |
26902 | c_mark_list = new_c_mark_list; |
26903 | } |
26904 | } |
26905 | |
26906 | void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc, |
26907 | uint32_t flags) |
26908 | { |
26909 | UNREFERENCED_PARAMETER(sc); |
26910 | //in order to save space on the array, mark the object, |
26911 | //knowing that it will be visited later |
26912 | assert (settings.concurrent); |
26913 | |
26914 | THREAD_NUMBER_FROM_CONTEXT; |
26915 | #ifndef MULTIPLE_HEAPS |
26916 | const int thread = 0; |
26917 | #endif //!MULTIPLE_HEAPS |
26918 | |
26919 | uint8_t* o = (uint8_t*)*ppObject; |
26920 | |
26921 | if (o == 0) |
26922 | return; |
26923 | |
26924 | HEAP_FROM_THREAD; |
26925 | |
26926 | gc_heap* hp = gc_heap::heap_of (o); |
26927 | |
26928 | if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address)) |
26929 | { |
26930 | return; |
26931 | } |
26932 | |
26933 | #ifdef INTERIOR_POINTERS |
26934 | if (flags & GC_CALL_INTERIOR) |
26935 | { |
26936 | o = hp->find_object (o, hp->background_saved_lowest_address); |
26937 | if (o == 0) |
26938 | return; |
26939 | } |
26940 | #endif //INTERIOR_POINTERS |
26941 | |
26942 | #ifdef FEATURE_CONSERVATIVE_GC |
26943 | // For conservative GC, a value on stack may point to middle of a free object. |
26944 | // In this case, we don't need to promote the pointer. |
26945 | if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree()) |
26946 | { |
26947 | return; |
26948 | } |
26949 | #endif //FEATURE_CONSERVATIVE_GC |
26950 | |
26951 | #ifdef _DEBUG |
26952 | ((CObjectHeader*)o)->Validate(); |
26953 | #endif //_DEBUG |
26954 | |
26955 | dprintf (3, ("Concurrent Background Promote %Ix" , (size_t)o)); |
26956 | if (o && (size (o) > loh_size_threshold)) |
26957 | { |
26958 | dprintf (3, ("Brc %Ix" , (size_t)o)); |
26959 | } |
26960 | |
26961 | if (hpt->c_mark_list_index >= hpt->c_mark_list_length) |
26962 | { |
26963 | hpt->background_grow_c_mark_list(); |
26964 | } |
26965 | dprintf (3, ("pushing %08x into mark_list" , (size_t)o)); |
26966 | hpt->c_mark_list [hpt->c_mark_list_index++] = o; |
26967 | |
26968 | STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, " GCHeap::Background Promote: Promote GC Root *%p = %p MT = %pT" , ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL); |
26969 | } |
26970 | |
26971 | void gc_heap::mark_absorb_new_alloc() |
26972 | { |
26973 | fix_allocation_contexts (FALSE); |
26974 | |
26975 | gen0_bricks_cleared = FALSE; |
26976 | |
26977 | clear_gen0_bricks(); |
26978 | } |
26979 | |
26980 | BOOL gc_heap::prepare_bgc_thread(gc_heap* gh) |
26981 | { |
26982 | BOOL success = FALSE; |
26983 | BOOL thread_created = FALSE; |
26984 | dprintf (2, ("Preparing gc thread" )); |
26985 | gh->bgc_threads_timeout_cs.Enter(); |
26986 | if (!(gh->bgc_thread_running)) |
26987 | { |
26988 | dprintf (2, ("GC thread not runnning" )); |
26989 | if ((gh->bgc_thread == 0) && create_bgc_thread(gh)) |
26990 | { |
26991 | success = TRUE; |
26992 | thread_created = TRUE; |
26993 | } |
26994 | } |
26995 | else |
26996 | { |
26997 | dprintf (3, ("GC thread already running" )); |
26998 | success = TRUE; |
26999 | } |
27000 | gh->bgc_threads_timeout_cs.Leave(); |
27001 | |
27002 | if(thread_created) |
27003 | FIRE_EVENT(GCCreateConcurrentThread_V1); |
27004 | |
27005 | return success; |
27006 | } |
27007 | |
27008 | BOOL gc_heap::create_bgc_thread(gc_heap* gh) |
27009 | { |
27010 | assert (background_gc_done_event.IsValid()); |
27011 | |
27012 | //dprintf (2, ("Creating BGC thread")); |
27013 | |
27014 | gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, ".NET Background GC" ); |
27015 | return gh->bgc_thread_running; |
27016 | } |
27017 | |
27018 | BOOL gc_heap::create_bgc_threads_support (int number_of_heaps) |
27019 | { |
27020 | BOOL ret = FALSE; |
27021 | dprintf (3, ("Creating concurrent GC thread for the first time" )); |
27022 | if (!background_gc_done_event.CreateManualEventNoThrow(TRUE)) |
27023 | { |
27024 | goto cleanup; |
27025 | } |
27026 | if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE)) |
27027 | { |
27028 | goto cleanup; |
27029 | } |
27030 | if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE)) |
27031 | { |
27032 | goto cleanup; |
27033 | } |
27034 | if (!bgc_start_event.CreateManualEventNoThrow(FALSE)) |
27035 | { |
27036 | goto cleanup; |
27037 | } |
27038 | |
27039 | #ifdef MULTIPLE_HEAPS |
27040 | bgc_t_join.init (number_of_heaps, join_flavor_bgc); |
27041 | #else |
27042 | UNREFERENCED_PARAMETER(number_of_heaps); |
27043 | #endif //MULTIPLE_HEAPS |
27044 | |
27045 | ret = TRUE; |
27046 | |
27047 | cleanup: |
27048 | |
27049 | if (!ret) |
27050 | { |
27051 | if (background_gc_done_event.IsValid()) |
27052 | { |
27053 | background_gc_done_event.CloseEvent(); |
27054 | } |
27055 | if (bgc_threads_sync_event.IsValid()) |
27056 | { |
27057 | bgc_threads_sync_event.CloseEvent(); |
27058 | } |
27059 | if (ee_proceed_event.IsValid()) |
27060 | { |
27061 | ee_proceed_event.CloseEvent(); |
27062 | } |
27063 | if (bgc_start_event.IsValid()) |
27064 | { |
27065 | bgc_start_event.CloseEvent(); |
27066 | } |
27067 | } |
27068 | |
27069 | return ret; |
27070 | } |
27071 | |
27072 | BOOL gc_heap::create_bgc_thread_support() |
27073 | { |
27074 | BOOL ret = FALSE; |
27075 | uint8_t** parr; |
27076 | |
27077 | if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE)) |
27078 | { |
27079 | goto cleanup; |
27080 | } |
27081 | |
27082 | //needs to have room for enough smallest objects fitting on a page |
27083 | parr = new (nothrow) uint8_t*[1 + OS_PAGE_SIZE / MIN_OBJECT_SIZE]; |
27084 | if (!parr) |
27085 | { |
27086 | goto cleanup; |
27087 | } |
27088 | |
27089 | make_c_mark_list (parr); |
27090 | |
27091 | ret = TRUE; |
27092 | |
27093 | cleanup: |
27094 | |
27095 | if (!ret) |
27096 | { |
27097 | if (gc_lh_block_event.IsValid()) |
27098 | { |
27099 | gc_lh_block_event.CloseEvent(); |
27100 | } |
27101 | } |
27102 | |
27103 | return ret; |
27104 | } |
27105 | |
27106 | int gc_heap::check_for_ephemeral_alloc() |
27107 | { |
27108 | int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1); |
27109 | |
27110 | if (gen == -1) |
27111 | { |
27112 | #ifdef MULTIPLE_HEAPS |
27113 | for (int heap_index = 0; heap_index < n_heaps; heap_index++) |
27114 | #endif //MULTIPLE_HEAPS |
27115 | { |
27116 | for (int i = 0; i <= (max_generation - 1); i++) |
27117 | { |
27118 | #ifdef MULTIPLE_HEAPS |
27119 | if (g_heaps[heap_index]->get_new_allocation (i) <= 0) |
27120 | #else |
27121 | if (get_new_allocation (i) <= 0) |
27122 | #endif //MULTIPLE_HEAPS |
27123 | { |
27124 | gen = max (gen, i); |
27125 | } |
27126 | else |
27127 | break; |
27128 | } |
27129 | } |
27130 | } |
27131 | |
27132 | return gen; |
27133 | } |
27134 | |
27135 | // Wait for gc to finish sequential part |
27136 | void gc_heap::wait_to_proceed() |
27137 | { |
27138 | assert (background_gc_done_event.IsValid()); |
27139 | assert (bgc_start_event.IsValid()); |
27140 | |
27141 | user_thread_wait(&ee_proceed_event, FALSE); |
27142 | } |
27143 | |
27144 | // Start a new concurrent gc |
27145 | void gc_heap::start_c_gc() |
27146 | { |
27147 | assert (background_gc_done_event.IsValid()); |
27148 | assert (bgc_start_event.IsValid()); |
27149 | |
27150 | //Need to make sure that the gc thread is in the right place. |
27151 | background_gc_done_event.Wait(INFINITE, FALSE); |
27152 | background_gc_done_event.Reset(); |
27153 | bgc_start_event.Set(); |
27154 | } |
27155 | |
27156 | void gc_heap::do_background_gc() |
27157 | { |
27158 | dprintf (2, ("starting a BGC" )); |
27159 | #ifdef MULTIPLE_HEAPS |
27160 | for (int i = 0; i < n_heaps; i++) |
27161 | { |
27162 | g_heaps[i]->init_background_gc(); |
27163 | } |
27164 | #else |
27165 | init_background_gc(); |
27166 | #endif //MULTIPLE_HEAPS |
27167 | //start the background gc |
27168 | start_c_gc (); |
27169 | |
27170 | //wait until we get restarted by the BGC. |
27171 | wait_to_proceed(); |
27172 | } |
27173 | |
27174 | void gc_heap::kill_gc_thread() |
27175 | { |
27176 | //assert (settings.concurrent == FALSE); |
27177 | |
27178 | // We are doing a two-stage shutdown now. |
27179 | // In the first stage, we do minimum work, and call ExitProcess at the end. |
27180 | // In the secodn stage, we have the Loader lock and only one thread is |
27181 | // alive. Hence we do not need to kill gc thread. |
27182 | background_gc_done_event.CloseEvent(); |
27183 | gc_lh_block_event.CloseEvent(); |
27184 | bgc_start_event.CloseEvent(); |
27185 | bgc_threads_timeout_cs.Destroy(); |
27186 | bgc_thread = 0; |
27187 | recursive_gc_sync::shutdown(); |
27188 | } |
27189 | |
27190 | void gc_heap::bgc_thread_function() |
27191 | { |
27192 | assert (background_gc_done_event.IsValid()); |
27193 | assert (bgc_start_event.IsValid()); |
27194 | |
27195 | dprintf (3, ("gc_thread thread starting..." )); |
27196 | |
27197 | BOOL do_exit = FALSE; |
27198 | |
27199 | bool cooperative_mode = true; |
27200 | bgc_thread_id.SetToCurrentThread(); |
27201 | dprintf (1, ("bgc_thread_id is set to %x" , (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging())); |
27202 | while (1) |
27203 | { |
27204 | // Wait for work to do... |
27205 | dprintf (3, ("bgc thread: waiting..." )); |
27206 | |
27207 | cooperative_mode = enable_preemptive (); |
27208 | //current_thread->m_fPreemptiveGCDisabled = 0; |
27209 | |
27210 | uint32_t result = bgc_start_event.Wait( |
27211 | #ifdef _DEBUG |
27212 | #ifdef MULTIPLE_HEAPS |
27213 | INFINITE, |
27214 | #else |
27215 | 2000, |
27216 | #endif //MULTIPLE_HEAPS |
27217 | #else //_DEBUG |
27218 | #ifdef MULTIPLE_HEAPS |
27219 | INFINITE, |
27220 | #else |
27221 | 20000, |
27222 | #endif //MULTIPLE_HEAPS |
27223 | #endif //_DEBUG |
27224 | FALSE); |
27225 | dprintf (2, ("gc thread: finished waiting" )); |
27226 | |
27227 | // not calling disable_preemptive here 'cause we |
27228 | // can't wait for GC complete here - RestartEE will be called |
27229 | // when we've done the init work. |
27230 | |
27231 | if (result == WAIT_TIMEOUT) |
27232 | { |
27233 | // Should join the bgc threads and terminate all of them |
27234 | // at once. |
27235 | dprintf (1, ("GC thread timeout" )); |
27236 | bgc_threads_timeout_cs.Enter(); |
27237 | if (!keep_bgc_threads_p) |
27238 | { |
27239 | dprintf (2, ("GC thread exiting" )); |
27240 | bgc_thread_running = FALSE; |
27241 | bgc_thread = 0; |
27242 | bgc_thread_id.Clear(); |
27243 | do_exit = TRUE; |
27244 | } |
27245 | bgc_threads_timeout_cs.Leave(); |
27246 | if (do_exit) |
27247 | break; |
27248 | else |
27249 | { |
27250 | dprintf (3, ("GC thread needed, not exiting" )); |
27251 | continue; |
27252 | } |
27253 | } |
27254 | // if we signal the thread with no concurrent work to do -> exit |
27255 | if (!settings.concurrent) |
27256 | { |
27257 | dprintf (3, ("no concurrent GC needed, exiting" )); |
27258 | break; |
27259 | } |
27260 | #ifdef TRACE_GC |
27261 | //trace_gc = TRUE; |
27262 | #endif //TRACE_GC |
27263 | recursive_gc_sync::begin_background(); |
27264 | dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d" , |
27265 | generation_free_list_space (generation_of (max_generation)), |
27266 | generation_free_obj_space (generation_of (max_generation)), |
27267 | dd_fragmentation (dynamic_data_of (max_generation)))); |
27268 | |
27269 | gc1(); |
27270 | |
27271 | current_bgc_state = bgc_not_in_process; |
27272 | |
27273 | #ifdef TRACE_GC |
27274 | //trace_gc = FALSE; |
27275 | #endif //TRACE_GC |
27276 | |
27277 | enable_preemptive (); |
27278 | #ifdef MULTIPLE_HEAPS |
27279 | bgc_t_join.join(this, gc_join_done); |
27280 | if (bgc_t_join.joined()) |
27281 | #endif //MULTIPLE_HEAPS |
27282 | { |
27283 | enter_spin_lock (&gc_lock); |
27284 | dprintf (SPINLOCK_LOG, ("bgc Egc" )); |
27285 | |
27286 | bgc_start_event.Reset(); |
27287 | do_post_gc(); |
27288 | #ifdef MULTIPLE_HEAPS |
27289 | for (int gen = max_generation; gen <= (max_generation + 1); gen++) |
27290 | { |
27291 | size_t desired_per_heap = 0; |
27292 | size_t total_desired = 0; |
27293 | gc_heap* hp = 0; |
27294 | dynamic_data* dd; |
27295 | for (int i = 0; i < n_heaps; i++) |
27296 | { |
27297 | hp = g_heaps[i]; |
27298 | dd = hp->dynamic_data_of (gen); |
27299 | size_t temp_total_desired = total_desired + dd_desired_allocation (dd); |
27300 | if (temp_total_desired < total_desired) |
27301 | { |
27302 | // we overflowed. |
27303 | total_desired = (size_t)MAX_PTR; |
27304 | break; |
27305 | } |
27306 | total_desired = temp_total_desired; |
27307 | } |
27308 | |
27309 | desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE)); |
27310 | |
27311 | for (int i = 0; i < n_heaps; i++) |
27312 | { |
27313 | hp = gc_heap::g_heaps[i]; |
27314 | dd = hp->dynamic_data_of (gen); |
27315 | dd_desired_allocation (dd) = desired_per_heap; |
27316 | dd_gc_new_allocation (dd) = desired_per_heap; |
27317 | dd_new_allocation (dd) = desired_per_heap; |
27318 | } |
27319 | } |
27320 | #endif //MULTIPLE_HEAPS |
27321 | #ifdef MULTIPLE_HEAPS |
27322 | fire_pevents(); |
27323 | #endif //MULTIPLE_HEAPS |
27324 | |
27325 | c_write (settings.concurrent, FALSE); |
27326 | recursive_gc_sync::end_background(); |
27327 | keep_bgc_threads_p = FALSE; |
27328 | background_gc_done_event.Set(); |
27329 | |
27330 | dprintf (SPINLOCK_LOG, ("bgc Lgc" )); |
27331 | leave_spin_lock (&gc_lock); |
27332 | #ifdef MULTIPLE_HEAPS |
27333 | dprintf(1, ("End of BGC - starting all BGC threads" )); |
27334 | bgc_t_join.restart(); |
27335 | #endif //MULTIPLE_HEAPS |
27336 | } |
27337 | // We can't disable preempt here because there might've been a GC already |
27338 | // started and decided to do a BGC and waiting for a BGC thread to restart |
27339 | // vm. That GC will be waiting in wait_to_proceed and we are waiting for it |
27340 | // to restart the VM so we deadlock. |
27341 | //gc_heap::disable_preemptive (true); |
27342 | } |
27343 | |
27344 | FIRE_EVENT(GCTerminateConcurrentThread_V1); |
27345 | |
27346 | dprintf (3, ("bgc_thread thread exiting" )); |
27347 | return; |
27348 | } |
27349 | |
27350 | #endif //BACKGROUND_GC |
27351 | |
27352 | //Clear the cards [start_card, end_card[ |
27353 | void gc_heap::clear_cards (size_t start_card, size_t end_card) |
27354 | { |
27355 | if (start_card < end_card) |
27356 | { |
27357 | size_t start_word = card_word (start_card); |
27358 | size_t end_word = card_word (end_card); |
27359 | if (start_word < end_word) |
27360 | { |
27361 | // Figure out the bit positions of the cards within their words |
27362 | unsigned bits = card_bit (start_card); |
27363 | card_table [start_word] &= lowbits (~0, bits); |
27364 | for (size_t i = start_word+1; i < end_word; i++) |
27365 | card_table [i] = 0; |
27366 | bits = card_bit (end_card); |
27367 | // Don't write beyond end_card (and possibly uncommitted card table space). |
27368 | if (bits != 0) |
27369 | { |
27370 | card_table [end_word] &= highbits (~0, bits); |
27371 | } |
27372 | } |
27373 | else |
27374 | { |
27375 | // If the start and end cards are in the same word, just clear the appropriate card |
27376 | // bits in that word. |
27377 | card_table [start_word] &= (lowbits (~0, card_bit (start_card)) | |
27378 | highbits (~0, card_bit (end_card))); |
27379 | } |
27380 | #ifdef VERYSLOWDEBUG |
27381 | size_t card = start_card; |
27382 | while (card < end_card) |
27383 | { |
27384 | assert (! (card_set_p (card))); |
27385 | card++; |
27386 | } |
27387 | #endif //VERYSLOWDEBUG |
27388 | dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[" , |
27389 | start_card, (size_t)card_address (start_card), |
27390 | end_card, (size_t)card_address (end_card))); |
27391 | } |
27392 | } |
27393 | |
27394 | void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address) |
27395 | { |
27396 | size_t start_card = card_of (align_on_card (start_address)); |
27397 | size_t end_card = card_of (align_lower_card (end_address)); |
27398 | clear_cards (start_card, end_card); |
27399 | } |
27400 | |
27401 | // copy [srccard, ...[ to [dst_card, end_card[ |
27402 | // This will set the same bit twice. Can be optimized. |
27403 | inline |
27404 | void gc_heap::copy_cards (size_t dst_card, |
27405 | size_t src_card, |
27406 | size_t end_card, |
27407 | BOOL nextp) |
27408 | { |
27409 | // If the range is empty, this function is a no-op - with the subtlety that |
27410 | // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be |
27411 | // outside the committed region. To avoid the access, leave early. |
27412 | if (!(dst_card < end_card)) |
27413 | return; |
27414 | |
27415 | unsigned int srcbit = card_bit (src_card); |
27416 | unsigned int dstbit = card_bit (dst_card); |
27417 | size_t srcwrd = card_word (src_card); |
27418 | size_t dstwrd = card_word (dst_card); |
27419 | unsigned int srctmp = card_table[srcwrd]; |
27420 | unsigned int dsttmp = card_table[dstwrd]; |
27421 | |
27422 | for (size_t card = dst_card; card < end_card; card++) |
27423 | { |
27424 | if (srctmp & (1 << srcbit)) |
27425 | dsttmp |= 1 << dstbit; |
27426 | else |
27427 | dsttmp &= ~(1 << dstbit); |
27428 | if (!(++srcbit % 32)) |
27429 | { |
27430 | srctmp = card_table[++srcwrd]; |
27431 | srcbit = 0; |
27432 | } |
27433 | |
27434 | if (nextp) |
27435 | { |
27436 | if (srctmp & (1 << srcbit)) |
27437 | dsttmp |= 1 << dstbit; |
27438 | } |
27439 | |
27440 | if (!(++dstbit % 32)) |
27441 | { |
27442 | card_table[dstwrd] = dsttmp; |
27443 | |
27444 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
27445 | if (dsttmp != 0) |
27446 | { |
27447 | card_bundle_set(cardw_card_bundle(dstwrd)); |
27448 | } |
27449 | #endif |
27450 | |
27451 | dstwrd++; |
27452 | dsttmp = card_table[dstwrd]; |
27453 | dstbit = 0; |
27454 | } |
27455 | } |
27456 | |
27457 | card_table[dstwrd] = dsttmp; |
27458 | |
27459 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
27460 | if (dsttmp != 0) |
27461 | { |
27462 | card_bundle_set(cardw_card_bundle(dstwrd)); |
27463 | } |
27464 | #endif |
27465 | } |
27466 | |
27467 | void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len) |
27468 | { |
27469 | ptrdiff_t relocation_distance = src - dest; |
27470 | size_t start_dest_card = card_of (align_on_card (dest)); |
27471 | size_t end_dest_card = card_of (dest + len - 1); |
27472 | size_t dest_card = start_dest_card; |
27473 | size_t src_card = card_of (card_address (dest_card)+relocation_distance); |
27474 | dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, " , |
27475 | src_card, (size_t)src, dest_card, (size_t)dest)); |
27476 | dprintf (3,(" %Ix->%Ix:%Ix[" , |
27477 | (size_t)src+len, end_dest_card, (size_t)dest+len)); |
27478 | |
27479 | dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix" , |
27480 | dest, src, len, relocation_distance, (align_on_card (dest)))); |
27481 | |
27482 | dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix" , |
27483 | start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest))); |
27484 | |
27485 | //First card has two boundaries |
27486 | if (start_dest_card != card_of (dest)) |
27487 | { |
27488 | if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&& |
27489 | card_set_p (card_of (card_address (start_dest_card) + relocation_distance))) |
27490 | { |
27491 | dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix" , |
27492 | (card_address (start_dest_card) + relocation_distance), |
27493 | card_of (card_address (start_dest_card) + relocation_distance), |
27494 | (src + len - 1), |
27495 | card_of (src + len - 1))); |
27496 | |
27497 | dprintf (3, ("setting card: %Ix" , card_of (dest))); |
27498 | set_card (card_of (dest)); |
27499 | } |
27500 | } |
27501 | |
27502 | if (card_set_p (card_of (src))) |
27503 | set_card (card_of (dest)); |
27504 | |
27505 | |
27506 | copy_cards (dest_card, src_card, end_dest_card, |
27507 | ((dest - align_lower_card (dest)) != (src - align_lower_card (src)))); |
27508 | |
27509 | //Last card has two boundaries. |
27510 | if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) && |
27511 | card_set_p (card_of (card_address (end_dest_card) + relocation_distance))) |
27512 | { |
27513 | dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix" , |
27514 | (card_address (end_dest_card) + relocation_distance), |
27515 | card_of (card_address (end_dest_card) + relocation_distance), |
27516 | src, |
27517 | card_of (src))); |
27518 | |
27519 | dprintf (3, ("setting card: %Ix" , end_dest_card)); |
27520 | set_card (end_dest_card); |
27521 | } |
27522 | |
27523 | if (card_set_p (card_of (src + len - 1))) |
27524 | set_card (end_dest_card); |
27525 | |
27526 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
27527 | card_bundles_set(cardw_card_bundle(card_word(card_of(dest))), cardw_card_bundle(align_cardw_on_bundle(card_word(end_dest_card)))); |
27528 | #endif |
27529 | } |
27530 | |
27531 | #ifdef BACKGROUND_GC |
27532 | // this does not need the Interlocked version of mark_array_set_marked. |
27533 | void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len) |
27534 | { |
27535 | dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[" , |
27536 | (size_t)src, (size_t)dest, |
27537 | (size_t)src+len, (size_t)dest+len)); |
27538 | |
27539 | uint8_t* src_o = src; |
27540 | uint8_t* dest_o; |
27541 | uint8_t* src_end = src + len; |
27542 | int align_const = get_alignment_constant (TRUE); |
27543 | ptrdiff_t reloc = dest - src; |
27544 | |
27545 | while (src_o < src_end) |
27546 | { |
27547 | uint8_t* next_o = src_o + Align (size (src_o), align_const); |
27548 | |
27549 | if (background_object_marked (src_o, TRUE)) |
27550 | { |
27551 | dest_o = src_o + reloc; |
27552 | |
27553 | //if (background_object_marked (dest_o, FALSE)) |
27554 | //{ |
27555 | // dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o))); |
27556 | // FATAL_GC_ERROR(); |
27557 | //} |
27558 | |
27559 | background_mark (dest_o, |
27560 | background_saved_lowest_address, |
27561 | background_saved_highest_address); |
27562 | dprintf (3, ("bc*%Ix*bc, b*%Ix*b" , (size_t)src_o, (size_t)(dest_o))); |
27563 | } |
27564 | |
27565 | src_o = next_o; |
27566 | } |
27567 | } |
27568 | #endif //BACKGROUND_GC |
27569 | |
27570 | void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o) |
27571 | { |
27572 | size_t new_current_brick = brick_of (o); |
27573 | set_brick (new_current_brick, |
27574 | (o - brick_address (new_current_brick))); |
27575 | size_t b = 1 + new_current_brick; |
27576 | size_t limit = brick_of (next_o); |
27577 | //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)", |
27578 | dprintf(3,("b:%Ix->%Ix-%Ix" , |
27579 | new_current_brick, (size_t)o, (size_t)next_o)); |
27580 | while (b < limit) |
27581 | { |
27582 | set_brick (b,(new_current_brick - b)); |
27583 | b++; |
27584 | } |
27585 | } |
27586 | |
27587 | // start can not be >= heap_segment_allocated for the segment. |
27588 | uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object) |
27589 | { |
27590 | size_t brick = brick_of (start); |
27591 | uint8_t* o = 0; |
27592 | //last_object == null -> no search shortcut needed |
27593 | if ((brick == brick_of (first_object) || (start <= first_object))) |
27594 | { |
27595 | o = first_object; |
27596 | } |
27597 | else |
27598 | { |
27599 | ptrdiff_t min_brick = (ptrdiff_t)brick_of (first_object); |
27600 | ptrdiff_t prev_brick = (ptrdiff_t)brick - 1; |
27601 | int brick_entry = 0; |
27602 | while (1) |
27603 | { |
27604 | if (prev_brick < min_brick) |
27605 | { |
27606 | break; |
27607 | } |
27608 | if ((brick_entry = get_brick_entry(prev_brick)) >= 0) |
27609 | { |
27610 | break; |
27611 | } |
27612 | assert (! ((brick_entry == 0))); |
27613 | prev_brick = (brick_entry + prev_brick); |
27614 | |
27615 | } |
27616 | o = ((prev_brick < min_brick) ? first_object : |
27617 | brick_address (prev_brick) + brick_entry - 1); |
27618 | assert (o <= start); |
27619 | } |
27620 | |
27621 | assert (Align (size (o)) >= Align (min_obj_size)); |
27622 | uint8_t* next_o = o + Align (size (o)); |
27623 | size_t curr_cl = (size_t)next_o / brick_size; |
27624 | size_t min_cl = (size_t)first_object / brick_size; |
27625 | |
27626 | //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o)); |
27627 | #ifdef TRACE_GC |
27628 | unsigned int n_o = 1; |
27629 | #endif //TRACE_GC |
27630 | |
27631 | uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1); |
27632 | |
27633 | while (next_o <= start) |
27634 | { |
27635 | do |
27636 | { |
27637 | #ifdef TRACE_GC |
27638 | n_o++; |
27639 | #endif //TRACE_GC |
27640 | o = next_o; |
27641 | assert (Align (size (o)) >= Align (min_obj_size)); |
27642 | next_o = o + Align (size (o)); |
27643 | Prefetch (next_o); |
27644 | }while (next_o < next_b); |
27645 | |
27646 | if (((size_t)next_o / brick_size) != curr_cl) |
27647 | { |
27648 | if (curr_cl >= min_cl) |
27649 | { |
27650 | fix_brick_to_highest (o, next_o); |
27651 | } |
27652 | curr_cl = (size_t) next_o / brick_size; |
27653 | } |
27654 | next_b = min (align_lower_brick (next_o) + brick_size, start+1); |
27655 | } |
27656 | |
27657 | size_t bo = brick_of (o); |
27658 | //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix", |
27659 | dprintf (3, ("%Id o, [%Ix-[%Ix" , |
27660 | n_o, bo, brick)); |
27661 | if (bo < brick) |
27662 | { |
27663 | set_brick (bo, (o - brick_address(bo))); |
27664 | size_t b = 1 + bo; |
27665 | int x = -1; |
27666 | while (b < brick) |
27667 | { |
27668 | set_brick (b,x--); |
27669 | b++; |
27670 | } |
27671 | } |
27672 | |
27673 | return o; |
27674 | } |
27675 | |
27676 | #ifdef CARD_BUNDLE |
27677 | |
27678 | // Find the first non-zero card word between cardw and cardw_end. |
27679 | // The index of the word we find is returned in cardw. |
27680 | BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end) |
27681 | { |
27682 | dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix" , |
27683 | dd_collection_count (dynamic_data_of (0)), cardw, cardw_end)); |
27684 | |
27685 | if (card_bundles_enabled()) |
27686 | { |
27687 | size_t cardb = cardw_card_bundle (cardw); |
27688 | size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end)); |
27689 | while (1) |
27690 | { |
27691 | // Find a non-zero bundle |
27692 | while ((cardb < end_cardb) && (card_bundle_set_p (cardb) == 0)) |
27693 | { |
27694 | cardb++; |
27695 | } |
27696 | if (cardb == end_cardb) |
27697 | return FALSE; |
27698 | |
27699 | uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)]; |
27700 | uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)]; |
27701 | while ((card_word < card_word_end) && !(*card_word)) |
27702 | { |
27703 | card_word++; |
27704 | } |
27705 | |
27706 | if (card_word != card_word_end) |
27707 | { |
27708 | cardw = (card_word - &card_table[0]); |
27709 | return TRUE; |
27710 | } |
27711 | else if ((cardw <= card_bundle_cardw (cardb)) && |
27712 | (card_word == &card_table [card_bundle_cardw (cardb+1)])) |
27713 | { |
27714 | // a whole bundle was explored and is empty |
27715 | dprintf (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[" , |
27716 | dd_collection_count (dynamic_data_of (0)), |
27717 | cardb, card_bundle_cardw (cardb), |
27718 | card_bundle_cardw (cardb+1))); |
27719 | card_bundle_clear (cardb); |
27720 | } |
27721 | |
27722 | cardb++; |
27723 | } |
27724 | } |
27725 | else |
27726 | { |
27727 | uint32_t* card_word = &card_table[cardw]; |
27728 | uint32_t* card_word_end = &card_table [cardw_end]; |
27729 | |
27730 | while (card_word < card_word_end) |
27731 | { |
27732 | if ((*card_word) != 0) |
27733 | { |
27734 | cardw = (card_word - &card_table [0]); |
27735 | return TRUE; |
27736 | } |
27737 | |
27738 | card_word++; |
27739 | } |
27740 | return FALSE; |
27741 | |
27742 | } |
27743 | |
27744 | } |
27745 | |
27746 | #endif //CARD_BUNDLE |
27747 | |
27748 | // Find cards that are set between two points in a card table. |
27749 | // Parameters |
27750 | // card_table : The card table. |
27751 | // card : [in/out] As input, the card to start searching from. |
27752 | // As output, the first card that's set. |
27753 | // card_word_end : The card word at which to stop looking. |
27754 | // end_card : [out] The last card which is set. |
27755 | BOOL gc_heap::find_card(uint32_t* card_table, |
27756 | size_t& card, |
27757 | size_t card_word_end, |
27758 | size_t& end_card) |
27759 | { |
27760 | uint32_t* last_card_word; |
27761 | uint32_t card_word_value; |
27762 | uint32_t bit_position; |
27763 | |
27764 | // Find the first card which is set |
27765 | last_card_word = &card_table [card_word (card)]; |
27766 | bit_position = card_bit (card); |
27767 | card_word_value = (*last_card_word) >> bit_position; |
27768 | if (!card_word_value) |
27769 | { |
27770 | bit_position = 0; |
27771 | #ifdef CARD_BUNDLE |
27772 | // Using the card bundle, go through the remaining card words between here and |
27773 | // card_word_end until we find one that is non-zero. |
27774 | size_t lcw = card_word(card) + 1; |
27775 | if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE) |
27776 | { |
27777 | return FALSE; |
27778 | } |
27779 | else |
27780 | { |
27781 | last_card_word = &card_table [lcw]; |
27782 | card_word_value = *last_card_word; |
27783 | } |
27784 | |
27785 | #else //CARD_BUNDLE |
27786 | // Go through the remaining card words between here and card_word_end until we find |
27787 | // one that is non-zero. |
27788 | do |
27789 | { |
27790 | ++last_card_word; |
27791 | } |
27792 | |
27793 | while ((last_card_word < &card_table [card_word_end]) && !(*last_card_word)); |
27794 | if (last_card_word < &card_table [card_word_end]) |
27795 | { |
27796 | card_word_value = *last_card_word; |
27797 | } |
27798 | else |
27799 | { |
27800 | // We failed to find any non-zero card words before we got to card_word_end |
27801 | return FALSE; |
27802 | } |
27803 | #endif //CARD_BUNDLE |
27804 | } |
27805 | |
27806 | |
27807 | // Look for the lowest bit set |
27808 | if (card_word_value) |
27809 | { |
27810 | while (!(card_word_value & 1)) |
27811 | { |
27812 | bit_position++; |
27813 | card_word_value = card_word_value / 2; |
27814 | } |
27815 | } |
27816 | |
27817 | // card is the card word index * card size + the bit index within the card |
27818 | card = (last_card_word - &card_table[0]) * card_word_width + bit_position; |
27819 | |
27820 | do |
27821 | { |
27822 | // Keep going until we get to an un-set card. |
27823 | bit_position++; |
27824 | card_word_value = card_word_value / 2; |
27825 | |
27826 | // If we reach the end of the card word and haven't hit a 0 yet, start going |
27827 | // card word by card word until we get to one that's not fully set (0xFFFF...) |
27828 | // or we reach card_word_end. |
27829 | if ((bit_position == card_word_width) && (last_card_word < &card_table [card_word_end])) |
27830 | { |
27831 | do |
27832 | { |
27833 | card_word_value = *(++last_card_word); |
27834 | } while ((last_card_word < &card_table [card_word_end]) && |
27835 | |
27836 | #ifdef _MSC_VER |
27837 | (card_word_value == (1 << card_word_width)-1) |
27838 | #else |
27839 | // if left shift count >= width of type, |
27840 | // gcc reports error. |
27841 | (card_word_value == ~0u) |
27842 | #endif // _MSC_VER |
27843 | ); |
27844 | bit_position = 0; |
27845 | } |
27846 | } while (card_word_value & 1); |
27847 | |
27848 | end_card = (last_card_word - &card_table [0])* card_word_width + bit_position; |
27849 | |
27850 | //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card)); |
27851 | dprintf (3, ("fc: [%Ix, %Ix[" , card, end_card)); |
27852 | return TRUE; |
27853 | } |
27854 | |
27855 | |
27856 | //because of heap expansion, computing end is complicated. |
27857 | uint8_t* compute_next_end (heap_segment* seg, uint8_t* low) |
27858 | { |
27859 | if ((low >= heap_segment_mem (seg)) && |
27860 | (low < heap_segment_allocated (seg))) |
27861 | return low; |
27862 | else |
27863 | return heap_segment_allocated (seg); |
27864 | } |
27865 | |
27866 | uint8_t* |
27867 | gc_heap::compute_next_boundary (uint8_t* low, int gen_number, |
27868 | BOOL relocating) |
27869 | { |
27870 | UNREFERENCED_PARAMETER(low); |
27871 | |
27872 | //when relocating, the fault line is the plan start of the younger |
27873 | //generation because the generation is promoted. |
27874 | if (relocating && (gen_number == (settings.condemned_generation + 1))) |
27875 | { |
27876 | generation* gen = generation_of (gen_number - 1); |
27877 | uint8_t* gen_alloc = generation_plan_allocation_start (gen); |
27878 | assert (gen_alloc); |
27879 | return gen_alloc; |
27880 | } |
27881 | else |
27882 | { |
27883 | assert (gen_number > settings.condemned_generation); |
27884 | return generation_allocation_start (generation_of (gen_number - 1 )); |
27885 | } |
27886 | |
27887 | } |
27888 | |
27889 | inline void |
27890 | gc_heap::keep_card_live (uint8_t* o, size_t& n_gen, |
27891 | size_t& cg_pointers_found) |
27892 | { |
27893 | THREAD_FROM_HEAP; |
27894 | if ((gc_low <= o) && (gc_high > o)) |
27895 | { |
27896 | n_gen++; |
27897 | } |
27898 | #ifdef MULTIPLE_HEAPS |
27899 | else if (o) |
27900 | { |
27901 | gc_heap* hp = heap_of (o); |
27902 | if (hp != this) |
27903 | { |
27904 | if ((hp->gc_low <= o) && |
27905 | (hp->gc_high > o)) |
27906 | { |
27907 | n_gen++; |
27908 | } |
27909 | } |
27910 | } |
27911 | #endif //MULTIPLE_HEAPS |
27912 | cg_pointers_found ++; |
27913 | dprintf (4, ("keep card live for %Ix" , o)); |
27914 | } |
27915 | |
27916 | inline void |
27917 | gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen, |
27918 | size_t& cg_pointers_found, |
27919 | card_fn fn, uint8_t* nhigh, |
27920 | uint8_t* next_boundary) |
27921 | { |
27922 | THREAD_FROM_HEAP; |
27923 | if ((gc_low <= *poo) && (gc_high > *poo)) |
27924 | { |
27925 | n_gen++; |
27926 | call_fn(fn) (poo THREAD_NUMBER_ARG); |
27927 | } |
27928 | #ifdef MULTIPLE_HEAPS |
27929 | else if (*poo) |
27930 | { |
27931 | gc_heap* hp = heap_of_gc (*poo); |
27932 | if (hp != this) |
27933 | { |
27934 | if ((hp->gc_low <= *poo) && |
27935 | (hp->gc_high > *poo)) |
27936 | { |
27937 | n_gen++; |
27938 | call_fn(fn) (poo THREAD_NUMBER_ARG); |
27939 | } |
27940 | if ((fn == &gc_heap::relocate_address) || |
27941 | ((hp->ephemeral_low <= *poo) && |
27942 | (hp->ephemeral_high > *poo))) |
27943 | { |
27944 | cg_pointers_found++; |
27945 | } |
27946 | } |
27947 | } |
27948 | #endif //MULTIPLE_HEAPS |
27949 | if ((next_boundary <= *poo) && (nhigh > *poo)) |
27950 | { |
27951 | cg_pointers_found ++; |
27952 | dprintf (4, ("cg pointer %Ix found, %Id so far" , |
27953 | (size_t)*poo, cg_pointers_found )); |
27954 | |
27955 | } |
27956 | } |
27957 | |
27958 | BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end, |
27959 | size_t& cg_pointers_found, |
27960 | size_t& n_eph, size_t& n_card_set, |
27961 | size_t& card, size_t& end_card, |
27962 | BOOL& foundp, uint8_t*& start_address, |
27963 | uint8_t*& limit, size_t& n_cards_cleared) |
27964 | { |
27965 | dprintf (3, ("pointer %Ix past card %Ix" , (size_t)po, (size_t)card)); |
27966 | dprintf (3, ("ct: %Id cg" , cg_pointers_found)); |
27967 | BOOL passed_end_card_p = FALSE; |
27968 | foundp = FALSE; |
27969 | |
27970 | if (cg_pointers_found == 0) |
27971 | { |
27972 | //dprintf(3,(" Clearing cards [%Ix, %Ix[ ", |
27973 | dprintf(3,(" CC [%Ix, %Ix[ " , |
27974 | (size_t)card_address(card), (size_t)po)); |
27975 | clear_cards (card, card_of(po)); |
27976 | n_card_set -= (card_of (po) - card); |
27977 | n_cards_cleared += (card_of (po) - card); |
27978 | |
27979 | } |
27980 | n_eph +=cg_pointers_found; |
27981 | cg_pointers_found = 0; |
27982 | card = card_of (po); |
27983 | if (card >= end_card) |
27984 | { |
27985 | passed_end_card_p = TRUE; |
27986 | dprintf (3, ("card %Ix exceeding end_card %Ix" , |
27987 | (size_t)card, (size_t)end_card)); |
27988 | foundp = find_card (card_table, card, card_word_end, end_card); |
27989 | if (foundp) |
27990 | { |
27991 | n_card_set+= end_card - card; |
27992 | start_address = card_address (card); |
27993 | dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix" , |
27994 | (size_t)card, (size_t)start_address, |
27995 | (size_t)card_address (end_card))); |
27996 | } |
27997 | limit = min (end, card_address (end_card)); |
27998 | |
27999 | assert (!((limit == card_address (end_card))&& |
28000 | card_set_p (end_card))); |
28001 | } |
28002 | |
28003 | return passed_end_card_p; |
28004 | } |
28005 | |
28006 | void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating) |
28007 | { |
28008 | #ifdef BACKGROUND_GC |
28009 | dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)" , |
28010 | current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start)); |
28011 | |
28012 | heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
28013 | PREFIX_ASSUME(soh_seg != NULL); |
28014 | |
28015 | while (soh_seg) |
28016 | { |
28017 | dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix" , |
28018 | soh_seg, |
28019 | heap_segment_background_allocated (soh_seg), |
28020 | heap_segment_allocated (soh_seg))); |
28021 | |
28022 | soh_seg = heap_segment_next_rw (soh_seg); |
28023 | } |
28024 | #endif //BACKGROUND_GC |
28025 | |
28026 | uint8_t* low = gc_low; |
28027 | uint8_t* high = gc_high; |
28028 | size_t end_card = 0; |
28029 | |
28030 | generation* oldest_gen = generation_of (max_generation); |
28031 | int curr_gen_number = max_generation; |
28032 | uint8_t* gen_boundary = generation_allocation_start(generation_of(curr_gen_number - 1)); |
28033 | uint8_t* next_boundary = compute_next_boundary(gc_low, curr_gen_number, relocating); |
28034 | |
28035 | heap_segment* seg = heap_segment_rw (generation_start_segment (oldest_gen)); |
28036 | PREFIX_ASSUME(seg != NULL); |
28037 | |
28038 | uint8_t* beg = generation_allocation_start (oldest_gen); |
28039 | uint8_t* end = compute_next_end (seg, low); |
28040 | uint8_t* last_object = beg; |
28041 | |
28042 | size_t cg_pointers_found = 0; |
28043 | |
28044 | size_t card_word_end = (card_of (align_on_card_word (end)) / card_word_width); |
28045 | |
28046 | size_t n_eph = 0; |
28047 | size_t n_gen = 0; |
28048 | size_t n_card_set = 0; |
28049 | uint8_t* nhigh = (relocating ? |
28050 | heap_segment_plan_allocated (ephemeral_heap_segment) : high); |
28051 | |
28052 | BOOL foundp = FALSE; |
28053 | uint8_t* start_address = 0; |
28054 | uint8_t* limit = 0; |
28055 | size_t card = card_of (beg); |
28056 | #ifdef BACKGROUND_GC |
28057 | BOOL consider_bgc_mark_p = FALSE; |
28058 | BOOL check_current_sweep_p = FALSE; |
28059 | BOOL check_saved_sweep_p = FALSE; |
28060 | should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p); |
28061 | #endif //BACKGROUND_GC |
28062 | |
28063 | dprintf(3, ("CMs: %Ix->%Ix" , (size_t)beg, (size_t)end)); |
28064 | size_t total_cards_cleared = 0; |
28065 | |
28066 | while (1) |
28067 | { |
28068 | if (card_of(last_object) > card) |
28069 | { |
28070 | dprintf (3, ("Found %Id cg pointers" , cg_pointers_found)); |
28071 | if (cg_pointers_found == 0) |
28072 | { |
28073 | dprintf(3,(" Clearing cards [%Ix, %Ix[ " , (size_t)card_address(card), (size_t)last_object)); |
28074 | clear_cards (card, card_of(last_object)); |
28075 | n_card_set -= (card_of (last_object) - card); |
28076 | total_cards_cleared += (card_of (last_object) - card); |
28077 | } |
28078 | |
28079 | n_eph += cg_pointers_found; |
28080 | cg_pointers_found = 0; |
28081 | card = card_of (last_object); |
28082 | } |
28083 | |
28084 | if (card >= end_card) |
28085 | { |
28086 | foundp = find_card (card_table, card, card_word_end, end_card); |
28087 | if (foundp) |
28088 | { |
28089 | n_card_set += end_card - card; |
28090 | start_address = max (beg, card_address (card)); |
28091 | } |
28092 | limit = min (end, card_address (end_card)); |
28093 | } |
28094 | if (!foundp || (last_object >= end) || (card_address (card) >= end)) |
28095 | { |
28096 | if (foundp && (cg_pointers_found == 0)) |
28097 | { |
28098 | dprintf(3,(" Clearing cards [%Ix, %Ix[ " , (size_t)card_address(card), |
28099 | (size_t)end)); |
28100 | clear_cards (card, card_of (end)); |
28101 | n_card_set -= (card_of (end) - card); |
28102 | total_cards_cleared += (card_of (end) - card); |
28103 | } |
28104 | n_eph += cg_pointers_found; |
28105 | cg_pointers_found = 0; |
28106 | if ((seg = heap_segment_next_in_range (seg)) != 0) |
28107 | { |
28108 | #ifdef BACKGROUND_GC |
28109 | should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p); |
28110 | #endif //BACKGROUND_GC |
28111 | beg = heap_segment_mem (seg); |
28112 | end = compute_next_end (seg, low); |
28113 | card_word_end = card_of (align_on_card_word (end)) / card_word_width; |
28114 | card = card_of (beg); |
28115 | last_object = beg; |
28116 | end_card = 0; |
28117 | continue; |
28118 | } |
28119 | else |
28120 | { |
28121 | break; |
28122 | } |
28123 | } |
28124 | |
28125 | assert (card_set_p (card)); |
28126 | { |
28127 | uint8_t* o = last_object; |
28128 | |
28129 | o = find_first_object (start_address, last_object); |
28130 | // Never visit an object twice. |
28131 | assert (o >= last_object); |
28132 | |
28133 | //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix", |
28134 | dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix" , |
28135 | card, (size_t)o, (size_t)limit, (size_t)gen_boundary)); |
28136 | |
28137 | while (o < limit) |
28138 | { |
28139 | assert (Align (size (o)) >= Align (min_obj_size)); |
28140 | size_t s = size (o); |
28141 | |
28142 | uint8_t* next_o = o + Align (s); |
28143 | Prefetch (next_o); |
28144 | |
28145 | if ((o >= gen_boundary) && |
28146 | (seg == ephemeral_heap_segment)) |
28147 | { |
28148 | dprintf (3, ("switching gen boundary %Ix" , (size_t)gen_boundary)); |
28149 | curr_gen_number--; |
28150 | assert ((curr_gen_number > 0)); |
28151 | gen_boundary = generation_allocation_start |
28152 | (generation_of (curr_gen_number - 1)); |
28153 | next_boundary = (compute_next_boundary |
28154 | (low, curr_gen_number, relocating)); |
28155 | } |
28156 | |
28157 | dprintf (4, ("|%Ix|" , (size_t)o)); |
28158 | |
28159 | if (next_o < start_address) |
28160 | { |
28161 | goto end_object; |
28162 | } |
28163 | |
28164 | #ifdef BACKGROUND_GC |
28165 | if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p)) |
28166 | { |
28167 | goto end_object; |
28168 | } |
28169 | #endif //BACKGROUND_GC |
28170 | |
28171 | #ifdef COLLECTIBLE_CLASS |
28172 | if (is_collectible(o)) |
28173 | { |
28174 | BOOL passed_end_card_p = FALSE; |
28175 | |
28176 | if (card_of (o) > card) |
28177 | { |
28178 | passed_end_card_p = card_transition (o, end, card_word_end, |
28179 | cg_pointers_found, |
28180 | n_eph, n_card_set, |
28181 | card, end_card, |
28182 | foundp, start_address, |
28183 | limit, total_cards_cleared); |
28184 | } |
28185 | |
28186 | if ((!passed_end_card_p || foundp) && (card_of (o) == card)) |
28187 | { |
28188 | // card is valid and it covers the head of the object |
28189 | if (fn == &gc_heap::relocate_address) |
28190 | { |
28191 | keep_card_live (o, n_gen, cg_pointers_found); |
28192 | } |
28193 | else |
28194 | { |
28195 | uint8_t* class_obj = get_class_object (o); |
28196 | mark_through_cards_helper (&class_obj, n_gen, |
28197 | cg_pointers_found, fn, |
28198 | nhigh, next_boundary); |
28199 | } |
28200 | } |
28201 | |
28202 | if (passed_end_card_p) |
28203 | { |
28204 | if (foundp && (card_address (card) < next_o)) |
28205 | { |
28206 | goto go_through_refs; |
28207 | } |
28208 | else if (foundp && (start_address < limit)) |
28209 | { |
28210 | next_o = find_first_object (start_address, o); |
28211 | goto end_object; |
28212 | } |
28213 | else |
28214 | goto end_limit; |
28215 | } |
28216 | } |
28217 | |
28218 | go_through_refs: |
28219 | #endif //COLLECTIBLE_CLASS |
28220 | |
28221 | if (contain_pointers (o)) |
28222 | { |
28223 | dprintf(3,("Going through %Ix start_address: %Ix" , (size_t)o, (size_t)start_address)); |
28224 | |
28225 | { |
28226 | dprintf (4, ("normal object path" )); |
28227 | go_through_object |
28228 | (method_table(o), o, s, poo, |
28229 | start_address, use_start, (o + s), |
28230 | { |
28231 | dprintf (4, ("<%Ix>:%Ix" , (size_t)poo, (size_t)*poo)); |
28232 | if (card_of ((uint8_t*)poo) > card) |
28233 | { |
28234 | BOOL passed_end_card_p = card_transition ((uint8_t*)poo, end, |
28235 | card_word_end, |
28236 | cg_pointers_found, |
28237 | n_eph, n_card_set, |
28238 | card, end_card, |
28239 | foundp, start_address, |
28240 | limit, total_cards_cleared); |
28241 | |
28242 | if (passed_end_card_p) |
28243 | { |
28244 | if (foundp && (card_address (card) < next_o)) |
28245 | { |
28246 | //new_start(); |
28247 | { |
28248 | if (ppstop <= (uint8_t**)start_address) |
28249 | {break;} |
28250 | else if (poo < (uint8_t**)start_address) |
28251 | {poo = (uint8_t**)start_address;} |
28252 | } |
28253 | } |
28254 | else if (foundp && (start_address < limit)) |
28255 | { |
28256 | next_o = find_first_object (start_address, o); |
28257 | goto end_object; |
28258 | } |
28259 | else |
28260 | goto end_limit; |
28261 | } |
28262 | } |
28263 | |
28264 | mark_through_cards_helper (poo, n_gen, |
28265 | cg_pointers_found, fn, |
28266 | nhigh, next_boundary); |
28267 | } |
28268 | ); |
28269 | } |
28270 | } |
28271 | |
28272 | end_object: |
28273 | if (((size_t)next_o / brick_size) != ((size_t) o / brick_size)) |
28274 | { |
28275 | if (brick_table [brick_of (o)] <0) |
28276 | fix_brick_to_highest (o, next_o); |
28277 | } |
28278 | o = next_o; |
28279 | } |
28280 | end_limit: |
28281 | last_object = o; |
28282 | } |
28283 | } |
28284 | // compute the efficiency ratio of the card table |
28285 | if (!relocating) |
28286 | { |
28287 | generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100); |
28288 | dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d" , |
28289 | n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio)); |
28290 | } |
28291 | else |
28292 | { |
28293 | dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d" , |
28294 | n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio)); |
28295 | } |
28296 | } |
28297 | |
28298 | #ifdef SEG_REUSE_STATS |
28299 | size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size) |
28300 | { |
28301 | size_t total_items = 0; |
28302 | *total_size = 0; |
28303 | for (int i = 0; i < count; i++) |
28304 | { |
28305 | total_items += ordered_indices[i]; |
28306 | *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i); |
28307 | dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d" , heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i))); |
28308 | } |
28309 | dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix" , heap_number, total_items, *total_size)); |
28310 | return total_items; |
28311 | } |
28312 | #endif // SEG_REUSE_STATS |
28313 | |
28314 | void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug) |
28315 | { |
28316 | // detect pinned plugs |
28317 | if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin()))) |
28318 | { |
28319 | deque_pinned_plug(); |
28320 | update_oldest_pinned_plug(); |
28321 | dprintf (3, ("dequed pin,now oldest pin is %Ix" , pinned_plug (oldest_pin()))); |
28322 | } |
28323 | else |
28324 | { |
28325 | size_t plug_size = last_plug_size + Align(min_obj_size); |
28326 | BOOL is_padded = FALSE; |
28327 | |
28328 | #ifdef SHORT_PLUGS |
28329 | plug_size += Align (min_obj_size); |
28330 | is_padded = TRUE; |
28331 | #endif //SHORT_PLUGS |
28332 | |
28333 | #ifdef RESPECT_LARGE_ALIGNMENT |
28334 | plug_size += switch_alignment_size (is_padded); |
28335 | #endif //RESPECT_LARGE_ALIGNMENT |
28336 | |
28337 | total_ephemeral_plugs += plug_size; |
28338 | size_t plug_size_power2 = round_up_power2 (plug_size); |
28339 | ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++; |
28340 | dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array" , |
28341 | heap_number, |
28342 | last_plug, |
28343 | plug_size, |
28344 | (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2))); |
28345 | } |
28346 | } |
28347 | |
28348 | void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug) |
28349 | { |
28350 | assert ((tree != NULL)); |
28351 | if (node_left_child (tree)) |
28352 | { |
28353 | count_plugs_in_brick (tree + node_left_child (tree), last_plug); |
28354 | } |
28355 | |
28356 | if (last_plug != 0) |
28357 | { |
28358 | uint8_t* plug = tree; |
28359 | size_t gap_size = node_gap_size (plug); |
28360 | uint8_t* gap = (plug - gap_size); |
28361 | uint8_t* last_plug_end = gap; |
28362 | size_t last_plug_size = (last_plug_end - last_plug); |
28363 | dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix" , |
28364 | tree, last_plug, gap_size, gap, last_plug_size)); |
28365 | |
28366 | if (tree == oldest_pinned_plug) |
28367 | { |
28368 | dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix" , |
28369 | tree, last_plug, last_plug_size)); |
28370 | mark* m = oldest_pin(); |
28371 | if (m->has_pre_plug_info()) |
28372 | { |
28373 | last_plug_size += sizeof (gap_reloc_pair); |
28374 | dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix" , tree, last_plug_size)); |
28375 | } |
28376 | } |
28377 | // Can't assert here - if it's a pinned plug it can be less. |
28378 | //assert (last_plug_size >= Align (min_obj_size)); |
28379 | |
28380 | count_plug (last_plug_size, last_plug); |
28381 | } |
28382 | |
28383 | last_plug = tree; |
28384 | |
28385 | if (node_right_child (tree)) |
28386 | { |
28387 | count_plugs_in_brick (tree + node_right_child (tree), last_plug); |
28388 | } |
28389 | } |
28390 | |
28391 | void gc_heap::build_ordered_plug_indices () |
28392 | { |
28393 | memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices)); |
28394 | memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices)); |
28395 | |
28396 | uint8_t* start_address = generation_limit (max_generation); |
28397 | uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment); |
28398 | size_t current_brick = brick_of (start_address); |
28399 | size_t end_brick = brick_of (end_address - 1); |
28400 | uint8_t* last_plug = 0; |
28401 | |
28402 | //Look for the right pinned plug to start from. |
28403 | reset_pinned_queue_bos(); |
28404 | while (!pinned_plug_que_empty_p()) |
28405 | { |
28406 | mark* m = oldest_pin(); |
28407 | if ((m->first >= start_address) && (m->first < end_address)) |
28408 | { |
28409 | dprintf (3, ("found a pin %Ix between %Ix and %Ix" , m->first, start_address, end_address)); |
28410 | |
28411 | break; |
28412 | } |
28413 | else |
28414 | deque_pinned_plug(); |
28415 | } |
28416 | |
28417 | update_oldest_pinned_plug(); |
28418 | |
28419 | while (current_brick <= end_brick) |
28420 | { |
28421 | int brick_entry = brick_table [ current_brick ]; |
28422 | if (brick_entry >= 0) |
28423 | { |
28424 | count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug); |
28425 | } |
28426 | |
28427 | current_brick++; |
28428 | } |
28429 | |
28430 | if (last_plug !=0) |
28431 | { |
28432 | count_plug (end_address - last_plug, last_plug); |
28433 | } |
28434 | |
28435 | // we need to make sure that after fitting all the existing plugs, we |
28436 | // have big enough free space left to guarantee that the next allocation |
28437 | // will succeed. |
28438 | size_t = END_SPACE_AFTER_GC + Align (min_obj_size); |
28439 | total_ephemeral_plugs += extra_size; |
28440 | dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs" )); |
28441 | ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++; |
28442 | |
28443 | memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices)); |
28444 | |
28445 | #ifdef SEG_REUSE_STATS |
28446 | dprintf (SEG_REUSE_LOG_0, ("Plugs:" )); |
28447 | size_t total_plug_power2 = 0; |
28448 | dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2); |
28449 | dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))" , |
28450 | total_ephemeral_plugs, |
28451 | total_plug_power2, |
28452 | (total_ephemeral_plugs ? |
28453 | (total_plug_power2 * 100 / total_ephemeral_plugs) : |
28454 | 0))); |
28455 | dprintf (SEG_REUSE_LOG_0, ("-------------------" )); |
28456 | #endif // SEG_REUSE_STATS |
28457 | } |
28458 | |
28459 | void gc_heap::init_ordered_free_space_indices () |
28460 | { |
28461 | memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices)); |
28462 | memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices)); |
28463 | } |
28464 | |
28465 | void gc_heap::trim_free_spaces_indices () |
28466 | { |
28467 | trimmed_free_space_index = -1; |
28468 | size_t max_count = max_free_space_items - 1; |
28469 | size_t count = 0; |
28470 | int i = 0; |
28471 | for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--) |
28472 | { |
28473 | count += ordered_free_space_indices[i]; |
28474 | |
28475 | if (count >= max_count) |
28476 | { |
28477 | break; |
28478 | } |
28479 | } |
28480 | |
28481 | ptrdiff_t = count - max_count; |
28482 | |
28483 | if (extra_free_space_items > 0) |
28484 | { |
28485 | ordered_free_space_indices[i] -= extra_free_space_items; |
28486 | free_space_items = max_count; |
28487 | trimmed_free_space_index = i; |
28488 | } |
28489 | else |
28490 | { |
28491 | free_space_items = count; |
28492 | } |
28493 | |
28494 | if (i == -1) |
28495 | { |
28496 | i = 0; |
28497 | } |
28498 | |
28499 | free_space_buckets = MAX_NUM_BUCKETS - i; |
28500 | |
28501 | for (--i; i >= 0; i--) |
28502 | { |
28503 | ordered_free_space_indices[i] = 0; |
28504 | } |
28505 | |
28506 | memcpy (saved_ordered_free_space_indices, |
28507 | ordered_free_space_indices, |
28508 | sizeof(ordered_free_space_indices)); |
28509 | } |
28510 | |
28511 | // We fit as many plugs as we can and update the number of plugs left and the number |
28512 | // of free spaces left. |
28513 | BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index) |
28514 | { |
28515 | assert (small_index <= big_index); |
28516 | assert (big_index < MAX_NUM_BUCKETS); |
28517 | |
28518 | size_t small_blocks = ordered_blocks[small_index]; |
28519 | |
28520 | if (small_blocks == 0) |
28521 | { |
28522 | return TRUE; |
28523 | } |
28524 | |
28525 | size_t big_spaces = ordered_spaces[big_index]; |
28526 | |
28527 | if (big_spaces == 0) |
28528 | { |
28529 | return FALSE; |
28530 | } |
28531 | |
28532 | dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces" , |
28533 | heap_number, |
28534 | small_blocks, (small_index + MIN_INDEX_POWER2), |
28535 | big_spaces, (big_index + MIN_INDEX_POWER2))); |
28536 | |
28537 | size_t big_to_small = big_spaces << (big_index - small_index); |
28538 | |
28539 | ptrdiff_t = big_to_small - small_blocks; |
28540 | dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks" , |
28541 | heap_number, |
28542 | big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2))); |
28543 | BOOL can_fit = (extra_small_spaces >= 0); |
28544 | |
28545 | if (can_fit) |
28546 | { |
28547 | dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks" , |
28548 | heap_number, |
28549 | extra_small_spaces, (small_index + MIN_INDEX_POWER2))); |
28550 | } |
28551 | |
28552 | int i = 0; |
28553 | |
28554 | dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0" , heap_number, (big_index + MIN_INDEX_POWER2))); |
28555 | ordered_spaces[big_index] = 0; |
28556 | if (extra_small_spaces > 0) |
28557 | { |
28558 | dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0" , heap_number, (small_index + MIN_INDEX_POWER2))); |
28559 | ordered_blocks[small_index] = 0; |
28560 | for (i = small_index; i < big_index; i++) |
28561 | { |
28562 | if (extra_small_spaces & 1) |
28563 | { |
28564 | dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d" , |
28565 | heap_number, |
28566 | (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1))); |
28567 | ordered_spaces[i] += 1; |
28568 | } |
28569 | extra_small_spaces >>= 1; |
28570 | } |
28571 | |
28572 | dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d" , |
28573 | heap_number, |
28574 | (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces))); |
28575 | ordered_spaces[i] += extra_small_spaces; |
28576 | } |
28577 | else |
28578 | { |
28579 | dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d" , |
28580 | heap_number, |
28581 | (small_index + MIN_INDEX_POWER2), |
28582 | ordered_blocks[small_index], |
28583 | (ordered_blocks[small_index] - big_to_small))); |
28584 | ordered_blocks[small_index] -= big_to_small; |
28585 | } |
28586 | |
28587 | #ifdef SEG_REUSE_STATS |
28588 | size_t temp; |
28589 | dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:" , heap_number)); |
28590 | dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp); |
28591 | |
28592 | dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:" , heap_number)); |
28593 | dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp); |
28594 | #endif //SEG_REUSE_STATS |
28595 | |
28596 | return can_fit; |
28597 | } |
28598 | |
28599 | // space_index gets updated to the biggest available space index. |
28600 | BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index) |
28601 | { |
28602 | assert (*space_index >= block_index); |
28603 | |
28604 | while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index)) |
28605 | { |
28606 | (*space_index)--; |
28607 | if (*space_index < block_index) |
28608 | { |
28609 | return FALSE; |
28610 | } |
28611 | } |
28612 | |
28613 | return TRUE; |
28614 | } |
28615 | |
28616 | BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count) |
28617 | { |
28618 | #ifdef FEATURE_STRUCTALIGN |
28619 | // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account |
28620 | return FALSE; |
28621 | #endif // FEATURE_STRUCTALIGN |
28622 | int space_index = count - 1; |
28623 | for (int block_index = (count - 1); block_index >= 0; block_index--) |
28624 | { |
28625 | if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index)) |
28626 | { |
28627 | return FALSE; |
28628 | } |
28629 | } |
28630 | |
28631 | return TRUE; |
28632 | } |
28633 | |
28634 | void gc_heap::build_ordered_free_spaces (heap_segment* seg) |
28635 | { |
28636 | assert (bestfit_seg); |
28637 | |
28638 | //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2, |
28639 | // ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets), |
28640 | // free_space_buckets, |
28641 | // free_space_items); |
28642 | |
28643 | bestfit_seg->add_buckets (MIN_INDEX_POWER2, |
28644 | ordered_free_space_indices, |
28645 | MAX_NUM_BUCKETS, |
28646 | free_space_items); |
28647 | |
28648 | assert (settings.condemned_generation == max_generation); |
28649 | |
28650 | uint8_t* first_address = heap_segment_mem (seg); |
28651 | uint8_t* end_address = heap_segment_reserved (seg); |
28652 | //look through the pinned plugs for relevant ones. |
28653 | //Look for the right pinned plug to start from. |
28654 | reset_pinned_queue_bos(); |
28655 | mark* m = 0; |
28656 | // See comment in can_expand_into_p why we need (max_generation + 1). |
28657 | size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1); |
28658 | BOOL has_fit_gen_starts = FALSE; |
28659 | |
28660 | while (!pinned_plug_que_empty_p()) |
28661 | { |
28662 | m = oldest_pin(); |
28663 | if ((pinned_plug (m) >= first_address) && |
28664 | (pinned_plug (m) < end_address) && |
28665 | (pinned_len (m) >= eph_gen_starts)) |
28666 | { |
28667 | |
28668 | assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin); |
28669 | break; |
28670 | } |
28671 | else |
28672 | { |
28673 | deque_pinned_plug(); |
28674 | } |
28675 | } |
28676 | |
28677 | if (!pinned_plug_que_empty_p()) |
28678 | { |
28679 | bestfit_seg->add ((void*)m, TRUE, TRUE); |
28680 | deque_pinned_plug(); |
28681 | m = oldest_pin(); |
28682 | has_fit_gen_starts = TRUE; |
28683 | } |
28684 | |
28685 | while (!pinned_plug_que_empty_p() && |
28686 | ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))) |
28687 | { |
28688 | bestfit_seg->add ((void*)m, TRUE, FALSE); |
28689 | deque_pinned_plug(); |
28690 | m = oldest_pin(); |
28691 | } |
28692 | |
28693 | if (commit_end_of_seg) |
28694 | { |
28695 | if (!has_fit_gen_starts) |
28696 | { |
28697 | assert (bestfit_first_pin == heap_segment_plan_allocated (seg)); |
28698 | } |
28699 | bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts)); |
28700 | } |
28701 | |
28702 | #ifdef _DEBUG |
28703 | bestfit_seg->check(); |
28704 | #endif //_DEBUG |
28705 | } |
28706 | |
28707 | BOOL gc_heap::try_best_fit (BOOL end_of_segment_p) |
28708 | { |
28709 | if (!end_of_segment_p) |
28710 | { |
28711 | trim_free_spaces_indices (); |
28712 | } |
28713 | |
28714 | BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices, |
28715 | ordered_free_space_indices, |
28716 | MAX_NUM_BUCKETS); |
28717 | |
28718 | return can_bestfit; |
28719 | } |
28720 | |
28721 | BOOL gc_heap::best_fit (size_t free_space, |
28722 | size_t largest_free_space, |
28723 | size_t additional_space, |
28724 | BOOL* use_additional_space) |
28725 | { |
28726 | dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism" , settings.condemned_generation)); |
28727 | |
28728 | assert (!additional_space || (additional_space && use_additional_space)); |
28729 | if (use_additional_space) |
28730 | { |
28731 | *use_additional_space = FALSE; |
28732 | } |
28733 | |
28734 | if (ordered_plug_indices_init == FALSE) |
28735 | { |
28736 | total_ephemeral_plugs = 0; |
28737 | build_ordered_plug_indices(); |
28738 | ordered_plug_indices_init = TRUE; |
28739 | } |
28740 | else |
28741 | { |
28742 | memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices)); |
28743 | } |
28744 | |
28745 | if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size))) |
28746 | { |
28747 | dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done" )); |
28748 | size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1)); |
28749 | BOOL can_fit_empty_eph = (largest_free_space >= empty_eph); |
28750 | if (!can_fit_empty_eph) |
28751 | { |
28752 | can_fit_empty_eph = (additional_space >= empty_eph); |
28753 | |
28754 | if (can_fit_empty_eph) |
28755 | { |
28756 | *use_additional_space = TRUE; |
28757 | } |
28758 | } |
28759 | |
28760 | return can_fit_empty_eph; |
28761 | } |
28762 | |
28763 | if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space)) |
28764 | { |
28765 | dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done" )); |
28766 | return FALSE; |
28767 | } |
28768 | |
28769 | if ((free_space + additional_space) == 0) |
28770 | { |
28771 | dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done" )); |
28772 | return FALSE; |
28773 | } |
28774 | |
28775 | #ifdef SEG_REUSE_STATS |
28776 | dprintf (SEG_REUSE_LOG_0, ("Free spaces:" )); |
28777 | size_t total_free_space_power2 = 0; |
28778 | size_t total_free_space_items = |
28779 | dump_buckets (ordered_free_space_indices, |
28780 | MAX_NUM_BUCKETS, |
28781 | &total_free_space_power2); |
28782 | dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id" , max_free_space_items)); |
28783 | |
28784 | dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix" , |
28785 | total_ephemeral_plugs, |
28786 | free_space, |
28787 | total_free_space_power2, |
28788 | (free_space ? (total_free_space_power2 * 100 / free_space) : 0), |
28789 | additional_space)); |
28790 | |
28791 | size_t saved_all_free_space_indices[MAX_NUM_BUCKETS]; |
28792 | memcpy (saved_all_free_space_indices, |
28793 | ordered_free_space_indices, |
28794 | sizeof(saved_all_free_space_indices)); |
28795 | |
28796 | #endif // SEG_REUSE_STATS |
28797 | |
28798 | if (total_ephemeral_plugs > (free_space + additional_space)) |
28799 | { |
28800 | return FALSE; |
28801 | } |
28802 | |
28803 | use_bestfit = try_best_fit(FALSE); |
28804 | |
28805 | if (!use_bestfit && additional_space) |
28806 | { |
28807 | int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space)); |
28808 | |
28809 | if (relative_free_space_index != -1) |
28810 | { |
28811 | int relative_plug_index = 0; |
28812 | size_t plugs_to_fit = 0; |
28813 | |
28814 | for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--) |
28815 | { |
28816 | plugs_to_fit = ordered_plug_indices[relative_plug_index]; |
28817 | if (plugs_to_fit != 0) |
28818 | { |
28819 | break; |
28820 | } |
28821 | } |
28822 | |
28823 | if ((relative_plug_index > relative_free_space_index) || |
28824 | ((relative_plug_index == relative_free_space_index) && |
28825 | (plugs_to_fit > 1))) |
28826 | { |
28827 | #ifdef SEG_REUSE_STATS |
28828 | dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)" , |
28829 | (relative_free_space_index + MIN_INDEX_POWER2), |
28830 | plugs_to_fit, |
28831 | (relative_plug_index + MIN_INDEX_POWER2))); |
28832 | #endif // SEG_REUSE_STATS |
28833 | goto adjust; |
28834 | } |
28835 | |
28836 | dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)" , (relative_free_space_index + MIN_INDEX_POWER2))); |
28837 | ordered_free_space_indices[relative_free_space_index]++; |
28838 | use_bestfit = try_best_fit(TRUE); |
28839 | if (use_bestfit) |
28840 | { |
28841 | free_space_items++; |
28842 | // Since we might've trimmed away some of the free spaces we had, we should see |
28843 | // if we really need to use end of seg space - if it's the same or smaller than |
28844 | // the largest space we trimmed we can just add that one back instead of |
28845 | // using end of seg. |
28846 | if (relative_free_space_index > trimmed_free_space_index) |
28847 | { |
28848 | *use_additional_space = TRUE; |
28849 | } |
28850 | else |
28851 | { |
28852 | // If the addition space is <= than the last trimmed space, we |
28853 | // should just use that last trimmed space instead. |
28854 | saved_ordered_free_space_indices[trimmed_free_space_index]++; |
28855 | } |
28856 | } |
28857 | } |
28858 | } |
28859 | |
28860 | adjust: |
28861 | |
28862 | if (!use_bestfit) |
28863 | { |
28864 | dprintf (SEG_REUSE_LOG_0, ("couldn't fit..." )); |
28865 | |
28866 | #ifdef SEG_REUSE_STATS |
28867 | size_t saved_max = max_free_space_items; |
28868 | BOOL temp_bestfit = FALSE; |
28869 | |
28870 | dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----" )); |
28871 | dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id" , max_free_space_items)); |
28872 | |
28873 | // TODO: need to take the end of segment into consideration. |
28874 | while (max_free_space_items <= total_free_space_items) |
28875 | { |
28876 | max_free_space_items += max_free_space_items / 2; |
28877 | dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id" , max_free_space_items)); |
28878 | memcpy (ordered_free_space_indices, |
28879 | saved_all_free_space_indices, |
28880 | sizeof(ordered_free_space_indices)); |
28881 | if (try_best_fit(FALSE)) |
28882 | { |
28883 | temp_bestfit = TRUE; |
28884 | break; |
28885 | } |
28886 | } |
28887 | |
28888 | if (temp_bestfit) |
28889 | { |
28890 | dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit" , max_free_space_items)); |
28891 | } |
28892 | else |
28893 | { |
28894 | dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space" )); |
28895 | } |
28896 | |
28897 | dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id" , saved_max)); |
28898 | max_free_space_items = saved_max; |
28899 | #endif // SEG_REUSE_STATS |
28900 | if (free_space_items) |
28901 | { |
28902 | max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2); |
28903 | max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES); |
28904 | } |
28905 | else |
28906 | { |
28907 | max_free_space_items = MAX_NUM_FREE_SPACES; |
28908 | } |
28909 | } |
28910 | |
28911 | dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id" , max_free_space_items)); |
28912 | dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n" )); |
28913 | |
28914 | return use_bestfit; |
28915 | } |
28916 | |
28917 | BOOL gc_heap::process_free_space (heap_segment* seg, |
28918 | size_t free_space, |
28919 | size_t min_free_size, |
28920 | size_t min_cont_size, |
28921 | size_t* total_free_space, |
28922 | size_t* largest_free_space) |
28923 | { |
28924 | *total_free_space += free_space; |
28925 | *largest_free_space = max (*largest_free_space, free_space); |
28926 | |
28927 | #ifdef SIMPLE_DPRINTF |
28928 | dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix" , |
28929 | free_space, *total_free_space, *largest_free_space)); |
28930 | #endif //SIMPLE_DPRINTF |
28931 | |
28932 | if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size)) |
28933 | { |
28934 | #ifdef SIMPLE_DPRINTF |
28935 | dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit" , |
28936 | settings.condemned_generation, |
28937 | *total_free_space, min_free_size, *largest_free_space, min_cont_size, |
28938 | (size_t)seg)); |
28939 | #else |
28940 | UNREFERENCED_PARAMETER(seg); |
28941 | #endif //SIMPLE_DPRINTF |
28942 | return TRUE; |
28943 | } |
28944 | |
28945 | int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space)); |
28946 | if (free_space_index != -1) |
28947 | { |
28948 | ordered_free_space_indices[free_space_index]++; |
28949 | } |
28950 | return FALSE; |
28951 | } |
28952 | |
28953 | BOOL gc_heap::expand_reused_seg_p() |
28954 | { |
28955 | BOOL reused_seg = FALSE; |
28956 | int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand); |
28957 | if ((heap_expand_mechanism == expand_reuse_bestfit) || |
28958 | (heap_expand_mechanism == expand_reuse_normal)) |
28959 | { |
28960 | reused_seg = TRUE; |
28961 | } |
28962 | |
28963 | return reused_seg; |
28964 | } |
28965 | |
28966 | BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size, |
28967 | allocator* gen_allocator) |
28968 | { |
28969 | min_cont_size += END_SPACE_AFTER_GC; |
28970 | use_bestfit = FALSE; |
28971 | commit_end_of_seg = FALSE; |
28972 | bestfit_first_pin = 0; |
28973 | uint8_t* first_address = heap_segment_mem (seg); |
28974 | uint8_t* end_address = heap_segment_reserved (seg); |
28975 | size_t = end_space_after_gc(); |
28976 | |
28977 | if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg)) |
28978 | { |
28979 | dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end" , |
28980 | first_address, end_address, end_extra_space)); |
28981 | return FALSE; |
28982 | } |
28983 | |
28984 | end_address -= end_extra_space; |
28985 | |
28986 | dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix" , |
28987 | settings.condemned_generation, min_free_size, min_cont_size)); |
28988 | size_t eph_gen_starts = eph_gen_starts_size; |
28989 | |
28990 | if (settings.condemned_generation == max_generation) |
28991 | { |
28992 | size_t free_space = 0; |
28993 | size_t largest_free_space = free_space; |
28994 | dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix" , first_address, end_address)); |
28995 | //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from. |
28996 | //We are going to allocate the generation starts in the 1st free space, |
28997 | //so start from the first free space that's big enough for gen starts and a min object size. |
28998 | // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it - |
28999 | // we could use it by allocating the last generation start a bit bigger but |
29000 | // the complexity isn't worth the effort (those plugs are from gen2 |
29001 | // already anyway). |
29002 | reset_pinned_queue_bos(); |
29003 | mark* m = 0; |
29004 | BOOL has_fit_gen_starts = FALSE; |
29005 | |
29006 | init_ordered_free_space_indices (); |
29007 | while (!pinned_plug_que_empty_p()) |
29008 | { |
29009 | m = oldest_pin(); |
29010 | if ((pinned_plug (m) >= first_address) && |
29011 | (pinned_plug (m) < end_address) && |
29012 | (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size)))) |
29013 | { |
29014 | break; |
29015 | } |
29016 | else |
29017 | { |
29018 | deque_pinned_plug(); |
29019 | } |
29020 | } |
29021 | |
29022 | if (!pinned_plug_que_empty_p()) |
29023 | { |
29024 | bestfit_first_pin = pinned_plug (m) - pinned_len (m); |
29025 | |
29026 | if (process_free_space (seg, |
29027 | pinned_len (m) - eph_gen_starts, |
29028 | min_free_size, min_cont_size, |
29029 | &free_space, &largest_free_space)) |
29030 | { |
29031 | return TRUE; |
29032 | } |
29033 | |
29034 | deque_pinned_plug(); |
29035 | m = oldest_pin(); |
29036 | has_fit_gen_starts = TRUE; |
29037 | } |
29038 | |
29039 | dprintf (3, ("first pin is %Ix" , pinned_plug (m))); |
29040 | |
29041 | //tally up free space |
29042 | while (!pinned_plug_que_empty_p() && |
29043 | ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))) |
29044 | { |
29045 | dprintf (3, ("looking at pin %Ix" , pinned_plug (m))); |
29046 | if (process_free_space (seg, |
29047 | pinned_len (m), |
29048 | min_free_size, min_cont_size, |
29049 | &free_space, &largest_free_space)) |
29050 | { |
29051 | return TRUE; |
29052 | } |
29053 | |
29054 | deque_pinned_plug(); |
29055 | m = oldest_pin(); |
29056 | } |
29057 | |
29058 | //try to find space at the end of the segment. |
29059 | size_t end_space = (end_address - heap_segment_plan_allocated (seg)); |
29060 | size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0); |
29061 | dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix" , end_space, additional_space)); |
29062 | if (end_space >= additional_space) |
29063 | { |
29064 | BOOL can_fit = TRUE; |
29065 | commit_end_of_seg = TRUE; |
29066 | |
29067 | if (largest_free_space < min_cont_size) |
29068 | { |
29069 | if (end_space >= min_cont_size) |
29070 | { |
29071 | additional_space = max (min_cont_size, additional_space); |
29072 | dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph" , |
29073 | seg)); |
29074 | } |
29075 | else |
29076 | { |
29077 | if (settings.concurrent) |
29078 | { |
29079 | can_fit = FALSE; |
29080 | commit_end_of_seg = FALSE; |
29081 | } |
29082 | else |
29083 | { |
29084 | size_t additional_space_bestfit = additional_space; |
29085 | if (!has_fit_gen_starts) |
29086 | { |
29087 | if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size))) |
29088 | { |
29089 | dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id" , |
29090 | additional_space_bestfit)); |
29091 | return FALSE; |
29092 | } |
29093 | |
29094 | bestfit_first_pin = heap_segment_plan_allocated (seg); |
29095 | additional_space_bestfit -= eph_gen_starts; |
29096 | } |
29097 | |
29098 | can_fit = best_fit (free_space, |
29099 | largest_free_space, |
29100 | additional_space_bestfit, |
29101 | &commit_end_of_seg); |
29102 | |
29103 | if (can_fit) |
29104 | { |
29105 | dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg" , |
29106 | seg, (commit_end_of_seg ? "with" : "without" ))); |
29107 | } |
29108 | else |
29109 | { |
29110 | dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix" , (free_space + end_space))); |
29111 | } |
29112 | } |
29113 | } |
29114 | } |
29115 | else |
29116 | { |
29117 | dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg" , seg)); |
29118 | } |
29119 | |
29120 | assert (additional_space <= end_space); |
29121 | if (commit_end_of_seg) |
29122 | { |
29123 | if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space)) |
29124 | { |
29125 | dprintf (2, ("Couldn't commit end of segment?!" )); |
29126 | use_bestfit = FALSE; |
29127 | |
29128 | return FALSE; |
29129 | } |
29130 | |
29131 | if (use_bestfit) |
29132 | { |
29133 | // We increase the index here because growing heap segment could create a discrepency with |
29134 | // the additional space we used (could be bigger). |
29135 | size_t free_space_end_of_seg = |
29136 | heap_segment_committed (seg) - heap_segment_plan_allocated (seg); |
29137 | int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg)); |
29138 | saved_ordered_free_space_indices[relative_free_space_index]++; |
29139 | } |
29140 | } |
29141 | |
29142 | if (use_bestfit) |
29143 | { |
29144 | memcpy (ordered_free_space_indices, |
29145 | saved_ordered_free_space_indices, |
29146 | sizeof(ordered_free_space_indices)); |
29147 | max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2); |
29148 | max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items); |
29149 | dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max" , free_space_items, max_free_space_items)); |
29150 | } |
29151 | |
29152 | return can_fit; |
29153 | } |
29154 | |
29155 | dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix" , (free_space + end_space))); |
29156 | return FALSE; |
29157 | } |
29158 | else |
29159 | { |
29160 | assert (settings.condemned_generation == (max_generation-1)); |
29161 | size_t free_space = (end_address - heap_segment_plan_allocated (seg)); |
29162 | size_t largest_free_space = free_space; |
29163 | dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix" , first_address, end_address)); |
29164 | //find the first free list in range of the current segment |
29165 | size_t sz_list = gen_allocator->first_bucket_size(); |
29166 | unsigned int a_l_idx = 0; |
29167 | uint8_t* free_list = 0; |
29168 | for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++) |
29169 | { |
29170 | if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1))) |
29171 | { |
29172 | free_list = gen_allocator->alloc_list_head_of (a_l_idx); |
29173 | while (free_list) |
29174 | { |
29175 | if ((free_list >= first_address) && |
29176 | (free_list < end_address) && |
29177 | (unused_array_size (free_list) >= eph_gen_starts)) |
29178 | { |
29179 | goto next; |
29180 | } |
29181 | else |
29182 | { |
29183 | free_list = free_list_slot (free_list); |
29184 | } |
29185 | } |
29186 | } |
29187 | } |
29188 | next: |
29189 | if (free_list) |
29190 | { |
29191 | init_ordered_free_space_indices (); |
29192 | if (process_free_space (seg, |
29193 | unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size), |
29194 | min_free_size, min_cont_size, |
29195 | &free_space, &largest_free_space)) |
29196 | { |
29197 | return TRUE; |
29198 | } |
29199 | |
29200 | free_list = free_list_slot (free_list); |
29201 | } |
29202 | else |
29203 | { |
29204 | dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list" )); |
29205 | return FALSE; |
29206 | } |
29207 | |
29208 | //tally up free space |
29209 | |
29210 | while (1) |
29211 | { |
29212 | while (free_list) |
29213 | { |
29214 | if ((free_list >= first_address) && (free_list < end_address) && |
29215 | process_free_space (seg, |
29216 | unused_array_size (free_list), |
29217 | min_free_size, min_cont_size, |
29218 | &free_space, &largest_free_space)) |
29219 | { |
29220 | return TRUE; |
29221 | } |
29222 | |
29223 | free_list = free_list_slot (free_list); |
29224 | } |
29225 | a_l_idx++; |
29226 | if (a_l_idx < gen_allocator->number_of_buckets()) |
29227 | { |
29228 | free_list = gen_allocator->alloc_list_head_of (a_l_idx); |
29229 | } |
29230 | else |
29231 | break; |
29232 | } |
29233 | |
29234 | dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix" , free_space)); |
29235 | return FALSE; |
29236 | |
29237 | /* |
29238 | BOOL can_fit = best_fit (free_space, 0, NULL); |
29239 | if (can_fit) |
29240 | { |
29241 | dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg)); |
29242 | } |
29243 | else |
29244 | { |
29245 | dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space)); |
29246 | } |
29247 | |
29248 | return can_fit; |
29249 | */ |
29250 | } |
29251 | } |
29252 | |
29253 | void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug, |
29254 | generation* gen, uint8_t* start_address, |
29255 | unsigned int& active_new_gen_number, |
29256 | uint8_t*& last_pinned_gap, BOOL& leftp, |
29257 | BOOL shortened_p |
29258 | #ifdef SHORT_PLUGS |
29259 | , mark* pinned_plug_entry |
29260 | #endif //SHORT_PLUGS |
29261 | ) |
29262 | { |
29263 | // detect generation boundaries |
29264 | // make sure that active_new_gen_number is not the youngest generation. |
29265 | // because the generation_limit wouldn't return the right thing in this case. |
29266 | if (!use_bestfit) |
29267 | { |
29268 | if ((active_new_gen_number > 1) && |
29269 | (last_plug >= generation_limit (active_new_gen_number))) |
29270 | { |
29271 | assert (last_plug >= start_address); |
29272 | active_new_gen_number--; |
29273 | realloc_plan_generation_start (generation_of (active_new_gen_number), gen); |
29274 | assert (generation_plan_allocation_start (generation_of (active_new_gen_number))); |
29275 | leftp = FALSE; |
29276 | } |
29277 | } |
29278 | |
29279 | // detect pinned plugs |
29280 | if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin()))) |
29281 | { |
29282 | size_t entry = deque_pinned_plug(); |
29283 | mark* m = pinned_plug_of (entry); |
29284 | |
29285 | size_t saved_pinned_len = pinned_len(m); |
29286 | pinned_len(m) = last_plug - last_pinned_gap; |
29287 | //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug)); |
29288 | |
29289 | if (m->has_post_plug_info()) |
29290 | { |
29291 | last_plug_size += sizeof (gap_reloc_pair); |
29292 | dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix" , last_plug, last_plug_size)) |
29293 | } |
29294 | |
29295 | last_pinned_gap = last_plug + last_plug_size; |
29296 | dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix" , |
29297 | pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size)); |
29298 | leftp = FALSE; |
29299 | |
29300 | //we are creating a generation fault. set the cards. |
29301 | { |
29302 | size_t end_card = card_of (align_on_card (last_plug + last_plug_size)); |
29303 | size_t card = card_of (last_plug); |
29304 | while (card != end_card) |
29305 | { |
29306 | set_card (card); |
29307 | card++; |
29308 | } |
29309 | } |
29310 | } |
29311 | else if (last_plug >= start_address) |
29312 | { |
29313 | #ifdef FEATURE_STRUCTALIGN |
29314 | int requiredAlignment; |
29315 | ptrdiff_t pad; |
29316 | node_aligninfo (last_plug, requiredAlignment, pad); |
29317 | |
29318 | // from how we previously aligned the plug's destination address, |
29319 | // compute the actual alignment offset. |
29320 | uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug); |
29321 | ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0); |
29322 | if (!alignmentOffset) |
29323 | { |
29324 | // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero. |
29325 | alignmentOffset = requiredAlignment; |
29326 | } |
29327 | |
29328 | //clear the alignment info because we are reallocating |
29329 | clear_node_aligninfo (last_plug); |
29330 | #else // FEATURE_STRUCTALIGN |
29331 | //clear the realignment flag because we are reallocating |
29332 | clear_node_realigned (last_plug); |
29333 | #endif // FEATURE_STRUCTALIGN |
29334 | BOOL adjacentp = FALSE; |
29335 | BOOL set_padding_on_saved_p = FALSE; |
29336 | |
29337 | if (shortened_p) |
29338 | { |
29339 | last_plug_size += sizeof (gap_reloc_pair); |
29340 | |
29341 | #ifdef SHORT_PLUGS |
29342 | assert (pinned_plug_entry != NULL); |
29343 | if (last_plug_size <= sizeof (plug_and_gap)) |
29344 | { |
29345 | set_padding_on_saved_p = TRUE; |
29346 | } |
29347 | #endif //SHORT_PLUGS |
29348 | |
29349 | dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix" , last_plug, last_plug_size)) |
29350 | } |
29351 | |
29352 | #ifdef SHORT_PLUGS |
29353 | clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry); |
29354 | #endif //SHORT_PLUGS |
29355 | |
29356 | uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug, |
29357 | #ifdef SHORT_PLUGS |
29358 | set_padding_on_saved_p, |
29359 | pinned_plug_entry, |
29360 | #endif //SHORT_PLUGS |
29361 | TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG); |
29362 | |
29363 | dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix" , new_address, (new_address + last_plug_size), last_plug_size)); |
29364 | assert (new_address); |
29365 | set_node_relocation_distance (last_plug, new_address - last_plug); |
29366 | #ifdef FEATURE_STRUCTALIGN |
29367 | if (leftp && node_alignpad (last_plug) == 0) |
29368 | #else // FEATURE_STRUCTALIGN |
29369 | if (leftp && !node_realigned (last_plug)) |
29370 | #endif // FEATURE_STRUCTALIGN |
29371 | { |
29372 | // TODO - temporarily disable L optimization because of a bug in it. |
29373 | //set_node_left (last_plug); |
29374 | } |
29375 | dprintf (3,(" Re-allocating %Ix->%Ix len %Id" , (size_t)last_plug, (size_t)new_address, last_plug_size)); |
29376 | leftp = adjacentp; |
29377 | } |
29378 | } |
29379 | |
29380 | void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug, |
29381 | uint8_t* start_address, |
29382 | generation* gen, |
29383 | unsigned int& active_new_gen_number, |
29384 | uint8_t*& last_pinned_gap, BOOL& leftp) |
29385 | { |
29386 | assert (tree != NULL); |
29387 | int left_node = node_left_child (tree); |
29388 | int right_node = node_right_child (tree); |
29389 | |
29390 | dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d" , |
29391 | tree, last_pinned_gap, last_plug, left_node, right_node)); |
29392 | |
29393 | if (left_node) |
29394 | { |
29395 | dprintf (3, ("LN: realloc %Ix(%Ix)" , (tree + left_node), last_plug)); |
29396 | realloc_in_brick ((tree + left_node), last_plug, start_address, |
29397 | gen, active_new_gen_number, last_pinned_gap, |
29398 | leftp); |
29399 | } |
29400 | |
29401 | if (last_plug != 0) |
29402 | { |
29403 | uint8_t* plug = tree; |
29404 | |
29405 | BOOL has_pre_plug_info_p = FALSE; |
29406 | BOOL has_post_plug_info_p = FALSE; |
29407 | mark* pinned_plug_entry = get_next_pinned_entry (tree, |
29408 | &has_pre_plug_info_p, |
29409 | &has_post_plug_info_p, |
29410 | FALSE); |
29411 | |
29412 | // We only care about the pre plug info 'cause that's what decides if the last plug is shortened. |
29413 | // The pinned plugs are handled in realloc_plug. |
29414 | size_t gap_size = node_gap_size (plug); |
29415 | uint8_t* gap = (plug - gap_size); |
29416 | uint8_t* last_plug_end = gap; |
29417 | size_t last_plug_size = (last_plug_end - last_plug); |
29418 | // Cannot assert this - a plug could be less than that due to the shortened ones. |
29419 | //assert (last_plug_size >= Align (min_obj_size)); |
29420 | dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d" , |
29421 | plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0))); |
29422 | realloc_plug (last_plug_size, last_plug, gen, start_address, |
29423 | active_new_gen_number, last_pinned_gap, |
29424 | leftp, has_pre_plug_info_p |
29425 | #ifdef SHORT_PLUGS |
29426 | , pinned_plug_entry |
29427 | #endif //SHORT_PLUGS |
29428 | ); |
29429 | } |
29430 | |
29431 | last_plug = tree; |
29432 | |
29433 | if (right_node) |
29434 | { |
29435 | dprintf (3, ("RN: realloc %Ix(%Ix)" , (tree + right_node), last_plug)); |
29436 | realloc_in_brick ((tree + right_node), last_plug, start_address, |
29437 | gen, active_new_gen_number, last_pinned_gap, |
29438 | leftp); |
29439 | } |
29440 | } |
29441 | |
29442 | void |
29443 | gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg, |
29444 | uint8_t* start_address, uint8_t* end_address, |
29445 | unsigned active_new_gen_number) |
29446 | { |
29447 | dprintf (3, ("--- Reallocing ---" )); |
29448 | |
29449 | if (use_bestfit) |
29450 | { |
29451 | //make sure that every generation has a planned allocation start |
29452 | int gen_number = max_generation - 1; |
29453 | while (gen_number >= 0) |
29454 | { |
29455 | generation* gen = generation_of (gen_number); |
29456 | if (0 == generation_plan_allocation_start (gen)) |
29457 | { |
29458 | generation_plan_allocation_start (gen) = |
29459 | bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size); |
29460 | generation_plan_allocation_start_size (gen) = Align (min_obj_size); |
29461 | assert (generation_plan_allocation_start (gen)); |
29462 | } |
29463 | gen_number--; |
29464 | } |
29465 | } |
29466 | |
29467 | uint8_t* first_address = start_address; |
29468 | //Look for the right pinned plug to start from. |
29469 | reset_pinned_queue_bos(); |
29470 | uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg); |
29471 | while (!pinned_plug_que_empty_p()) |
29472 | { |
29473 | mark* m = oldest_pin(); |
29474 | if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address)) |
29475 | { |
29476 | if (pinned_plug (m) < first_address) |
29477 | { |
29478 | first_address = pinned_plug (m); |
29479 | } |
29480 | break; |
29481 | } |
29482 | else |
29483 | deque_pinned_plug(); |
29484 | } |
29485 | |
29486 | size_t current_brick = brick_of (first_address); |
29487 | size_t end_brick = brick_of (end_address-1); |
29488 | uint8_t* last_plug = 0; |
29489 | |
29490 | uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg); |
29491 | BOOL leftp = FALSE; |
29492 | |
29493 | dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix" , |
29494 | start_address, first_address, pinned_plug (oldest_pin()))); |
29495 | |
29496 | while (current_brick <= end_brick) |
29497 | { |
29498 | int brick_entry = brick_table [ current_brick ]; |
29499 | if (brick_entry >= 0) |
29500 | { |
29501 | realloc_in_brick ((brick_address (current_brick) + brick_entry - 1), |
29502 | last_plug, start_address, consing_gen, |
29503 | active_new_gen_number, last_pinned_gap, |
29504 | leftp); |
29505 | } |
29506 | current_brick++; |
29507 | } |
29508 | |
29509 | if (last_plug != 0) |
29510 | { |
29511 | realloc_plug (end_address - last_plug, last_plug, consing_gen, |
29512 | start_address, |
29513 | active_new_gen_number, last_pinned_gap, |
29514 | leftp, FALSE |
29515 | #ifdef SHORT_PLUGS |
29516 | , NULL |
29517 | #endif //SHORT_PLUGS |
29518 | ); |
29519 | } |
29520 | |
29521 | //Fix the old segment allocated size |
29522 | assert (last_pinned_gap >= heap_segment_mem (seg)); |
29523 | assert (last_pinned_gap <= heap_segment_committed (seg)); |
29524 | heap_segment_plan_allocated (seg) = last_pinned_gap; |
29525 | } |
29526 | |
29527 | void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end) |
29528 | { |
29529 | #ifdef VERIFY_HEAP |
29530 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
29531 | { |
29532 | BOOL contains_pinned_plugs = FALSE; |
29533 | size_t mi = 0; |
29534 | mark* m = 0; |
29535 | while (mi != mark_stack_tos) |
29536 | { |
29537 | m = pinned_plug_of (mi); |
29538 | if ((pinned_plug (m) >= start) && (pinned_plug (m) < end)) |
29539 | { |
29540 | contains_pinned_plugs = TRUE; |
29541 | break; |
29542 | } |
29543 | else |
29544 | mi++; |
29545 | } |
29546 | |
29547 | if (contains_pinned_plugs) |
29548 | { |
29549 | FATAL_GC_ERROR(); |
29550 | } |
29551 | } |
29552 | #endif //VERIFY_HEAP |
29553 | } |
29554 | |
29555 | void gc_heap::set_expand_in_full_gc (int condemned_gen_number) |
29556 | { |
29557 | if (!should_expand_in_full_gc) |
29558 | { |
29559 | if ((condemned_gen_number != max_generation) && |
29560 | (settings.pause_mode != pause_low_latency) && |
29561 | (settings.pause_mode != pause_sustained_low_latency)) |
29562 | { |
29563 | should_expand_in_full_gc = TRUE; |
29564 | } |
29565 | } |
29566 | } |
29567 | |
29568 | void gc_heap::save_ephemeral_generation_starts() |
29569 | { |
29570 | for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++) |
29571 | { |
29572 | saved_ephemeral_plan_start[ephemeral_generation] = |
29573 | generation_plan_allocation_start (generation_of (ephemeral_generation)); |
29574 | saved_ephemeral_plan_start_size[ephemeral_generation] = |
29575 | generation_plan_allocation_start_size (generation_of (ephemeral_generation)); |
29576 | } |
29577 | } |
29578 | |
29579 | generation* gc_heap::expand_heap (int condemned_generation, |
29580 | generation* consing_gen, |
29581 | heap_segment* new_heap_segment) |
29582 | { |
29583 | UNREFERENCED_PARAMETER(condemned_generation); |
29584 | assert (condemned_generation >= (max_generation -1)); |
29585 | unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap |
29586 | uint8_t* start_address = generation_limit (max_generation); |
29587 | uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment); |
29588 | BOOL should_promote_ephemeral = FALSE; |
29589 | ptrdiff_t eph_size = total_ephemeral_size; |
29590 | #ifdef BACKGROUND_GC |
29591 | dprintf(2,("%s: ---- Heap Expansion ----" , (recursive_gc_sync::background_running_p() ? "FGC" : "NGC" ))); |
29592 | #endif //BACKGROUND_GC |
29593 | settings.heap_expansion = TRUE; |
29594 | |
29595 | #ifdef BACKGROUND_GC |
29596 | if (cm_in_progress) |
29597 | { |
29598 | if (!expanded_in_fgc) |
29599 | { |
29600 | expanded_in_fgc = TRUE; |
29601 | } |
29602 | } |
29603 | #endif //BACKGROUND_GC |
29604 | |
29605 | //reset the elevation state for next time. |
29606 | dprintf (2, ("Elevation: elevation = el_none" )); |
29607 | if (settings.should_lock_elevation && !expand_reused_seg_p()) |
29608 | settings.should_lock_elevation = FALSE; |
29609 | |
29610 | heap_segment* new_seg = new_heap_segment; |
29611 | |
29612 | if (!new_seg) |
29613 | return consing_gen; |
29614 | |
29615 | //copy the card and brick tables |
29616 | if (g_gc_card_table!= card_table) |
29617 | copy_brick_card_table(); |
29618 | |
29619 | BOOL new_segment_p = (heap_segment_next (new_seg) == 0); |
29620 | dprintf (2, ("new_segment_p %Ix" , (size_t)new_segment_p)); |
29621 | |
29622 | assert (generation_plan_allocation_start (generation_of (max_generation-1))); |
29623 | assert (generation_plan_allocation_start (generation_of (max_generation-1)) >= |
29624 | heap_segment_mem (ephemeral_heap_segment)); |
29625 | assert (generation_plan_allocation_start (generation_of (max_generation-1)) <= |
29626 | heap_segment_committed (ephemeral_heap_segment)); |
29627 | |
29628 | assert (generation_plan_allocation_start (youngest_generation)); |
29629 | assert (generation_plan_allocation_start (youngest_generation) < |
29630 | heap_segment_plan_allocated (ephemeral_heap_segment)); |
29631 | |
29632 | if (settings.pause_mode == pause_no_gc) |
29633 | { |
29634 | // We don't reuse for no gc, so the size used on the new eph seg is eph_size. |
29635 | if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc)) |
29636 | should_promote_ephemeral = TRUE; |
29637 | } |
29638 | else |
29639 | { |
29640 | if (!use_bestfit) |
29641 | { |
29642 | should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral); |
29643 | } |
29644 | } |
29645 | |
29646 | if (should_promote_ephemeral) |
29647 | { |
29648 | ephemeral_promotion = TRUE; |
29649 | get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep); |
29650 | dprintf (2, ("promoting ephemeral" )); |
29651 | save_ephemeral_generation_starts(); |
29652 | } |
29653 | else |
29654 | { |
29655 | // commit the new ephemeral segment all at once if it is a new one. |
29656 | if ((eph_size > 0) && new_segment_p) |
29657 | { |
29658 | #ifdef FEATURE_STRUCTALIGN |
29659 | // The destination may require a larger alignment padding than the source. |
29660 | // Assume the worst possible alignment padding. |
29661 | eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET); |
29662 | #endif // FEATURE_STRUCTALIGN |
29663 | #ifdef RESPECT_LARGE_ALIGNMENT |
29664 | //Since the generation start can be larger than min_obj_size |
29665 | //The alignment could be switched. |
29666 | eph_size += switch_alignment_size(FALSE); |
29667 | #endif //RESPECT_LARGE_ALIGNMENT |
29668 | //Since the generation start can be larger than min_obj_size |
29669 | //Compare the alignment of the first object in gen1 |
29670 | if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0) |
29671 | { |
29672 | fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE); |
29673 | return consing_gen; |
29674 | } |
29675 | heap_segment_used (new_seg) = heap_segment_committed (new_seg); |
29676 | } |
29677 | |
29678 | //Fix the end of the old ephemeral heap segment |
29679 | heap_segment_plan_allocated (ephemeral_heap_segment) = |
29680 | generation_plan_allocation_start (generation_of (max_generation-1)); |
29681 | |
29682 | dprintf (3, ("Old ephemeral allocated set to %Ix" , |
29683 | (size_t)heap_segment_plan_allocated (ephemeral_heap_segment))); |
29684 | } |
29685 | |
29686 | if (new_segment_p) |
29687 | { |
29688 | // TODO - Is this really necessary? We should think about it. |
29689 | //initialize the first brick |
29690 | size_t first_brick = brick_of (heap_segment_mem (new_seg)); |
29691 | set_brick (first_brick, |
29692 | heap_segment_mem (new_seg) - brick_address (first_brick)); |
29693 | } |
29694 | |
29695 | //From this point on, we cannot run out of memory |
29696 | |
29697 | //reset the allocation of the consing generation back to the end of the |
29698 | //old ephemeral segment |
29699 | generation_allocation_limit (consing_gen) = |
29700 | heap_segment_plan_allocated (ephemeral_heap_segment); |
29701 | generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen); |
29702 | generation_allocation_segment (consing_gen) = ephemeral_heap_segment; |
29703 | |
29704 | //clear the generation gap for all of the ephemeral generations |
29705 | { |
29706 | int generation_num = max_generation-1; |
29707 | while (generation_num >= 0) |
29708 | { |
29709 | generation* gen = generation_of (generation_num); |
29710 | generation_plan_allocation_start (gen) = 0; |
29711 | generation_num--; |
29712 | } |
29713 | } |
29714 | |
29715 | heap_segment* old_seg = ephemeral_heap_segment; |
29716 | ephemeral_heap_segment = new_seg; |
29717 | |
29718 | //Note: the ephemeral segment shouldn't be threaded onto the segment chain |
29719 | //because the relocation and compact phases shouldn't see it |
29720 | |
29721 | // set the generation members used by allocate_in_expanded_heap |
29722 | // and switch to ephemeral generation |
29723 | consing_gen = ensure_ephemeral_heap_segment (consing_gen); |
29724 | |
29725 | if (!should_promote_ephemeral) |
29726 | { |
29727 | realloc_plugs (consing_gen, old_seg, start_address, end_address, |
29728 | active_new_gen_number); |
29729 | } |
29730 | |
29731 | if (!use_bestfit) |
29732 | { |
29733 | repair_allocation_in_expanded_heap (consing_gen); |
29734 | } |
29735 | |
29736 | // assert that the generation gap for all of the ephemeral generations were allocated. |
29737 | #ifdef _DEBUG |
29738 | { |
29739 | int generation_num = max_generation-1; |
29740 | while (generation_num >= 0) |
29741 | { |
29742 | generation* gen = generation_of (generation_num); |
29743 | assert (generation_plan_allocation_start (gen)); |
29744 | generation_num--; |
29745 | } |
29746 | } |
29747 | #endif // _DEBUG |
29748 | |
29749 | if (!new_segment_p) |
29750 | { |
29751 | dprintf (2, ("Demoting ephemeral segment" )); |
29752 | //demote the entire segment. |
29753 | settings.demotion = TRUE; |
29754 | get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit); |
29755 | demotion_low = heap_segment_mem (ephemeral_heap_segment); |
29756 | demotion_high = heap_segment_reserved (ephemeral_heap_segment); |
29757 | } |
29758 | else |
29759 | { |
29760 | demotion_low = MAX_PTR; |
29761 | demotion_high = 0; |
29762 | #ifndef MULTIPLE_HEAPS |
29763 | settings.demotion = FALSE; |
29764 | get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit); |
29765 | #endif //!MULTIPLE_HEAPS |
29766 | } |
29767 | ptrdiff_t eph_size1 = total_ephemeral_size; |
29768 | MAYBE_UNUSED_VAR(eph_size1); |
29769 | |
29770 | if (!should_promote_ephemeral && new_segment_p) |
29771 | { |
29772 | assert (eph_size1 <= eph_size); |
29773 | } |
29774 | |
29775 | if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg)) |
29776 | { |
29777 | // This is to catch when we accidently delete a segment that has pins. |
29778 | verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg)); |
29779 | } |
29780 | |
29781 | verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg)); |
29782 | |
29783 | dprintf(2,("---- End of Heap Expansion ----" )); |
29784 | return consing_gen; |
29785 | } |
29786 | |
29787 | void gc_heap::set_static_data() |
29788 | { |
29789 | static_data* pause_mode_sdata = static_data_table[latency_level]; |
29790 | for (int i = 0; i < NUMBERGENERATIONS; i++) |
29791 | { |
29792 | dynamic_data* dd = dynamic_data_of (i); |
29793 | static_data* sdata = &pause_mode_sdata[i]; |
29794 | |
29795 | dd->sdata = sdata; |
29796 | dd->min_size = sdata->min_size; |
29797 | |
29798 | dprintf (GTC_LOG, ("PM: %d - min: %Id, max: %Id, fr_l: %Id, fr_b: %d%%" , |
29799 | settings.pause_mode, |
29800 | dd->min_size, dd_max_size, |
29801 | sdata->fragmentation_limit, (int)(sdata->fragmentation_burden_limit * 100))); |
29802 | } |
29803 | } |
29804 | |
29805 | // Initialize the values that are not const. |
29806 | void gc_heap::init_static_data() |
29807 | { |
29808 | size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size()); |
29809 | size_t gen0_min_size = Align(gen0size / 8 * 5); |
29810 | |
29811 | size_t gen0_max_size = |
29812 | #ifdef MULTIPLE_HEAPS |
29813 | max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024)); |
29814 | #else //MULTIPLE_HEAPS |
29815 | (gc_can_use_concurrent ? |
29816 | 6*1024*1024 : |
29817 | max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024))); |
29818 | #endif //MULTIPLE_HEAPS |
29819 | |
29820 | // TODO: gen0_max_size has a 200mb cap; gen1_max_size should also have a cap. |
29821 | size_t gen1_max_size = |
29822 | #ifdef MULTIPLE_HEAPS |
29823 | max (6*1024*1024, Align(soh_segment_size/2)); |
29824 | #else //MULTIPLE_HEAPS |
29825 | (gc_can_use_concurrent ? |
29826 | 6*1024*1024 : |
29827 | max (6*1024*1024, Align(soh_segment_size/2))); |
29828 | #endif //MULTIPLE_HEAPS |
29829 | |
29830 | dprintf (GTC_LOG, ("gen0size: %Id, gen0 min: %Id, max: %Id, gen1 max: %Id" , |
29831 | gen0size, gen0_min_size, gen0_max_size, gen1_max_size)); |
29832 | |
29833 | for (int i = latency_level_first; i <= latency_level_last; i++) |
29834 | { |
29835 | static_data_table[i][0].min_size = gen0_min_size; |
29836 | static_data_table[i][0].max_size = gen0_max_size; |
29837 | static_data_table[i][1].max_size = gen1_max_size; |
29838 | } |
29839 | } |
29840 | |
29841 | bool gc_heap::init_dynamic_data() |
29842 | { |
29843 | qpf = GCToOSInterface::QueryPerformanceFrequency(); |
29844 | |
29845 | uint32_t now = (uint32_t)GetHighPrecisionTimeStamp(); |
29846 | |
29847 | set_static_data(); |
29848 | |
29849 | for (int i = 0; i <= max_generation+1; i++) |
29850 | { |
29851 | dynamic_data* dd = dynamic_data_of (i); |
29852 | dd->gc_clock = 0; |
29853 | dd->time_clock = now; |
29854 | dd->current_size = 0; |
29855 | dd->promoted_size = 0; |
29856 | dd->collection_count = 0; |
29857 | dd->new_allocation = dd->min_size; |
29858 | dd->gc_new_allocation = dd->new_allocation; |
29859 | dd->desired_allocation = dd->new_allocation; |
29860 | dd->fragmentation = 0; |
29861 | } |
29862 | |
29863 | #ifdef GC_CONFIG_DRIVEN |
29864 | if (heap_number == 0) |
29865 | time_init = now; |
29866 | #endif //GC_CONFIG_DRIVEN |
29867 | |
29868 | return true; |
29869 | } |
29870 | |
29871 | float gc_heap::surv_to_growth (float cst, float limit, float max_limit) |
29872 | { |
29873 | if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f)))) |
29874 | return ((limit - limit*cst) / (1.0f - (cst * limit))); |
29875 | else |
29876 | return max_limit; |
29877 | } |
29878 | |
29879 | |
29880 | //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may |
29881 | //not be correct (collection happened too soon). Correct with a linear estimation based on the previous |
29882 | //value of the budget |
29883 | static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, |
29884 | size_t previous_desired_allocation, size_t collection_count) |
29885 | { |
29886 | if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0)) |
29887 | { |
29888 | dprintf (2, ("allocation fraction: %d" , (int)(allocation_fraction/100.0))); |
29889 | new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation); |
29890 | } |
29891 | #if 0 |
29892 | size_t smoothing = 3; // exponential smoothing factor |
29893 | if (smoothing > collection_count) |
29894 | smoothing = collection_count; |
29895 | new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1)); |
29896 | #else |
29897 | UNREFERENCED_PARAMETER(collection_count); |
29898 | #endif //0 |
29899 | return new_allocation; |
29900 | } |
29901 | |
29902 | size_t gc_heap::desired_new_allocation (dynamic_data* dd, |
29903 | size_t out, int gen_number, |
29904 | int pass) |
29905 | { |
29906 | gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); |
29907 | |
29908 | if (dd_begin_data_size (dd) == 0) |
29909 | { |
29910 | size_t new_allocation = dd_min_size (dd); |
29911 | current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation; |
29912 | return new_allocation; |
29913 | } |
29914 | else |
29915 | { |
29916 | float cst; |
29917 | size_t previous_desired_allocation = dd_desired_allocation (dd); |
29918 | size_t current_size = dd_current_size (dd); |
29919 | float max_limit = dd_max_limit (dd); |
29920 | float limit = dd_limit (dd); |
29921 | size_t min_gc_size = dd_min_size (dd); |
29922 | float f = 0; |
29923 | size_t max_size = dd_max_size (dd); |
29924 | size_t new_allocation = 0; |
29925 | float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd)); |
29926 | if (gen_number >= max_generation) |
29927 | { |
29928 | size_t new_size = 0; |
29929 | |
29930 | cst = min (1.0f, float (out) / float (dd_begin_data_size (dd))); |
29931 | |
29932 | f = surv_to_growth (cst, limit, max_limit); |
29933 | size_t max_growth_size = (size_t)(max_size / f); |
29934 | if (current_size >= max_growth_size) |
29935 | { |
29936 | new_size = max_size; |
29937 | } |
29938 | else |
29939 | { |
29940 | new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size); |
29941 | } |
29942 | |
29943 | assert ((new_size >= current_size) || (new_size == max_size)); |
29944 | |
29945 | if (gen_number == max_generation) |
29946 | { |
29947 | new_allocation = max((new_size - current_size), min_gc_size); |
29948 | |
29949 | new_allocation = linear_allocation_model (allocation_fraction, new_allocation, |
29950 | dd_desired_allocation (dd), dd_collection_count (dd)); |
29951 | |
29952 | if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size)))) |
29953 | { |
29954 | //reducing allocation in case of fragmentation |
29955 | size_t new_allocation1 = max (min_gc_size, |
29956 | // CAN OVERFLOW |
29957 | (size_t)((float)new_allocation * current_size / |
29958 | ((float)current_size + 2*dd_fragmentation (dd)))); |
29959 | dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id" , |
29960 | new_allocation, new_allocation1)); |
29961 | new_allocation = new_allocation1; |
29962 | } |
29963 | } |
29964 | else //large object heap |
29965 | { |
29966 | uint32_t memory_load = 0; |
29967 | uint64_t available_physical = 0; |
29968 | get_memory_info (&memory_load, &available_physical); |
29969 | if (heap_number == 0) |
29970 | settings.exit_memory_load = memory_load; |
29971 | if (available_physical > 1024*1024) |
29972 | available_physical -= 1024*1024; |
29973 | |
29974 | uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number)); |
29975 | if (available_free > (uint64_t)MAX_PTR) |
29976 | { |
29977 | available_free = (uint64_t)MAX_PTR; |
29978 | } |
29979 | |
29980 | //try to avoid OOM during large object allocation |
29981 | new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))), |
29982 | (size_t)available_free), |
29983 | max ((current_size/4), min_gc_size)); |
29984 | |
29985 | new_allocation = linear_allocation_model (allocation_fraction, new_allocation, |
29986 | dd_desired_allocation (dd), dd_collection_count (dd)); |
29987 | |
29988 | } |
29989 | } |
29990 | else |
29991 | { |
29992 | size_t survivors = out; |
29993 | cst = float (survivors) / float (dd_begin_data_size (dd)); |
29994 | f = surv_to_growth (cst, limit, max_limit); |
29995 | new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size); |
29996 | |
29997 | new_allocation = linear_allocation_model (allocation_fraction, new_allocation, |
29998 | dd_desired_allocation (dd), dd_collection_count (dd)); |
29999 | |
30000 | if (gen_number == 0) |
30001 | { |
30002 | if (pass == 0) |
30003 | { |
30004 | |
30005 | //printf ("%f, %Id\n", cst, new_allocation); |
30006 | size_t free_space = generation_free_list_space (generation_of (gen_number)); |
30007 | // DTREVIEW - is min_gc_size really a good choice? |
30008 | // on 64-bit this will almost always be true. |
30009 | dprintf (GTC_LOG, ("frag: %Id, min: %Id" , free_space, min_gc_size)); |
30010 | if (free_space > min_gc_size) |
30011 | { |
30012 | settings.gen0_reduction_count = 2; |
30013 | } |
30014 | else |
30015 | { |
30016 | if (settings.gen0_reduction_count > 0) |
30017 | settings.gen0_reduction_count--; |
30018 | } |
30019 | } |
30020 | if (settings.gen0_reduction_count > 0) |
30021 | { |
30022 | dprintf (2, ("Reducing new allocation based on fragmentation" )); |
30023 | new_allocation = min (new_allocation, |
30024 | max (min_gc_size, (max_size/3))); |
30025 | } |
30026 | } |
30027 | } |
30028 | |
30029 | size_t new_allocation_ret = |
30030 | Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1)))); |
30031 | int gen_data_index = gen_number; |
30032 | gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]); |
30033 | gen_data->new_allocation = new_allocation_ret; |
30034 | |
30035 | dd_surv (dd) = cst; |
30036 | |
30037 | #ifdef SIMPLE_DPRINTF |
30038 | dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id" , |
30039 | heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)), |
30040 | (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation)); |
30041 | #else |
30042 | dprintf (1,("gen: %d in: %Id out: %Id " , gen_number, generation_allocation_size (generation_of (gen_number)), out)); |
30043 | dprintf (1,("current: %Id alloc: %Id " , current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)))); |
30044 | dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id" , |
30045 | (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation)); |
30046 | #endif //SIMPLE_DPRINTF |
30047 | |
30048 | return new_allocation_ret; |
30049 | } |
30050 | } |
30051 | |
30052 | //returns the planned size of a generation (including free list element) |
30053 | size_t gc_heap::generation_plan_size (int gen_number) |
30054 | { |
30055 | if (0 == gen_number) |
30056 | return max((heap_segment_plan_allocated (ephemeral_heap_segment) - |
30057 | generation_plan_allocation_start (generation_of (gen_number))), |
30058 | (int)Align (min_obj_size)); |
30059 | else |
30060 | { |
30061 | generation* gen = generation_of (gen_number); |
30062 | if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment) |
30063 | return (generation_plan_allocation_start (generation_of (gen_number - 1)) - |
30064 | generation_plan_allocation_start (generation_of (gen_number))); |
30065 | else |
30066 | { |
30067 | size_t gensize = 0; |
30068 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
30069 | |
30070 | PREFIX_ASSUME(seg != NULL); |
30071 | |
30072 | while (seg && (seg != ephemeral_heap_segment)) |
30073 | { |
30074 | gensize += heap_segment_plan_allocated (seg) - |
30075 | heap_segment_mem (seg); |
30076 | seg = heap_segment_next_rw (seg); |
30077 | } |
30078 | if (seg) |
30079 | { |
30080 | gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) - |
30081 | heap_segment_mem (ephemeral_heap_segment)); |
30082 | } |
30083 | return gensize; |
30084 | } |
30085 | } |
30086 | |
30087 | } |
30088 | |
30089 | //returns the size of a generation (including free list element) |
30090 | size_t gc_heap::generation_size (int gen_number) |
30091 | { |
30092 | if (0 == gen_number) |
30093 | return max((heap_segment_allocated (ephemeral_heap_segment) - |
30094 | generation_allocation_start (generation_of (gen_number))), |
30095 | (int)Align (min_obj_size)); |
30096 | else |
30097 | { |
30098 | generation* gen = generation_of (gen_number); |
30099 | if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment) |
30100 | return (generation_allocation_start (generation_of (gen_number - 1)) - |
30101 | generation_allocation_start (generation_of (gen_number))); |
30102 | else |
30103 | { |
30104 | size_t gensize = 0; |
30105 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
30106 | |
30107 | PREFIX_ASSUME(seg != NULL); |
30108 | |
30109 | while (seg && (seg != ephemeral_heap_segment)) |
30110 | { |
30111 | gensize += heap_segment_allocated (seg) - |
30112 | heap_segment_mem (seg); |
30113 | seg = heap_segment_next_rw (seg); |
30114 | } |
30115 | if (seg) |
30116 | { |
30117 | gensize += (generation_allocation_start (generation_of (gen_number - 1)) - |
30118 | heap_segment_mem (ephemeral_heap_segment)); |
30119 | } |
30120 | |
30121 | return gensize; |
30122 | } |
30123 | } |
30124 | |
30125 | } |
30126 | |
30127 | size_t gc_heap::compute_in (int gen_number) |
30128 | { |
30129 | assert (gen_number != 0); |
30130 | dynamic_data* dd = dynamic_data_of (gen_number); |
30131 | |
30132 | size_t in = generation_allocation_size (generation_of (gen_number)); |
30133 | |
30134 | if (gen_number == max_generation && ephemeral_promotion) |
30135 | { |
30136 | in = 0; |
30137 | for (int i = 0; i <= max_generation; i++) |
30138 | { |
30139 | dynamic_data* dd = dynamic_data_of (i); |
30140 | in += dd_survived_size (dd); |
30141 | if (i != max_generation) |
30142 | { |
30143 | generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd); |
30144 | } |
30145 | } |
30146 | } |
30147 | |
30148 | dd_gc_new_allocation (dd) -= in; |
30149 | dd_new_allocation (dd) = dd_gc_new_allocation (dd); |
30150 | |
30151 | gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); |
30152 | gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]); |
30153 | gen_data->in = in; |
30154 | |
30155 | generation_allocation_size (generation_of (gen_number)) = 0; |
30156 | return in; |
30157 | } |
30158 | |
30159 | void gc_heap::compute_promoted_allocation (int gen_number) |
30160 | { |
30161 | compute_in (gen_number); |
30162 | } |
30163 | |
30164 | #ifdef BIT64 |
30165 | inline |
30166 | size_t gc_heap::trim_youngest_desired (uint32_t memory_load, |
30167 | size_t total_new_allocation, |
30168 | size_t total_min_allocation) |
30169 | { |
30170 | if (memory_load < MAX_ALLOWED_MEM_LOAD) |
30171 | { |
30172 | // If the total of memory load and gen0 budget exceeds |
30173 | // our max memory load limit, trim the gen0 budget so the total |
30174 | // is the max memory load limit. |
30175 | size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent; |
30176 | return min (total_new_allocation, remain_memory_load); |
30177 | } |
30178 | else |
30179 | { |
30180 | return max (mem_one_percent, total_min_allocation); |
30181 | } |
30182 | } |
30183 | |
30184 | size_t gc_heap::joined_youngest_desired (size_t new_allocation) |
30185 | { |
30186 | dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id" , settings.entry_memory_load, new_allocation)); |
30187 | |
30188 | size_t final_new_allocation = new_allocation; |
30189 | if (new_allocation > MIN_YOUNGEST_GEN_DESIRED) |
30190 | { |
30191 | uint32_t num_heaps = 1; |
30192 | |
30193 | #ifdef MULTIPLE_HEAPS |
30194 | num_heaps = gc_heap::n_heaps; |
30195 | #endif //MULTIPLE_HEAPS |
30196 | |
30197 | size_t total_new_allocation = new_allocation * num_heaps; |
30198 | size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps; |
30199 | |
30200 | if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) || |
30201 | (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation))) |
30202 | { |
30203 | uint32_t memory_load = 0; |
30204 | get_memory_info (&memory_load); |
30205 | settings.exit_memory_load = memory_load; |
30206 | dprintf (2, ("Current emory load: %d" , memory_load)); |
30207 | |
30208 | size_t final_total = |
30209 | trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation); |
30210 | size_t max_new_allocation = |
30211 | #ifdef MULTIPLE_HEAPS |
30212 | dd_max_size (g_heaps[0]->dynamic_data_of (0)); |
30213 | #else //MULTIPLE_HEAPS |
30214 | dd_max_size (dynamic_data_of (0)); |
30215 | #endif //MULTIPLE_HEAPS |
30216 | |
30217 | final_new_allocation = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation); |
30218 | } |
30219 | } |
30220 | |
30221 | if (final_new_allocation < new_allocation) |
30222 | { |
30223 | settings.gen0_reduction_count = 2; |
30224 | } |
30225 | |
30226 | return final_new_allocation; |
30227 | } |
30228 | #endif // BIT64 |
30229 | |
30230 | inline |
30231 | gc_history_per_heap* gc_heap::get_gc_data_per_heap() |
30232 | { |
30233 | #ifdef BACKGROUND_GC |
30234 | return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap); |
30235 | #else |
30236 | return &gc_data_per_heap; |
30237 | #endif //BACKGROUND_GC |
30238 | } |
30239 | |
30240 | void gc_heap::compute_new_dynamic_data (int gen_number) |
30241 | { |
30242 | PREFIX_ASSUME(gen_number >= 0); |
30243 | PREFIX_ASSUME(gen_number <= max_generation); |
30244 | |
30245 | dynamic_data* dd = dynamic_data_of (gen_number); |
30246 | generation* gen = generation_of (gen_number); |
30247 | size_t in = (gen_number==0) ? 0 : compute_in (gen_number); |
30248 | |
30249 | size_t total_gen_size = generation_size (gen_number); |
30250 | //keep track of fragmentation |
30251 | dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen); |
30252 | dd_current_size (dd) = total_gen_size - dd_fragmentation (dd); |
30253 | |
30254 | gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); |
30255 | |
30256 | size_t out = dd_survived_size (dd); |
30257 | |
30258 | gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]); |
30259 | gen_data->size_after = total_gen_size; |
30260 | gen_data->free_list_space_after = generation_free_list_space (gen); |
30261 | gen_data->free_obj_space_after = generation_free_obj_space (gen); |
30262 | |
30263 | if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1)) |
30264 | { |
30265 | // When we are in the low latency mode, we can still be |
30266 | // condemning more than gen1's 'cause of induced GCs. |
30267 | dd_desired_allocation (dd) = low_latency_alloc; |
30268 | } |
30269 | else |
30270 | { |
30271 | if (gen_number == 0) |
30272 | { |
30273 | //compensate for dead finalizable objects promotion. |
30274 | //they shoudn't be counted for growth. |
30275 | size_t final_promoted = 0; |
30276 | final_promoted = min (promoted_bytes (heap_number), out); |
30277 | // Prefast: this is clear from above but prefast needs to be told explicitly |
30278 | PREFIX_ASSUME(final_promoted <= out); |
30279 | |
30280 | dprintf (2, ("gen: %d final promoted: %Id" , gen_number, final_promoted)); |
30281 | dd_freach_previous_promotion (dd) = final_promoted; |
30282 | size_t lower_bound = desired_new_allocation (dd, out-final_promoted, gen_number, 0); |
30283 | |
30284 | if (settings.condemned_generation == 0) |
30285 | { |
30286 | //there is no noise. |
30287 | dd_desired_allocation (dd) = lower_bound; |
30288 | } |
30289 | else |
30290 | { |
30291 | size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1); |
30292 | |
30293 | // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO> |
30294 | //assert ( lower_bound <= higher_bound); |
30295 | |
30296 | //discount the noise. Change the desired allocation |
30297 | //only if the previous value is outside of the range. |
30298 | if (dd_desired_allocation (dd) < lower_bound) |
30299 | { |
30300 | dd_desired_allocation (dd) = lower_bound; |
30301 | } |
30302 | else if (dd_desired_allocation (dd) > higher_bound) |
30303 | { |
30304 | dd_desired_allocation (dd) = higher_bound; |
30305 | } |
30306 | #if defined (BIT64) && !defined (MULTIPLE_HEAPS) |
30307 | dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd)); |
30308 | #endif // BIT64 && !MULTIPLE_HEAPS |
30309 | trim_youngest_desired_low_memory(); |
30310 | dprintf (2, ("final gen0 new_alloc: %Id" , dd_desired_allocation (dd))); |
30311 | } |
30312 | } |
30313 | else |
30314 | { |
30315 | dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0); |
30316 | } |
30317 | } |
30318 | |
30319 | gen_data->pinned_surv = dd_pinned_survived_size (dd); |
30320 | gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd); |
30321 | |
30322 | dd_gc_new_allocation (dd) = dd_desired_allocation (dd); |
30323 | dd_new_allocation (dd) = dd_gc_new_allocation (dd); |
30324 | |
30325 | //update counter |
30326 | dd_promoted_size (dd) = out; |
30327 | if (gen_number == max_generation) |
30328 | { |
30329 | dd = dynamic_data_of (max_generation+1); |
30330 | total_gen_size = generation_size (max_generation + 1); |
30331 | dd_fragmentation (dd) = generation_free_list_space (large_object_generation) + |
30332 | generation_free_obj_space (large_object_generation); |
30333 | dd_current_size (dd) = total_gen_size - dd_fragmentation (dd); |
30334 | dd_survived_size (dd) = dd_current_size (dd); |
30335 | in = 0; |
30336 | out = dd_current_size (dd); |
30337 | dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0); |
30338 | dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd), |
30339 | get_alignment_constant (FALSE)); |
30340 | dd_new_allocation (dd) = dd_gc_new_allocation (dd); |
30341 | |
30342 | gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]); |
30343 | gen_data->size_after = total_gen_size; |
30344 | gen_data->free_list_space_after = generation_free_list_space (large_object_generation); |
30345 | gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation); |
30346 | gen_data->npinned_surv = out; |
30347 | #ifdef BACKGROUND_GC |
30348 | end_loh_size = total_gen_size; |
30349 | #endif //BACKGROUND_GC |
30350 | //update counter |
30351 | dd_promoted_size (dd) = out; |
30352 | } |
30353 | } |
30354 | |
30355 | void gc_heap::trim_youngest_desired_low_memory() |
30356 | { |
30357 | if (g_low_memory_status) |
30358 | { |
30359 | size_t committed_mem = 0; |
30360 | heap_segment* seg = generation_start_segment (generation_of (max_generation)); |
30361 | while (seg) |
30362 | { |
30363 | committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg); |
30364 | seg = heap_segment_next (seg); |
30365 | } |
30366 | seg = generation_start_segment (generation_of (max_generation + 1)); |
30367 | while (seg) |
30368 | { |
30369 | committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg); |
30370 | seg = heap_segment_next (seg); |
30371 | } |
30372 | |
30373 | dynamic_data* dd = dynamic_data_of (0); |
30374 | size_t current = dd_desired_allocation (dd); |
30375 | size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_size (dd)); |
30376 | |
30377 | dd_desired_allocation (dd) = min (current, candidate); |
30378 | } |
30379 | } |
30380 | |
30381 | void gc_heap::decommit_ephemeral_segment_pages() |
30382 | { |
30383 | if (settings.concurrent) |
30384 | { |
30385 | return; |
30386 | } |
30387 | |
30388 | size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment); |
30389 | dynamic_data* dd = dynamic_data_of (0); |
30390 | |
30391 | #ifndef MULTIPLE_HEAPS |
30392 | size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024)); |
30393 | size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT); |
30394 | size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time; |
30395 | |
30396 | if (dd_desired_allocation (dd) > gc_gen0_desired_high) |
30397 | { |
30398 | gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space; |
30399 | } |
30400 | |
30401 | if (ephemeral_elapsed >= decommit_timeout) |
30402 | { |
30403 | slack_space = min (slack_space, gc_gen0_desired_high); |
30404 | |
30405 | gc_last_ephemeral_decommit_time = dd_time_clock(dd); |
30406 | gc_gen0_desired_high = 0; |
30407 | } |
30408 | #endif //!MULTIPLE_HEAPS |
30409 | |
30410 | if (settings.condemned_generation >= (max_generation-1)) |
30411 | { |
30412 | size_t new_slack_space = |
30413 | #ifdef BIT64 |
30414 | max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd)); |
30415 | #else |
30416 | #ifdef FEATURE_CORECLR |
30417 | dd_desired_allocation (dd); |
30418 | #else |
30419 | dd_max_size (dd); |
30420 | #endif //FEATURE_CORECLR |
30421 | #endif // BIT64 |
30422 | |
30423 | slack_space = min (slack_space, new_slack_space); |
30424 | } |
30425 | |
30426 | decommit_heap_segment_pages (ephemeral_heap_segment, slack_space); |
30427 | |
30428 | gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap(); |
30429 | current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment); |
30430 | } |
30431 | |
30432 | //This is meant to be called by decide_on_compacting. |
30433 | |
30434 | size_t gc_heap::generation_fragmentation (generation* gen, |
30435 | generation* consing_gen, |
30436 | uint8_t* end) |
30437 | { |
30438 | size_t frag; |
30439 | uint8_t* alloc = generation_allocation_pointer (consing_gen); |
30440 | // If the allocation pointer has reached the ephemeral segment |
30441 | // fine, otherwise the whole ephemeral segment is considered |
30442 | // fragmentation |
30443 | if (in_range_for_segment (alloc, ephemeral_heap_segment)) |
30444 | { |
30445 | if (alloc <= heap_segment_allocated(ephemeral_heap_segment)) |
30446 | frag = end - alloc; |
30447 | else |
30448 | { |
30449 | // case when no survivors, allocated set to beginning |
30450 | frag = 0; |
30451 | } |
30452 | dprintf (3, ("ephemeral frag: %Id" , frag)); |
30453 | } |
30454 | else |
30455 | frag = (heap_segment_allocated (ephemeral_heap_segment) - |
30456 | heap_segment_mem (ephemeral_heap_segment)); |
30457 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
30458 | |
30459 | PREFIX_ASSUME(seg != NULL); |
30460 | |
30461 | while (seg != ephemeral_heap_segment) |
30462 | { |
30463 | frag += (heap_segment_allocated (seg) - |
30464 | heap_segment_plan_allocated (seg)); |
30465 | dprintf (3, ("seg: %Ix, frag: %Id" , (size_t)seg, |
30466 | (heap_segment_allocated (seg) - |
30467 | heap_segment_plan_allocated (seg)))); |
30468 | |
30469 | seg = heap_segment_next_rw (seg); |
30470 | assert (seg); |
30471 | } |
30472 | dprintf (3, ("frag: %Id discounting pinned plugs" , frag)); |
30473 | //add the length of the dequeued plug free space |
30474 | size_t bos = 0; |
30475 | while (bos < mark_stack_bos) |
30476 | { |
30477 | frag += (pinned_len (pinned_plug_of (bos))); |
30478 | bos++; |
30479 | } |
30480 | |
30481 | return frag; |
30482 | } |
30483 | |
30484 | // for SOH this returns the total sizes of the generation and its |
30485 | // younger generation(s). |
30486 | // for LOH this returns just LOH size. |
30487 | size_t gc_heap::generation_sizes (generation* gen) |
30488 | { |
30489 | size_t result = 0; |
30490 | if (generation_start_segment (gen ) == ephemeral_heap_segment) |
30491 | result = (heap_segment_allocated (ephemeral_heap_segment) - |
30492 | generation_allocation_start (gen)); |
30493 | else |
30494 | { |
30495 | heap_segment* seg = heap_segment_in_range (generation_start_segment (gen)); |
30496 | |
30497 | PREFIX_ASSUME(seg != NULL); |
30498 | |
30499 | while (seg) |
30500 | { |
30501 | result += (heap_segment_allocated (seg) - |
30502 | heap_segment_mem (seg)); |
30503 | seg = heap_segment_next_in_range (seg); |
30504 | } |
30505 | } |
30506 | |
30507 | return result; |
30508 | } |
30509 | |
30510 | BOOL gc_heap::decide_on_compacting (int condemned_gen_number, |
30511 | size_t fragmentation, |
30512 | BOOL& should_expand) |
30513 | { |
30514 | BOOL should_compact = FALSE; |
30515 | should_expand = FALSE; |
30516 | generation* gen = generation_of (condemned_gen_number); |
30517 | dynamic_data* dd = dynamic_data_of (condemned_gen_number); |
30518 | size_t gen_sizes = generation_sizes(gen); |
30519 | float fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) : |
30520 | (float (fragmentation) / gen_sizes) ); |
30521 | |
30522 | dprintf (GTC_LOG, ("fragmentation: %Id (%d%%)" , fragmentation, (int)(fragmentation_burden * 100.0))); |
30523 | |
30524 | #ifdef STRESS_HEAP |
30525 | // for pure GC stress runs we need compaction, for GC stress "mix" |
30526 | // we need to ensure a better mix of compacting and sweeping collections |
30527 | if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent |
30528 | && !g_pConfig->IsGCStressMix()) |
30529 | should_compact = TRUE; |
30530 | |
30531 | #ifdef GC_STATS |
30532 | // in GC stress "mix" mode, for stress induced collections make sure we |
30533 | // keep sweeps and compactions relatively balanced. do not (yet) force sweeps |
30534 | // against the GC's determination, as it may lead to premature OOMs. |
30535 | if (g_pConfig->IsGCStressMix() && settings.stress_induced) |
30536 | { |
30537 | int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC; |
30538 | int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions; |
30539 | if (compactions < sweeps / 10) |
30540 | { |
30541 | should_compact = TRUE; |
30542 | } |
30543 | } |
30544 | #endif // GC_STATS |
30545 | #endif //STRESS_HEAP |
30546 | |
30547 | if (GCConfig::GetForceCompact()) |
30548 | should_compact = TRUE; |
30549 | |
30550 | if ((condemned_gen_number == max_generation) && last_gc_before_oom) |
30551 | { |
30552 | should_compact = TRUE; |
30553 | last_gc_before_oom = FALSE; |
30554 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc); |
30555 | } |
30556 | |
30557 | if (settings.reason == reason_induced_compacting) |
30558 | { |
30559 | dprintf (2, ("induced compacting GC" )); |
30560 | should_compact = TRUE; |
30561 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting); |
30562 | } |
30563 | |
30564 | if (settings.reason == reason_pm_full_gc) |
30565 | { |
30566 | assert (condemned_gen_number == max_generation); |
30567 | if (heap_number == 0) |
30568 | { |
30569 | dprintf (GTC_LOG, ("PM doing compacting full GC after a gen1" )); |
30570 | } |
30571 | should_compact = TRUE; |
30572 | } |
30573 | |
30574 | dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%" , |
30575 | fragmentation, (int) (100*fragmentation_burden))); |
30576 | |
30577 | if (provisional_mode_triggered && (condemned_gen_number == (max_generation - 1))) |
30578 | { |
30579 | dprintf (GTC_LOG, ("gen1 in PM always compact" )); |
30580 | should_compact = TRUE; |
30581 | } |
30582 | |
30583 | if (!should_compact) |
30584 | { |
30585 | if (dt_low_ephemeral_space_p (tuning_deciding_compaction)) |
30586 | { |
30587 | dprintf(GTC_LOG, ("compacting due to low ephemeral" )); |
30588 | should_compact = TRUE; |
30589 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral); |
30590 | } |
30591 | } |
30592 | |
30593 | if (should_compact) |
30594 | { |
30595 | if ((condemned_gen_number >= (max_generation - 1))) |
30596 | { |
30597 | if (dt_low_ephemeral_space_p (tuning_deciding_expansion)) |
30598 | { |
30599 | dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction" )); |
30600 | should_expand = TRUE; |
30601 | } |
30602 | } |
30603 | } |
30604 | |
30605 | #ifdef BIT64 |
30606 | BOOL high_memory = FALSE; |
30607 | #endif // BIT64 |
30608 | |
30609 | if (!should_compact) |
30610 | { |
30611 | // We are not putting this in dt_high_frag_p because it's not exactly |
30612 | // high fragmentation - it's just enough planned fragmentation for us to |
30613 | // want to compact. Also the "fragmentation" we are talking about here |
30614 | // is different from anywhere else. |
30615 | BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) && |
30616 | (fragmentation_burden >= dd_fragmentation_burden_limit (dd))); |
30617 | |
30618 | if (frag_exceeded) |
30619 | { |
30620 | #ifdef BACKGROUND_GC |
30621 | // do not force compaction if this was a stress-induced GC |
30622 | IN_STRESS_HEAP(if (!settings.stress_induced)) |
30623 | { |
30624 | #endif // BACKGROUND_GC |
30625 | assert (settings.concurrent == FALSE); |
30626 | should_compact = TRUE; |
30627 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag); |
30628 | #ifdef BACKGROUND_GC |
30629 | } |
30630 | #endif // BACKGROUND_GC |
30631 | } |
30632 | |
30633 | #ifdef BIT64 |
30634 | // check for high memory situation |
30635 | if(!should_compact) |
30636 | { |
30637 | uint32_t num_heaps = 1; |
30638 | #ifdef MULTIPLE_HEAPS |
30639 | num_heaps = gc_heap::n_heaps; |
30640 | #endif // MULTIPLE_HEAPS |
30641 | |
30642 | ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation); |
30643 | if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th)) |
30644 | { |
30645 | if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps))) |
30646 | { |
30647 | dprintf(GTC_LOG,("compacting due to fragmentation in high memory" )); |
30648 | should_compact = TRUE; |
30649 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag); |
30650 | } |
30651 | high_memory = TRUE; |
30652 | } |
30653 | else if(settings.entry_memory_load >= v_high_memory_load_th) |
30654 | { |
30655 | if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps))) |
30656 | { |
30657 | dprintf(GTC_LOG,("compacting due to fragmentation in very high memory" )); |
30658 | should_compact = TRUE; |
30659 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag); |
30660 | } |
30661 | high_memory = TRUE; |
30662 | } |
30663 | } |
30664 | #endif // BIT64 |
30665 | } |
30666 | |
30667 | // The purpose of calling ensure_gap_allocation here is to make sure |
30668 | // that we actually are able to commit the memory to allocate generation |
30669 | // starts. |
30670 | if ((should_compact == FALSE) && |
30671 | (ensure_gap_allocation (condemned_gen_number) == FALSE)) |
30672 | { |
30673 | should_compact = TRUE; |
30674 | get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps); |
30675 | } |
30676 | |
30677 | if (settings.condemned_generation == max_generation) |
30678 | { |
30679 | //check the progress |
30680 | if ( |
30681 | #ifdef BIT64 |
30682 | (high_memory && !should_compact) || |
30683 | #endif // BIT64 |
30684 | (generation_plan_allocation_start (generation_of (max_generation - 1)) >= |
30685 | generation_allocation_start (generation_of (max_generation - 1)))) |
30686 | { |
30687 | dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked" , |
30688 | generation_size (max_generation), |
30689 | generation_plan_size (max_generation))); |
30690 | //no progress -> lock |
30691 | settings.should_lock_elevation = TRUE; |
30692 | } |
30693 | } |
30694 | |
30695 | if (settings.pause_mode == pause_no_gc) |
30696 | { |
30697 | should_compact = TRUE; |
30698 | if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment)) |
30699 | < soh_allocation_no_gc) |
30700 | { |
30701 | should_expand = TRUE; |
30702 | } |
30703 | } |
30704 | |
30705 | dprintf (2, ("will %s" , (should_compact ? "compact" : "sweep" ))); |
30706 | return should_compact; |
30707 | } |
30708 | |
30709 | size_t align_lower_good_size_allocation (size_t size) |
30710 | { |
30711 | return (size/64)*64; |
30712 | } |
30713 | |
30714 | size_t gc_heap::approximate_new_allocation() |
30715 | { |
30716 | dynamic_data* dd0 = dynamic_data_of (0); |
30717 | return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3)); |
30718 | } |
30719 | |
30720 | // After we did a GC we expect to have at least this |
30721 | // much space at the end of the segment to satisfy |
30722 | // a reasonable amount of allocation requests. |
30723 | size_t gc_heap::end_space_after_gc() |
30724 | { |
30725 | return max ((dd_min_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size))); |
30726 | } |
30727 | |
30728 | BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp) |
30729 | { |
30730 | uint8_t* start = 0; |
30731 | |
30732 | if ((tp == tuning_deciding_condemned_gen) || |
30733 | (tp == tuning_deciding_compaction)) |
30734 | { |
30735 | start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment)); |
30736 | if (settings.concurrent) |
30737 | { |
30738 | dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)" , |
30739 | (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated))); |
30740 | } |
30741 | else |
30742 | { |
30743 | dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)" , |
30744 | (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)))); |
30745 | } |
30746 | } |
30747 | else if (tp == tuning_deciding_expansion) |
30748 | { |
30749 | start = heap_segment_plan_allocated (ephemeral_heap_segment); |
30750 | dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan" , |
30751 | (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start))); |
30752 | } |
30753 | else |
30754 | { |
30755 | assert (tp == tuning_deciding_full_gc); |
30756 | dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)" , |
30757 | (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated))); |
30758 | start = alloc_allocated; |
30759 | } |
30760 | |
30761 | if (start == 0) // empty ephemeral generations |
30762 | { |
30763 | assert (tp == tuning_deciding_expansion); |
30764 | // if there are no survivors in the ephemeral segment, |
30765 | // this should be the beginning of ephemeral segment. |
30766 | start = generation_allocation_pointer (generation_of (max_generation)); |
30767 | assert (start == heap_segment_mem (ephemeral_heap_segment)); |
30768 | } |
30769 | |
30770 | if (tp == tuning_deciding_expansion) |
30771 | { |
30772 | assert (settings.condemned_generation >= (max_generation-1)); |
30773 | size_t gen0size = approximate_new_allocation(); |
30774 | size_t eph_size = gen0size; |
30775 | |
30776 | for (int j = 1; j <= max_generation-1; j++) |
30777 | { |
30778 | eph_size += 2*dd_min_size (dynamic_data_of(j)); |
30779 | } |
30780 | |
30781 | // We must find room for one large object and enough room for gen0size |
30782 | if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size) |
30783 | { |
30784 | dprintf (3, ("Enough room before end of segment" )); |
30785 | return TRUE; |
30786 | } |
30787 | else |
30788 | { |
30789 | size_t room = align_lower_good_size_allocation |
30790 | (heap_segment_reserved (ephemeral_heap_segment) - start); |
30791 | size_t end_seg = room; |
30792 | |
30793 | //look at the plug free space |
30794 | size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size); |
30795 | bool large_chunk_found = FALSE; |
30796 | size_t bos = 0; |
30797 | uint8_t* gen0start = generation_plan_allocation_start (youngest_generation); |
30798 | dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix" , (size_t)gen0start)); |
30799 | if (gen0start == 0) |
30800 | return FALSE; |
30801 | dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id" , |
30802 | room, gen0size)); |
30803 | while ((bos < mark_stack_bos) && |
30804 | !((room >= gen0size) && large_chunk_found)) |
30805 | { |
30806 | uint8_t* plug = pinned_plug (pinned_plug_of (bos)); |
30807 | if (in_range_for_segment (plug, ephemeral_heap_segment)) |
30808 | { |
30809 | if (plug >= gen0start) |
30810 | { |
30811 | size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos))); |
30812 | room += chunk; |
30813 | if (!large_chunk_found) |
30814 | { |
30815 | large_chunk_found = (chunk >= largest_alloc); |
30816 | } |
30817 | dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id" , |
30818 | room, large_chunk_found)); |
30819 | } |
30820 | } |
30821 | bos++; |
30822 | } |
30823 | |
30824 | if (room >= gen0size) |
30825 | { |
30826 | if (large_chunk_found) |
30827 | { |
30828 | sufficient_gen0_space_p = TRUE; |
30829 | |
30830 | dprintf (3, ("Enough room" )); |
30831 | return TRUE; |
30832 | } |
30833 | else |
30834 | { |
30835 | // now we need to find largest_alloc at the end of the segment. |
30836 | if (end_seg >= end_space_after_gc()) |
30837 | { |
30838 | dprintf (3, ("Enough room (may need end of seg)" )); |
30839 | return TRUE; |
30840 | } |
30841 | } |
30842 | } |
30843 | |
30844 | dprintf (3, ("Not enough room" )); |
30845 | return FALSE; |
30846 | } |
30847 | } |
30848 | else |
30849 | { |
30850 | size_t end_space = 0; |
30851 | dynamic_data* dd = dynamic_data_of (0); |
30852 | if ((tp == tuning_deciding_condemned_gen) || |
30853 | (tp == tuning_deciding_full_gc)) |
30854 | { |
30855 | end_space = max (2*dd_min_size (dd), end_space_after_gc()); |
30856 | } |
30857 | else |
30858 | { |
30859 | assert (tp == tuning_deciding_compaction); |
30860 | end_space = approximate_new_allocation(); |
30861 | } |
30862 | |
30863 | if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space)) |
30864 | { |
30865 | dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction" )); |
30866 | } |
30867 | return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space); |
30868 | } |
30869 | } |
30870 | |
30871 | CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes) |
30872 | { |
30873 | //create a new alloc context because gen3context is shared. |
30874 | alloc_context acontext; |
30875 | acontext.alloc_ptr = 0; |
30876 | acontext.alloc_limit = 0; |
30877 | acontext.alloc_bytes = 0; |
30878 | #ifdef MULTIPLE_HEAPS |
30879 | acontext.set_alloc_heap(vm_heap); |
30880 | #endif //MULTIPLE_HEAPS |
30881 | |
30882 | #if BIT64 |
30883 | size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size)); |
30884 | #else |
30885 | size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size)); |
30886 | #endif |
30887 | |
30888 | if (jsize >= maxObjectSize) |
30889 | { |
30890 | if (GCConfig::GetBreakOnOOM()) |
30891 | { |
30892 | GCToOSInterface::DebugBreak(); |
30893 | } |
30894 | return NULL; |
30895 | } |
30896 | |
30897 | size_t size = AlignQword (jsize); |
30898 | int align_const = get_alignment_constant (FALSE); |
30899 | #ifdef FEATURE_LOH_COMPACTION |
30900 | size_t pad = Align (loh_padding_obj_size, align_const); |
30901 | #else |
30902 | size_t pad = 0; |
30903 | #endif //FEATURE_LOH_COMPACTION |
30904 | |
30905 | assert (size >= Align (min_obj_size, align_const)); |
30906 | #ifdef _MSC_VER |
30907 | #pragma inline_depth(0) |
30908 | #endif //_MSC_VER |
30909 | if (! allocate_more_space (&acontext, (size + pad), max_generation+1)) |
30910 | { |
30911 | return 0; |
30912 | } |
30913 | |
30914 | #ifdef _MSC_VER |
30915 | #pragma inline_depth(20) |
30916 | #endif //_MSC_VER |
30917 | |
30918 | #ifdef MARK_ARRAY |
30919 | uint8_t* current_lowest_address = lowest_address; |
30920 | uint8_t* current_highest_address = highest_address; |
30921 | #ifdef BACKGROUND_GC |
30922 | if (recursive_gc_sync::background_running_p()) |
30923 | { |
30924 | current_lowest_address = background_saved_lowest_address; |
30925 | current_highest_address = background_saved_highest_address; |
30926 | } |
30927 | #endif //BACKGROUND_GC |
30928 | #endif // MARK_ARRAY |
30929 | |
30930 | #ifdef FEATURE_LOH_COMPACTION |
30931 | // The GC allocator made a free object already in this alloc context and |
30932 | // adjusted the alloc_ptr accordingly. |
30933 | #endif //FEATURE_LOH_COMPACTION |
30934 | |
30935 | uint8_t* result = acontext.alloc_ptr; |
30936 | |
30937 | assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size); |
30938 | alloc_bytes += size; |
30939 | |
30940 | CObjectHeader* obj = (CObjectHeader*)result; |
30941 | |
30942 | #ifdef MARK_ARRAY |
30943 | if (recursive_gc_sync::background_running_p()) |
30944 | { |
30945 | if ((result < current_highest_address) && (result >= current_lowest_address)) |
30946 | { |
30947 | dprintf (3, ("Clearing mark bit at address %Ix" , |
30948 | (size_t)(&mark_array [mark_word_of (result)]))); |
30949 | |
30950 | mark_array_clear_marked (result); |
30951 | } |
30952 | #ifdef BACKGROUND_GC |
30953 | //the object has to cover one full mark uint32_t |
30954 | assert (size > mark_word_size); |
30955 | if (current_c_gc_state != c_gc_state_free) |
30956 | { |
30957 | dprintf (3, ("Concurrent allocation of a large object %Ix" , |
30958 | (size_t)obj)); |
30959 | //mark the new block specially so we know it is a new object |
30960 | if ((result < current_highest_address) && (result >= current_lowest_address)) |
30961 | { |
30962 | dprintf (3, ("Setting mark bit at address %Ix" , |
30963 | (size_t)(&mark_array [mark_word_of (result)]))); |
30964 | |
30965 | mark_array_set_marked (result); |
30966 | } |
30967 | } |
30968 | #endif //BACKGROUND_GC |
30969 | } |
30970 | #endif //MARK_ARRAY |
30971 | |
30972 | assert (obj != 0); |
30973 | assert ((size_t)obj == Align ((size_t)obj, align_const)); |
30974 | |
30975 | return obj; |
30976 | } |
30977 | |
30978 | void reset_memory (uint8_t* o, size_t sizeo) |
30979 | { |
30980 | if (sizeo > 128 * 1024) |
30981 | { |
30982 | // We cannot reset the memory for the useful part of a free object. |
30983 | size_t size_to_skip = min_free_list - plug_skew; |
30984 | |
30985 | size_t page_start = align_on_page ((size_t)(o + size_to_skip)); |
30986 | size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start; |
30987 | // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail |
30988 | // on write watched memory. |
30989 | if (reset_mm_p) |
30990 | { |
30991 | #ifdef MULTIPLE_HEAPS |
30992 | bool unlock_p = true; |
30993 | #else |
30994 | // We don't do unlock because there could be many processes using workstation GC and it's |
30995 | // bad perf to have many threads doing unlock at the same time. |
30996 | bool unlock_p = false; |
30997 | #endif //MULTIPLE_HEAPS |
30998 | |
30999 | reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, unlock_p); |
31000 | } |
31001 | } |
31002 | } |
31003 | |
31004 | void gc_heap::reset_large_object (uint8_t* o) |
31005 | { |
31006 | // If it's a large object, allow the O/S to discard the backing store for these pages. |
31007 | reset_memory (o, size(o)); |
31008 | } |
31009 | |
31010 | BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp) |
31011 | { |
31012 | BOOL m = FALSE; |
31013 | // It shouldn't be necessary to do these comparisons because this is only used for blocking |
31014 | // GCs and LOH segments cannot be out of range. |
31015 | if ((o >= lowest_address) && (o < highest_address)) |
31016 | { |
31017 | if (marked (o)) |
31018 | { |
31019 | if (clearp) |
31020 | { |
31021 | clear_marked (o); |
31022 | if (pinned (o)) |
31023 | clear_pinned(o); |
31024 | } |
31025 | m = TRUE; |
31026 | } |
31027 | else |
31028 | m = FALSE; |
31029 | } |
31030 | else |
31031 | m = TRUE; |
31032 | return m; |
31033 | } |
31034 | |
31035 | void gc_heap::walk_survivors_relocation (void* profiling_context, record_surv_fn fn) |
31036 | { |
31037 | // Now walk the portion of memory that is actually being relocated. |
31038 | walk_relocation (profiling_context, fn); |
31039 | |
31040 | #ifdef FEATURE_LOH_COMPACTION |
31041 | if (loh_compacted_p) |
31042 | { |
31043 | walk_relocation_for_loh (profiling_context, fn); |
31044 | } |
31045 | #endif //FEATURE_LOH_COMPACTION |
31046 | } |
31047 | |
31048 | void gc_heap::walk_survivors_for_loh (void* profiling_context, record_surv_fn fn) |
31049 | { |
31050 | generation* gen = large_object_generation; |
31051 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen));; |
31052 | |
31053 | PREFIX_ASSUME(seg != NULL); |
31054 | |
31055 | uint8_t* o = generation_allocation_start (gen); |
31056 | uint8_t* plug_end = o; |
31057 | uint8_t* plug_start = o; |
31058 | |
31059 | while (1) |
31060 | { |
31061 | if (o >= heap_segment_allocated (seg)) |
31062 | { |
31063 | seg = heap_segment_next (seg); |
31064 | if (seg == 0) |
31065 | break; |
31066 | else |
31067 | o = heap_segment_mem (seg); |
31068 | } |
31069 | if (large_object_marked(o, FALSE)) |
31070 | { |
31071 | plug_start = o; |
31072 | |
31073 | BOOL m = TRUE; |
31074 | while (m) |
31075 | { |
31076 | o = o + AlignQword (size (o)); |
31077 | if (o >= heap_segment_allocated (seg)) |
31078 | { |
31079 | break; |
31080 | } |
31081 | m = large_object_marked (o, FALSE); |
31082 | } |
31083 | |
31084 | plug_end = o; |
31085 | |
31086 | fn (plug_start, plug_end, 0, profiling_context, false, false); |
31087 | } |
31088 | else |
31089 | { |
31090 | while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE)) |
31091 | { |
31092 | o = o + AlignQword (size (o)); |
31093 | } |
31094 | } |
31095 | } |
31096 | } |
31097 | |
31098 | #ifdef BACKGROUND_GC |
31099 | |
31100 | BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp) |
31101 | { |
31102 | BOOL m = FALSE; |
31103 | if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address)) |
31104 | { |
31105 | if (mark_array_marked (o)) |
31106 | { |
31107 | if (clearp) |
31108 | { |
31109 | mark_array_clear_marked (o); |
31110 | //dprintf (3, ("mark array bit for object %Ix is cleared", o)); |
31111 | dprintf (3, ("CM: %Ix" , o)); |
31112 | } |
31113 | m = TRUE; |
31114 | } |
31115 | else |
31116 | m = FALSE; |
31117 | } |
31118 | else |
31119 | m = TRUE; |
31120 | |
31121 | dprintf (3, ("o %Ix(%d) %s" , o, size(o), (m ? "was bm" : "was NOT bm" ))); |
31122 | return m; |
31123 | } |
31124 | |
31125 | void gc_heap::background_delay_delete_loh_segments() |
31126 | { |
31127 | generation* gen = large_object_generation; |
31128 | heap_segment* seg = heap_segment_rw (generation_start_segment (large_object_generation)); |
31129 | heap_segment* prev_seg = 0; |
31130 | |
31131 | while (seg) |
31132 | { |
31133 | heap_segment* next_seg = heap_segment_next (seg); |
31134 | if (seg->flags & heap_segment_flags_loh_delete) |
31135 | { |
31136 | dprintf (3, ("deleting %Ix-%Ix-%Ix" , (size_t)seg, heap_segment_allocated (seg), heap_segment_reserved (seg))); |
31137 | delete_heap_segment (seg, (GCConfig::GetRetainVM() != 0)); |
31138 | heap_segment_next (prev_seg) = next_seg; |
31139 | } |
31140 | else |
31141 | { |
31142 | prev_seg = seg; |
31143 | } |
31144 | |
31145 | seg = next_seg; |
31146 | } |
31147 | } |
31148 | |
31149 | uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p) |
31150 | { |
31151 | return |
31152 | (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg)); |
31153 | } |
31154 | |
31155 | void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b) |
31156 | { |
31157 | #ifdef VERIFY_HEAP |
31158 | if (end > start) |
31159 | { |
31160 | if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) && |
31161 | !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_MEM_FILL)) |
31162 | { |
31163 | dprintf (3, ("setting mem to %c [%Ix, [%Ix" , b, start, end)); |
31164 | memset (start, b, (end - start)); |
31165 | } |
31166 | } |
31167 | #endif //VERIFY_HEAP |
31168 | } |
31169 | |
31170 | void gc_heap::generation_delete_heap_segment (generation* gen, |
31171 | heap_segment* seg, |
31172 | heap_segment* prev_seg, |
31173 | heap_segment* next_seg) |
31174 | { |
31175 | dprintf (3, ("bgc sweep: deleting seg %Ix" , seg)); |
31176 | if (gen == large_object_generation) |
31177 | { |
31178 | dprintf (3, ("Preparing empty large segment %Ix for deletion" , (size_t)seg)); |
31179 | |
31180 | // We cannot thread segs in here onto freeable_large_heap_segment because |
31181 | // grow_brick_card_tables could be committing mark array which needs to read |
31182 | // the seg list. So we delay it till next time we suspend EE. |
31183 | seg->flags |= heap_segment_flags_loh_delete; |
31184 | // Since we will be decommitting the seg, we need to prevent heap verification |
31185 | // to verify this segment. |
31186 | heap_segment_allocated (seg) = heap_segment_mem (seg); |
31187 | } |
31188 | else |
31189 | { |
31190 | if (seg == ephemeral_heap_segment) |
31191 | { |
31192 | FATAL_GC_ERROR(); |
31193 | } |
31194 | |
31195 | heap_segment_next (next_seg) = prev_seg; |
31196 | |
31197 | dprintf (3, ("Preparing empty small segment %Ix for deletion" , (size_t)seg)); |
31198 | heap_segment_next (seg) = freeable_small_heap_segment; |
31199 | freeable_small_heap_segment = seg; |
31200 | } |
31201 | |
31202 | decommit_heap_segment (seg); |
31203 | seg->flags |= heap_segment_flags_decommitted; |
31204 | |
31205 | set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb); |
31206 | } |
31207 | |
31208 | void gc_heap::process_background_segment_end (heap_segment* seg, |
31209 | generation* gen, |
31210 | uint8_t* last_plug_end, |
31211 | heap_segment* start_seg, |
31212 | BOOL* delete_p) |
31213 | { |
31214 | *delete_p = FALSE; |
31215 | uint8_t* allocated = heap_segment_allocated (seg); |
31216 | uint8_t* background_allocated = heap_segment_background_allocated (seg); |
31217 | BOOL loh_p = heap_segment_loh_p (seg); |
31218 | |
31219 | dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)" , |
31220 | (size_t)heap_segment_mem (seg), background_allocated, allocated)); |
31221 | |
31222 | if (!loh_p && (allocated != background_allocated)) |
31223 | { |
31224 | assert (gen != large_object_generation); |
31225 | |
31226 | dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[" , |
31227 | (size_t)last_plug_end, background_allocated)); |
31228 | thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation)); |
31229 | |
31230 | |
31231 | fix_brick_to_highest (last_plug_end, background_allocated); |
31232 | |
31233 | // When we allowed fgc's during going through gaps, we could have erased the brick |
31234 | // that corresponds to bgc_allocated 'cause we had to update the brick there, |
31235 | // recover it here. |
31236 | fix_brick_to_highest (background_allocated, background_allocated); |
31237 | } |
31238 | else |
31239 | { |
31240 | // by default, if allocated == background_allocated, it can't |
31241 | // be the ephemeral segment. |
31242 | if (seg == ephemeral_heap_segment) |
31243 | { |
31244 | FATAL_GC_ERROR(); |
31245 | } |
31246 | |
31247 | if (allocated == heap_segment_mem (seg)) |
31248 | { |
31249 | // this can happen with LOH segments when multiple threads |
31250 | // allocate new segments and not all of them were needed to |
31251 | // satisfy allocation requests. |
31252 | assert (gen == large_object_generation); |
31253 | } |
31254 | |
31255 | if (last_plug_end == heap_segment_mem (seg)) |
31256 | { |
31257 | dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted" , |
31258 | (size_t)allocated, (*delete_p ? "should" : "should not" ))); |
31259 | |
31260 | if (seg != start_seg) |
31261 | { |
31262 | *delete_p = TRUE; |
31263 | } |
31264 | } |
31265 | else |
31266 | { |
31267 | dprintf (3, ("Trimming seg to %Ix[" , (size_t)last_plug_end)); |
31268 | heap_segment_allocated (seg) = last_plug_end; |
31269 | set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb); |
31270 | |
31271 | decommit_heap_segment_pages (seg, 0); |
31272 | } |
31273 | } |
31274 | |
31275 | dprintf (3, ("verifying seg %Ix's mark array was completely cleared" , seg)); |
31276 | bgc_verify_mark_array_cleared (seg); |
31277 | } |
31278 | |
31279 | void gc_heap::process_n_background_segments (heap_segment* seg, |
31280 | heap_segment* prev_seg, |
31281 | generation* gen) |
31282 | { |
31283 | assert (gen != large_object_generation); |
31284 | |
31285 | while (seg) |
31286 | { |
31287 | dprintf (2, ("processing seg %Ix (not seen by bgc mark)" , seg)); |
31288 | heap_segment* next_seg = heap_segment_next (seg); |
31289 | |
31290 | if (heap_segment_read_only_p (seg)) |
31291 | { |
31292 | prev_seg = seg; |
31293 | } |
31294 | else |
31295 | { |
31296 | if (heap_segment_allocated (seg) == heap_segment_mem (seg)) |
31297 | { |
31298 | // This can happen - if we have a LOH segment where nothing survived |
31299 | // or a SOH segment allocated by a gen1 GC when BGC was going where |
31300 | // nothing survived last time we did a gen1 GC. |
31301 | generation_delete_heap_segment (gen, seg, prev_seg, next_seg); |
31302 | } |
31303 | else |
31304 | { |
31305 | prev_seg = seg; |
31306 | } |
31307 | } |
31308 | |
31309 | verify_soh_segment_list(); |
31310 | seg = next_seg; |
31311 | } |
31312 | } |
31313 | |
31314 | inline |
31315 | BOOL gc_heap::fgc_should_consider_object (uint8_t* o, |
31316 | heap_segment* seg, |
31317 | BOOL consider_bgc_mark_p, |
31318 | BOOL check_current_sweep_p, |
31319 | BOOL check_saved_sweep_p) |
31320 | { |
31321 | // the logic for this function must be kept in sync with the analogous function |
31322 | // in ToolBox\SOS\Strike\gc.cpp |
31323 | |
31324 | // TRUE means we don't need to check the bgc mark bit |
31325 | // FALSE means we do. |
31326 | BOOL no_bgc_mark_p = FALSE; |
31327 | |
31328 | if (consider_bgc_mark_p) |
31329 | { |
31330 | if (check_current_sweep_p && (o < current_sweep_pos)) |
31331 | { |
31332 | dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix" , o, current_sweep_pos)); |
31333 | no_bgc_mark_p = TRUE; |
31334 | } |
31335 | |
31336 | if (!no_bgc_mark_p) |
31337 | { |
31338 | if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start)) |
31339 | { |
31340 | dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix" , o, saved_sweep_ephemeral_start)); |
31341 | no_bgc_mark_p = TRUE; |
31342 | } |
31343 | |
31344 | if (!check_saved_sweep_p) |
31345 | { |
31346 | uint8_t* background_allocated = heap_segment_background_allocated (seg); |
31347 | // if this was the saved ephemeral segment, check_saved_sweep_p |
31348 | // would've been true. |
31349 | assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start); |
31350 | // background_allocated could be 0 for the new segments acquired during bgc |
31351 | // sweep and we still want no_bgc_mark_p to be true. |
31352 | if (o >= background_allocated) |
31353 | { |
31354 | dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix" , o, background_allocated)); |
31355 | no_bgc_mark_p = TRUE; |
31356 | } |
31357 | } |
31358 | } |
31359 | } |
31360 | else |
31361 | { |
31362 | no_bgc_mark_p = TRUE; |
31363 | } |
31364 | |
31365 | dprintf (3, ("bgc mark %Ix: %s (bm: %s)" , o, (no_bgc_mark_p ? "no" : "yes" ), (background_object_marked (o, FALSE) ? "yes" : "no" ))); |
31366 | return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE)); |
31367 | } |
31368 | |
31369 | // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all |
31370 | // if it's TRUE, check_current_sweep_p tells you if you should consider the |
31371 | // current sweep position or not. |
31372 | void gc_heap::should_check_bgc_mark (heap_segment* seg, |
31373 | BOOL* consider_bgc_mark_p, |
31374 | BOOL* check_current_sweep_p, |
31375 | BOOL* check_saved_sweep_p) |
31376 | { |
31377 | // the logic for this function must be kept in sync with the analogous function |
31378 | // in ToolBox\SOS\Strike\gc.cpp |
31379 | *consider_bgc_mark_p = FALSE; |
31380 | *check_current_sweep_p = FALSE; |
31381 | *check_saved_sweep_p = FALSE; |
31382 | |
31383 | if (current_c_gc_state == c_gc_state_planning) |
31384 | { |
31385 | // We are doing the current_sweep_pos comparison here because we have yet to |
31386 | // turn on the swept flag for the segment but in_range_for_segment will return |
31387 | // FALSE if the address is the same as reserved. |
31388 | if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg))) |
31389 | { |
31390 | dprintf (3, ("seg %Ix is already swept by bgc" , seg)); |
31391 | } |
31392 | else |
31393 | { |
31394 | *consider_bgc_mark_p = TRUE; |
31395 | |
31396 | dprintf (3, ("seg %Ix hasn't been swept by bgc" , seg)); |
31397 | |
31398 | if (seg == saved_sweep_ephemeral_seg) |
31399 | { |
31400 | dprintf (3, ("seg %Ix is the saved ephemeral seg" , seg)); |
31401 | *check_saved_sweep_p = TRUE; |
31402 | } |
31403 | |
31404 | if (in_range_for_segment (current_sweep_pos, seg)) |
31405 | { |
31406 | dprintf (3, ("current sweep pos is %Ix and within seg %Ix" , |
31407 | current_sweep_pos, seg)); |
31408 | *check_current_sweep_p = TRUE; |
31409 | } |
31410 | } |
31411 | } |
31412 | } |
31413 | |
31414 | void gc_heap::background_ephemeral_sweep() |
31415 | { |
31416 | dprintf (3, ("bgc ephemeral sweep" )); |
31417 | |
31418 | int align_const = get_alignment_constant (TRUE); |
31419 | |
31420 | saved_sweep_ephemeral_seg = ephemeral_heap_segment; |
31421 | saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1)); |
31422 | |
31423 | // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list, |
31424 | // we thread onto a list first then publish it when we are done. |
31425 | allocator youngest_free_list; |
31426 | size_t youngest_free_list_space = 0; |
31427 | size_t youngest_free_obj_space = 0; |
31428 | |
31429 | youngest_free_list.clear(); |
31430 | |
31431 | for (int i = 0; i <= (max_generation - 1); i++) |
31432 | { |
31433 | generation* gen_to_reset = generation_of (i); |
31434 | assert (generation_free_list_space (gen_to_reset) == 0); |
31435 | // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added |
31436 | // something there. |
31437 | } |
31438 | |
31439 | for (int i = (max_generation - 1); i >= 0; i--) |
31440 | { |
31441 | generation* current_gen = generation_of (i); |
31442 | uint8_t* o = generation_allocation_start (current_gen); |
31443 | //Skip the generation gap object |
31444 | o = o + Align(size (o), align_const); |
31445 | uint8_t* end = ((i > 0) ? |
31446 | generation_allocation_start (generation_of (i - 1)) : |
31447 | heap_segment_allocated (ephemeral_heap_segment)); |
31448 | |
31449 | uint8_t* plug_end = o; |
31450 | uint8_t* plug_start = o; |
31451 | BOOL marked_p = FALSE; |
31452 | |
31453 | while (o < end) |
31454 | { |
31455 | marked_p = background_object_marked (o, TRUE); |
31456 | if (marked_p) |
31457 | { |
31458 | plug_start = o; |
31459 | size_t plug_size = plug_start - plug_end; |
31460 | |
31461 | if (i >= 1) |
31462 | { |
31463 | thread_gap (plug_end, plug_size, current_gen); |
31464 | } |
31465 | else |
31466 | { |
31467 | if (plug_size > 0) |
31468 | { |
31469 | make_unused_array (plug_end, plug_size); |
31470 | if (plug_size >= min_free_list) |
31471 | { |
31472 | youngest_free_list_space += plug_size; |
31473 | youngest_free_list.thread_item (plug_end, plug_size); |
31474 | } |
31475 | else |
31476 | { |
31477 | youngest_free_obj_space += plug_size; |
31478 | } |
31479 | } |
31480 | } |
31481 | |
31482 | fix_brick_to_highest (plug_end, plug_start); |
31483 | fix_brick_to_highest (plug_start, plug_start); |
31484 | |
31485 | BOOL m = TRUE; |
31486 | while (m) |
31487 | { |
31488 | o = o + Align (size (o), align_const); |
31489 | if (o >= end) |
31490 | { |
31491 | break; |
31492 | } |
31493 | |
31494 | m = background_object_marked (o, TRUE); |
31495 | } |
31496 | plug_end = o; |
31497 | dprintf (3, ("bgs: plug [%Ix, %Ix[" , (size_t)plug_start, (size_t)plug_end)); |
31498 | } |
31499 | else |
31500 | { |
31501 | while ((o < end) && !background_object_marked (o, FALSE)) |
31502 | { |
31503 | o = o + Align (size (o), align_const); |
31504 | } |
31505 | } |
31506 | } |
31507 | |
31508 | if (plug_end != end) |
31509 | { |
31510 | if (i >= 1) |
31511 | { |
31512 | thread_gap (plug_end, end - plug_end, current_gen); |
31513 | fix_brick_to_highest (plug_end, end); |
31514 | } |
31515 | else |
31516 | { |
31517 | heap_segment_allocated (ephemeral_heap_segment) = plug_end; |
31518 | // the following line is temporary. |
31519 | heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end; |
31520 | #ifdef VERIFY_HEAP |
31521 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
31522 | { |
31523 | make_unused_array (plug_end, (end - plug_end)); |
31524 | } |
31525 | #endif //VERIFY_HEAP |
31526 | } |
31527 | } |
31528 | |
31529 | dd_fragmentation (dynamic_data_of (i)) = |
31530 | generation_free_list_space (current_gen) + generation_free_obj_space (current_gen); |
31531 | } |
31532 | |
31533 | generation* youngest_gen = generation_of (0); |
31534 | generation_free_list_space (youngest_gen) = youngest_free_list_space; |
31535 | generation_free_obj_space (youngest_gen) = youngest_free_obj_space; |
31536 | dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space; |
31537 | generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list); |
31538 | } |
31539 | |
31540 | void gc_heap::background_sweep() |
31541 | { |
31542 | generation* gen = generation_of (max_generation); |
31543 | dynamic_data* dd = dynamic_data_of (max_generation); |
31544 | // For SOH segments we go backwards. |
31545 | heap_segment* start_seg = ephemeral_heap_segment; |
31546 | PREFIX_ASSUME(start_seg != NULL); |
31547 | heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
31548 | heap_segment* seg = start_seg; |
31549 | uint8_t* o = heap_segment_mem (seg); |
31550 | |
31551 | heap_segment* prev_seg = heap_segment_next (seg); |
31552 | int align_const = get_alignment_constant (TRUE); |
31553 | if (seg == fseg) |
31554 | { |
31555 | assert (o == generation_allocation_start (generation_of (max_generation))); |
31556 | o = o + Align(size (o), align_const); |
31557 | } |
31558 | |
31559 | uint8_t* plug_end = o; |
31560 | uint8_t* plug_start = o; |
31561 | next_sweep_obj = o; |
31562 | current_sweep_pos = o; |
31563 | |
31564 | //uint8_t* end = background_next_end (seg, (gen == large_object_generation)); |
31565 | uint8_t* end = heap_segment_background_allocated (seg); |
31566 | BOOL delete_p = FALSE; |
31567 | |
31568 | //concurrent_print_time_delta ("finished with mark and start with sweep"); |
31569 | concurrent_print_time_delta ("Sw" ); |
31570 | dprintf (2, ("---- (GC%d)Background Sweep Phase ----" , VolatileLoad(&settings.gc_index))); |
31571 | |
31572 | //block concurrent allocation for large objects |
31573 | dprintf (3, ("lh state: planning" )); |
31574 | if (gc_lh_block_event.IsValid()) |
31575 | { |
31576 | gc_lh_block_event.Reset(); |
31577 | } |
31578 | |
31579 | for (int i = 0; i <= (max_generation + 1); i++) |
31580 | { |
31581 | generation* gen_to_reset = generation_of (i); |
31582 | generation_allocator (gen_to_reset)->clear(); |
31583 | generation_free_list_space (gen_to_reset) = 0; |
31584 | generation_free_obj_space (gen_to_reset) = 0; |
31585 | generation_free_list_allocated (gen_to_reset) = 0; |
31586 | generation_end_seg_allocated (gen_to_reset) = 0; |
31587 | generation_condemned_allocated (gen_to_reset) = 0; |
31588 | //reset the allocation so foreground gc can allocate into older generation |
31589 | generation_allocation_pointer (gen_to_reset)= 0; |
31590 | generation_allocation_limit (gen_to_reset) = 0; |
31591 | generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset)); |
31592 | } |
31593 | |
31594 | FIRE_EVENT(BGC2ndNonConEnd); |
31595 | |
31596 | loh_alloc_thread_count = 0; |
31597 | current_bgc_state = bgc_sweep_soh; |
31598 | verify_soh_segment_list(); |
31599 | |
31600 | #ifdef FEATURE_BASICFREEZE |
31601 | if ((generation_start_segment (gen) != ephemeral_heap_segment) && |
31602 | ro_segments_in_range) |
31603 | { |
31604 | sweep_ro_segments (generation_start_segment (gen)); |
31605 | } |
31606 | #endif // FEATURE_BASICFREEZE |
31607 | |
31608 | //TODO BACKGROUND_GC: can we move this to where we switch to the LOH? |
31609 | if (current_c_gc_state != c_gc_state_planning) |
31610 | { |
31611 | current_c_gc_state = c_gc_state_planning; |
31612 | } |
31613 | |
31614 | concurrent_print_time_delta ("Swe" ); |
31615 | |
31616 | heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1))); |
31617 | PREFIX_ASSUME(loh_seg != NULL); |
31618 | while (loh_seg ) |
31619 | { |
31620 | loh_seg->flags &= ~heap_segment_flags_swept; |
31621 | heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg); |
31622 | loh_seg = heap_segment_next_rw (loh_seg); |
31623 | } |
31624 | |
31625 | #ifdef MULTIPLE_HEAPS |
31626 | bgc_t_join.join(this, gc_join_restart_ee); |
31627 | if (bgc_t_join.joined()) |
31628 | #endif //MULTIPLE_HEAPS |
31629 | { |
31630 | #ifdef MULTIPLE_HEAPS |
31631 | dprintf(2, ("Starting BGC threads for resuming EE" )); |
31632 | bgc_t_join.restart(); |
31633 | #endif //MULTIPLE_HEAPS |
31634 | } |
31635 | |
31636 | if (heap_number == 0) |
31637 | { |
31638 | restart_EE (); |
31639 | } |
31640 | |
31641 | FIRE_EVENT(BGC2ndConBegin); |
31642 | |
31643 | background_ephemeral_sweep(); |
31644 | |
31645 | concurrent_print_time_delta ("Swe eph" ); |
31646 | |
31647 | #ifdef MULTIPLE_HEAPS |
31648 | bgc_t_join.join(this, gc_join_after_ephemeral_sweep); |
31649 | if (bgc_t_join.joined()) |
31650 | #endif //MULTIPLE_HEAPS |
31651 | { |
31652 | #ifdef FEATURE_EVENT_TRACE |
31653 | bgc_heap_walk_for_etw_p = GCEventStatus::IsEnabled(GCEventProvider_Default, |
31654 | GCEventKeyword_GCHeapSurvivalAndMovement, |
31655 | GCEventLevel_Information); |
31656 | #endif //FEATURE_EVENT_TRACE |
31657 | |
31658 | leave_spin_lock (&gc_lock); |
31659 | |
31660 | #ifdef MULTIPLE_HEAPS |
31661 | dprintf(2, ("Starting BGC threads for BGC sweeping" )); |
31662 | bgc_t_join.restart(); |
31663 | #endif //MULTIPLE_HEAPS |
31664 | } |
31665 | |
31666 | disable_preemptive (true); |
31667 | |
31668 | dprintf (2, ("bgs: sweeping gen2 objects" )); |
31669 | dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix" , (size_t)seg, |
31670 | (size_t)heap_segment_mem (seg), |
31671 | (size_t)heap_segment_allocated (seg), |
31672 | (size_t)heap_segment_background_allocated (seg))); |
31673 | |
31674 | int num_objs = 256; |
31675 | int current_num_objs = 0; |
31676 | heap_segment* next_seg = 0; |
31677 | |
31678 | while (1) |
31679 | { |
31680 | if (o >= end) |
31681 | { |
31682 | if (gen == large_object_generation) |
31683 | { |
31684 | next_seg = heap_segment_next (seg); |
31685 | } |
31686 | else |
31687 | { |
31688 | next_seg = heap_segment_prev (fseg, seg); |
31689 | } |
31690 | |
31691 | delete_p = FALSE; |
31692 | |
31693 | if (!heap_segment_read_only_p (seg)) |
31694 | { |
31695 | if (gen == large_object_generation) |
31696 | { |
31697 | // we can treat all LOH segments as in the bgc domain |
31698 | // regardless of whether we saw in bgc mark or not |
31699 | // because we don't allow LOH allocations during bgc |
31700 | // sweep anyway - the LOH segments can't change. |
31701 | process_background_segment_end (seg, gen, plug_end, |
31702 | start_seg, &delete_p); |
31703 | } |
31704 | else |
31705 | { |
31706 | assert (heap_segment_background_allocated (seg) != 0); |
31707 | process_background_segment_end (seg, gen, plug_end, |
31708 | start_seg, &delete_p); |
31709 | |
31710 | assert (next_seg || !delete_p); |
31711 | } |
31712 | } |
31713 | |
31714 | if (delete_p) |
31715 | { |
31716 | generation_delete_heap_segment (gen, seg, prev_seg, next_seg); |
31717 | } |
31718 | else |
31719 | { |
31720 | prev_seg = seg; |
31721 | dprintf (2, ("seg %Ix has been swept" , seg)); |
31722 | seg->flags |= heap_segment_flags_swept; |
31723 | } |
31724 | |
31725 | verify_soh_segment_list(); |
31726 | |
31727 | seg = next_seg; |
31728 | |
31729 | dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix" , seg, next_seg, prev_seg)); |
31730 | |
31731 | if (seg == 0) |
31732 | { |
31733 | generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen)); |
31734 | |
31735 | PREFIX_ASSUME(generation_allocation_segment(gen) != NULL); |
31736 | |
31737 | if (gen != large_object_generation) |
31738 | { |
31739 | dprintf (2, ("bgs: sweeping gen3 objects" )); |
31740 | concurrent_print_time_delta ("Swe SOH" ); |
31741 | FIRE_EVENT(BGC1stSweepEnd, 0); |
31742 | |
31743 | enter_spin_lock (&more_space_lock_loh); |
31744 | add_saved_spinlock_info (true, me_acquire, mt_bgc_loh_sweep); |
31745 | |
31746 | concurrent_print_time_delta ("Swe LOH took msl" ); |
31747 | |
31748 | // We wait till all allocating threads are completely done. |
31749 | int spin_count = yp_spin_count_unit; |
31750 | while (loh_alloc_thread_count) |
31751 | { |
31752 | spin_and_switch (spin_count, (loh_alloc_thread_count == 0)); |
31753 | } |
31754 | |
31755 | current_bgc_state = bgc_sweep_loh; |
31756 | gen = generation_of (max_generation+1); |
31757 | start_seg = heap_segment_rw (generation_start_segment (gen)); |
31758 | |
31759 | PREFIX_ASSUME(start_seg != NULL); |
31760 | |
31761 | seg = start_seg; |
31762 | prev_seg = 0; |
31763 | o = generation_allocation_start (gen); |
31764 | assert (method_table (o) == g_gc_pFreeObjectMethodTable); |
31765 | align_const = get_alignment_constant (FALSE); |
31766 | o = o + Align(size (o), align_const); |
31767 | plug_end = o; |
31768 | end = heap_segment_allocated (seg); |
31769 | dprintf (2, ("sweeping gen3 objects" )); |
31770 | generation_free_obj_space (gen) = 0; |
31771 | generation_allocator (gen)->clear(); |
31772 | generation_free_list_space (gen) = 0; |
31773 | |
31774 | dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix" , (size_t)seg, |
31775 | (size_t)heap_segment_mem (seg), |
31776 | (size_t)heap_segment_allocated (seg), |
31777 | (size_t)heap_segment_background_allocated (seg))); |
31778 | } |
31779 | else |
31780 | break; |
31781 | } |
31782 | else |
31783 | { |
31784 | o = heap_segment_mem (seg); |
31785 | if (seg == fseg) |
31786 | { |
31787 | assert (gen != large_object_generation); |
31788 | assert (o == generation_allocation_start (generation_of (max_generation))); |
31789 | align_const = get_alignment_constant (TRUE); |
31790 | o = o + Align(size (o), align_const); |
31791 | } |
31792 | |
31793 | plug_end = o; |
31794 | current_sweep_pos = o; |
31795 | next_sweep_obj = o; |
31796 | |
31797 | allow_fgc(); |
31798 | end = background_next_end (seg, (gen == large_object_generation)); |
31799 | dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix" , (size_t)seg, |
31800 | (size_t)heap_segment_mem (seg), |
31801 | (size_t)heap_segment_allocated (seg), |
31802 | (size_t)heap_segment_background_allocated (seg))); |
31803 | } |
31804 | } |
31805 | |
31806 | if ((o < end) && background_object_marked (o, TRUE)) |
31807 | { |
31808 | plug_start = o; |
31809 | if (gen == large_object_generation) |
31810 | { |
31811 | dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)" , plug_end, plug_start, plug_start-plug_end)); |
31812 | } |
31813 | |
31814 | thread_gap (plug_end, plug_start-plug_end, gen); |
31815 | if (gen != large_object_generation) |
31816 | { |
31817 | add_gen_free (max_generation, plug_start-plug_end); |
31818 | fix_brick_to_highest (plug_end, plug_start); |
31819 | // we need to fix the brick for the next plug here 'cause an FGC can |
31820 | // happen and can't read a stale brick. |
31821 | fix_brick_to_highest (plug_start, plug_start); |
31822 | } |
31823 | |
31824 | BOOL m = TRUE; |
31825 | |
31826 | while (m) |
31827 | { |
31828 | next_sweep_obj = o + Align(size (o), align_const); |
31829 | current_num_objs++; |
31830 | if (current_num_objs >= num_objs) |
31831 | { |
31832 | current_sweep_pos = next_sweep_obj; |
31833 | |
31834 | allow_fgc(); |
31835 | current_num_objs = 0; |
31836 | } |
31837 | |
31838 | o = next_sweep_obj; |
31839 | if (o >= end) |
31840 | { |
31841 | break; |
31842 | } |
31843 | |
31844 | m = background_object_marked (o, TRUE); |
31845 | } |
31846 | plug_end = o; |
31847 | if (gen != large_object_generation) |
31848 | { |
31849 | add_gen_plug (max_generation, plug_end-plug_start); |
31850 | dd_survived_size (dd) += (plug_end - plug_start); |
31851 | } |
31852 | dprintf (3, ("bgs: plug [%Ix, %Ix[" , (size_t)plug_start, (size_t)plug_end)); |
31853 | } |
31854 | else |
31855 | { |
31856 | while ((o < end) && !background_object_marked (o, FALSE)) |
31857 | { |
31858 | next_sweep_obj = o + Align(size (o), align_const);; |
31859 | current_num_objs++; |
31860 | if (current_num_objs >= num_objs) |
31861 | { |
31862 | current_sweep_pos = plug_end; |
31863 | dprintf (1234, ("f: swept till %Ix" , current_sweep_pos)); |
31864 | allow_fgc(); |
31865 | current_num_objs = 0; |
31866 | } |
31867 | |
31868 | o = next_sweep_obj; |
31869 | } |
31870 | } |
31871 | } |
31872 | |
31873 | size_t total_loh_size = generation_size (max_generation + 1); |
31874 | size_t total_soh_size = generation_sizes (generation_of (max_generation)); |
31875 | |
31876 | dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id" , heap_number, total_loh_size, total_soh_size)); |
31877 | |
31878 | dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id" , |
31879 | generation_free_list_space (generation_of (max_generation)), |
31880 | generation_free_obj_space (generation_of (max_generation)))); |
31881 | dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id" , |
31882 | heap_number, |
31883 | generation_free_list_space (generation_of (max_generation + 1)), |
31884 | generation_free_obj_space (generation_of (max_generation + 1)))); |
31885 | |
31886 | FIRE_EVENT(BGC2ndConEnd); |
31887 | concurrent_print_time_delta ("background sweep" ); |
31888 | |
31889 | heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation))); |
31890 | PREFIX_ASSUME(reset_seg != NULL); |
31891 | |
31892 | while (reset_seg) |
31893 | { |
31894 | heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg); |
31895 | heap_segment_background_allocated (reset_seg) = 0; |
31896 | reset_seg = heap_segment_next_rw (reset_seg); |
31897 | } |
31898 | |
31899 | generation* loh_gen = generation_of (max_generation + 1); |
31900 | generation_allocation_segment (loh_gen) = heap_segment_rw (generation_start_segment (loh_gen)); |
31901 | |
31902 | // We calculate dynamic data here because if we wait till we signal the lh event, |
31903 | // the allocation thread can change the fragmentation and we may read an intermediate |
31904 | // value (which can be greater than the generation size). Plus by that time it won't |
31905 | // be accurate. |
31906 | compute_new_dynamic_data (max_generation); |
31907 | |
31908 | enable_preemptive (); |
31909 | |
31910 | #ifdef MULTIPLE_HEAPS |
31911 | bgc_t_join.join(this, gc_join_set_state_free); |
31912 | if (bgc_t_join.joined()) |
31913 | #endif //MULTIPLE_HEAPS |
31914 | { |
31915 | // TODO: We are using this join just to set the state. Should |
31916 | // look into eliminating it - check to make sure things that use |
31917 | // this state can live with per heap state like should_check_bgc_mark. |
31918 | current_c_gc_state = c_gc_state_free; |
31919 | |
31920 | #ifdef MULTIPLE_HEAPS |
31921 | dprintf(2, ("Starting BGC threads after background sweep phase" )); |
31922 | bgc_t_join.restart(); |
31923 | #endif //MULTIPLE_HEAPS |
31924 | } |
31925 | |
31926 | disable_preemptive (true); |
31927 | |
31928 | if (gc_lh_block_event.IsValid()) |
31929 | { |
31930 | gc_lh_block_event.Set(); |
31931 | } |
31932 | |
31933 | add_saved_spinlock_info (true, me_release, mt_bgc_loh_sweep); |
31934 | leave_spin_lock (&more_space_lock_loh); |
31935 | |
31936 | //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index))); |
31937 | dprintf (GTC_LOG, ("---- (GC%d)ESw ----" , VolatileLoad(&settings.gc_index))); |
31938 | } |
31939 | #endif //BACKGROUND_GC |
31940 | |
31941 | void gc_heap::sweep_large_objects () |
31942 | { |
31943 | //this min value is for the sake of the dynamic tuning. |
31944 | //so we know that we are not starting even if we have no |
31945 | //survivors. |
31946 | generation* gen = large_object_generation; |
31947 | heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen)); |
31948 | |
31949 | PREFIX_ASSUME(start_seg != NULL); |
31950 | |
31951 | heap_segment* seg = start_seg; |
31952 | heap_segment* prev_seg = 0; |
31953 | uint8_t* o = generation_allocation_start (gen); |
31954 | int align_const = get_alignment_constant (FALSE); |
31955 | |
31956 | //Skip the generation gap object |
31957 | o = o + Align(size (o), align_const); |
31958 | |
31959 | uint8_t* plug_end = o; |
31960 | uint8_t* plug_start = o; |
31961 | |
31962 | generation_allocator (gen)->clear(); |
31963 | generation_free_list_space (gen) = 0; |
31964 | generation_free_obj_space (gen) = 0; |
31965 | |
31966 | |
31967 | dprintf (3, ("sweeping large objects" )); |
31968 | dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix" , |
31969 | (size_t)seg, |
31970 | (size_t)heap_segment_mem (seg), |
31971 | (size_t)heap_segment_allocated (seg), |
31972 | o)); |
31973 | |
31974 | while (1) |
31975 | { |
31976 | if (o >= heap_segment_allocated (seg)) |
31977 | { |
31978 | heap_segment* next_seg = heap_segment_next (seg); |
31979 | //delete the empty segment if not the only one |
31980 | if ((plug_end == heap_segment_mem (seg)) && |
31981 | (seg != start_seg) && !heap_segment_read_only_p (seg)) |
31982 | { |
31983 | //prepare for deletion |
31984 | dprintf (3, ("Preparing empty large segment %Ix" , (size_t)seg)); |
31985 | assert (prev_seg); |
31986 | heap_segment_next (prev_seg) = next_seg; |
31987 | heap_segment_next (seg) = freeable_large_heap_segment; |
31988 | freeable_large_heap_segment = seg; |
31989 | } |
31990 | else |
31991 | { |
31992 | if (!heap_segment_read_only_p (seg)) |
31993 | { |
31994 | dprintf (3, ("Trimming seg to %Ix[" , (size_t)plug_end)); |
31995 | heap_segment_allocated (seg) = plug_end; |
31996 | decommit_heap_segment_pages (seg, 0); |
31997 | } |
31998 | prev_seg = seg; |
31999 | } |
32000 | seg = next_seg; |
32001 | if (seg == 0) |
32002 | break; |
32003 | else |
32004 | { |
32005 | o = heap_segment_mem (seg); |
32006 | plug_end = o; |
32007 | dprintf (3, ("seg: %Ix, [%Ix, %Ix[" , (size_t)seg, |
32008 | (size_t)heap_segment_mem (seg), |
32009 | (size_t)heap_segment_allocated (seg))); |
32010 | } |
32011 | } |
32012 | if (large_object_marked(o, TRUE)) |
32013 | { |
32014 | plug_start = o; |
32015 | //everything between plug_end and plug_start is free |
32016 | thread_gap (plug_end, plug_start-plug_end, gen); |
32017 | |
32018 | BOOL m = TRUE; |
32019 | while (m) |
32020 | { |
32021 | o = o + AlignQword (size (o)); |
32022 | if (o >= heap_segment_allocated (seg)) |
32023 | { |
32024 | break; |
32025 | } |
32026 | m = large_object_marked (o, TRUE); |
32027 | } |
32028 | plug_end = o; |
32029 | dprintf (3, ("plug [%Ix, %Ix[" , (size_t)plug_start, (size_t)plug_end)); |
32030 | } |
32031 | else |
32032 | { |
32033 | while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE)) |
32034 | { |
32035 | o = o + AlignQword (size (o)); |
32036 | } |
32037 | } |
32038 | } |
32039 | |
32040 | generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen)); |
32041 | |
32042 | PREFIX_ASSUME(generation_allocation_segment(gen) != NULL); |
32043 | } |
32044 | |
32045 | void gc_heap::relocate_in_large_objects () |
32046 | { |
32047 | relocate_args args; |
32048 | args.low = gc_low; |
32049 | args.high = gc_high; |
32050 | args.last_plug = 0; |
32051 | |
32052 | generation* gen = large_object_generation; |
32053 | |
32054 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
32055 | |
32056 | PREFIX_ASSUME(seg != NULL); |
32057 | |
32058 | uint8_t* o = generation_allocation_start (gen); |
32059 | |
32060 | while (1) |
32061 | { |
32062 | if (o >= heap_segment_allocated (seg)) |
32063 | { |
32064 | seg = heap_segment_next_rw (seg); |
32065 | if (seg == 0) |
32066 | break; |
32067 | else |
32068 | { |
32069 | o = heap_segment_mem (seg); |
32070 | } |
32071 | } |
32072 | while (o < heap_segment_allocated (seg)) |
32073 | { |
32074 | check_class_object_demotion (o); |
32075 | if (contain_pointers (o)) |
32076 | { |
32077 | dprintf(3, ("Relocating through large object %Ix" , (size_t)o)); |
32078 | go_through_object_nostart (method_table (o), o, size(o), pval, |
32079 | { |
32080 | reloc_survivor_helper (pval); |
32081 | }); |
32082 | } |
32083 | o = o + AlignQword (size (o)); |
32084 | } |
32085 | } |
32086 | } |
32087 | |
32088 | void gc_heap::mark_through_cards_for_large_objects (card_fn fn, |
32089 | BOOL relocating) |
32090 | { |
32091 | uint8_t* low = gc_low; |
32092 | size_t end_card = 0; |
32093 | generation* oldest_gen = generation_of (max_generation+1); |
32094 | heap_segment* seg = heap_segment_rw (generation_start_segment (oldest_gen)); |
32095 | |
32096 | PREFIX_ASSUME(seg != NULL); |
32097 | |
32098 | uint8_t* beg = generation_allocation_start (oldest_gen); |
32099 | uint8_t* end = heap_segment_allocated (seg); |
32100 | |
32101 | size_t cg_pointers_found = 0; |
32102 | |
32103 | size_t card_word_end = (card_of (align_on_card_word (end)) / |
32104 | card_word_width); |
32105 | |
32106 | size_t n_eph = 0; |
32107 | size_t n_gen = 0; |
32108 | size_t n_card_set = 0; |
32109 | uint8_t* next_boundary = (relocating ? |
32110 | generation_plan_allocation_start (generation_of (max_generation -1)) : |
32111 | ephemeral_low); |
32112 | |
32113 | uint8_t* nhigh = (relocating ? |
32114 | heap_segment_plan_allocated (ephemeral_heap_segment) : |
32115 | ephemeral_high); |
32116 | |
32117 | BOOL foundp = FALSE; |
32118 | uint8_t* start_address = 0; |
32119 | uint8_t* limit = 0; |
32120 | size_t card = card_of (beg); |
32121 | uint8_t* o = beg; |
32122 | #ifdef BACKGROUND_GC |
32123 | BOOL consider_bgc_mark_p = FALSE; |
32124 | BOOL check_current_sweep_p = FALSE; |
32125 | BOOL check_saved_sweep_p = FALSE; |
32126 | should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p); |
32127 | #endif //BACKGROUND_GC |
32128 | |
32129 | size_t total_cards_cleared = 0; |
32130 | |
32131 | //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end)); |
32132 | dprintf(3, ("CMl: %Ix->%Ix" , (size_t)beg, (size_t)end)); |
32133 | while (1) |
32134 | { |
32135 | if ((o < end) && (card_of(o) > card)) |
32136 | { |
32137 | dprintf (3, ("Found %Id cg pointers" , cg_pointers_found)); |
32138 | if (cg_pointers_found == 0) |
32139 | { |
32140 | dprintf(3,(" Clearing cards [%Ix, %Ix[ " , (size_t)card_address(card), (size_t)o)); |
32141 | clear_cards (card, card_of((uint8_t*)o)); |
32142 | total_cards_cleared += (card_of((uint8_t*)o) - card); |
32143 | } |
32144 | n_eph +=cg_pointers_found; |
32145 | cg_pointers_found = 0; |
32146 | card = card_of ((uint8_t*)o); |
32147 | } |
32148 | if ((o < end) &&(card >= end_card)) |
32149 | { |
32150 | foundp = find_card (card_table, card, card_word_end, end_card); |
32151 | if (foundp) |
32152 | { |
32153 | n_card_set+= end_card - card; |
32154 | start_address = max (beg, card_address (card)); |
32155 | } |
32156 | limit = min (end, card_address (end_card)); |
32157 | } |
32158 | if ((!foundp) || (o >= end) || (card_address (card) >= end)) |
32159 | { |
32160 | if ((foundp) && (cg_pointers_found == 0)) |
32161 | { |
32162 | dprintf(3,(" Clearing cards [%Ix, %Ix[ " , (size_t)card_address(card), |
32163 | (size_t)card_address(card+1))); |
32164 | clear_cards (card, card+1); |
32165 | total_cards_cleared += 1; |
32166 | } |
32167 | n_eph +=cg_pointers_found; |
32168 | cg_pointers_found = 0; |
32169 | if ((seg = heap_segment_next_rw (seg)) != 0) |
32170 | { |
32171 | #ifdef BACKGROUND_GC |
32172 | should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p); |
32173 | #endif //BACKGROUND_GC |
32174 | beg = heap_segment_mem (seg); |
32175 | end = compute_next_end (seg, low); |
32176 | card_word_end = card_of (align_on_card_word (end)) / card_word_width; |
32177 | card = card_of (beg); |
32178 | o = beg; |
32179 | end_card = 0; |
32180 | continue; |
32181 | } |
32182 | else |
32183 | { |
32184 | break; |
32185 | } |
32186 | } |
32187 | |
32188 | assert (card_set_p (card)); |
32189 | { |
32190 | dprintf(3,("card %Ix: o: %Ix, l: %Ix[ " , |
32191 | card, (size_t)o, (size_t)limit)); |
32192 | |
32193 | assert (Align (size (o)) >= Align (min_obj_size)); |
32194 | size_t s = size (o); |
32195 | uint8_t* next_o = o + AlignQword (s); |
32196 | Prefetch (next_o); |
32197 | |
32198 | while (o < limit) |
32199 | { |
32200 | s = size (o); |
32201 | assert (Align (s) >= Align (min_obj_size)); |
32202 | next_o = o + AlignQword (s); |
32203 | Prefetch (next_o); |
32204 | |
32205 | dprintf (4, ("|%Ix|" , (size_t)o)); |
32206 | if (next_o < start_address) |
32207 | { |
32208 | goto end_object; |
32209 | } |
32210 | |
32211 | #ifdef BACKGROUND_GC |
32212 | if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p)) |
32213 | { |
32214 | goto end_object; |
32215 | } |
32216 | #endif //BACKGROUND_GC |
32217 | |
32218 | #ifdef COLLECTIBLE_CLASS |
32219 | if (is_collectible(o)) |
32220 | { |
32221 | BOOL passed_end_card_p = FALSE; |
32222 | |
32223 | if (card_of (o) > card) |
32224 | { |
32225 | passed_end_card_p = card_transition (o, end, card_word_end, |
32226 | cg_pointers_found, |
32227 | n_eph, n_card_set, |
32228 | card, end_card, |
32229 | foundp, start_address, |
32230 | limit, total_cards_cleared); |
32231 | } |
32232 | |
32233 | if ((!passed_end_card_p || foundp) && (card_of (o) == card)) |
32234 | { |
32235 | // card is valid and it covers the head of the object |
32236 | if (fn == &gc_heap::relocate_address) |
32237 | { |
32238 | keep_card_live (o, n_gen, cg_pointers_found); |
32239 | } |
32240 | else |
32241 | { |
32242 | uint8_t* class_obj = get_class_object (o); |
32243 | mark_through_cards_helper (&class_obj, n_gen, |
32244 | cg_pointers_found, fn, |
32245 | nhigh, next_boundary); |
32246 | } |
32247 | } |
32248 | |
32249 | if (passed_end_card_p) |
32250 | { |
32251 | if (foundp && (card_address (card) < next_o)) |
32252 | { |
32253 | goto go_through_refs; |
32254 | } |
32255 | else |
32256 | { |
32257 | goto end_object; |
32258 | } |
32259 | } |
32260 | } |
32261 | |
32262 | go_through_refs: |
32263 | #endif //COLLECTIBLE_CLASS |
32264 | |
32265 | if (contain_pointers (o)) |
32266 | { |
32267 | dprintf(3,("Going through %Ix" , (size_t)o)); |
32268 | |
32269 | go_through_object (method_table(o), o, s, poo, |
32270 | start_address, use_start, (o + s), |
32271 | { |
32272 | if (card_of ((uint8_t*)poo) > card) |
32273 | { |
32274 | BOOL passed_end_card_p = card_transition ((uint8_t*)poo, end, |
32275 | card_word_end, |
32276 | cg_pointers_found, |
32277 | n_eph, n_card_set, |
32278 | card, end_card, |
32279 | foundp, start_address, |
32280 | limit, total_cards_cleared); |
32281 | |
32282 | if (passed_end_card_p) |
32283 | { |
32284 | if (foundp && (card_address (card) < next_o)) |
32285 | { |
32286 | //new_start(); |
32287 | { |
32288 | if (ppstop <= (uint8_t**)start_address) |
32289 | {break;} |
32290 | else if (poo < (uint8_t**)start_address) |
32291 | {poo = (uint8_t**)start_address;} |
32292 | } |
32293 | } |
32294 | else |
32295 | { |
32296 | goto end_object; |
32297 | } |
32298 | } |
32299 | } |
32300 | |
32301 | mark_through_cards_helper (poo, n_gen, |
32302 | cg_pointers_found, fn, |
32303 | nhigh, next_boundary); |
32304 | } |
32305 | ); |
32306 | } |
32307 | |
32308 | end_object: |
32309 | o = next_o; |
32310 | } |
32311 | |
32312 | } |
32313 | } |
32314 | |
32315 | // compute the efficiency ratio of the card table |
32316 | if (!relocating) |
32317 | { |
32318 | generation_skip_ratio = min (((n_eph > 800) ? |
32319 | (int)(((float)n_gen / (float)n_eph) * 100) : 100), |
32320 | generation_skip_ratio); |
32321 | |
32322 | dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d" , |
32323 | n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio)); |
32324 | } |
32325 | else |
32326 | { |
32327 | dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d" , |
32328 | n_eph, n_gen, n_card_set, generation_skip_ratio)); |
32329 | } |
32330 | } |
32331 | |
32332 | void gc_heap::descr_segment (heap_segment* seg ) |
32333 | { |
32334 | #ifdef TRACE_GC |
32335 | uint8_t* x = heap_segment_mem (seg); |
32336 | while (x < heap_segment_allocated (seg)) |
32337 | { |
32338 | dprintf(2, ( "%Ix: %d " , (size_t)x, size (x))); |
32339 | x = x + Align(size (x)); |
32340 | } |
32341 | #else // TRACE_GC |
32342 | UNREFERENCED_PARAMETER(seg); |
32343 | #endif // TRACE_GC |
32344 | } |
32345 | |
32346 | void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context) |
32347 | { |
32348 | #ifdef MULTIPLE_HEAPS |
32349 | int n_heaps = g_theGCHeap->GetNumberOfHeaps (); |
32350 | for (int i = 0; i < n_heaps; i++) |
32351 | { |
32352 | gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap; |
32353 | #else //MULTIPLE_HEAPS |
32354 | { |
32355 | gc_heap* hp = NULL; |
32356 | #ifdef _PREFAST_ |
32357 | // prefix complains about us dereferencing hp in wks build even though we only access static members |
32358 | // this way. not sure how to shut it up except for this ugly workaround: |
32359 | PREFIX_ASSUME(hp != NULL); |
32360 | #endif // _PREFAST_ |
32361 | #endif //MULTIPLE_HEAPS |
32362 | |
32363 | int curr_gen_number0 = max_generation+1; |
32364 | while (curr_gen_number0 >= 0) |
32365 | { |
32366 | generation* gen = hp->generation_of (curr_gen_number0); |
32367 | heap_segment* seg = generation_start_segment (gen); |
32368 | while (seg && (seg != hp->ephemeral_heap_segment)) |
32369 | { |
32370 | assert (curr_gen_number0 > 0); |
32371 | |
32372 | // report bounds from heap_segment_mem (seg) to |
32373 | // heap_segment_allocated (seg); |
32374 | // for generation # curr_gen_number0 |
32375 | // for heap # heap_no |
32376 | |
32377 | fn(context, curr_gen_number0, heap_segment_mem (seg), |
32378 | heap_segment_allocated (seg), |
32379 | curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg)); |
32380 | |
32381 | seg = heap_segment_next (seg); |
32382 | } |
32383 | if (seg) |
32384 | { |
32385 | assert (seg == hp->ephemeral_heap_segment); |
32386 | assert (curr_gen_number0 <= max_generation); |
32387 | // |
32388 | if (curr_gen_number0 == max_generation) |
32389 | { |
32390 | if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1))) |
32391 | { |
32392 | // report bounds from heap_segment_mem (seg) to |
32393 | // generation_allocation_start (generation_of (max_generation-1)) |
32394 | // for heap # heap_number |
32395 | |
32396 | fn(context, curr_gen_number0, heap_segment_mem (seg), |
32397 | generation_allocation_start (hp->generation_of (max_generation-1)), |
32398 | generation_allocation_start (hp->generation_of (max_generation-1)) ); |
32399 | } |
32400 | } |
32401 | else if (curr_gen_number0 != 0) |
32402 | { |
32403 | //report bounds from generation_allocation_start (generation_of (curr_gen_number0)) |
32404 | // to generation_allocation_start (generation_of (curr_gen_number0-1)) |
32405 | // for heap # heap_number |
32406 | |
32407 | fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)), |
32408 | generation_allocation_start (hp->generation_of (curr_gen_number0-1)), |
32409 | generation_allocation_start (hp->generation_of (curr_gen_number0-1))); |
32410 | } |
32411 | else |
32412 | { |
32413 | //report bounds from generation_allocation_start (generation_of (curr_gen_number0)) |
32414 | // to heap_segment_allocated (ephemeral_heap_segment); |
32415 | // for heap # heap_number |
32416 | |
32417 | fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)), |
32418 | heap_segment_allocated (hp->ephemeral_heap_segment), |
32419 | heap_segment_reserved (hp->ephemeral_heap_segment) ); |
32420 | } |
32421 | } |
32422 | curr_gen_number0--; |
32423 | } |
32424 | } |
32425 | } |
32426 | |
32427 | #ifdef TRACE_GC |
32428 | // Note that when logging is on it can take a long time to go through the free items. |
32429 | void gc_heap::print_free_list (int gen, heap_segment* seg) |
32430 | { |
32431 | UNREFERENCED_PARAMETER(gen); |
32432 | UNREFERENCED_PARAMETER(seg); |
32433 | /* |
32434 | if (settings.concurrent == FALSE) |
32435 | { |
32436 | uint8_t* seg_start = heap_segment_mem (seg); |
32437 | uint8_t* seg_end = heap_segment_allocated (seg); |
32438 | |
32439 | dprintf (3, ("Free list in seg %Ix:", seg_start)); |
32440 | |
32441 | size_t total_free_item = 0; |
32442 | |
32443 | allocator* gen_allocator = generation_allocator (generation_of (gen)); |
32444 | for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++) |
32445 | { |
32446 | uint8_t* fo = gen_allocator->alloc_list_head_of (b); |
32447 | while (fo) |
32448 | { |
32449 | if (fo >= seg_start && fo < seg_end) |
32450 | { |
32451 | total_free_item++; |
32452 | |
32453 | size_t free_item_len = size(fo); |
32454 | |
32455 | dprintf (3, ("[%Ix, %Ix[:%Id", |
32456 | (size_t)fo, |
32457 | (size_t)(fo + free_item_len), |
32458 | free_item_len)); |
32459 | } |
32460 | |
32461 | fo = free_list_slot (fo); |
32462 | } |
32463 | } |
32464 | |
32465 | dprintf (3, ("total %Id free items", total_free_item)); |
32466 | } |
32467 | */ |
32468 | } |
32469 | #endif //TRACE_GC |
32470 | |
32471 | void gc_heap::descr_generations (BOOL begin_gc_p) |
32472 | { |
32473 | UNREFERENCED_PARAMETER(begin_gc_p); |
32474 | #ifdef STRESS_LOG |
32475 | if (StressLog::StressLogOn(LF_GC, LL_INFO10)) |
32476 | { |
32477 | gc_heap* hp = 0; |
32478 | #ifdef MULTIPLE_HEAPS |
32479 | hp= this; |
32480 | #endif //MULTIPLE_HEAPS |
32481 | |
32482 | STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n" , hp); |
32483 | for (int n = max_generation; n >= 0; --n) |
32484 | { |
32485 | STRESS_LOG4(LF_GC, LL_INFO10, " Generation %d [%p, %p] cur = %p\n" , |
32486 | n, |
32487 | generation_allocation_start(generation_of(n)), |
32488 | generation_allocation_limit(generation_of(n)), |
32489 | generation_allocation_pointer(generation_of(n))); |
32490 | |
32491 | heap_segment* seg = generation_start_segment(generation_of(n)); |
32492 | while (seg) |
32493 | { |
32494 | STRESS_LOG4(LF_GC, LL_INFO10, " Segment mem %p alloc = %p used %p committed %p\n" , |
32495 | heap_segment_mem(seg), |
32496 | heap_segment_allocated(seg), |
32497 | heap_segment_used(seg), |
32498 | heap_segment_committed(seg)); |
32499 | seg = heap_segment_next(seg); |
32500 | } |
32501 | } |
32502 | } |
32503 | #endif // STRESS_LOG |
32504 | |
32505 | #ifdef TRACE_GC |
32506 | dprintf (2, ("lowest_address: %Ix highest_address: %Ix" , |
32507 | (size_t) lowest_address, (size_t) highest_address)); |
32508 | #ifdef BACKGROUND_GC |
32509 | dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix" , |
32510 | (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address)); |
32511 | #endif //BACKGROUND_GC |
32512 | |
32513 | if (heap_number == 0) |
32514 | { |
32515 | dprintf (1, ("total heap size: %Id, commit size: %Id" , get_total_heap_size(), get_total_committed_size())); |
32516 | } |
32517 | |
32518 | int curr_gen_number = max_generation+1; |
32519 | while (curr_gen_number >= 0) |
32520 | { |
32521 | size_t total_gen_size = generation_size (curr_gen_number); |
32522 | #ifdef SIMPLE_DPRINTF |
32523 | dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s" , |
32524 | (begin_gc_p ? "BEG" : "END" ), |
32525 | settings.condemned_generation, |
32526 | curr_gen_number, |
32527 | total_gen_size, |
32528 | dd_fragmentation (dynamic_data_of (curr_gen_number)), |
32529 | generation_free_list_space (generation_of (curr_gen_number)), |
32530 | generation_free_obj_space (generation_of (curr_gen_number)), |
32531 | (total_gen_size ? |
32532 | (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) : |
32533 | 0), |
32534 | (begin_gc_p ? ("" ) : (settings.compaction ? "(compact)" : "(sweep)" )), |
32535 | (settings.heap_expansion ? "(EX)" : " " ), |
32536 | (settings.promotion ? "Promotion" : "NoPromotion" ))); |
32537 | #else |
32538 | dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id" , |
32539 | curr_gen_number, |
32540 | size (generation_allocation_start (generation_of (curr_gen_number))), |
32541 | total_gen_size, |
32542 | dd_fragmentation (dynamic_data_of (curr_gen_number)))); |
32543 | #endif //SIMPLE_DPRINTF |
32544 | |
32545 | generation* gen = generation_of (curr_gen_number); |
32546 | heap_segment* seg = generation_start_segment (gen); |
32547 | while (seg && (seg != ephemeral_heap_segment)) |
32548 | { |
32549 | dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)" , |
32550 | curr_gen_number, |
32551 | (size_t)heap_segment_mem (seg), |
32552 | (size_t)heap_segment_allocated (seg), |
32553 | (size_t)heap_segment_committed (seg), |
32554 | (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)), |
32555 | (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg)))); |
32556 | print_free_list (curr_gen_number, seg); |
32557 | seg = heap_segment_next (seg); |
32558 | } |
32559 | if (seg && (seg != generation_start_segment (gen))) |
32560 | { |
32561 | dprintf (GTC_LOG, ("g%d: [%Ix %Ix[" , |
32562 | curr_gen_number, |
32563 | (size_t)heap_segment_mem (seg), |
32564 | (size_t)generation_allocation_start (generation_of (curr_gen_number-1)))); |
32565 | print_free_list (curr_gen_number, seg); |
32566 | |
32567 | } |
32568 | else if (seg) |
32569 | { |
32570 | dprintf (GTC_LOG, ("g%d: [%Ix %Ix[" , |
32571 | curr_gen_number, |
32572 | (size_t)generation_allocation_start (generation_of (curr_gen_number)), |
32573 | (size_t)(((curr_gen_number == 0)) ? |
32574 | (heap_segment_allocated |
32575 | (generation_start_segment |
32576 | (generation_of (curr_gen_number)))) : |
32577 | (generation_allocation_start |
32578 | (generation_of (curr_gen_number - 1)))) |
32579 | )); |
32580 | print_free_list (curr_gen_number, seg); |
32581 | } |
32582 | curr_gen_number--; |
32583 | } |
32584 | |
32585 | #endif //TRACE_GC |
32586 | } |
32587 | |
32588 | #undef TRACE_GC |
32589 | |
32590 | //#define TRACE_GC |
32591 | |
32592 | //----------------------------------------------------------------------------- |
32593 | // |
32594 | // VM Specific support |
32595 | // |
32596 | //----------------------------------------------------------------------------- |
32597 | |
32598 | |
32599 | #ifdef TRACE_GC |
32600 | |
32601 | unsigned int PromotedObjectCount = 0; |
32602 | unsigned int CreatedObjectCount = 0; |
32603 | unsigned int AllocDuration = 0; |
32604 | unsigned int AllocCount = 0; |
32605 | unsigned int AllocBigCount = 0; |
32606 | unsigned int AllocSmallCount = 0; |
32607 | unsigned int AllocStart = 0; |
32608 | #endif //TRACE_GC |
32609 | |
32610 | //Static member variables. |
32611 | VOLATILE(BOOL) GCHeap::GcInProgress = FALSE; |
32612 | //GCTODO |
32613 | //CMCSafeLock* GCHeap::fGcLock; |
32614 | GCEvent *GCHeap::WaitForGCEvent = NULL; |
32615 | //GCTODO |
32616 | #ifdef TRACE_GC |
32617 | unsigned int GCHeap::GcDuration; |
32618 | #endif //TRACE_GC |
32619 | unsigned GCHeap::GcCondemnedGeneration = 0; |
32620 | size_t GCHeap::totalSurvivedSize = 0; |
32621 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
32622 | CFinalize* GCHeap::m_Finalize = 0; |
32623 | BOOL GCHeap::GcCollectClasses = FALSE; |
32624 | VOLATILE(int32_t) GCHeap::m_GCFLock = 0; |
32625 | |
32626 | #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way |
32627 | #ifdef STRESS_HEAP |
32628 | #ifdef BACKGROUND_GC |
32629 | int GCHeap::gc_stress_fgcs_in_bgc = 0; |
32630 | #endif // BACKGROUND_GC |
32631 | #ifndef MULTIPLE_HEAPS |
32632 | OBJECTHANDLE GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS]; |
32633 | int GCHeap::m_CurStressObj = 0; |
32634 | #endif // !MULTIPLE_HEAPS |
32635 | #endif // STRESS_HEAP |
32636 | #endif // FEATURE_REDHAWK |
32637 | |
32638 | #endif //FEATURE_PREMORTEM_FINALIZATION |
32639 | |
32640 | class NoGCRegionLockHolder |
32641 | { |
32642 | public: |
32643 | NoGCRegionLockHolder() |
32644 | { |
32645 | enter_spin_lock_noinstru(&g_no_gc_lock); |
32646 | } |
32647 | |
32648 | ~NoGCRegionLockHolder() |
32649 | { |
32650 | leave_spin_lock_noinstru(&g_no_gc_lock); |
32651 | } |
32652 | }; |
32653 | |
32654 | // An explanation of locking for finalization: |
32655 | // |
32656 | // Multiple threads allocate objects. During the allocation, they are serialized by |
32657 | // the AllocLock above. But they release that lock before they register the object |
32658 | // for finalization. That's because there is much contention for the alloc lock, but |
32659 | // finalization is presumed to be a rare case. |
32660 | // |
32661 | // So registering an object for finalization must be protected by the FinalizeLock. |
32662 | // |
32663 | // There is another logical queue that involves finalization. When objects registered |
32664 | // for finalization become unreachable, they are moved from the "registered" queue to |
32665 | // the "unreachable" queue. Note that this only happens inside a GC, so no other |
32666 | // threads can be manipulating either queue at that time. Once the GC is over and |
32667 | // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable" |
32668 | // queue and call their finalizers. This dequeue operation is also protected with |
32669 | // the finalize lock. |
32670 | // |
32671 | // At first, this seems unnecessary. Only one thread is ever enqueuing or dequeuing |
32672 | // on the unreachable queue (either the GC thread during a GC or the finalizer thread |
32673 | // when a GC is not in progress). The reason we share a lock with threads enqueuing |
32674 | // on the "registered" queue is that the "registered" and "unreachable" queues are |
32675 | // interrelated. |
32676 | // |
32677 | // They are actually two regions of a longer list, which can only grow at one end. |
32678 | // So to enqueue an object to the "registered" list, you actually rotate an unreachable |
32679 | // object at the boundary between the logical queues, out to the other end of the |
32680 | // unreachable queue -- where all growing takes place. Then you move the boundary |
32681 | // pointer so that the gap we created at the boundary is now on the "registered" |
32682 | // side rather than the "unreachable" side. Now the object can be placed into the |
32683 | // "registered" side at that point. This is much more efficient than doing moves |
32684 | // of arbitrarily long regions, but it causes the two queues to require a shared lock. |
32685 | // |
32686 | // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock. Instead, it relies |
32687 | // on the fact that the lock will only be taken for a brief period and that it will |
32688 | // never provoke or allow a GC while the lock is held. This is critical. If the |
32689 | // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to |
32690 | // allow a GC), then the Alloc client would have to GC protect a finalizable object |
32691 | // to protect against that eventuality. That is too slow! |
32692 | |
32693 | |
32694 | |
32695 | BOOL IsValidObject99(uint8_t *pObject) |
32696 | { |
32697 | #ifdef VERIFY_HEAP |
32698 | if (!((CObjectHeader*)pObject)->IsFree()) |
32699 | ((CObjectHeader *) pObject)->Validate(); |
32700 | #endif //VERIFY_HEAP |
32701 | return(TRUE); |
32702 | } |
32703 | |
32704 | #ifdef BACKGROUND_GC |
32705 | BOOL gc_heap::bgc_mark_array_range (heap_segment* seg, |
32706 | BOOL whole_seg_p, |
32707 | uint8_t** range_beg, |
32708 | uint8_t** range_end) |
32709 | { |
32710 | uint8_t* seg_start = heap_segment_mem (seg); |
32711 | uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg))); |
32712 | |
32713 | if ((seg_start < background_saved_highest_address) && |
32714 | (seg_end > background_saved_lowest_address)) |
32715 | { |
32716 | *range_beg = max (seg_start, background_saved_lowest_address); |
32717 | *range_end = min (seg_end, background_saved_highest_address); |
32718 | return TRUE; |
32719 | } |
32720 | else |
32721 | { |
32722 | return FALSE; |
32723 | } |
32724 | } |
32725 | |
32726 | void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg) |
32727 | { |
32728 | #if defined (VERIFY_HEAP) && defined (MARK_ARRAY) |
32729 | if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
32730 | { |
32731 | uint8_t* range_beg = 0; |
32732 | uint8_t* range_end = 0; |
32733 | |
32734 | if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end)) |
32735 | { |
32736 | size_t markw = mark_word_of (range_beg); |
32737 | size_t markw_end = mark_word_of (range_end); |
32738 | while (markw < markw_end) |
32739 | { |
32740 | if (mark_array [markw]) |
32741 | { |
32742 | dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
32743 | markw, mark_array [markw], mark_word_address (markw))); |
32744 | FATAL_GC_ERROR(); |
32745 | } |
32746 | markw++; |
32747 | } |
32748 | uint8_t* p = mark_word_address (markw_end); |
32749 | while (p < range_end) |
32750 | { |
32751 | assert (!(mark_array_marked (p))); |
32752 | p++; |
32753 | } |
32754 | } |
32755 | } |
32756 | #endif //VERIFY_HEAP && MARK_ARRAY |
32757 | } |
32758 | |
32759 | void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s) |
32760 | { |
32761 | #if defined (VERIFY_HEAP) && defined (MARK_ARRAY) |
32762 | size_t start_mark_bit = mark_bit_of (obj) + 1; |
32763 | size_t end_mark_bit = mark_bit_of (obj + s); |
32764 | unsigned int startbit = mark_bit_bit (start_mark_bit); |
32765 | unsigned int endbit = mark_bit_bit (end_mark_bit); |
32766 | size_t startwrd = mark_bit_word (start_mark_bit); |
32767 | size_t endwrd = mark_bit_word (end_mark_bit); |
32768 | unsigned int result = 0; |
32769 | |
32770 | unsigned int firstwrd = ~(lowbits (~0, startbit)); |
32771 | unsigned int lastwrd = ~(highbits (~0, endbit)); |
32772 | |
32773 | if (startwrd == endwrd) |
32774 | { |
32775 | unsigned int wrd = firstwrd & lastwrd; |
32776 | result = mark_array[startwrd] & wrd; |
32777 | if (result) |
32778 | { |
32779 | FATAL_GC_ERROR(); |
32780 | } |
32781 | return; |
32782 | } |
32783 | |
32784 | // verify the first mark word is cleared. |
32785 | if (startbit) |
32786 | { |
32787 | result = mark_array[startwrd] & firstwrd; |
32788 | if (result) |
32789 | { |
32790 | FATAL_GC_ERROR(); |
32791 | } |
32792 | startwrd++; |
32793 | } |
32794 | |
32795 | for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++) |
32796 | { |
32797 | result = mark_array[wrdtmp]; |
32798 | if (result) |
32799 | { |
32800 | FATAL_GC_ERROR(); |
32801 | } |
32802 | } |
32803 | |
32804 | // set the last mark word. |
32805 | if (endbit) |
32806 | { |
32807 | result = mark_array[endwrd] & lastwrd; |
32808 | if (result) |
32809 | { |
32810 | FATAL_GC_ERROR(); |
32811 | } |
32812 | } |
32813 | #endif //VERIFY_HEAP && MARK_ARRAY |
32814 | } |
32815 | |
32816 | void gc_heap::clear_all_mark_array() |
32817 | { |
32818 | #ifdef MARK_ARRAY |
32819 | //size_t num_dwords_written = 0; |
32820 | //size_t begin_time = GetHighPrecisionTimeStamp(); |
32821 | |
32822 | generation* gen = generation_of (max_generation); |
32823 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
32824 | |
32825 | while (1) |
32826 | { |
32827 | if (seg == 0) |
32828 | { |
32829 | if (gen != large_object_generation) |
32830 | { |
32831 | gen = generation_of (max_generation+1); |
32832 | seg = heap_segment_rw (generation_start_segment (gen)); |
32833 | } |
32834 | else |
32835 | { |
32836 | break; |
32837 | } |
32838 | } |
32839 | |
32840 | uint8_t* range_beg = 0; |
32841 | uint8_t* range_end = 0; |
32842 | |
32843 | if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end)) |
32844 | { |
32845 | size_t markw = mark_word_of (range_beg); |
32846 | size_t markw_end = mark_word_of (range_end); |
32847 | size_t size_total = (markw_end - markw) * sizeof (uint32_t); |
32848 | //num_dwords_written = markw_end - markw; |
32849 | size_t size = 0; |
32850 | size_t size_left = 0; |
32851 | |
32852 | assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0); |
32853 | |
32854 | if ((size_total & (sizeof(PTR_PTR) - 1)) != 0) |
32855 | { |
32856 | size = (size_total & ~(sizeof(PTR_PTR) - 1)); |
32857 | size_left = size_total - size; |
32858 | assert ((size_left & (sizeof (uint32_t) - 1)) == 0); |
32859 | } |
32860 | else |
32861 | { |
32862 | size = size_total; |
32863 | } |
32864 | |
32865 | memclr ((uint8_t*)&mark_array[markw], size); |
32866 | |
32867 | if (size_left != 0) |
32868 | { |
32869 | uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)]; |
32870 | for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++) |
32871 | { |
32872 | *markw_to_clear = 0; |
32873 | markw_to_clear++; |
32874 | } |
32875 | } |
32876 | } |
32877 | |
32878 | seg = heap_segment_next_rw (seg); |
32879 | } |
32880 | |
32881 | //size_t end_time = GetHighPrecisionTimeStamp() - begin_time; |
32882 | |
32883 | //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t)); |
32884 | |
32885 | #endif //MARK_ARRAY |
32886 | } |
32887 | |
32888 | #endif //BACKGROUND_GC |
32889 | |
32890 | void gc_heap::verify_mark_array_cleared (heap_segment* seg) |
32891 | { |
32892 | #if defined (VERIFY_HEAP) && defined (MARK_ARRAY) |
32893 | assert (card_table == g_gc_card_table); |
32894 | size_t markw = mark_word_of (heap_segment_mem (seg)); |
32895 | size_t markw_end = mark_word_of (heap_segment_reserved (seg)); |
32896 | |
32897 | while (markw < markw_end) |
32898 | { |
32899 | if (mark_array [markw]) |
32900 | { |
32901 | dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
32902 | markw, mark_array [markw], mark_word_address (markw))); |
32903 | FATAL_GC_ERROR(); |
32904 | } |
32905 | markw++; |
32906 | } |
32907 | #endif //VERIFY_HEAP && MARK_ARRAY |
32908 | } |
32909 | |
32910 | void gc_heap::verify_mark_array_cleared () |
32911 | { |
32912 | #if defined (VERIFY_HEAP) && defined (MARK_ARRAY) |
32913 | if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
32914 | { |
32915 | generation* gen = generation_of (max_generation); |
32916 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
32917 | |
32918 | while (1) |
32919 | { |
32920 | if (seg == 0) |
32921 | { |
32922 | if (gen != large_object_generation) |
32923 | { |
32924 | gen = generation_of (max_generation+1); |
32925 | seg = heap_segment_rw (generation_start_segment (gen)); |
32926 | } |
32927 | else |
32928 | { |
32929 | break; |
32930 | } |
32931 | } |
32932 | |
32933 | bgc_verify_mark_array_cleared (seg); |
32934 | seg = heap_segment_next_rw (seg); |
32935 | } |
32936 | } |
32937 | #endif //VERIFY_HEAP && MARK_ARRAY |
32938 | } |
32939 | |
32940 | void gc_heap::verify_seg_end_mark_array_cleared() |
32941 | { |
32942 | #if defined (VERIFY_HEAP) && defined (MARK_ARRAY) |
32943 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
32944 | { |
32945 | generation* gen = generation_of (max_generation); |
32946 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
32947 | |
32948 | while (1) |
32949 | { |
32950 | if (seg == 0) |
32951 | { |
32952 | if (gen != large_object_generation) |
32953 | { |
32954 | gen = generation_of (max_generation+1); |
32955 | seg = heap_segment_rw (generation_start_segment (gen)); |
32956 | } |
32957 | else |
32958 | { |
32959 | break; |
32960 | } |
32961 | } |
32962 | |
32963 | // We already cleared all mark array bits for ephemeral generations |
32964 | // at the beginning of bgc sweep |
32965 | uint8_t* from = ((seg == ephemeral_heap_segment) ? |
32966 | generation_allocation_start (generation_of (max_generation - 1)) : |
32967 | heap_segment_allocated (seg)); |
32968 | size_t markw = mark_word_of (align_on_mark_word (from)); |
32969 | size_t markw_end = mark_word_of (heap_segment_reserved (seg)); |
32970 | |
32971 | while (from < mark_word_address (markw)) |
32972 | { |
32973 | if (is_mark_bit_set (from)) |
32974 | { |
32975 | dprintf (3, ("mark bit for %Ix was not cleared" , from)); |
32976 | FATAL_GC_ERROR(); |
32977 | } |
32978 | |
32979 | from += mark_bit_pitch; |
32980 | } |
32981 | |
32982 | while (markw < markw_end) |
32983 | { |
32984 | if (mark_array [markw]) |
32985 | { |
32986 | dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared" , |
32987 | markw, mark_array [markw], mark_word_address (markw))); |
32988 | FATAL_GC_ERROR(); |
32989 | } |
32990 | markw++; |
32991 | } |
32992 | seg = heap_segment_next_rw (seg); |
32993 | } |
32994 | } |
32995 | #endif //VERIFY_HEAP && MARK_ARRAY |
32996 | } |
32997 | |
32998 | // This function is called to make sure we don't mess up the segment list |
32999 | // in SOH. It's called by: |
33000 | // 1) begin and end of ephemeral GCs |
33001 | // 2) during bgc sweep when we switch segments. |
33002 | void gc_heap::verify_soh_segment_list() |
33003 | { |
33004 | #ifdef VERIFY_HEAP |
33005 | if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) |
33006 | { |
33007 | generation* gen = generation_of (max_generation); |
33008 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
33009 | heap_segment* last_seg = 0; |
33010 | while (seg) |
33011 | { |
33012 | last_seg = seg; |
33013 | seg = heap_segment_next_rw (seg); |
33014 | } |
33015 | if (last_seg != ephemeral_heap_segment) |
33016 | { |
33017 | FATAL_GC_ERROR(); |
33018 | } |
33019 | } |
33020 | #endif //VERIFY_HEAP |
33021 | } |
33022 | |
33023 | // This function can be called at any foreground GCs or blocking GCs. For background GCs, |
33024 | // it can be called at the end of the final marking; and at any point during background |
33025 | // sweep. |
33026 | // NOTE - to be able to call this function during background sweep, we need to temporarily |
33027 | // NOT clear the mark array bits as we go. |
33028 | void gc_heap::verify_partial () |
33029 | { |
33030 | #ifdef BACKGROUND_GC |
33031 | //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index); |
33032 | //generation* gen = large_object_generation; |
33033 | generation* gen = generation_of (max_generation); |
33034 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
33035 | int align_const = get_alignment_constant (gen != large_object_generation); |
33036 | |
33037 | uint8_t* o = 0; |
33038 | uint8_t* end = 0; |
33039 | size_t s = 0; |
33040 | |
33041 | // Different ways to fail. |
33042 | BOOL mark_missed_p = FALSE; |
33043 | BOOL bad_ref_p = FALSE; |
33044 | BOOL free_ref_p = FALSE; |
33045 | |
33046 | while (1) |
33047 | { |
33048 | if (seg == 0) |
33049 | { |
33050 | if (gen != large_object_generation) |
33051 | { |
33052 | //switch to LOH |
33053 | gen = large_object_generation; |
33054 | align_const = get_alignment_constant (gen != large_object_generation); |
33055 | seg = heap_segment_rw (generation_start_segment (gen)); |
33056 | continue; |
33057 | } |
33058 | else |
33059 | { |
33060 | break; |
33061 | } |
33062 | } |
33063 | |
33064 | o = heap_segment_mem (seg); |
33065 | end = heap_segment_allocated (seg); |
33066 | //printf ("validating [%Ix-[%Ix\n", o, end); |
33067 | while (o < end) |
33068 | { |
33069 | s = size (o); |
33070 | |
33071 | BOOL marked_p = background_object_marked (o, FALSE); |
33072 | |
33073 | if (marked_p) |
33074 | { |
33075 | go_through_object_cl (method_table (o), o, s, oo, |
33076 | { |
33077 | if (*oo) |
33078 | { |
33079 | //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o)); |
33080 | MethodTable *pMT = method_table (*oo); |
33081 | |
33082 | if (pMT == g_gc_pFreeObjectMethodTable) |
33083 | { |
33084 | free_ref_p = TRUE; |
33085 | FATAL_GC_ERROR(); |
33086 | } |
33087 | |
33088 | if (!pMT->SanityCheck()) |
33089 | { |
33090 | bad_ref_p = TRUE; |
33091 | dprintf (3, ("Bad member of %Ix %Ix" , |
33092 | (size_t)oo, (size_t)*oo)); |
33093 | FATAL_GC_ERROR(); |
33094 | } |
33095 | |
33096 | if (current_bgc_state == bgc_final_marking) |
33097 | { |
33098 | if (marked_p && !background_object_marked (*oo, FALSE)) |
33099 | { |
33100 | mark_missed_p = TRUE; |
33101 | FATAL_GC_ERROR(); |
33102 | } |
33103 | } |
33104 | } |
33105 | } |
33106 | ); |
33107 | } |
33108 | |
33109 | o = o + Align(s, align_const); |
33110 | } |
33111 | seg = heap_segment_next_rw (seg); |
33112 | } |
33113 | |
33114 | //printf ("didn't find any large object large enough...\n"); |
33115 | //printf ("finished verifying loh\n"); |
33116 | #endif //BACKGROUND_GC |
33117 | } |
33118 | |
33119 | #ifdef VERIFY_HEAP |
33120 | |
33121 | void |
33122 | gc_heap::verify_free_lists () |
33123 | { |
33124 | for (int gen_num = 0; gen_num <= max_generation+1; gen_num++) |
33125 | { |
33126 | dprintf (3, ("Verifying free list for gen:%d" , gen_num)); |
33127 | allocator* gen_alloc = generation_allocator (generation_of (gen_num)); |
33128 | size_t sz = gen_alloc->first_bucket_size(); |
33129 | bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p(); |
33130 | |
33131 | for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++) |
33132 | { |
33133 | uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number); |
33134 | uint8_t* prev = 0; |
33135 | while (free_list) |
33136 | { |
33137 | if (!((CObjectHeader*)free_list)->IsFree()) |
33138 | { |
33139 | dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)" , |
33140 | (size_t)free_list)); |
33141 | FATAL_GC_ERROR(); |
33142 | } |
33143 | if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz)) |
33144 | || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2))) |
33145 | { |
33146 | dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket" , |
33147 | (size_t)free_list)); |
33148 | FATAL_GC_ERROR(); |
33149 | } |
33150 | if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY)) |
33151 | { |
33152 | dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot" , |
33153 | (size_t)free_list)); |
33154 | FATAL_GC_ERROR(); |
33155 | } |
33156 | if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num)) |
33157 | { |
33158 | dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list" , |
33159 | (size_t)free_list)); |
33160 | FATAL_GC_ERROR(); |
33161 | } |
33162 | |
33163 | prev = free_list; |
33164 | free_list = free_list_slot (free_list); |
33165 | } |
33166 | //verify the sanity of the tail |
33167 | uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number); |
33168 | if (!((tail == 0) || (tail == prev))) |
33169 | { |
33170 | dprintf (3, ("Verifying Heap: tail of free list is not correct" )); |
33171 | FATAL_GC_ERROR(); |
33172 | } |
33173 | if (tail == 0) |
33174 | { |
33175 | uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number); |
33176 | if ((head != 0) && (free_list_slot (head) != 0)) |
33177 | { |
33178 | dprintf (3, ("Verifying Heap: tail of free list is not correct" )); |
33179 | FATAL_GC_ERROR(); |
33180 | } |
33181 | } |
33182 | |
33183 | sz *=2; |
33184 | } |
33185 | } |
33186 | } |
33187 | |
33188 | void |
33189 | gc_heap::verify_heap (BOOL begin_gc_p) |
33190 | { |
33191 | int heap_verify_level = static_cast<int>(GCConfig::GetHeapVerifyLevel()); |
33192 | size_t last_valid_brick = 0; |
33193 | BOOL bCurrentBrickInvalid = FALSE; |
33194 | BOOL large_brick_p = TRUE; |
33195 | size_t curr_brick = 0; |
33196 | size_t prev_brick = (size_t)-1; |
33197 | int curr_gen_num = max_generation+1; |
33198 | heap_segment* seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) )); |
33199 | |
33200 | PREFIX_ASSUME(seg != NULL); |
33201 | |
33202 | uint8_t* curr_object = heap_segment_mem (seg); |
33203 | uint8_t* prev_object = 0; |
33204 | uint8_t* begin_youngest = generation_allocation_start(generation_of(0)); |
33205 | uint8_t* end_youngest = heap_segment_allocated (ephemeral_heap_segment); |
33206 | uint8_t* next_boundary = generation_allocation_start (generation_of (max_generation - 1)); |
33207 | int align_const = get_alignment_constant (FALSE); |
33208 | size_t total_objects_verified = 0; |
33209 | size_t total_objects_verified_deep = 0; |
33210 | |
33211 | #ifdef BACKGROUND_GC |
33212 | BOOL consider_bgc_mark_p = FALSE; |
33213 | BOOL check_current_sweep_p = FALSE; |
33214 | BOOL check_saved_sweep_p = FALSE; |
33215 | should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p); |
33216 | #endif //BACKGROUND_GC |
33217 | |
33218 | #ifdef MULTIPLE_HEAPS |
33219 | t_join* current_join = &gc_t_join; |
33220 | #ifdef BACKGROUND_GC |
33221 | if (settings.concurrent && (bgc_thread_id.IsCurrentThread())) |
33222 | { |
33223 | // We always call verify_heap on entry of GC on the SVR GC threads. |
33224 | current_join = &bgc_t_join; |
33225 | } |
33226 | #endif //BACKGROUND_GC |
33227 | #endif //MULTIPLE_HEAPS |
33228 | |
33229 | UNREFERENCED_PARAMETER(begin_gc_p); |
33230 | #ifdef BACKGROUND_GC |
33231 | dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin" , |
33232 | (begin_gc_p ? "BEG" : "END" ), |
33233 | VolatileLoad(&settings.gc_index), |
33234 | (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC" )))); |
33235 | #else |
33236 | dprintf (2,("[%s]GC#%d: Verifying heap - begin" , |
33237 | (begin_gc_p ? "BEG" : "END" ), VolatileLoad(&settings.gc_index))); |
33238 | #endif //BACKGROUND_GC |
33239 | |
33240 | #ifndef MULTIPLE_HEAPS |
33241 | if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) || |
33242 | (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment))) |
33243 | { |
33244 | FATAL_GC_ERROR(); |
33245 | } |
33246 | #endif //MULTIPLE_HEAPS |
33247 | |
33248 | #ifdef BACKGROUND_GC |
33249 | //don't touch the memory because the program is allocating from it. |
33250 | if (!settings.concurrent) |
33251 | #endif //BACKGROUND_GC |
33252 | { |
33253 | if (!(heap_verify_level & GCConfig::HEAPVERIFY_NO_MEM_FILL)) |
33254 | { |
33255 | //uninit the unused portions of segments. |
33256 | generation* gen1 = large_object_generation; |
33257 | heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1)); |
33258 | PREFIX_ASSUME(seg1 != NULL); |
33259 | |
33260 | while (1) |
33261 | { |
33262 | if (seg1) |
33263 | { |
33264 | uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew; |
33265 | if (heap_segment_used (seg1) > clear_start) |
33266 | { |
33267 | dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa" , |
33268 | heap_segment_mem (seg1), |
33269 | clear_start , |
33270 | heap_segment_used (seg1))); |
33271 | memset (heap_segment_allocated (seg1) - plug_skew, 0xaa, |
33272 | (heap_segment_used (seg1) - clear_start)); |
33273 | } |
33274 | seg1 = heap_segment_next_rw (seg1); |
33275 | } |
33276 | else |
33277 | { |
33278 | if (gen1 == large_object_generation) |
33279 | { |
33280 | gen1 = generation_of (max_generation); |
33281 | seg1 = heap_segment_rw (generation_start_segment (gen1)); |
33282 | PREFIX_ASSUME(seg1 != NULL); |
33283 | } |
33284 | else |
33285 | { |
33286 | break; |
33287 | } |
33288 | } |
33289 | } |
33290 | } |
33291 | } |
33292 | |
33293 | #ifdef MULTIPLE_HEAPS |
33294 | current_join->join(this, gc_join_verify_copy_table); |
33295 | if (current_join->joined()) |
33296 | { |
33297 | // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point |
33298 | for (int i = 0; i < n_heaps; i++) |
33299 | { |
33300 | //copy the card and brick tables |
33301 | if (g_gc_card_table != g_heaps[i]->card_table) |
33302 | { |
33303 | g_heaps[i]->copy_brick_card_table(); |
33304 | } |
33305 | } |
33306 | |
33307 | current_join->restart(); |
33308 | } |
33309 | #else |
33310 | if (g_gc_card_table != card_table) |
33311 | copy_brick_card_table(); |
33312 | #endif //MULTIPLE_HEAPS |
33313 | |
33314 | //verify that the generation structures makes sense |
33315 | { |
33316 | generation* gen = generation_of (max_generation); |
33317 | |
33318 | assert (generation_allocation_start (gen) == |
33319 | heap_segment_mem (heap_segment_rw (generation_start_segment (gen)))); |
33320 | int gen_num = max_generation-1; |
33321 | generation* prev_gen = gen; |
33322 | while (gen_num >= 0) |
33323 | { |
33324 | gen = generation_of (gen_num); |
33325 | assert (generation_allocation_segment (gen) == ephemeral_heap_segment); |
33326 | assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment)); |
33327 | assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment)); |
33328 | |
33329 | if (generation_start_segment (prev_gen ) == |
33330 | generation_start_segment (gen)) |
33331 | { |
33332 | assert (generation_allocation_start (prev_gen) < |
33333 | generation_allocation_start (gen)); |
33334 | } |
33335 | prev_gen = gen; |
33336 | gen_num--; |
33337 | } |
33338 | } |
33339 | |
33340 | while (1) |
33341 | { |
33342 | // Handle segment transitions |
33343 | if (curr_object >= heap_segment_allocated (seg)) |
33344 | { |
33345 | if (curr_object > heap_segment_allocated(seg)) |
33346 | { |
33347 | dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)" , |
33348 | (size_t)curr_object, (size_t)seg)); |
33349 | FATAL_GC_ERROR(); |
33350 | } |
33351 | seg = heap_segment_next_in_range (seg); |
33352 | if (seg) |
33353 | { |
33354 | #ifdef BACKGROUND_GC |
33355 | should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p); |
33356 | #endif //BACKGROUND_GC |
33357 | curr_object = heap_segment_mem(seg); |
33358 | prev_object = 0; |
33359 | continue; |
33360 | } |
33361 | else |
33362 | { |
33363 | if (curr_gen_num == (max_generation+1)) |
33364 | { |
33365 | curr_gen_num--; |
33366 | seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num))); |
33367 | |
33368 | PREFIX_ASSUME(seg != NULL); |
33369 | |
33370 | #ifdef BACKGROUND_GC |
33371 | should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p); |
33372 | #endif //BACKGROUND_GC |
33373 | curr_object = heap_segment_mem (seg); |
33374 | prev_object = 0; |
33375 | large_brick_p = FALSE; |
33376 | align_const = get_alignment_constant (TRUE); |
33377 | } |
33378 | else |
33379 | break; // Done Verifying Heap -- no more segments |
33380 | } |
33381 | } |
33382 | |
33383 | // Are we at the end of the youngest_generation? |
33384 | if (seg == ephemeral_heap_segment) |
33385 | { |
33386 | if (curr_object >= end_youngest) |
33387 | { |
33388 | // prev_object length is too long if we hit this int3 |
33389 | if (curr_object > end_youngest) |
33390 | { |
33391 | dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix" , |
33392 | (size_t)curr_object, (size_t)end_youngest)); |
33393 | FATAL_GC_ERROR(); |
33394 | } |
33395 | break; |
33396 | } |
33397 | |
33398 | if ((curr_object >= next_boundary) && (curr_gen_num > 0)) |
33399 | { |
33400 | curr_gen_num--; |
33401 | if (curr_gen_num > 0) |
33402 | { |
33403 | next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1)); |
33404 | } |
33405 | } |
33406 | } |
33407 | |
33408 | //if (is_mark_set (curr_object)) |
33409 | //{ |
33410 | // printf ("curr_object: %Ix is marked!",(size_t)curr_object); |
33411 | // FATAL_GC_ERROR(); |
33412 | //} |
33413 | |
33414 | size_t s = size (curr_object); |
33415 | dprintf (3, ("o: %Ix, s: %d" , (size_t)curr_object, s)); |
33416 | if (s == 0) |
33417 | { |
33418 | dprintf (3, ("Verifying Heap: size of current object %Ix == 0" , curr_object)); |
33419 | FATAL_GC_ERROR(); |
33420 | } |
33421 | |
33422 | // If object is not in the youngest generation, then lets |
33423 | // verify that the brick table is correct.... |
33424 | if (((seg != ephemeral_heap_segment) || |
33425 | (brick_of(curr_object) < brick_of(begin_youngest)))) |
33426 | { |
33427 | curr_brick = brick_of(curr_object); |
33428 | |
33429 | // Brick Table Verification... |
33430 | // |
33431 | // On brick transition |
33432 | // if brick is negative |
33433 | // verify that brick indirects to previous valid brick |
33434 | // else |
33435 | // set current brick invalid flag to be flipped if we |
33436 | // encounter an object at the correct place |
33437 | // |
33438 | if (curr_brick != prev_brick) |
33439 | { |
33440 | // If the last brick we were examining had positive |
33441 | // entry but we never found the matching object, then |
33442 | // we have a problem |
33443 | // If prev_brick was the last one of the segment |
33444 | // it's ok for it to be invalid because it is never looked at |
33445 | if (bCurrentBrickInvalid && |
33446 | (curr_brick != brick_of (heap_segment_mem (seg))) && |
33447 | !heap_segment_read_only_p (seg)) |
33448 | { |
33449 | dprintf (3, ("curr brick %Ix invalid" , curr_brick)); |
33450 | FATAL_GC_ERROR(); |
33451 | } |
33452 | |
33453 | if (large_brick_p) |
33454 | { |
33455 | //large objects verify the table only if they are in |
33456 | //range. |
33457 | if ((heap_segment_reserved (seg) <= highest_address) && |
33458 | (heap_segment_mem (seg) >= lowest_address) && |
33459 | brick_table [curr_brick] != 0) |
33460 | { |
33461 | dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768" , |
33462 | curr_brick, (size_t)curr_object)); |
33463 | FATAL_GC_ERROR(); |
33464 | } |
33465 | else |
33466 | { |
33467 | bCurrentBrickInvalid = FALSE; |
33468 | } |
33469 | } |
33470 | else |
33471 | { |
33472 | // If the current brick contains a negative value make sure |
33473 | // that the indirection terminates at the last valid brick |
33474 | if (brick_table [curr_brick] <= 0) |
33475 | { |
33476 | if (brick_table [curr_brick] == 0) |
33477 | { |
33478 | dprintf(3, ("curr_brick %Ix for object %Ix set to 0" , |
33479 | curr_brick, (size_t)curr_object)); |
33480 | FATAL_GC_ERROR(); |
33481 | } |
33482 | ptrdiff_t i = curr_brick; |
33483 | while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) && |
33484 | (brick_table[i] < 0)) |
33485 | { |
33486 | i = i + brick_table[i]; |
33487 | } |
33488 | if (i < ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1)) |
33489 | { |
33490 | dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix" , |
33491 | i, brick_of (heap_segment_mem (seg)), |
33492 | curr_brick)); |
33493 | FATAL_GC_ERROR(); |
33494 | } |
33495 | // if (i != last_valid_brick) |
33496 | // FATAL_GC_ERROR(); |
33497 | bCurrentBrickInvalid = FALSE; |
33498 | } |
33499 | else if (!heap_segment_read_only_p (seg)) |
33500 | { |
33501 | bCurrentBrickInvalid = TRUE; |
33502 | } |
33503 | } |
33504 | } |
33505 | |
33506 | if (bCurrentBrickInvalid) |
33507 | { |
33508 | if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1)) |
33509 | { |
33510 | bCurrentBrickInvalid = FALSE; |
33511 | last_valid_brick = curr_brick; |
33512 | } |
33513 | } |
33514 | } |
33515 | |
33516 | if (*((uint8_t**)curr_object) != (uint8_t *) g_gc_pFreeObjectMethodTable) |
33517 | { |
33518 | #ifdef FEATURE_LOH_COMPACTION |
33519 | if ((curr_gen_num == (max_generation+1)) && (prev_object != 0)) |
33520 | { |
33521 | assert (method_table (prev_object) == g_gc_pFreeObjectMethodTable); |
33522 | } |
33523 | #endif //FEATURE_LOH_COMPACTION |
33524 | |
33525 | total_objects_verified++; |
33526 | |
33527 | BOOL can_verify_deep = TRUE; |
33528 | #ifdef BACKGROUND_GC |
33529 | can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p); |
33530 | #endif //BACKGROUND_GC |
33531 | |
33532 | BOOL deep_verify_obj = can_verify_deep; |
33533 | if ((heap_verify_level & GCConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction) |
33534 | deep_verify_obj = FALSE; |
33535 | |
33536 | ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj); |
33537 | |
33538 | if (can_verify_deep) |
33539 | { |
33540 | if (curr_gen_num > 0) |
33541 | { |
33542 | BOOL need_card_p = FALSE; |
33543 | if (contain_pointers_or_collectible (curr_object)) |
33544 | { |
33545 | dprintf (4, ("curr_object: %Ix" , (size_t)curr_object)); |
33546 | size_t crd = card_of (curr_object); |
33547 | BOOL found_card_p = card_set_p (crd); |
33548 | |
33549 | #ifdef COLLECTIBLE_CLASS |
33550 | if (is_collectible(curr_object)) |
33551 | { |
33552 | uint8_t* class_obj = get_class_object (curr_object); |
33553 | if ((class_obj < ephemeral_high) && (class_obj >= next_boundary)) |
33554 | { |
33555 | if (!found_card_p) |
33556 | { |
33557 | dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix" , |
33558 | card_of (curr_object), (size_t)curr_object, class_obj)); |
33559 | |
33560 | FATAL_GC_ERROR(); |
33561 | } |
33562 | } |
33563 | } |
33564 | #endif //COLLECTIBLE_CLASS |
33565 | |
33566 | if (contain_pointers(curr_object)) |
33567 | { |
33568 | go_through_object_nostart |
33569 | (method_table(curr_object), curr_object, s, oo, |
33570 | { |
33571 | if ((crd != card_of ((uint8_t*)oo)) && !found_card_p) |
33572 | { |
33573 | crd = card_of ((uint8_t*)oo); |
33574 | found_card_p = card_set_p (crd); |
33575 | need_card_p = FALSE; |
33576 | } |
33577 | if ((*oo < ephemeral_high) && (*oo >= next_boundary)) |
33578 | { |
33579 | need_card_p = TRUE; |
33580 | } |
33581 | |
33582 | if (need_card_p && !found_card_p) |
33583 | { |
33584 | |
33585 | dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[" , |
33586 | card_of (curr_object), (size_t)curr_object, |
33587 | card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const))); |
33588 | FATAL_GC_ERROR(); |
33589 | } |
33590 | } |
33591 | ); |
33592 | } |
33593 | if (need_card_p && !found_card_p) |
33594 | { |
33595 | dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[" , |
33596 | card_of (curr_object), (size_t)curr_object, |
33597 | card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const))); |
33598 | FATAL_GC_ERROR(); |
33599 | } |
33600 | } |
33601 | } |
33602 | total_objects_verified_deep++; |
33603 | } |
33604 | } |
33605 | |
33606 | prev_object = curr_object; |
33607 | prev_brick = curr_brick; |
33608 | curr_object = curr_object + Align(s, align_const); |
33609 | if (curr_object < prev_object) |
33610 | { |
33611 | dprintf (3, ("overflow because of a bad object size: %Ix size %Ix" , prev_object, s)); |
33612 | FATAL_GC_ERROR(); |
33613 | } |
33614 | } |
33615 | |
33616 | #ifdef BACKGROUND_GC |
33617 | dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id" , |
33618 | (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC" )), |
33619 | (begin_gc_p ? "BEG" : "END" ), |
33620 | ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan" ), |
33621 | total_objects_verified, total_objects_verified_deep)); |
33622 | if (current_c_gc_state != c_gc_state_planning) |
33623 | { |
33624 | assert (total_objects_verified == total_objects_verified_deep); |
33625 | } |
33626 | #endif //BACKGROUND_GC |
33627 | |
33628 | verify_free_lists(); |
33629 | |
33630 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
33631 | finalize_queue->CheckFinalizerObjects(); |
33632 | #endif // FEATURE_PREMORTEM_FINALIZATION |
33633 | |
33634 | { |
33635 | // to be consistent with handle table APIs pass a ScanContext* |
33636 | // to provide the heap number. the SC isn't complete though so |
33637 | // limit its scope to handle table verification. |
33638 | ScanContext sc; |
33639 | sc.thread_number = heap_number; |
33640 | GCScan::VerifyHandleTable(max_generation, max_generation, &sc); |
33641 | } |
33642 | |
33643 | #ifdef MULTIPLE_HEAPS |
33644 | current_join->join(this, gc_join_verify_objects_done); |
33645 | if (current_join->joined()) |
33646 | #endif //MULTIPLE_HEAPS |
33647 | { |
33648 | GCToEEInterface::VerifySyncTableEntry(); |
33649 | #ifdef MULTIPLE_HEAPS |
33650 | current_join->restart(); |
33651 | #endif //MULTIPLE_HEAPS |
33652 | } |
33653 | |
33654 | #ifdef BACKGROUND_GC |
33655 | if (!settings.concurrent) |
33656 | { |
33657 | if (current_c_gc_state == c_gc_state_planning) |
33658 | { |
33659 | // temporarily commenting this out 'cause an FGC |
33660 | // could be triggered before we sweep ephemeral. |
33661 | //verify_seg_end_mark_array_cleared(); |
33662 | } |
33663 | } |
33664 | |
33665 | if (settings.concurrent) |
33666 | { |
33667 | verify_mark_array_cleared(); |
33668 | } |
33669 | dprintf (2,("GC%d(%s): Verifying heap - end" , |
33670 | VolatileLoad(&settings.gc_index), |
33671 | (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC" )))); |
33672 | #else |
33673 | dprintf (2,("GC#d: Verifying heap - end" , VolatileLoad(&settings.gc_index))); |
33674 | #endif //BACKGROUND_GC |
33675 | } |
33676 | |
33677 | #endif //VERIFY_HEAP |
33678 | |
33679 | |
33680 | void GCHeap::ValidateObjectMember (Object* obj) |
33681 | { |
33682 | #ifdef VERIFY_HEAP |
33683 | size_t s = size (obj); |
33684 | uint8_t* o = (uint8_t*)obj; |
33685 | |
33686 | go_through_object_cl (method_table (obj), o, s, oo, |
33687 | { |
33688 | uint8_t* child_o = *oo; |
33689 | if (child_o) |
33690 | { |
33691 | dprintf (3, ("VOM: m: %Ix obj %Ix" , (size_t)child_o, o)); |
33692 | MethodTable *pMT = method_table (child_o); |
33693 | assert(pMT); |
33694 | if (!pMT->SanityCheck()) { |
33695 | dprintf (3, ("Bad member of %Ix %Ix" , |
33696 | (size_t)oo, (size_t)child_o)); |
33697 | FATAL_GC_ERROR(); |
33698 | } |
33699 | } |
33700 | } ); |
33701 | #endif // VERIFY_HEAP |
33702 | } |
33703 | |
33704 | void (CObjectHeader* hdr) |
33705 | { |
33706 | UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced |
33707 | hdr->~CObjectHeader(); |
33708 | } |
33709 | |
33710 | HRESULT GCHeap::Shutdown () |
33711 | { |
33712 | deleteGCShadow(); |
33713 | |
33714 | GCScan::GcRuntimeStructuresValid (FALSE); |
33715 | |
33716 | // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all |
33717 | // threads except the one performing the shutdown. |
33718 | // ASSERT( !GcInProgress ); |
33719 | |
33720 | // Guard against any more GC occurring and against any threads blocking |
33721 | // for GC to complete when the GC heap is gone. This fixes a race condition |
33722 | // where a thread in GC is destroyed as part of process destruction and |
33723 | // the remaining threads block for GC complete. |
33724 | |
33725 | //GCTODO |
33726 | //EnterAllocLock(); |
33727 | //Enter(); |
33728 | //EnterFinalizeLock(); |
33729 | //SetGCDone(); |
33730 | |
33731 | // during shutdown lot of threads are suspended |
33732 | // on this even, we don't want to wake them up just yet |
33733 | //CloseHandle (WaitForGCEvent); |
33734 | |
33735 | //find out if the global card table hasn't been used yet |
33736 | uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))]; |
33737 | if (card_table_refcount (ct) == 0) |
33738 | { |
33739 | destroy_card_table (ct); |
33740 | g_gc_card_table = nullptr; |
33741 | |
33742 | #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES |
33743 | g_gc_card_bundle_table = nullptr; |
33744 | #endif |
33745 | #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
33746 | SoftwareWriteWatch::StaticClose(); |
33747 | #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |
33748 | } |
33749 | |
33750 | //destroy all segments on the standby list |
33751 | while(gc_heap::segment_standby_list != 0) |
33752 | { |
33753 | heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list); |
33754 | #ifdef MULTIPLE_HEAPS |
33755 | (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE); |
33756 | #else //MULTIPLE_HEAPS |
33757 | pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE); |
33758 | #endif //MULTIPLE_HEAPS |
33759 | gc_heap::segment_standby_list = next_seg; |
33760 | } |
33761 | |
33762 | |
33763 | #ifdef MULTIPLE_HEAPS |
33764 | |
33765 | for (int i = 0; i < gc_heap::n_heaps; i ++) |
33766 | { |
33767 | delete gc_heap::g_heaps[i]->vm_heap; |
33768 | //destroy pure GC stuff |
33769 | gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]); |
33770 | } |
33771 | #else |
33772 | gc_heap::destroy_gc_heap (pGenGCHeap); |
33773 | |
33774 | #endif //MULTIPLE_HEAPS |
33775 | gc_heap::shutdown_gc(); |
33776 | |
33777 | return S_OK; |
33778 | } |
33779 | |
33780 | // Wait until a garbage collection is complete |
33781 | // returns NOERROR if wait was OK, other error code if failure. |
33782 | // WARNING: This will not undo the must complete state. If you are |
33783 | // in a must complete when you call this, you'd better know what you're |
33784 | // doing. |
33785 | |
33786 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
33787 | static |
33788 | HRESULT AllocateCFinalize(CFinalize **pCFinalize) |
33789 | { |
33790 | *pCFinalize = new (nothrow) CFinalize(); |
33791 | if (*pCFinalize == NULL || !(*pCFinalize)->Initialize()) |
33792 | return E_OUTOFMEMORY; |
33793 | |
33794 | return S_OK; |
33795 | } |
33796 | #endif // FEATURE_PREMORTEM_FINALIZATION |
33797 | |
33798 | // init the instance heap |
33799 | HRESULT GCHeap::Init(size_t hn) |
33800 | { |
33801 | HRESULT hres = S_OK; |
33802 | |
33803 | #ifdef MULTIPLE_HEAPS |
33804 | if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0) |
33805 | hres = E_OUTOFMEMORY; |
33806 | #else |
33807 | UNREFERENCED_PARAMETER(hn); |
33808 | if (!gc_heap::make_gc_heap()) |
33809 | hres = E_OUTOFMEMORY; |
33810 | #endif //MULTIPLE_HEAPS |
33811 | |
33812 | // Failed. |
33813 | return hres; |
33814 | } |
33815 | |
33816 | //System wide initialization |
33817 | HRESULT GCHeap::Initialize () |
33818 | { |
33819 | HRESULT hr = S_OK; |
33820 | |
33821 | g_gc_pFreeObjectMethodTable = GCToEEInterface::GetFreeObjectMethodTable(); |
33822 | g_num_processors = GCToOSInterface::GetTotalProcessorCount(); |
33823 | assert(g_num_processors != 0); |
33824 | |
33825 | //Initialize the static members. |
33826 | #ifdef TRACE_GC |
33827 | GcDuration = 0; |
33828 | CreatedObjectCount = 0; |
33829 | #endif //TRACE_GC |
33830 | |
33831 | size_t seg_size = get_valid_segment_size(); |
33832 | gc_heap::soh_segment_size = seg_size; |
33833 | size_t large_seg_size = get_valid_segment_size(TRUE); |
33834 | gc_heap::min_loh_segment_size = large_seg_size; |
33835 | gc_heap::min_segment_size = min (seg_size, large_seg_size); |
33836 | #ifdef SEG_MAPPING_TABLE |
33837 | gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size); |
33838 | #endif //SEG_MAPPING_TABLE |
33839 | |
33840 | #ifdef MULTIPLE_HEAPS |
33841 | uint32_t nhp_from_config = static_cast<uint32_t>(GCConfig::GetHeapCount()); |
33842 | |
33843 | // GetGCProcessCpuCount only returns up to 64 procs. |
33844 | unsigned int nhp_from_process = GCToOSInterface::CanEnableGCCPUGroups() ? |
33845 | GCToOSInterface::GetTotalProcessorCount(): |
33846 | GCToOSInterface::GetCurrentProcessCpuCount(); |
33847 | |
33848 | unsigned int nhp = ((nhp_from_config == 0) ? nhp_from_process : |
33849 | (min (nhp_from_config, nhp_from_process))); |
33850 | |
33851 | |
33852 | nhp = min (nhp, MAX_SUPPORTED_CPUS); |
33853 | |
33854 | if (GCConfig::GetNoAffinitize()) |
33855 | gc_heap::gc_thread_no_affinitize_p = true; |
33856 | |
33857 | #if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORECLR) |
33858 | if (!(gc_heap::gc_thread_no_affinitize_p)) |
33859 | { |
33860 | if (!(GCToOSInterface::CanEnableGCCPUGroups())) |
33861 | { |
33862 | size_t gc_thread_affinity_mask = static_cast<size_t>(GCConfig::GetGCHeapAffinitizeMask()); |
33863 | |
33864 | uintptr_t pmask, smask; |
33865 | if (GCToOSInterface::GetCurrentProcessAffinityMask(&pmask, &smask)) |
33866 | { |
33867 | pmask &= smask; |
33868 | |
33869 | if (gc_thread_affinity_mask) |
33870 | { |
33871 | pmask &= gc_thread_affinity_mask; |
33872 | } |
33873 | |
33874 | process_mask = pmask; |
33875 | |
33876 | unsigned int set_bits_in_pmask = 0; |
33877 | while (pmask) |
33878 | { |
33879 | if (pmask & 1) |
33880 | set_bits_in_pmask++; |
33881 | pmask >>= 1; |
33882 | } |
33883 | |
33884 | nhp = min (nhp, set_bits_in_pmask); |
33885 | } |
33886 | else |
33887 | { |
33888 | gc_heap::gc_thread_no_affinitize_p = true; |
33889 | } |
33890 | } |
33891 | } |
33892 | #endif //!FEATURE_REDHAWK && !FEATURE_CORECLR |
33893 | |
33894 | hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp); |
33895 | #else |
33896 | hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/); |
33897 | #endif //MULTIPLE_HEAPS |
33898 | |
33899 | if (hr != S_OK) |
33900 | return hr; |
33901 | |
33902 | gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit(); |
33903 | |
33904 | gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100; |
33905 | #ifndef MULTIPLE_HEAPS |
33906 | gc_heap::mem_one_percent /= g_num_processors; |
33907 | #endif //!MULTIPLE_HEAPS |
33908 | |
33909 | uint32_t highmem_th_from_config = (uint32_t)GCConfig::GetGCHighMemPercent(); |
33910 | if (highmem_th_from_config) |
33911 | { |
33912 | gc_heap::high_memory_load_th = min (99, highmem_th_from_config); |
33913 | gc_heap::v_high_memory_load_th = min (99, (highmem_th_from_config + 7)); |
33914 | } |
33915 | else |
33916 | { |
33917 | // We should only use this if we are in the "many process" mode which really is only applicable |
33918 | // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory. |
33919 | // For now I am using an estimate to calculate these numbers but this should really be obtained |
33920 | // programmatically going forward. |
33921 | // I am assuming 47 processes using WKS GC and 3 using SVR GC. |
33922 | // I am assuming 3 in part due to the "very high memory load" is 97%. |
33923 | int available_mem_th = 10; |
33924 | if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024)) |
33925 | { |
33926 | int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(GCToOSInterface::GetTotalProcessorCount())); |
33927 | available_mem_th = min (available_mem_th, adjusted_available_mem_th); |
33928 | } |
33929 | |
33930 | gc_heap::high_memory_load_th = 100 - available_mem_th; |
33931 | gc_heap::v_high_memory_load_th = 97; |
33932 | } |
33933 | |
33934 | gc_heap::m_high_memory_load_th = min ((gc_heap::high_memory_load_th + 5), gc_heap::v_high_memory_load_th); |
33935 | |
33936 | gc_heap::pm_stress_on = (GCConfig::GetGCProvModeStress() != 0); |
33937 | |
33938 | #if defined(BIT64) |
33939 | gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent; |
33940 | #endif // BIT64 |
33941 | |
33942 | WaitForGCEvent = new (nothrow) GCEvent; |
33943 | |
33944 | if (!WaitForGCEvent) |
33945 | { |
33946 | return E_OUTOFMEMORY; |
33947 | } |
33948 | |
33949 | if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE)) |
33950 | { |
33951 | return E_FAIL; |
33952 | } |
33953 | |
33954 | #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way |
33955 | #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS) |
33956 | if (GCStress<cfg_any>::IsEnabled()) { |
33957 | for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++) |
33958 | { |
33959 | m_StressObjs[i] = CreateGlobalHandle(0); |
33960 | } |
33961 | m_CurStressObj = 0; |
33962 | } |
33963 | #endif //STRESS_HEAP && !MULTIPLE_HEAPS |
33964 | #endif // FEATURE_REDHAWK |
33965 | |
33966 | initGCShadow(); // If we are debugging write barriers, initialize heap shadow |
33967 | |
33968 | #ifdef MULTIPLE_HEAPS |
33969 | |
33970 | for (unsigned i = 0; i < nhp; i++) |
33971 | { |
33972 | GCHeap* Hp = new (nothrow) GCHeap(); |
33973 | if (!Hp) |
33974 | return E_OUTOFMEMORY; |
33975 | |
33976 | if ((hr = Hp->Init (i))!= S_OK) |
33977 | { |
33978 | return hr; |
33979 | } |
33980 | } |
33981 | // initialize numa node to heap map |
33982 | heap_select::init_numa_node_to_heap_map(nhp); |
33983 | #else |
33984 | hr = Init (0); |
33985 | #endif //MULTIPLE_HEAPS |
33986 | |
33987 | if (hr == S_OK) |
33988 | { |
33989 | GCScan::GcRuntimeStructuresValid (TRUE); |
33990 | |
33991 | GCToEEInterface::DiagUpdateGenerationBounds(); |
33992 | } |
33993 | |
33994 | return hr; |
33995 | }; |
33996 | |
33997 | //// |
33998 | // GC callback functions |
33999 | bool GCHeap::IsPromoted(Object* object) |
34000 | { |
34001 | #ifdef _DEBUG |
34002 | ((CObjectHeader*)object)->Validate(); |
34003 | #endif //_DEBUG |
34004 | |
34005 | uint8_t* o = (uint8_t*)object; |
34006 | |
34007 | if (gc_heap::settings.condemned_generation == max_generation) |
34008 | { |
34009 | #ifdef MULTIPLE_HEAPS |
34010 | gc_heap* hp = gc_heap::g_heaps[0]; |
34011 | #else |
34012 | gc_heap* hp = pGenGCHeap; |
34013 | #endif //MULTIPLE_HEAPS |
34014 | |
34015 | #ifdef BACKGROUND_GC |
34016 | if (gc_heap::settings.concurrent) |
34017 | { |
34018 | bool is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))|| |
34019 | hp->background_marked (o)); |
34020 | return is_marked; |
34021 | } |
34022 | else |
34023 | #endif //BACKGROUND_GC |
34024 | { |
34025 | return (!((o < hp->highest_address) && (o >= hp->lowest_address)) |
34026 | || hp->is_mark_set (o)); |
34027 | } |
34028 | } |
34029 | else |
34030 | { |
34031 | gc_heap* hp = gc_heap::heap_of (o); |
34032 | return (!((o < hp->gc_high) && (o >= hp->gc_low)) |
34033 | || hp->is_mark_set (o)); |
34034 | } |
34035 | } |
34036 | |
34037 | size_t GCHeap::GetPromotedBytes(int heap_index) |
34038 | { |
34039 | #ifdef BACKGROUND_GC |
34040 | if (gc_heap::settings.concurrent) |
34041 | { |
34042 | return gc_heap::bpromoted_bytes (heap_index); |
34043 | } |
34044 | else |
34045 | #endif //BACKGROUND_GC |
34046 | { |
34047 | return gc_heap::promoted_bytes (heap_index); |
34048 | } |
34049 | } |
34050 | |
34051 | void GCHeap::SetYieldProcessorScalingFactor (float scalingFactor) |
34052 | { |
34053 | assert (yp_spin_count_unit != 0); |
34054 | int saved_yp_spin_count_unit = yp_spin_count_unit; |
34055 | yp_spin_count_unit = (int)((float)yp_spin_count_unit * scalingFactor / (float)9); |
34056 | |
34057 | // It's very suspicious if it becomes 0 |
34058 | if (yp_spin_count_unit == 0) |
34059 | { |
34060 | yp_spin_count_unit = saved_yp_spin_count_unit; |
34061 | } |
34062 | } |
34063 | |
34064 | unsigned int GCHeap::WhichGeneration (Object* object) |
34065 | { |
34066 | gc_heap* hp = gc_heap::heap_of ((uint8_t*)object); |
34067 | unsigned int g = hp->object_gennum ((uint8_t*)object); |
34068 | dprintf (3, ("%Ix is in gen %d" , (size_t)object, g)); |
34069 | return g; |
34070 | } |
34071 | |
34072 | bool GCHeap::IsEphemeral (Object* object) |
34073 | { |
34074 | uint8_t* o = (uint8_t*)object; |
34075 | gc_heap* hp = gc_heap::heap_of (o); |
34076 | return !!hp->ephemeral_pointer_p (o); |
34077 | } |
34078 | |
34079 | // Return NULL if can't find next object. When EE is not suspended, |
34080 | // the result is not accurate: if the input arg is in gen0, the function could |
34081 | // return zeroed out memory as next object |
34082 | Object * GCHeap::NextObj (Object * object) |
34083 | { |
34084 | #ifdef VERIFY_HEAP |
34085 | uint8_t* o = (uint8_t*)object; |
34086 | |
34087 | #ifndef FEATURE_BASICFREEZE |
34088 | if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address))) |
34089 | { |
34090 | return NULL; |
34091 | } |
34092 | #endif //!FEATURE_BASICFREEZE |
34093 | |
34094 | heap_segment * hs = gc_heap::find_segment (o, FALSE); |
34095 | if (!hs) |
34096 | { |
34097 | return NULL; |
34098 | } |
34099 | |
34100 | BOOL large_object_p = heap_segment_loh_p (hs); |
34101 | if (large_object_p) |
34102 | return NULL; //could be racing with another core allocating. |
34103 | #ifdef MULTIPLE_HEAPS |
34104 | gc_heap* hp = heap_segment_heap (hs); |
34105 | #else //MULTIPLE_HEAPS |
34106 | gc_heap* hp = 0; |
34107 | #endif //MULTIPLE_HEAPS |
34108 | unsigned int g = hp->object_gennum ((uint8_t*)object); |
34109 | if ((g == 0) && hp->settings.demotion) |
34110 | return NULL;//could be racing with another core allocating. |
34111 | int align_const = get_alignment_constant (!large_object_p); |
34112 | uint8_t* nextobj = o + Align (size (o), align_const); |
34113 | if (nextobj <= o) // either overflow or 0 sized object. |
34114 | { |
34115 | return NULL; |
34116 | } |
34117 | |
34118 | if ((nextobj < heap_segment_mem(hs)) || |
34119 | (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) || |
34120 | (nextobj >= hp->alloc_allocated)) |
34121 | { |
34122 | return NULL; |
34123 | } |
34124 | |
34125 | return (Object *)nextobj; |
34126 | #else |
34127 | return nullptr; |
34128 | #endif // VERIFY_HEAP |
34129 | } |
34130 | |
34131 | #ifdef VERIFY_HEAP |
34132 | |
34133 | #ifdef FEATURE_BASICFREEZE |
34134 | BOOL GCHeap::IsInFrozenSegment (Object * object) |
34135 | { |
34136 | uint8_t* o = (uint8_t*)object; |
34137 | heap_segment * hs = gc_heap::find_segment (o, FALSE); |
34138 | //We create a frozen object for each frozen segment before the segment is inserted |
34139 | //to segment list; during ngen, we could also create frozen objects in segments which |
34140 | //don't belong to current GC heap. |
34141 | //So we return true if hs is NULL. It might create a hole about detecting invalidate |
34142 | //object. But given all other checks present, the hole should be very small |
34143 | return !hs || heap_segment_read_only_p (hs); |
34144 | } |
34145 | #endif //FEATURE_BASICFREEZE |
34146 | |
34147 | #endif //VERIFY_HEAP |
34148 | |
34149 | // returns TRUE if the pointer is in one of the GC heaps. |
34150 | bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only) |
34151 | { |
34152 | STATIC_CONTRACT_SO_TOLERANT; |
34153 | |
34154 | // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment |
34155 | // no longer calls GCEvent::Wait which eventually takes a lock. |
34156 | |
34157 | uint8_t* object = (uint8_t*) vpObject; |
34158 | #ifndef FEATURE_BASICFREEZE |
34159 | if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address))) |
34160 | return FALSE; |
34161 | #endif //!FEATURE_BASICFREEZE |
34162 | |
34163 | heap_segment * hs = gc_heap::find_segment (object, small_heap_only); |
34164 | return !!hs; |
34165 | } |
34166 | |
34167 | #ifdef STRESS_PINNING |
34168 | static n_promote = 0; |
34169 | #endif //STRESS_PINNING |
34170 | // promote an object |
34171 | void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags) |
34172 | { |
34173 | THREAD_NUMBER_FROM_CONTEXT; |
34174 | #ifndef MULTIPLE_HEAPS |
34175 | const int thread = 0; |
34176 | #endif //!MULTIPLE_HEAPS |
34177 | |
34178 | uint8_t* o = (uint8_t*)*ppObject; |
34179 | |
34180 | if (o == 0) |
34181 | return; |
34182 | |
34183 | #ifdef DEBUG_DestroyedHandleValue |
34184 | // we can race with destroy handle during concurrent scan |
34185 | if (o == (uint8_t*)DEBUG_DestroyedHandleValue) |
34186 | return; |
34187 | #endif //DEBUG_DestroyedHandleValue |
34188 | |
34189 | HEAP_FROM_THREAD; |
34190 | |
34191 | gc_heap* hp = gc_heap::heap_of (o); |
34192 | |
34193 | dprintf (3, ("Promote %Ix" , (size_t)o)); |
34194 | |
34195 | #ifdef INTERIOR_POINTERS |
34196 | if (flags & GC_CALL_INTERIOR) |
34197 | { |
34198 | if ((o < hp->gc_low) || (o >= hp->gc_high)) |
34199 | { |
34200 | return; |
34201 | } |
34202 | if ( (o = hp->find_object (o, hp->gc_low)) == 0) |
34203 | { |
34204 | return; |
34205 | } |
34206 | |
34207 | } |
34208 | #endif //INTERIOR_POINTERS |
34209 | |
34210 | #ifdef FEATURE_CONSERVATIVE_GC |
34211 | // For conservative GC, a value on stack may point to middle of a free object. |
34212 | // In this case, we don't need to promote the pointer. |
34213 | if (GCConfig::GetConservativeGC() |
34214 | && ((CObjectHeader*)o)->IsFree()) |
34215 | { |
34216 | return; |
34217 | } |
34218 | #endif |
34219 | |
34220 | #ifdef _DEBUG |
34221 | ((CObjectHeader*)o)->ValidatePromote(sc, flags); |
34222 | #else |
34223 | UNREFERENCED_PARAMETER(sc); |
34224 | #endif //_DEBUG |
34225 | |
34226 | if (flags & GC_CALL_PINNED) |
34227 | hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high); |
34228 | |
34229 | #ifdef STRESS_PINNING |
34230 | if ((++n_promote % 20) == 1) |
34231 | hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high); |
34232 | #endif //STRESS_PINNING |
34233 | |
34234 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
34235 | size_t promoted_size_begin = hp->promoted_bytes (thread); |
34236 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
34237 | |
34238 | if ((o >= hp->gc_low) && (o < hp->gc_high)) |
34239 | { |
34240 | hpt->mark_object_simple (&o THREAD_NUMBER_ARG); |
34241 | } |
34242 | |
34243 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
34244 | size_t promoted_size_end = hp->promoted_bytes (thread); |
34245 | if (g_fEnableAppDomainMonitoring) |
34246 | { |
34247 | if (sc->pCurrentDomain) |
34248 | { |
34249 | GCToEEInterface::RecordSurvivedBytesForHeap((promoted_size_end - promoted_size_begin), thread, sc->pCurrentDomain); |
34250 | } |
34251 | } |
34252 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
34253 | |
34254 | STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL); |
34255 | } |
34256 | |
34257 | void GCHeap::Relocate (Object** ppObject, ScanContext* sc, |
34258 | uint32_t flags) |
34259 | { |
34260 | UNREFERENCED_PARAMETER(sc); |
34261 | |
34262 | uint8_t* object = (uint8_t*)(Object*)(*ppObject); |
34263 | |
34264 | THREAD_NUMBER_FROM_CONTEXT; |
34265 | |
34266 | //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject)); |
34267 | dprintf (3, ("R: %Ix" , (size_t)ppObject)); |
34268 | |
34269 | if (object == 0) |
34270 | return; |
34271 | |
34272 | gc_heap* hp = gc_heap::heap_of (object); |
34273 | |
34274 | #ifdef _DEBUG |
34275 | if (!(flags & GC_CALL_INTERIOR)) |
34276 | { |
34277 | // We cannot validate this object if it's in the condemned gen because it could |
34278 | // be one of the objects that were overwritten by an artificial gap due to a pinned plug. |
34279 | if (!((object >= hp->gc_low) && (object < hp->gc_high))) |
34280 | { |
34281 | ((CObjectHeader*)object)->Validate(FALSE); |
34282 | } |
34283 | } |
34284 | #endif //_DEBUG |
34285 | |
34286 | dprintf (3, ("Relocate %Ix\n" , (size_t)object)); |
34287 | |
34288 | uint8_t* ; |
34289 | |
34290 | if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction) |
34291 | { |
34292 | if (!((object >= hp->gc_low) && (object < hp->gc_high))) |
34293 | { |
34294 | return; |
34295 | } |
34296 | |
34297 | if (gc_heap::loh_object_p (object)) |
34298 | { |
34299 | pheader = hp->find_object (object, 0); |
34300 | if (pheader == 0) |
34301 | { |
34302 | return; |
34303 | } |
34304 | |
34305 | ptrdiff_t ref_offset = object - pheader; |
34306 | hp->relocate_address(&pheader THREAD_NUMBER_ARG); |
34307 | *ppObject = (Object*)(pheader + ref_offset); |
34308 | return; |
34309 | } |
34310 | } |
34311 | |
34312 | { |
34313 | pheader = object; |
34314 | hp->relocate_address(&pheader THREAD_NUMBER_ARG); |
34315 | *ppObject = (Object*)pheader; |
34316 | } |
34317 | |
34318 | STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0)); |
34319 | } |
34320 | |
34321 | /*static*/ bool GCHeap::IsObjectInFixedHeap(Object *pObj) |
34322 | { |
34323 | // For now we simply look at the size of the object to determine if it in the |
34324 | // fixed heap or not. If the bit indicating this gets set at some point |
34325 | // we should key off that instead. |
34326 | return size( pObj ) >= loh_size_threshold; |
34327 | } |
34328 | |
34329 | #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way |
34330 | #ifdef STRESS_HEAP |
34331 | |
34332 | void StressHeapDummy (); |
34333 | |
34334 | static int32_t GCStressStartCount = -1; |
34335 | static int32_t GCStressCurCount = 0; |
34336 | static int32_t GCStressStartAtJit = -1; |
34337 | |
34338 | // the maximum number of foreground GCs we'll induce during one BGC |
34339 | // (this number does not include "naturally" occuring GCs). |
34340 | static int32_t GCStressMaxFGCsPerBGC = -1; |
34341 | |
34342 | // CLRRandom implementation can produce FPU exceptions if |
34343 | // the test/application run by CLR is enabling any FPU exceptions. |
34344 | // We want to avoid any unexpected exception coming from stress |
34345 | // infrastructure, so CLRRandom is not an option. |
34346 | // The code below is a replicate of CRT rand() implementation. |
34347 | // Using CRT rand() is not an option because we will interfere with the user application |
34348 | // that may also use it. |
34349 | int StressRNG(int iMaxValue) |
34350 | { |
34351 | static BOOL bisRandInit = FALSE; |
34352 | static int lHoldrand = 1L; |
34353 | |
34354 | if (!bisRandInit) |
34355 | { |
34356 | lHoldrand = (int)time(NULL); |
34357 | bisRandInit = TRUE; |
34358 | } |
34359 | int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff); |
34360 | return randValue % iMaxValue; |
34361 | } |
34362 | #endif // STRESS_HEAP |
34363 | #endif // !FEATURE_REDHAWK |
34364 | |
34365 | // free up object so that things will move and then do a GC |
34366 | //return TRUE if GC actually happens, otherwise FALSE |
34367 | bool GCHeap::StressHeap(gc_alloc_context * context) |
34368 | { |
34369 | #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK) |
34370 | alloc_context* acontext = static_cast<alloc_context*>(context); |
34371 | assert(context != nullptr); |
34372 | |
34373 | // if GC stress was dynamically disabled during this run we return FALSE |
34374 | if (!GCStressPolicy::IsEnabled()) |
34375 | return FALSE; |
34376 | |
34377 | #ifdef _DEBUG |
34378 | if (g_pConfig->FastGCStressLevel() && !GCToEEInterface::GetThread()->StressHeapIsEnabled()) { |
34379 | return FALSE; |
34380 | } |
34381 | |
34382 | #endif //_DEBUG |
34383 | |
34384 | if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE) |
34385 | #ifdef _DEBUG |
34386 | || g_pConfig->FastGCStressLevel() > 1 |
34387 | #endif //_DEBUG |
34388 | ) { |
34389 | if (!Thread::UniqueStack(&acontext)) { |
34390 | return FALSE; |
34391 | } |
34392 | } |
34393 | |
34394 | #ifdef BACKGROUND_GC |
34395 | // don't trigger a GC from the GC threads but still trigger GCs from user threads. |
34396 | if (GCToEEInterface::WasCurrentThreadCreatedByGC()) |
34397 | { |
34398 | return FALSE; |
34399 | } |
34400 | #endif //BACKGROUND_GC |
34401 | |
34402 | if (GCStressStartAtJit == -1 || GCStressStartCount == -1) |
34403 | { |
34404 | GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart); |
34405 | GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit); |
34406 | } |
34407 | |
34408 | if (GCStressMaxFGCsPerBGC == -1) |
34409 | { |
34410 | GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC); |
34411 | if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1) |
34412 | GCStressMaxFGCsPerBGC = 6; |
34413 | } |
34414 | |
34415 | #ifdef _DEBUG |
34416 | if (g_JitCount < GCStressStartAtJit) |
34417 | return FALSE; |
34418 | #endif //_DEBUG |
34419 | |
34420 | // Allow programmer to skip the first N Stress GCs so that you can |
34421 | // get to the interesting ones faster. |
34422 | Interlocked::Increment(&GCStressCurCount); |
34423 | if (GCStressCurCount < GCStressStartCount) |
34424 | return FALSE; |
34425 | |
34426 | // throttle the number of stress-induced GCs by a factor given by GCStressStep |
34427 | if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0) |
34428 | { |
34429 | return FALSE; |
34430 | } |
34431 | |
34432 | #ifdef BACKGROUND_GC |
34433 | if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress()) |
34434 | { |
34435 | // allow a maximum number of stress induced FGCs during one BGC |
34436 | if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC) |
34437 | return FALSE; |
34438 | ++gc_stress_fgcs_in_bgc; |
34439 | } |
34440 | #endif // BACKGROUND_GC |
34441 | |
34442 | if (g_pStringClass == 0) |
34443 | { |
34444 | // If the String class has not been loaded, dont do any stressing. This should |
34445 | // be kept to a minimum to get as complete coverage as possible. |
34446 | _ASSERTE(g_fEEInit); |
34447 | return FALSE; |
34448 | } |
34449 | |
34450 | #ifndef MULTIPLE_HEAPS |
34451 | static int32_t OneAtATime = -1; |
34452 | |
34453 | // Only bother with this if the stress level is big enough and if nobody else is |
34454 | // doing it right now. Note that some callers are inside the AllocLock and are |
34455 | // guaranteed synchronized. But others are using AllocationContexts and have no |
34456 | // particular synchronization. |
34457 | // |
34458 | // For this latter case, we want a very high-speed way of limiting this to one |
34459 | // at a time. A secondary advantage is that we release part of our StressObjs |
34460 | // buffer sparingly but just as effectively. |
34461 | |
34462 | if (Interlocked::Increment(&OneAtATime) == 0 && |
34463 | !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize) |
34464 | { |
34465 | StringObject* str; |
34466 | |
34467 | // If the current string is used up |
34468 | if (HndFetchHandle(m_StressObjs[m_CurStressObj]) == 0) |
34469 | { |
34470 | // Populate handles with strings |
34471 | int i = m_CurStressObj; |
34472 | while(HndFetchHandle(m_StressObjs[i]) == 0) |
34473 | { |
34474 | _ASSERTE(m_StressObjs[i] != 0); |
34475 | unsigned strLen = ((unsigned)loh_size_threshold - 32) / sizeof(WCHAR); |
34476 | unsigned strSize = PtrAlign(StringObject::GetSize(strLen)); |
34477 | |
34478 | // update the cached type handle before allocating |
34479 | SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass)); |
34480 | str = (StringObject*) pGenGCHeap->allocate (strSize, acontext); |
34481 | if (str) |
34482 | { |
34483 | str->SetMethodTable (g_pStringClass); |
34484 | str->SetStringLength (strLen); |
34485 | HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str)); |
34486 | } |
34487 | i = (i + 1) % NUM_HEAP_STRESS_OBJS; |
34488 | if (i == m_CurStressObj) break; |
34489 | } |
34490 | |
34491 | // advance the current handle to the next string |
34492 | m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS; |
34493 | } |
34494 | |
34495 | // Get the current string |
34496 | str = (StringObject*) OBJECTREFToObject(HndFetchHandle(m_StressObjs[m_CurStressObj])); |
34497 | if (str) |
34498 | { |
34499 | // Chop off the end of the string and form a new object out of it. |
34500 | // This will 'free' an object at the begining of the heap, which will |
34501 | // force data movement. Note that we can only do this so many times. |
34502 | // before we have to move on to the next string. |
34503 | unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31); |
34504 | if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR)) |
34505 | { |
34506 | unsigned sizeToNextObj = (unsigned)Align(size(str)); |
34507 | uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj; |
34508 | pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj); |
34509 | str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR))); |
34510 | } |
34511 | else |
34512 | { |
34513 | // Let the string itself become garbage. |
34514 | // will be realloced next time around |
34515 | HndAssignHandle(m_StressObjs[m_CurStressObj], 0); |
34516 | } |
34517 | } |
34518 | } |
34519 | Interlocked::Decrement(&OneAtATime); |
34520 | #endif // !MULTIPLE_HEAPS |
34521 | if (IsConcurrentGCEnabled()) |
34522 | { |
34523 | int rgen = StressRNG(10); |
34524 | |
34525 | // gen0:gen1:gen2 distribution: 40:40:20 |
34526 | if (rgen >= 8) |
34527 | rgen = 2; |
34528 | else if (rgen >= 4) |
34529 | rgen = 1; |
34530 | else |
34531 | rgen = 0; |
34532 | |
34533 | GarbageCollectTry (rgen, FALSE, collection_gcstress); |
34534 | } |
34535 | else |
34536 | { |
34537 | GarbageCollect(max_generation, FALSE, collection_gcstress); |
34538 | } |
34539 | |
34540 | return TRUE; |
34541 | #else |
34542 | UNREFERENCED_PARAMETER(context); |
34543 | return FALSE; |
34544 | #endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK) |
34545 | } |
34546 | |
34547 | |
34548 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
34549 | #define REGISTER_FOR_FINALIZATION(_object, _size) \ |
34550 | hp->finalize_queue->RegisterForFinalization (0, (_object), (_size)) |
34551 | #else // FEATURE_PREMORTEM_FINALIZATION |
34552 | #define REGISTER_FOR_FINALIZATION(_object, _size) true |
34553 | #endif // FEATURE_PREMORTEM_FINALIZATION |
34554 | |
34555 | #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do { \ |
34556 | if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size))) \ |
34557 | { \ |
34558 | STRESS_LOG_OOM_STACK(_size); \ |
34559 | return NULL; \ |
34560 | } \ |
34561 | } while (false) |
34562 | |
34563 | // |
34564 | // Small Object Allocator |
34565 | // |
34566 | // |
34567 | // Allocate small object with an alignment requirement of 8-bytes. |
34568 | Object* |
34569 | GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags ) |
34570 | { |
34571 | #ifdef FEATURE_64BIT_ALIGNMENT |
34572 | CONTRACTL { |
34573 | NOTHROW; |
34574 | GC_TRIGGERS; |
34575 | } CONTRACTL_END; |
34576 | |
34577 | alloc_context* acontext = static_cast<alloc_context*>(ctx); |
34578 | |
34579 | #ifdef MULTIPLE_HEAPS |
34580 | if (acontext->get_alloc_heap() == 0) |
34581 | { |
34582 | AssignHeap (acontext); |
34583 | assert (acontext->get_alloc_heap()); |
34584 | } |
34585 | |
34586 | gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap; |
34587 | #else |
34588 | gc_heap* hp = pGenGCHeap; |
34589 | #endif //MULTIPLE_HEAPS |
34590 | |
34591 | return AllocAlign8Common(hp, acontext, size, flags); |
34592 | #else |
34593 | UNREFERENCED_PARAMETER(ctx); |
34594 | UNREFERENCED_PARAMETER(size); |
34595 | UNREFERENCED_PARAMETER(flags); |
34596 | assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!" ); |
34597 | return nullptr; |
34598 | #endif //FEATURE_64BIT_ALIGNMENT |
34599 | } |
34600 | |
34601 | // Common code used by both variants of AllocAlign8 above. |
34602 | Object* |
34603 | GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags) |
34604 | { |
34605 | #ifdef FEATURE_64BIT_ALIGNMENT |
34606 | CONTRACTL { |
34607 | NOTHROW; |
34608 | GC_TRIGGERS; |
34609 | } CONTRACTL_END; |
34610 | |
34611 | gc_heap* hp = (gc_heap*)_hp; |
34612 | |
34613 | TRIGGERSGC(); |
34614 | |
34615 | Object* newAlloc = NULL; |
34616 | |
34617 | #ifdef TRACE_GC |
34618 | #ifdef COUNT_CYCLES |
34619 | AllocStart = GetCycleCount32(); |
34620 | unsigned finish; |
34621 | #elif defined(ENABLE_INSTRUMENTATION) |
34622 | unsigned AllocStart = GetInstLogTime(); |
34623 | unsigned finish; |
34624 | #endif //COUNT_CYCLES |
34625 | #endif //TRACE_GC |
34626 | |
34627 | if (size < loh_size_threshold) |
34628 | { |
34629 | #ifdef TRACE_GC |
34630 | AllocSmallCount++; |
34631 | #endif //TRACE_GC |
34632 | |
34633 | // Depending on where in the object the payload requiring 8-byte alignment resides we might have to |
34634 | // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned |
34635 | // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag. |
34636 | size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0; |
34637 | |
34638 | // Retrieve the address of the next allocation from the context (note that we're inside the alloc |
34639 | // lock at this point). |
34640 | uint8_t* result = acontext->alloc_ptr; |
34641 | |
34642 | // Will an allocation at this point yield the correct alignment and fit into the remainder of the |
34643 | // context? |
34644 | if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit)) |
34645 | { |
34646 | // Yes, we can just go ahead and make the allocation. |
34647 | newAlloc = (Object*) hp->allocate (size, acontext); |
34648 | ASSERT(((size_t)newAlloc & 7) == desiredAlignment); |
34649 | } |
34650 | else |
34651 | { |
34652 | // No, either the next available address is not aligned in the way we require it or there's |
34653 | // not enough space to allocate an object of the required size. In both cases we allocate a |
34654 | // padding object (marked as a free object). This object's size is such that it will reverse |
34655 | // the alignment of the next header (asserted below). |
34656 | // |
34657 | // We allocate both together then decide based on the result whether we'll format the space as |
34658 | // free object + real object or real object + free object. |
34659 | ASSERT((Align(min_obj_size) & 7) == 4); |
34660 | CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext); |
34661 | if (freeobj) |
34662 | { |
34663 | if (((size_t)freeobj & 7) == desiredAlignment) |
34664 | { |
34665 | // New allocation has desired alignment, return this one and place the free object at the |
34666 | // end of the allocated space. |
34667 | newAlloc = (Object*)freeobj; |
34668 | freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size)); |
34669 | } |
34670 | else |
34671 | { |
34672 | // New allocation is still mis-aligned, format the initial space as a free object and the |
34673 | // rest of the space should be correctly aligned for the real object. |
34674 | newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size)); |
34675 | ASSERT(((size_t)newAlloc & 7) == desiredAlignment); |
34676 | } |
34677 | freeobj->SetFree(min_obj_size); |
34678 | } |
34679 | } |
34680 | } |
34681 | else |
34682 | { |
34683 | // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't |
34684 | // support mis-aligned object headers so we can't support biased headers as above. Luckily for us |
34685 | // we've managed to arrange things so the only case where we see a bias is for boxed value types and |
34686 | // these can never get large enough to be allocated on the LOH. |
34687 | ASSERT(65536 < loh_size_threshold); |
34688 | ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0); |
34689 | |
34690 | alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1)); |
34691 | |
34692 | newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh); |
34693 | ASSERT(((size_t)newAlloc & 7) == 0); |
34694 | } |
34695 | |
34696 | CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE); |
34697 | |
34698 | #ifdef TRACE_GC |
34699 | #ifdef COUNT_CYCLES |
34700 | finish = GetCycleCount32(); |
34701 | #elif defined(ENABLE_INSTRUMENTATION) |
34702 | finish = GetInstLogTime(); |
34703 | #endif //COUNT_CYCLES |
34704 | AllocDuration += finish - AllocStart; |
34705 | AllocCount++; |
34706 | #endif //TRACE_GC |
34707 | return newAlloc; |
34708 | #else |
34709 | UNREFERENCED_PARAMETER(_hp); |
34710 | UNREFERENCED_PARAMETER(acontext); |
34711 | UNREFERENCED_PARAMETER(size); |
34712 | UNREFERENCED_PARAMETER(flags); |
34713 | assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!" ); |
34714 | return nullptr; |
34715 | #endif // FEATURE_64BIT_ALIGNMENT |
34716 | } |
34717 | |
34718 | Object * |
34719 | GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL) |
34720 | { |
34721 | CONTRACTL { |
34722 | NOTHROW; |
34723 | GC_TRIGGERS; |
34724 | } CONTRACTL_END; |
34725 | |
34726 | TRIGGERSGC(); |
34727 | |
34728 | Object* newAlloc = NULL; |
34729 | |
34730 | #ifdef TRACE_GC |
34731 | #ifdef COUNT_CYCLES |
34732 | AllocStart = GetCycleCount32(); |
34733 | unsigned finish; |
34734 | #elif defined(ENABLE_INSTRUMENTATION) |
34735 | unsigned AllocStart = GetInstLogTime(); |
34736 | unsigned finish; |
34737 | #endif //COUNT_CYCLES |
34738 | #endif //TRACE_GC |
34739 | |
34740 | #ifdef MULTIPLE_HEAPS |
34741 | //take the first heap.... |
34742 | gc_heap* hp = gc_heap::g_heaps[0]; |
34743 | #else |
34744 | gc_heap* hp = pGenGCHeap; |
34745 | #ifdef _PREFAST_ |
34746 | // prefix complains about us dereferencing hp in wks build even though we only access static members |
34747 | // this way. not sure how to shut it up except for this ugly workaround: |
34748 | PREFIX_ASSUME(hp != NULL); |
34749 | #endif //_PREFAST_ |
34750 | #endif //MULTIPLE_HEAPS |
34751 | |
34752 | alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1)); |
34753 | |
34754 | newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh); |
34755 | #ifdef FEATURE_STRUCTALIGN |
34756 | newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size); |
34757 | #endif // FEATURE_STRUCTALIGN |
34758 | CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE); |
34759 | |
34760 | #ifdef TRACE_GC |
34761 | #ifdef COUNT_CYCLES |
34762 | finish = GetCycleCount32(); |
34763 | #elif defined(ENABLE_INSTRUMENTATION) |
34764 | finish = GetInstLogTime(); |
34765 | #endif //COUNT_CYCLES |
34766 | AllocDuration += finish - AllocStart; |
34767 | AllocCount++; |
34768 | #endif //TRACE_GC |
34769 | return newAlloc; |
34770 | } |
34771 | |
34772 | Object* |
34773 | GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL) |
34774 | { |
34775 | CONTRACTL { |
34776 | NOTHROW; |
34777 | GC_TRIGGERS; |
34778 | } CONTRACTL_END; |
34779 | |
34780 | TRIGGERSGC(); |
34781 | |
34782 | Object* newAlloc = NULL; |
34783 | alloc_context* acontext = static_cast<alloc_context*>(context); |
34784 | |
34785 | #ifdef TRACE_GC |
34786 | #ifdef COUNT_CYCLES |
34787 | AllocStart = GetCycleCount32(); |
34788 | unsigned finish; |
34789 | #elif defined(ENABLE_INSTRUMENTATION) |
34790 | unsigned AllocStart = GetInstLogTime(); |
34791 | unsigned finish; |
34792 | #endif //COUNT_CYCLES |
34793 | #endif //TRACE_GC |
34794 | |
34795 | #ifdef MULTIPLE_HEAPS |
34796 | if (acontext->get_alloc_heap() == 0) |
34797 | { |
34798 | AssignHeap (acontext); |
34799 | assert (acontext->get_alloc_heap()); |
34800 | } |
34801 | #endif //MULTIPLE_HEAPS |
34802 | |
34803 | #ifdef MULTIPLE_HEAPS |
34804 | gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap; |
34805 | #else |
34806 | gc_heap* hp = pGenGCHeap; |
34807 | #ifdef _PREFAST_ |
34808 | // prefix complains about us dereferencing hp in wks build even though we only access static members |
34809 | // this way. not sure how to shut it up except for this ugly workaround: |
34810 | PREFIX_ASSUME(hp != NULL); |
34811 | #endif //_PREFAST_ |
34812 | #endif //MULTIPLE_HEAPS |
34813 | |
34814 | if (size < loh_size_threshold) |
34815 | { |
34816 | |
34817 | #ifdef TRACE_GC |
34818 | AllocSmallCount++; |
34819 | #endif //TRACE_GC |
34820 | newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext); |
34821 | #ifdef FEATURE_STRUCTALIGN |
34822 | newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext); |
34823 | #endif // FEATURE_STRUCTALIGN |
34824 | // ASSERT (newAlloc); |
34825 | } |
34826 | else |
34827 | { |
34828 | newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh); |
34829 | #ifdef FEATURE_STRUCTALIGN |
34830 | newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size); |
34831 | #endif // FEATURE_STRUCTALIGN |
34832 | } |
34833 | |
34834 | CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE); |
34835 | |
34836 | #ifdef TRACE_GC |
34837 | #ifdef COUNT_CYCLES |
34838 | finish = GetCycleCount32(); |
34839 | #elif defined(ENABLE_INSTRUMENTATION) |
34840 | finish = GetInstLogTime(); |
34841 | #endif //COUNT_CYCLES |
34842 | AllocDuration += finish - AllocStart; |
34843 | AllocCount++; |
34844 | #endif //TRACE_GC |
34845 | return newAlloc; |
34846 | } |
34847 | |
34848 | void |
34849 | GCHeap::FixAllocContext (gc_alloc_context* context, void* arg, void *heap) |
34850 | { |
34851 | alloc_context* acontext = static_cast<alloc_context*>(context); |
34852 | #ifdef MULTIPLE_HEAPS |
34853 | |
34854 | if (arg != 0) |
34855 | acontext->alloc_count = 0; |
34856 | |
34857 | uint8_t * alloc_ptr = acontext->alloc_ptr; |
34858 | |
34859 | if (!alloc_ptr) |
34860 | return; |
34861 | |
34862 | // The acontext->alloc_heap can be out of sync with the ptrs because |
34863 | // of heap re-assignment in allocate |
34864 | gc_heap* hp = gc_heap::heap_of (alloc_ptr); |
34865 | #else |
34866 | gc_heap* hp = pGenGCHeap; |
34867 | #endif //MULTIPLE_HEAPS |
34868 | |
34869 | if (heap == NULL || heap == hp) |
34870 | { |
34871 | hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE), |
34872 | get_alignment_constant(TRUE)); |
34873 | } |
34874 | } |
34875 | |
34876 | Object* |
34877 | GCHeap::GetContainingObject (void *pInteriorPtr, bool fCollectedGenOnly) |
34878 | { |
34879 | uint8_t *o = (uint8_t*)pInteriorPtr; |
34880 | |
34881 | gc_heap* hp = gc_heap::heap_of (o); |
34882 | |
34883 | uint8_t* lowest = (fCollectedGenOnly ? hp->gc_low : hp->lowest_address); |
34884 | uint8_t* highest = (fCollectedGenOnly ? hp->gc_high : hp->highest_address); |
34885 | |
34886 | if (o >= lowest && o < highest) |
34887 | { |
34888 | o = hp->find_object (o, lowest); |
34889 | } |
34890 | else |
34891 | { |
34892 | o = NULL; |
34893 | } |
34894 | |
34895 | return (Object *)o; |
34896 | } |
34897 | |
34898 | BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p) |
34899 | { |
34900 | if (dd_new_allocation (dd) < 0) |
34901 | { |
34902 | return TRUE; |
34903 | } |
34904 | |
34905 | if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3)) |
34906 | { |
34907 | return TRUE; |
34908 | } |
34909 | |
34910 | return FALSE; |
34911 | } |
34912 | |
34913 | //---------------------------------------------------------------------------- |
34914 | // #GarbageCollector |
34915 | // |
34916 | // API to ensure that a complete new garbage collection takes place |
34917 | // |
34918 | HRESULT |
34919 | GCHeap::GarbageCollect (int generation, bool low_memory_p, int mode) |
34920 | { |
34921 | #if defined(BIT64) |
34922 | if (low_memory_p) |
34923 | { |
34924 | size_t total_allocated = 0; |
34925 | size_t total_desired = 0; |
34926 | #ifdef MULTIPLE_HEAPS |
34927 | int hn = 0; |
34928 | for (hn = 0; hn < gc_heap::n_heaps; hn++) |
34929 | { |
34930 | gc_heap* hp = gc_heap::g_heaps [hn]; |
34931 | total_desired += dd_desired_allocation (hp->dynamic_data_of (0)); |
34932 | total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))- |
34933 | dd_new_allocation (hp->dynamic_data_of (0)); |
34934 | } |
34935 | #else |
34936 | gc_heap* hp = pGenGCHeap; |
34937 | total_desired = dd_desired_allocation (hp->dynamic_data_of (0)); |
34938 | total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))- |
34939 | dd_new_allocation (hp->dynamic_data_of (0)); |
34940 | #endif //MULTIPLE_HEAPS |
34941 | |
34942 | if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent)) |
34943 | { |
34944 | dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning" , |
34945 | total_allocated, total_desired)); |
34946 | |
34947 | return S_OK; |
34948 | } |
34949 | } |
34950 | #endif // BIT64 |
34951 | |
34952 | #ifdef MULTIPLE_HEAPS |
34953 | gc_heap* hpt = gc_heap::g_heaps[0]; |
34954 | #else |
34955 | gc_heap* hpt = 0; |
34956 | #endif //MULTIPLE_HEAPS |
34957 | |
34958 | generation = (generation < 0) ? max_generation : min (generation, max_generation); |
34959 | dynamic_data* dd = hpt->dynamic_data_of (generation); |
34960 | |
34961 | #ifdef BACKGROUND_GC |
34962 | if (recursive_gc_sync::background_running_p()) |
34963 | { |
34964 | if ((mode == collection_optimized) || (mode & collection_non_blocking)) |
34965 | { |
34966 | return S_OK; |
34967 | } |
34968 | if (mode & collection_blocking) |
34969 | { |
34970 | pGenGCHeap->background_gc_wait(); |
34971 | if (mode & collection_optimized) |
34972 | { |
34973 | return S_OK; |
34974 | } |
34975 | } |
34976 | } |
34977 | #endif //BACKGROUND_GC |
34978 | |
34979 | if (mode & collection_optimized) |
34980 | { |
34981 | if (pGenGCHeap->gc_started) |
34982 | { |
34983 | return S_OK; |
34984 | } |
34985 | else |
34986 | { |
34987 | BOOL should_collect = FALSE; |
34988 | BOOL should_check_loh = (generation == max_generation); |
34989 | #ifdef MULTIPLE_HEAPS |
34990 | for (int i = 0; i < gc_heap::n_heaps; i++) |
34991 | { |
34992 | dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation); |
34993 | dynamic_data* dd2 = (should_check_loh ? |
34994 | (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) : |
34995 | 0); |
34996 | |
34997 | if (should_collect_optimized (dd1, low_memory_p)) |
34998 | { |
34999 | should_collect = TRUE; |
35000 | break; |
35001 | } |
35002 | if (dd2 && should_collect_optimized (dd2, low_memory_p)) |
35003 | { |
35004 | should_collect = TRUE; |
35005 | break; |
35006 | } |
35007 | } |
35008 | #else |
35009 | should_collect = should_collect_optimized (dd, low_memory_p); |
35010 | if (!should_collect && should_check_loh) |
35011 | { |
35012 | should_collect = |
35013 | should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p); |
35014 | } |
35015 | #endif //MULTIPLE_HEAPS |
35016 | if (!should_collect) |
35017 | { |
35018 | return S_OK; |
35019 | } |
35020 | } |
35021 | } |
35022 | |
35023 | size_t CollectionCountAtEntry = dd_collection_count (dd); |
35024 | size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking]; |
35025 | size_t CurrentCollectionCount = 0; |
35026 | |
35027 | retry: |
35028 | |
35029 | CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode); |
35030 | |
35031 | if ((mode & collection_blocking) && |
35032 | (generation == max_generation) && |
35033 | (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry)) |
35034 | { |
35035 | #ifdef BACKGROUND_GC |
35036 | if (recursive_gc_sync::background_running_p()) |
35037 | { |
35038 | pGenGCHeap->background_gc_wait(); |
35039 | } |
35040 | #endif //BACKGROUND_GC |
35041 | |
35042 | goto retry; |
35043 | } |
35044 | |
35045 | if (CollectionCountAtEntry == CurrentCollectionCount) |
35046 | { |
35047 | goto retry; |
35048 | } |
35049 | |
35050 | return S_OK; |
35051 | } |
35052 | |
35053 | size_t |
35054 | GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode) |
35055 | { |
35056 | int gen = (generation < 0) ? |
35057 | max_generation : min (generation, max_generation); |
35058 | |
35059 | gc_reason reason = reason_empty; |
35060 | |
35061 | if (low_memory_p) |
35062 | { |
35063 | if (mode & collection_blocking) |
35064 | { |
35065 | reason = reason_lowmemory_blocking; |
35066 | } |
35067 | else |
35068 | { |
35069 | reason = reason_lowmemory; |
35070 | } |
35071 | } |
35072 | else |
35073 | { |
35074 | reason = reason_induced; |
35075 | } |
35076 | |
35077 | if (reason == reason_induced) |
35078 | { |
35079 | if (mode & collection_compacting) |
35080 | { |
35081 | reason = reason_induced_compacting; |
35082 | } |
35083 | else if (mode & collection_non_blocking) |
35084 | { |
35085 | reason = reason_induced_noforce; |
35086 | } |
35087 | #ifdef STRESS_HEAP |
35088 | else if (mode & collection_gcstress) |
35089 | { |
35090 | reason = reason_gcstress; |
35091 | } |
35092 | #endif |
35093 | } |
35094 | |
35095 | return GarbageCollectGeneration (gen, reason); |
35096 | } |
35097 | |
35098 | void gc_heap::do_pre_gc() |
35099 | { |
35100 | STRESS_LOG_GC_STACK; |
35101 | |
35102 | #ifdef STRESS_LOG |
35103 | STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index), |
35104 | (uint32_t)settings.condemned_generation, |
35105 | (uint32_t)settings.reason); |
35106 | #endif // STRESS_LOG |
35107 | |
35108 | #ifdef MULTIPLE_HEAPS |
35109 | gc_heap* hp = g_heaps[0]; |
35110 | #else |
35111 | gc_heap* hp = 0; |
35112 | #endif //MULTIPLE_HEAPS |
35113 | |
35114 | #ifdef BACKGROUND_GC |
35115 | settings.b_state = hp->current_bgc_state; |
35116 | #endif //BACKGROUND_GC |
35117 | |
35118 | #ifdef BACKGROUND_GC |
35119 | dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)" , |
35120 | VolatileLoad(&settings.gc_index), |
35121 | dd_collection_count (hp->dynamic_data_of (0)), |
35122 | settings.condemned_generation, |
35123 | (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC" )), |
35124 | settings.b_state)); |
35125 | #else |
35126 | dprintf (1, ("*GC* %d(gen0:%d)(%d)" , |
35127 | VolatileLoad(&settings.gc_index), |
35128 | dd_collection_count(hp->dynamic_data_of(0)), |
35129 | settings.condemned_generation)); |
35130 | #endif //BACKGROUND_GC |
35131 | |
35132 | // TODO: this can happen...it's because of the way we are calling |
35133 | // do_pre_gc, will fix later. |
35134 | //if (last_gc_index > VolatileLoad(&settings.gc_index)) |
35135 | //{ |
35136 | // FATAL_GC_ERROR(); |
35137 | //} |
35138 | |
35139 | last_gc_index = VolatileLoad(&settings.gc_index); |
35140 | GCHeap::UpdatePreGCCounters(); |
35141 | |
35142 | if (settings.concurrent) |
35143 | { |
35144 | #ifdef BACKGROUND_GC |
35145 | full_gc_counts[gc_type_background]++; |
35146 | #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK) |
35147 | GCHeap::gc_stress_fgcs_in_bgc = 0; |
35148 | #endif // STRESS_HEAP && !FEATURE_REDHAWK |
35149 | #endif // BACKGROUND_GC |
35150 | } |
35151 | else |
35152 | { |
35153 | if (settings.condemned_generation == max_generation) |
35154 | { |
35155 | full_gc_counts[gc_type_blocking]++; |
35156 | } |
35157 | else |
35158 | { |
35159 | #ifdef BACKGROUND_GC |
35160 | if (settings.background_p) |
35161 | { |
35162 | ephemeral_fgc_counts[settings.condemned_generation]++; |
35163 | } |
35164 | #endif //BACKGROUND_GC |
35165 | } |
35166 | } |
35167 | |
35168 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
35169 | if (g_fEnableAppDomainMonitoring) |
35170 | { |
35171 | GCToEEInterface::ResetTotalSurvivedBytes(); |
35172 | } |
35173 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
35174 | } |
35175 | |
35176 | #ifdef GC_CONFIG_DRIVEN |
35177 | void gc_heap::record_interesting_info_per_heap() |
35178 | { |
35179 | // datapoints are always from the last blocking GC so don't record again |
35180 | // for BGCs. |
35181 | if (!(settings.concurrent)) |
35182 | { |
35183 | for (int i = 0; i < max_idp_count; i++) |
35184 | { |
35185 | interesting_data_per_heap[i] += interesting_data_per_gc[i]; |
35186 | } |
35187 | } |
35188 | |
35189 | int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact); |
35190 | if (compact_reason >= 0) |
35191 | (compact_reasons_per_heap[compact_reason])++; |
35192 | int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand); |
35193 | if (expand_mechanism >= 0) |
35194 | (expand_mechanisms_per_heap[expand_mechanism])++; |
35195 | |
35196 | for (int i = 0; i < max_gc_mechanism_bits_count; i++) |
35197 | { |
35198 | if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i)) |
35199 | (interesting_mechanism_bits_per_heap[i])++; |
35200 | } |
35201 | |
35202 | // h# | GC | gen | C | EX | NF | BF | ML | DM || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP | |
35203 | cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |" , |
35204 | heap_number, |
35205 | (size_t)settings.gc_index, |
35206 | settings.condemned_generation, |
35207 | // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs. |
35208 | (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W" ) : "" ), // compaction |
35209 | ((expand_mechanism >= 0)? "X" : "" ), // EX |
35210 | ((expand_mechanism == expand_reuse_normal) ? "X" : "" ), // NF |
35211 | ((expand_mechanism == expand_reuse_bestfit) ? "X" : "" ), // BF |
35212 | (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : "" ), // ML |
35213 | (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : "" ), // DM |
35214 | interesting_data_per_gc[idp_pre_short], |
35215 | interesting_data_per_gc[idp_post_short], |
35216 | interesting_data_per_gc[idp_merged_pin], |
35217 | interesting_data_per_gc[idp_converted_pin], |
35218 | interesting_data_per_gc[idp_pre_pin], |
35219 | interesting_data_per_gc[idp_post_pin], |
35220 | interesting_data_per_gc[idp_pre_and_post_pin], |
35221 | interesting_data_per_gc[idp_pre_short_padded], |
35222 | interesting_data_per_gc[idp_post_short_padded])); |
35223 | } |
35224 | |
35225 | void gc_heap::record_global_mechanisms() |
35226 | { |
35227 | for (int i = 0; i < max_global_mechanisms_count; i++) |
35228 | { |
35229 | if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i)) |
35230 | { |
35231 | ::record_global_mechanism (i); |
35232 | } |
35233 | } |
35234 | } |
35235 | |
35236 | BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p) |
35237 | { |
35238 | if (!compact_ratio) |
35239 | return (!compact_p); |
35240 | |
35241 | size_t compact_count = compact_or_sweep_gcs[0]; |
35242 | size_t sweep_count = compact_or_sweep_gcs[1]; |
35243 | |
35244 | size_t total_count = compact_count + sweep_count; |
35245 | BOOL should_compact = compact_p; |
35246 | if (total_count > 3) |
35247 | { |
35248 | if (compact_p) |
35249 | { |
35250 | int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1)); |
35251 | if (temp_ratio > compact_ratio) |
35252 | { |
35253 | // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n", |
35254 | // (compact_count + 1), (total_count + 1), temp_ratio)); |
35255 | should_compact = FALSE; |
35256 | } |
35257 | } |
35258 | else |
35259 | { |
35260 | int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1)); |
35261 | if (temp_ratio > (100 - compact_ratio)) |
35262 | { |
35263 | // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n", |
35264 | // (sweep_count + 1), (total_count + 1), temp_ratio)); |
35265 | should_compact = TRUE; |
35266 | } |
35267 | } |
35268 | } |
35269 | |
35270 | return !should_compact; |
35271 | } |
35272 | #endif //GC_CONFIG_DRIVEN |
35273 | |
35274 | bool gc_heap::is_pm_ratio_exceeded() |
35275 | { |
35276 | size_t maxgen_frag = 0; |
35277 | size_t maxgen_size = 0; |
35278 | size_t total_heap_size = get_total_heap_size(); |
35279 | |
35280 | #ifdef MULTIPLE_HEAPS |
35281 | for (int i = 0; i < gc_heap::n_heaps; i++) |
35282 | { |
35283 | gc_heap* hp = gc_heap::g_heaps[i]; |
35284 | #else //MULTIPLE_HEAPS |
35285 | { |
35286 | gc_heap* hp = pGenGCHeap; |
35287 | #endif //MULTIPLE_HEAPS |
35288 | |
35289 | maxgen_frag += dd_fragmentation (hp->dynamic_data_of (max_generation)); |
35290 | maxgen_size += hp->generation_size (max_generation); |
35291 | } |
35292 | |
35293 | double maxgen_ratio = (double)maxgen_size / (double)total_heap_size; |
35294 | double maxgen_frag_ratio = (double)maxgen_frag / (double)maxgen_size; |
35295 | dprintf (GTC_LOG, ("maxgen %Id(%d%% total heap), frag: %Id (%d%% maxgen)" , |
35296 | maxgen_size, (int)(maxgen_ratio * 100.0), |
35297 | maxgen_frag, (int)(maxgen_frag_ratio * 100.0))); |
35298 | |
35299 | bool maxgen_highfrag_p = ((maxgen_ratio > 0.5) && (maxgen_frag_ratio > 0.1)); |
35300 | |
35301 | // We need to adjust elevation here because if there's enough fragmentation it's not |
35302 | // unproductive. |
35303 | if (maxgen_highfrag_p) |
35304 | { |
35305 | settings.should_lock_elevation = FALSE; |
35306 | dprintf (GTC_LOG, ("high frag gen2, turn off elevation" )); |
35307 | } |
35308 | |
35309 | return maxgen_highfrag_p; |
35310 | } |
35311 | |
35312 | void gc_heap::do_post_gc() |
35313 | { |
35314 | if (!settings.concurrent) |
35315 | { |
35316 | initGCShadow(); |
35317 | } |
35318 | |
35319 | #ifdef TRACE_GC |
35320 | #ifdef COUNT_CYCLES |
35321 | AllocStart = GetCycleCount32(); |
35322 | #else |
35323 | AllocStart = clock(); |
35324 | #endif //COUNT_CYCLES |
35325 | #endif //TRACE_GC |
35326 | |
35327 | #ifdef MULTIPLE_HEAPS |
35328 | gc_heap* hp = g_heaps[0]; |
35329 | #else |
35330 | gc_heap* hp = 0; |
35331 | #endif //MULTIPLE_HEAPS |
35332 | |
35333 | GCToEEInterface::GcDone(settings.condemned_generation); |
35334 | |
35335 | GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index), |
35336 | (uint32_t)settings.condemned_generation, |
35337 | (uint32_t)settings.reason, |
35338 | !!settings.concurrent); |
35339 | |
35340 | //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", |
35341 | dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)" , |
35342 | VolatileLoad(&settings.gc_index), |
35343 | dd_collection_count(hp->dynamic_data_of(0)), |
35344 | settings.condemned_generation, |
35345 | (settings.concurrent ? "BGC" : "GC" ))); |
35346 | |
35347 | if (settings.exit_memory_load != 0) |
35348 | last_gc_memory_load = settings.exit_memory_load; |
35349 | else if (settings.entry_memory_load != 0) |
35350 | last_gc_memory_load = settings.entry_memory_load; |
35351 | |
35352 | last_gc_heap_size = get_total_heap_size(); |
35353 | last_gc_fragmentation = get_total_fragmentation(); |
35354 | |
35355 | // Note we only do this at the end of full blocking GCs because we do not want |
35356 | // to turn on this provisional mode during the middle of a BGC. |
35357 | if ((settings.condemned_generation == max_generation) && (!settings.concurrent)) |
35358 | { |
35359 | if (pm_stress_on) |
35360 | { |
35361 | size_t full_compacting_gc_count = full_gc_counts[gc_type_compacting]; |
35362 | if (provisional_mode_triggered) |
35363 | { |
35364 | uint64_t r = gc_rand::get_rand(10); |
35365 | if ((full_compacting_gc_count - provisional_triggered_gc_count) >= r) |
35366 | { |
35367 | provisional_mode_triggered = false; |
35368 | provisional_off_gc_count = full_compacting_gc_count; |
35369 | dprintf (GTC_LOG, ("%Id NGC2s when turned on, %Id NGCs since(%Id)" , |
35370 | provisional_triggered_gc_count, (full_compacting_gc_count - provisional_triggered_gc_count), |
35371 | num_provisional_triggered)); |
35372 | } |
35373 | } |
35374 | else |
35375 | { |
35376 | uint64_t r = gc_rand::get_rand(5); |
35377 | if ((full_compacting_gc_count - provisional_off_gc_count) >= r) |
35378 | { |
35379 | provisional_mode_triggered = true; |
35380 | provisional_triggered_gc_count = full_compacting_gc_count; |
35381 | num_provisional_triggered++; |
35382 | dprintf (GTC_LOG, ("%Id NGC2s when turned off, %Id NGCs since(%Id)" , |
35383 | provisional_off_gc_count, (full_compacting_gc_count - provisional_off_gc_count), |
35384 | num_provisional_triggered)); |
35385 | } |
35386 | } |
35387 | } |
35388 | else |
35389 | { |
35390 | if (provisional_mode_triggered) |
35391 | { |
35392 | if ((settings.entry_memory_load < high_memory_load_th) || |
35393 | !is_pm_ratio_exceeded()) |
35394 | { |
35395 | dprintf (GTC_LOG, ("turning off PM" )); |
35396 | provisional_mode_triggered = false; |
35397 | } |
35398 | } |
35399 | else if ((settings.entry_memory_load >= high_memory_load_th) && is_pm_ratio_exceeded()) |
35400 | { |
35401 | dprintf (GTC_LOG, ("highmem && highfrag - turning on PM" )); |
35402 | provisional_mode_triggered = true; |
35403 | num_provisional_triggered++; |
35404 | } |
35405 | } |
35406 | } |
35407 | |
35408 | GCHeap::UpdatePostGCCounters(); |
35409 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
35410 | //if (g_fEnableARM) |
35411 | //{ |
35412 | // SystemDomain::GetADSurvivedBytes(); |
35413 | //} |
35414 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
35415 | |
35416 | #ifdef STRESS_LOG |
35417 | STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index), |
35418 | (uint32_t)settings.condemned_generation, |
35419 | (uint32_t)settings.reason); |
35420 | #endif // STRESS_LOG |
35421 | |
35422 | #ifdef GC_CONFIG_DRIVEN |
35423 | if (!settings.concurrent) |
35424 | { |
35425 | if (settings.compaction) |
35426 | (compact_or_sweep_gcs[0])++; |
35427 | else |
35428 | (compact_or_sweep_gcs[1])++; |
35429 | } |
35430 | |
35431 | #ifdef MULTIPLE_HEAPS |
35432 | for (int i = 0; i < n_heaps; i++) |
35433 | g_heaps[i]->record_interesting_info_per_heap(); |
35434 | #else |
35435 | record_interesting_info_per_heap(); |
35436 | #endif //MULTIPLE_HEAPS |
35437 | record_global_mechanisms(); |
35438 | #endif //GC_CONFIG_DRIVEN |
35439 | } |
35440 | |
35441 | unsigned GCHeap::GetGcCount() |
35442 | { |
35443 | return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index); |
35444 | } |
35445 | |
35446 | size_t |
35447 | GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason) |
35448 | { |
35449 | dprintf (2, ("triggered a GC!" )); |
35450 | |
35451 | #ifdef MULTIPLE_HEAPS |
35452 | gc_heap* hpt = gc_heap::g_heaps[0]; |
35453 | #else |
35454 | gc_heap* hpt = 0; |
35455 | #endif //MULTIPLE_HEAPS |
35456 | bool cooperative_mode = true; |
35457 | dynamic_data* dd = hpt->dynamic_data_of (gen); |
35458 | size_t localCount = dd_collection_count (dd); |
35459 | |
35460 | enter_spin_lock (&gc_heap::gc_lock); |
35461 | dprintf (SPINLOCK_LOG, ("GC Egc" )); |
35462 | ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock); |
35463 | |
35464 | //don't trigger another GC if one was already in progress |
35465 | //while waiting for the lock |
35466 | { |
35467 | size_t col_count = dd_collection_count (dd); |
35468 | |
35469 | if (localCount != col_count) |
35470 | { |
35471 | #ifdef SYNCHRONIZATION_STATS |
35472 | gc_lock_contended++; |
35473 | #endif //SYNCHRONIZATION_STATS |
35474 | dprintf (SPINLOCK_LOG, ("no need GC Lgc" )); |
35475 | leave_spin_lock (&gc_heap::gc_lock); |
35476 | |
35477 | // We don't need to release msl here 'cause this means a GC |
35478 | // has happened and would have release all msl's. |
35479 | return col_count; |
35480 | } |
35481 | } |
35482 | |
35483 | #ifdef COUNT_CYCLES |
35484 | int gc_start = GetCycleCount32(); |
35485 | #endif //COUNT_CYCLES |
35486 | |
35487 | #ifdef TRACE_GC |
35488 | #ifdef COUNT_CYCLES |
35489 | AllocDuration += GetCycleCount32() - AllocStart; |
35490 | #else |
35491 | AllocDuration += clock() - AllocStart; |
35492 | #endif //COUNT_CYCLES |
35493 | #endif //TRACE_GC |
35494 | |
35495 | gc_heap::g_low_memory_status = (reason == reason_lowmemory) || |
35496 | (reason == reason_lowmemory_blocking) || |
35497 | (gc_heap::latency_level == latency_level_memory_footprint); |
35498 | |
35499 | gc_trigger_reason = reason; |
35500 | |
35501 | #ifdef MULTIPLE_HEAPS |
35502 | for (int i = 0; i < gc_heap::n_heaps; i++) |
35503 | { |
35504 | gc_heap::g_heaps[i]->reset_gc_done(); |
35505 | } |
35506 | #else |
35507 | gc_heap::reset_gc_done(); |
35508 | #endif //MULTIPLE_HEAPS |
35509 | |
35510 | gc_heap::gc_started = TRUE; |
35511 | |
35512 | { |
35513 | init_sync_log_stats(); |
35514 | |
35515 | #ifndef MULTIPLE_HEAPS |
35516 | cooperative_mode = gc_heap::enable_preemptive (); |
35517 | |
35518 | dprintf (2, ("Suspending EE" )); |
35519 | BEGIN_TIMING(suspend_ee_during_log); |
35520 | GCToEEInterface::SuspendEE(SUSPEND_FOR_GC); |
35521 | END_TIMING(suspend_ee_during_log); |
35522 | gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc(); |
35523 | gc_heap::disable_preemptive (cooperative_mode); |
35524 | if (gc_heap::proceed_with_gc_p) |
35525 | pGenGCHeap->settings.init_mechanisms(); |
35526 | else |
35527 | gc_heap::update_collection_counts_for_no_gc(); |
35528 | |
35529 | #endif //!MULTIPLE_HEAPS |
35530 | } |
35531 | |
35532 | // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0)); |
35533 | |
35534 | #ifdef TRACE_GC |
35535 | #ifdef COUNT_CYCLES |
35536 | unsigned start; |
35537 | unsigned finish; |
35538 | start = GetCycleCount32(); |
35539 | #else |
35540 | clock_t start; |
35541 | clock_t finish; |
35542 | start = clock(); |
35543 | #endif //COUNT_CYCLES |
35544 | PromotedObjectCount = 0; |
35545 | #endif //TRACE_GC |
35546 | |
35547 | unsigned int condemned_generation_number = gen; |
35548 | |
35549 | // We want to get a stack from the user thread that triggered the GC |
35550 | // instead of on the GC thread which is the case for Server GC. |
35551 | // But we are doing it for Workstation GC as well to be uniform. |
35552 | FIRE_EVENT(GCTriggered, static_cast<uint32_t>(reason)); |
35553 | |
35554 | #ifdef MULTIPLE_HEAPS |
35555 | GcCondemnedGeneration = condemned_generation_number; |
35556 | |
35557 | cooperative_mode = gc_heap::enable_preemptive (); |
35558 | |
35559 | BEGIN_TIMING(gc_during_log); |
35560 | gc_heap::ee_suspend_event.Set(); |
35561 | gc_heap::wait_for_gc_done(); |
35562 | END_TIMING(gc_during_log); |
35563 | |
35564 | gc_heap::disable_preemptive (cooperative_mode); |
35565 | |
35566 | condemned_generation_number = GcCondemnedGeneration; |
35567 | #else |
35568 | if (gc_heap::proceed_with_gc_p) |
35569 | { |
35570 | BEGIN_TIMING(gc_during_log); |
35571 | pGenGCHeap->garbage_collect (condemned_generation_number); |
35572 | if (gc_heap::pm_trigger_full_gc) |
35573 | { |
35574 | pGenGCHeap->garbage_collect_pm_full_gc(); |
35575 | } |
35576 | END_TIMING(gc_during_log); |
35577 | } |
35578 | #endif //MULTIPLE_HEAPS |
35579 | |
35580 | #ifdef TRACE_GC |
35581 | #ifdef COUNT_CYCLES |
35582 | finish = GetCycleCount32(); |
35583 | #else |
35584 | finish = clock(); |
35585 | #endif //COUNT_CYCLES |
35586 | GcDuration += finish - start; |
35587 | dprintf (3, |
35588 | ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d" , |
35589 | VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number, |
35590 | finish - start, GcDuration, |
35591 | AllocCount ? (AllocDuration / AllocCount) : 0, |
35592 | AllocSmallCount, AllocBigCount)); |
35593 | AllocCount = 0; |
35594 | AllocDuration = 0; |
35595 | #endif // TRACE_GC |
35596 | |
35597 | #ifdef BACKGROUND_GC |
35598 | // We are deciding whether we should fire the alloc wait end event here |
35599 | // because in begin_foreground we could be calling end_foreground |
35600 | // if we need to retry. |
35601 | if (gc_heap::alloc_wait_event_p) |
35602 | { |
35603 | hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc); |
35604 | gc_heap::alloc_wait_event_p = FALSE; |
35605 | } |
35606 | #endif //BACKGROUND_GC |
35607 | |
35608 | #ifndef MULTIPLE_HEAPS |
35609 | #ifdef BACKGROUND_GC |
35610 | if (!gc_heap::dont_restart_ee_p) |
35611 | { |
35612 | #endif //BACKGROUND_GC |
35613 | BEGIN_TIMING(restart_ee_during_log); |
35614 | GCToEEInterface::RestartEE(TRUE); |
35615 | END_TIMING(restart_ee_during_log); |
35616 | #ifdef BACKGROUND_GC |
35617 | } |
35618 | #endif //BACKGROUND_GC |
35619 | #endif //!MULTIPLE_HEAPS |
35620 | |
35621 | #ifdef COUNT_CYCLES |
35622 | printf ("GC: %d Time: %d\n" , GcCondemnedGeneration, |
35623 | GetCycleCount32() - gc_start); |
35624 | #endif //COUNT_CYCLES |
35625 | |
35626 | #ifndef MULTIPLE_HEAPS |
35627 | process_sync_log_stats(); |
35628 | gc_heap::gc_started = FALSE; |
35629 | gc_heap::set_gc_done(); |
35630 | dprintf (SPINLOCK_LOG, ("GC Lgc" )); |
35631 | leave_spin_lock (&gc_heap::gc_lock); |
35632 | #endif //!MULTIPLE_HEAPS |
35633 | |
35634 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
35635 | GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers); |
35636 | #endif // FEATURE_PREMORTEM_FINALIZATION |
35637 | |
35638 | return dd_collection_count (dd); |
35639 | } |
35640 | |
35641 | size_t GCHeap::GetTotalBytesInUse () |
35642 | { |
35643 | #ifdef MULTIPLE_HEAPS |
35644 | //enumarate all the heaps and get their size. |
35645 | size_t tot_size = 0; |
35646 | for (int i = 0; i < gc_heap::n_heaps; i++) |
35647 | { |
35648 | GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap; |
35649 | tot_size += Hp->ApproxTotalBytesInUse (FALSE); |
35650 | } |
35651 | return tot_size; |
35652 | #else |
35653 | return ApproxTotalBytesInUse (); |
35654 | #endif //MULTIPLE_HEAPS |
35655 | } |
35656 | |
35657 | int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count) |
35658 | { |
35659 | if (get_bgc_fgc_count != 0) |
35660 | { |
35661 | #ifdef BACKGROUND_GC |
35662 | if (generation == max_generation) |
35663 | { |
35664 | return (int)(gc_heap::full_gc_counts[gc_type_background]); |
35665 | } |
35666 | else |
35667 | { |
35668 | return (int)(gc_heap::ephemeral_fgc_counts[generation]); |
35669 | } |
35670 | #else |
35671 | return 0; |
35672 | #endif //BACKGROUND_GC |
35673 | } |
35674 | |
35675 | #ifdef MULTIPLE_HEAPS |
35676 | gc_heap* hp = gc_heap::g_heaps [0]; |
35677 | #else //MULTIPLE_HEAPS |
35678 | gc_heap* hp = pGenGCHeap; |
35679 | #endif //MULTIPLE_HEAPS |
35680 | if (generation > max_generation) |
35681 | return 0; |
35682 | else |
35683 | return (int)dd_collection_count (hp->dynamic_data_of (generation)); |
35684 | } |
35685 | |
35686 | size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only) |
35687 | { |
35688 | size_t totsize = 0; |
35689 | //GCTODO |
35690 | //ASSERT(InMustComplete()); |
35691 | enter_spin_lock (&pGenGCHeap->gc_lock); |
35692 | |
35693 | heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0)); |
35694 | // Get small block heap size info |
35695 | totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg)); |
35696 | heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation)); |
35697 | while (seg1 != eph_seg) |
35698 | { |
35699 | totsize += heap_segment_allocated (seg1) - |
35700 | heap_segment_mem (seg1); |
35701 | seg1 = heap_segment_next (seg1); |
35702 | } |
35703 | |
35704 | //discount the fragmentation |
35705 | for (int i = 0; i <= max_generation; i++) |
35706 | { |
35707 | generation* gen = pGenGCHeap->generation_of (i); |
35708 | totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen)); |
35709 | } |
35710 | |
35711 | if (!small_heap_only) |
35712 | { |
35713 | heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1)); |
35714 | |
35715 | while (seg2 != 0) |
35716 | { |
35717 | totsize += heap_segment_allocated (seg2) - |
35718 | heap_segment_mem (seg2); |
35719 | seg2 = heap_segment_next (seg2); |
35720 | } |
35721 | |
35722 | //discount the fragmentation |
35723 | generation* loh_gen = pGenGCHeap->generation_of (max_generation+1); |
35724 | size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen); |
35725 | totsize -= frag; |
35726 | } |
35727 | leave_spin_lock (&pGenGCHeap->gc_lock); |
35728 | return totsize; |
35729 | } |
35730 | |
35731 | #ifdef MULTIPLE_HEAPS |
35732 | void GCHeap::AssignHeap (alloc_context* acontext) |
35733 | { |
35734 | // Assign heap based on processor |
35735 | acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0))); |
35736 | acontext->set_home_heap(acontext->get_alloc_heap()); |
35737 | } |
35738 | GCHeap* GCHeap::GetHeap (int n) |
35739 | { |
35740 | assert (n < gc_heap::n_heaps); |
35741 | return gc_heap::g_heaps [n]->vm_heap; |
35742 | } |
35743 | #endif //MULTIPLE_HEAPS |
35744 | |
35745 | bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number) |
35746 | { |
35747 | alloc_context* acontext = static_cast<alloc_context*>(context); |
35748 | #ifdef MULTIPLE_HEAPS |
35749 | return ((acontext->get_home_heap() == GetHeap(thread_number)) || |
35750 | ((acontext->get_home_heap() == 0) && (thread_number == 0))); |
35751 | #else |
35752 | UNREFERENCED_PARAMETER(acontext); |
35753 | UNREFERENCED_PARAMETER(thread_number); |
35754 | return true; |
35755 | #endif //MULTIPLE_HEAPS |
35756 | } |
35757 | |
35758 | // Returns the number of processors required to trigger the use of thread based allocation contexts |
35759 | int GCHeap::GetNumberOfHeaps () |
35760 | { |
35761 | #ifdef MULTIPLE_HEAPS |
35762 | return gc_heap::n_heaps; |
35763 | #else |
35764 | return 1; |
35765 | #endif //MULTIPLE_HEAPS |
35766 | } |
35767 | |
35768 | /* |
35769 | in this way we spend extra time cycling through all the heaps while create the handle |
35770 | it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number) |
35771 | */ |
35772 | int GCHeap::GetHomeHeapNumber () |
35773 | { |
35774 | #ifdef MULTIPLE_HEAPS |
35775 | gc_alloc_context* ctx = GCToEEInterface::GetAllocContext(); |
35776 | if (!ctx) |
35777 | { |
35778 | return 0; |
35779 | } |
35780 | |
35781 | GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap(); |
35782 | return (hp ? hp->pGenGCHeap->heap_number : 0); |
35783 | #else |
35784 | return 0; |
35785 | #endif //MULTIPLE_HEAPS |
35786 | } |
35787 | |
35788 | unsigned int GCHeap::GetCondemnedGeneration() |
35789 | { |
35790 | return gc_heap::settings.condemned_generation; |
35791 | } |
35792 | |
35793 | void GCHeap::GetMemoryInfo(uint32_t* highMemLoadThreshold, |
35794 | uint64_t* totalPhysicalMem, |
35795 | uint32_t* lastRecordedMemLoad, |
35796 | size_t* lastRecordedHeapSize, |
35797 | size_t* lastRecordedFragmentation) |
35798 | { |
35799 | *highMemLoadThreshold = gc_heap::high_memory_load_th; |
35800 | *totalPhysicalMem = gc_heap::total_physical_mem; |
35801 | *lastRecordedMemLoad = gc_heap::last_gc_memory_load; |
35802 | *lastRecordedHeapSize = gc_heap::last_gc_heap_size; |
35803 | *lastRecordedFragmentation = gc_heap::last_gc_fragmentation; |
35804 | } |
35805 | |
35806 | int GCHeap::GetGcLatencyMode() |
35807 | { |
35808 | return (int)(pGenGCHeap->settings.pause_mode); |
35809 | } |
35810 | |
35811 | int GCHeap::SetGcLatencyMode (int newLatencyMode) |
35812 | { |
35813 | if (gc_heap::settings.pause_mode == pause_no_gc) |
35814 | return (int)set_pause_mode_no_gc; |
35815 | |
35816 | gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode; |
35817 | |
35818 | if (new_mode == pause_low_latency) |
35819 | { |
35820 | #ifndef MULTIPLE_HEAPS |
35821 | pGenGCHeap->settings.pause_mode = new_mode; |
35822 | #endif //!MULTIPLE_HEAPS |
35823 | } |
35824 | else if (new_mode == pause_sustained_low_latency) |
35825 | { |
35826 | #ifdef BACKGROUND_GC |
35827 | if (gc_heap::gc_can_use_concurrent) |
35828 | { |
35829 | pGenGCHeap->settings.pause_mode = new_mode; |
35830 | } |
35831 | #endif //BACKGROUND_GC |
35832 | } |
35833 | else |
35834 | { |
35835 | pGenGCHeap->settings.pause_mode = new_mode; |
35836 | } |
35837 | |
35838 | #ifdef BACKGROUND_GC |
35839 | if (recursive_gc_sync::background_running_p()) |
35840 | { |
35841 | // If we get here, it means we are doing an FGC. If the pause |
35842 | // mode was altered we will need to save it in the BGC settings. |
35843 | if (gc_heap::saved_bgc_settings.pause_mode != new_mode) |
35844 | { |
35845 | gc_heap::saved_bgc_settings.pause_mode = new_mode; |
35846 | } |
35847 | } |
35848 | #endif //BACKGROUND_GC |
35849 | |
35850 | return (int)set_pause_mode_success; |
35851 | } |
35852 | |
35853 | int GCHeap::GetLOHCompactionMode() |
35854 | { |
35855 | return pGenGCHeap->loh_compaction_mode; |
35856 | } |
35857 | |
35858 | void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode) |
35859 | { |
35860 | #ifdef FEATURE_LOH_COMPACTION |
35861 | pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode; |
35862 | #endif //FEATURE_LOH_COMPACTION |
35863 | } |
35864 | |
35865 | bool GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage, |
35866 | uint32_t lohPercentage) |
35867 | { |
35868 | #ifdef MULTIPLE_HEAPS |
35869 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
35870 | { |
35871 | gc_heap* hp = gc_heap::g_heaps [hn]; |
35872 | hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0)); |
35873 | } |
35874 | #else //MULTIPLE_HEAPS |
35875 | pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0)); |
35876 | #endif //MULTIPLE_HEAPS |
35877 | |
35878 | pGenGCHeap->full_gc_approach_event.Reset(); |
35879 | pGenGCHeap->full_gc_end_event.Reset(); |
35880 | pGenGCHeap->full_gc_approach_event_set = false; |
35881 | |
35882 | pGenGCHeap->fgn_maxgen_percent = gen2Percentage; |
35883 | pGenGCHeap->fgn_loh_percent = lohPercentage; |
35884 | |
35885 | return TRUE; |
35886 | } |
35887 | |
35888 | bool GCHeap::CancelFullGCNotification() |
35889 | { |
35890 | pGenGCHeap->fgn_maxgen_percent = 0; |
35891 | pGenGCHeap->fgn_loh_percent = 0; |
35892 | |
35893 | pGenGCHeap->full_gc_approach_event.Set(); |
35894 | pGenGCHeap->full_gc_end_event.Set(); |
35895 | |
35896 | return TRUE; |
35897 | } |
35898 | |
35899 | int GCHeap::WaitForFullGCApproach(int millisecondsTimeout) |
35900 | { |
35901 | dprintf (2, ("WFGA: Begin wait" )); |
35902 | int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout); |
35903 | dprintf (2, ("WFGA: End wait" )); |
35904 | return result; |
35905 | } |
35906 | |
35907 | int GCHeap::WaitForFullGCComplete(int millisecondsTimeout) |
35908 | { |
35909 | dprintf (2, ("WFGE: Begin wait" )); |
35910 | int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout); |
35911 | dprintf (2, ("WFGE: End wait" )); |
35912 | return result; |
35913 | } |
35914 | |
35915 | int GCHeap::StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC) |
35916 | { |
35917 | NoGCRegionLockHolder lh; |
35918 | |
35919 | dprintf (1, ("begin no gc called" )); |
35920 | start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC); |
35921 | if (status == start_no_gc_success) |
35922 | { |
35923 | GarbageCollect (max_generation); |
35924 | status = gc_heap::get_start_no_gc_region_status(); |
35925 | } |
35926 | |
35927 | if (status != start_no_gc_success) |
35928 | gc_heap::handle_failure_for_no_gc(); |
35929 | |
35930 | return (int)status; |
35931 | } |
35932 | |
35933 | int GCHeap::EndNoGCRegion() |
35934 | { |
35935 | NoGCRegionLockHolder lh; |
35936 | return (int)gc_heap::end_no_gc_region(); |
35937 | } |
35938 | |
35939 | void GCHeap::PublishObject (uint8_t* Obj) |
35940 | { |
35941 | #ifdef BACKGROUND_GC |
35942 | gc_heap* hp = gc_heap::heap_of (Obj); |
35943 | hp->bgc_alloc_lock->loh_alloc_done (Obj); |
35944 | hp->bgc_untrack_loh_alloc(); |
35945 | #endif //BACKGROUND_GC |
35946 | } |
35947 | |
35948 | // The spec for this one isn't clear. This function |
35949 | // returns the size that can be allocated without |
35950 | // triggering a GC of any kind. |
35951 | size_t GCHeap::ApproxFreeBytes() |
35952 | { |
35953 | //GCTODO |
35954 | //ASSERT(InMustComplete()); |
35955 | enter_spin_lock (&pGenGCHeap->gc_lock); |
35956 | |
35957 | generation* gen = pGenGCHeap->generation_of (0); |
35958 | size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen); |
35959 | |
35960 | leave_spin_lock (&pGenGCHeap->gc_lock); |
35961 | |
35962 | return res; |
35963 | } |
35964 | |
35965 | HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters) |
35966 | { |
35967 | if ((gen < 0) || (gen > max_generation)) |
35968 | return E_FAIL; |
35969 | #ifdef MULTIPLE_HEAPS |
35970 | counters->current_size = 0; |
35971 | counters->promoted_size = 0; |
35972 | counters->collection_count = 0; |
35973 | |
35974 | //enumarate all the heaps and get their counters. |
35975 | for (int i = 0; i < gc_heap::n_heaps; i++) |
35976 | { |
35977 | dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen); |
35978 | |
35979 | counters->current_size += dd_current_size (dd); |
35980 | counters->promoted_size += dd_promoted_size (dd); |
35981 | if (i == 0) |
35982 | counters->collection_count += dd_collection_count (dd); |
35983 | } |
35984 | #else |
35985 | dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen); |
35986 | counters->current_size = dd_current_size (dd); |
35987 | counters->promoted_size = dd_promoted_size (dd); |
35988 | counters->collection_count = dd_collection_count (dd); |
35989 | #endif //MULTIPLE_HEAPS |
35990 | return S_OK; |
35991 | } |
35992 | |
35993 | // Get the segment size to use, making sure it conforms. |
35994 | size_t GCHeap::GetValidSegmentSize(bool large_seg) |
35995 | { |
35996 | return get_valid_segment_size (large_seg); |
35997 | } |
35998 | |
35999 | // Get the max gen0 heap size, making sure it conforms. |
36000 | size_t GCHeap::GetValidGen0MaxSize(size_t seg_size) |
36001 | { |
36002 | size_t gen0size = static_cast<size_t>(GCConfig::GetGen0Size()); |
36003 | |
36004 | if ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size)) |
36005 | { |
36006 | #ifdef SERVER_GC |
36007 | // performance data seems to indicate halving the size results |
36008 | // in optimal perf. Ask for adjusted gen0 size. |
36009 | gen0size = max(GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),(256*1024)); |
36010 | |
36011 | // if gen0 size is too large given the available memory, reduce it. |
36012 | // Get true cache size, as we don't want to reduce below this. |
36013 | size_t trueSize = max(GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE),(256*1024)); |
36014 | dprintf (2, ("cache: %Id-%Id, cpu: %Id" , |
36015 | GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE), |
36016 | GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE))); |
36017 | |
36018 | int n_heaps = gc_heap::n_heaps; |
36019 | #else //SERVER_GC |
36020 | size_t trueSize = GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE); |
36021 | gen0size = max((4*trueSize/5),(256*1024)); |
36022 | trueSize = max(trueSize, (256*1024)); |
36023 | int n_heaps = 1; |
36024 | #endif //SERVER_GC |
36025 | |
36026 | // if the total min GC across heaps will exceed 1/6th of available memory, |
36027 | // then reduce the min GC size until it either fits or has been reduced to cache size. |
36028 | while ((gen0size * n_heaps) > GCToOSInterface::GetPhysicalMemoryLimit() / 6) |
36029 | { |
36030 | gen0size = gen0size / 2; |
36031 | if (gen0size <= trueSize) |
36032 | { |
36033 | gen0size = trueSize; |
36034 | break; |
36035 | } |
36036 | } |
36037 | } |
36038 | |
36039 | // Generation 0 must never be more than 1/2 the segment size. |
36040 | if (gen0size >= (seg_size / 2)) |
36041 | gen0size = seg_size / 2; |
36042 | |
36043 | return (gen0size); |
36044 | } |
36045 | |
36046 | void GCHeap::SetReservedVMLimit (size_t vmlimit) |
36047 | { |
36048 | gc_heap::reserved_memory_limit = vmlimit; |
36049 | } |
36050 | |
36051 | |
36052 | //versions of same method on each heap |
36053 | |
36054 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
36055 | |
36056 | Object* GCHeap::GetNextFinalizableObject() |
36057 | { |
36058 | |
36059 | #ifdef MULTIPLE_HEAPS |
36060 | |
36061 | //return the first non critical one in the first queue. |
36062 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36063 | { |
36064 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36065 | Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE); |
36066 | if (O) |
36067 | return O; |
36068 | } |
36069 | //return the first non crtitical/critical one in the first queue. |
36070 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36071 | { |
36072 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36073 | Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE); |
36074 | if (O) |
36075 | return O; |
36076 | } |
36077 | return 0; |
36078 | |
36079 | |
36080 | #else //MULTIPLE_HEAPS |
36081 | return pGenGCHeap->finalize_queue->GetNextFinalizableObject(); |
36082 | #endif //MULTIPLE_HEAPS |
36083 | |
36084 | } |
36085 | |
36086 | size_t GCHeap::GetNumberFinalizableObjects() |
36087 | { |
36088 | #ifdef MULTIPLE_HEAPS |
36089 | size_t cnt = 0; |
36090 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36091 | { |
36092 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36093 | cnt += hp->finalize_queue->GetNumberFinalizableObjects(); |
36094 | } |
36095 | return cnt; |
36096 | |
36097 | |
36098 | #else //MULTIPLE_HEAPS |
36099 | return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects(); |
36100 | #endif //MULTIPLE_HEAPS |
36101 | } |
36102 | |
36103 | size_t GCHeap::GetFinalizablePromotedCount() |
36104 | { |
36105 | #ifdef MULTIPLE_HEAPS |
36106 | size_t cnt = 0; |
36107 | |
36108 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36109 | { |
36110 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36111 | cnt += hp->finalize_queue->GetPromotedCount(); |
36112 | } |
36113 | return cnt; |
36114 | |
36115 | #else //MULTIPLE_HEAPS |
36116 | return pGenGCHeap->finalize_queue->GetPromotedCount(); |
36117 | #endif //MULTIPLE_HEAPS |
36118 | } |
36119 | |
36120 | bool GCHeap::FinalizeAppDomain(void *pDomain, bool fRunFinalizers) |
36121 | { |
36122 | #ifdef MULTIPLE_HEAPS |
36123 | bool foundp = false; |
36124 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36125 | { |
36126 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36127 | if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers)) |
36128 | foundp = true; |
36129 | } |
36130 | return foundp; |
36131 | |
36132 | #else //MULTIPLE_HEAPS |
36133 | return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers); |
36134 | #endif //MULTIPLE_HEAPS |
36135 | } |
36136 | |
36137 | bool GCHeap::ShouldRestartFinalizerWatchDog() |
36138 | { |
36139 | // This condition was historically used as part of the condition to detect finalizer thread timeouts |
36140 | return gc_heap::gc_lock.lock != -1; |
36141 | } |
36142 | |
36143 | void GCHeap::SetFinalizeQueueForShutdown(bool fHasLock) |
36144 | { |
36145 | #ifdef MULTIPLE_HEAPS |
36146 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36147 | { |
36148 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36149 | hp->finalize_queue->SetSegForShutDown(fHasLock); |
36150 | } |
36151 | |
36152 | #else //MULTIPLE_HEAPS |
36153 | pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock); |
36154 | #endif //MULTIPLE_HEAPS |
36155 | } |
36156 | |
36157 | //--------------------------------------------------------------------------- |
36158 | // Finalized class tracking |
36159 | //--------------------------------------------------------------------------- |
36160 | |
36161 | bool GCHeap::RegisterForFinalization (int gen, Object* obj) |
36162 | { |
36163 | if (gen == -1) |
36164 | gen = 0; |
36165 | if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)) |
36166 | { |
36167 | //just reset the bit |
36168 | ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN); |
36169 | return true; |
36170 | } |
36171 | else |
36172 | { |
36173 | gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj); |
36174 | return hp->finalize_queue->RegisterForFinalization (gen, obj); |
36175 | } |
36176 | } |
36177 | |
36178 | void GCHeap::SetFinalizationRun (Object* obj) |
36179 | { |
36180 | ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN); |
36181 | } |
36182 | |
36183 | |
36184 | //-------------------------------------------------------------------- |
36185 | // |
36186 | // Support for finalization |
36187 | // |
36188 | //-------------------------------------------------------------------- |
36189 | |
36190 | inline |
36191 | unsigned int gen_segment (int gen) |
36192 | { |
36193 | assert (((signed)NUMBERGENERATIONS - gen - 1)>=0); |
36194 | return (NUMBERGENERATIONS - gen - 1); |
36195 | } |
36196 | |
36197 | bool CFinalize::Initialize() |
36198 | { |
36199 | CONTRACTL { |
36200 | NOTHROW; |
36201 | GC_NOTRIGGER; |
36202 | } CONTRACTL_END; |
36203 | |
36204 | m_Array = new (nothrow)(Object*[100]); |
36205 | |
36206 | if (!m_Array) |
36207 | { |
36208 | ASSERT (m_Array); |
36209 | STRESS_LOG_OOM_STACK(sizeof(Object*[100])); |
36210 | if (GCConfig::GetBreakOnOOM()) |
36211 | { |
36212 | GCToOSInterface::DebugBreak(); |
36213 | } |
36214 | return false; |
36215 | } |
36216 | m_EndArray = &m_Array[100]; |
36217 | |
36218 | for (int i =0; i < FreeList; i++) |
36219 | { |
36220 | SegQueueLimit (i) = m_Array; |
36221 | } |
36222 | m_PromotedCount = 0; |
36223 | lock = -1; |
36224 | #ifdef _DEBUG |
36225 | lockowner_threadid.Clear(); |
36226 | #endif // _DEBUG |
36227 | |
36228 | return true; |
36229 | } |
36230 | |
36231 | CFinalize::~CFinalize() |
36232 | { |
36233 | delete m_Array; |
36234 | } |
36235 | |
36236 | size_t CFinalize::GetPromotedCount () |
36237 | { |
36238 | return m_PromotedCount; |
36239 | } |
36240 | |
36241 | inline |
36242 | void CFinalize::EnterFinalizeLock() |
36243 | { |
36244 | _ASSERTE(dbgOnly_IsSpecialEEThread() || |
36245 | GCToEEInterface::GetThread() == 0 || |
36246 | GCToEEInterface::IsPreemptiveGCDisabled()); |
36247 | |
36248 | retry: |
36249 | if (Interlocked::CompareExchange(&lock, 0, -1) >= 0) |
36250 | { |
36251 | unsigned int i = 0; |
36252 | while (lock >= 0) |
36253 | { |
36254 | YieldProcessor(); // indicate to the processor that we are spining |
36255 | if (++i & 7) |
36256 | GCToOSInterface::YieldThread (0); |
36257 | else |
36258 | GCToOSInterface::Sleep (5); |
36259 | } |
36260 | goto retry; |
36261 | } |
36262 | |
36263 | #ifdef _DEBUG |
36264 | lockowner_threadid.SetToCurrentThread(); |
36265 | #endif // _DEBUG |
36266 | } |
36267 | |
36268 | inline |
36269 | void CFinalize::LeaveFinalizeLock() |
36270 | { |
36271 | _ASSERTE(dbgOnly_IsSpecialEEThread() || |
36272 | GCToEEInterface::GetThread() == 0 || |
36273 | GCToEEInterface::IsPreemptiveGCDisabled()); |
36274 | |
36275 | #ifdef _DEBUG |
36276 | lockowner_threadid.Clear(); |
36277 | #endif // _DEBUG |
36278 | lock = -1; |
36279 | } |
36280 | |
36281 | bool |
36282 | CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size) |
36283 | { |
36284 | CONTRACTL { |
36285 | NOTHROW; |
36286 | GC_NOTRIGGER; |
36287 | } CONTRACTL_END; |
36288 | |
36289 | EnterFinalizeLock(); |
36290 | // Adjust gen |
36291 | unsigned int dest = 0; |
36292 | |
36293 | if (g_fFinalizerRunOnShutDown) |
36294 | { |
36295 | //no method table available yet, |
36296 | //put it in the finalizer queue and sort out when |
36297 | //dequeueing |
36298 | dest = FinalizerListSeg; |
36299 | } |
36300 | |
36301 | else |
36302 | dest = gen_segment (gen); |
36303 | |
36304 | // Adjust boundary for segments so that GC will keep objects alive. |
36305 | Object*** s_i = &SegQueue (FreeList); |
36306 | if ((*s_i) == m_EndArray) |
36307 | { |
36308 | if (!GrowArray()) |
36309 | { |
36310 | LeaveFinalizeLock(); |
36311 | if (method_table(obj) == NULL) |
36312 | { |
36313 | // If the object is uninitialized, a valid size should have been passed. |
36314 | assert (size >= Align (min_obj_size)); |
36315 | dprintf (3, ("Making unused array [%Ix, %Ix[" , (size_t)obj, (size_t)(obj+size))); |
36316 | ((CObjectHeader*)obj)->SetFree(size); |
36317 | } |
36318 | STRESS_LOG_OOM_STACK(0); |
36319 | if (GCConfig::GetBreakOnOOM()) |
36320 | { |
36321 | GCToOSInterface::DebugBreak(); |
36322 | } |
36323 | return false; |
36324 | } |
36325 | } |
36326 | Object*** end_si = &SegQueueLimit (dest); |
36327 | do |
36328 | { |
36329 | //is the segment empty? |
36330 | if (!(*s_i == *(s_i-1))) |
36331 | { |
36332 | //no, swap the end elements. |
36333 | *(*s_i) = *(*(s_i-1)); |
36334 | } |
36335 | //increment the fill pointer |
36336 | (*s_i)++; |
36337 | //go to the next segment. |
36338 | s_i--; |
36339 | } while (s_i > end_si); |
36340 | |
36341 | // We have reached the destination segment |
36342 | // store the object |
36343 | **s_i = obj; |
36344 | // increment the fill pointer |
36345 | (*s_i)++; |
36346 | |
36347 | LeaveFinalizeLock(); |
36348 | |
36349 | return true; |
36350 | } |
36351 | |
36352 | Object* |
36353 | CFinalize::GetNextFinalizableObject (BOOL only_non_critical) |
36354 | { |
36355 | Object* obj = 0; |
36356 | //serialize |
36357 | EnterFinalizeLock(); |
36358 | |
36359 | retry: |
36360 | if (!IsSegEmpty(FinalizerListSeg)) |
36361 | { |
36362 | if (g_fFinalizerRunOnShutDown) |
36363 | { |
36364 | obj = *(SegQueueLimit (FinalizerListSeg)-1); |
36365 | if (method_table(obj)->HasCriticalFinalizer()) |
36366 | { |
36367 | MoveItem ((SegQueueLimit (FinalizerListSeg)-1), |
36368 | FinalizerListSeg, CriticalFinalizerListSeg); |
36369 | goto retry; |
36370 | } |
36371 | else |
36372 | --SegQueueLimit (FinalizerListSeg); |
36373 | } |
36374 | else |
36375 | obj = *(--SegQueueLimit (FinalizerListSeg)); |
36376 | |
36377 | } |
36378 | else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg)) |
36379 | { |
36380 | //the FinalizerList is empty, we can adjust both |
36381 | // limit instead of moving the object to the free list |
36382 | obj = *(--SegQueueLimit (CriticalFinalizerListSeg)); |
36383 | --SegQueueLimit (FinalizerListSeg); |
36384 | } |
36385 | if (obj) |
36386 | { |
36387 | dprintf (3, ("running finalizer for %Ix (mt: %Ix)" , obj, method_table (obj))); |
36388 | } |
36389 | LeaveFinalizeLock(); |
36390 | return obj; |
36391 | } |
36392 | |
36393 | void |
36394 | CFinalize::SetSegForShutDown(BOOL fHasLock) |
36395 | { |
36396 | int i; |
36397 | |
36398 | if (!fHasLock) |
36399 | EnterFinalizeLock(); |
36400 | for (i = 0; i <= max_generation; i++) |
36401 | { |
36402 | unsigned int seg = gen_segment (i); |
36403 | Object** startIndex = SegQueueLimit (seg)-1; |
36404 | Object** stopIndex = SegQueue (seg); |
36405 | for (Object** po = startIndex; po >= stopIndex; po--) |
36406 | { |
36407 | Object* obj = *po; |
36408 | if (method_table(obj)->HasCriticalFinalizer()) |
36409 | { |
36410 | MoveItem (po, seg, CriticalFinalizerListSeg); |
36411 | } |
36412 | else |
36413 | { |
36414 | MoveItem (po, seg, FinalizerListSeg); |
36415 | } |
36416 | } |
36417 | } |
36418 | if (!fHasLock) |
36419 | LeaveFinalizeLock(); |
36420 | } |
36421 | |
36422 | void |
36423 | CFinalize::DiscardNonCriticalObjects() |
36424 | { |
36425 | //empty the finalization queue |
36426 | Object** startIndex = SegQueueLimit (FinalizerListSeg)-1; |
36427 | Object** stopIndex = SegQueue (FinalizerListSeg); |
36428 | for (Object** po = startIndex; po >= stopIndex; po--) |
36429 | { |
36430 | MoveItem (po, FinalizerListSeg, FreeList); |
36431 | } |
36432 | } |
36433 | |
36434 | size_t |
36435 | CFinalize::GetNumberFinalizableObjects() |
36436 | { |
36437 | return SegQueueLimit (FinalizerListSeg) - |
36438 | (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg)); |
36439 | } |
36440 | |
36441 | BOOL |
36442 | CFinalize::FinalizeSegForAppDomain (void *pDomain, |
36443 | BOOL fRunFinalizers, |
36444 | unsigned int Seg) |
36445 | { |
36446 | BOOL finalizedFound = FALSE; |
36447 | Object** endIndex = SegQueue (Seg); |
36448 | for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--) |
36449 | { |
36450 | CObjectHeader* obj = (CObjectHeader*)*i; |
36451 | |
36452 | // Objects are put into the finalization queue before they are complete (ie their methodtable |
36453 | // may be null) so we must check that the object we found has a method table before checking |
36454 | // if it has the index we are looking for. If the methodtable is null, it can't be from the |
36455 | // unloading domain, so skip it. |
36456 | if (method_table(obj) == NULL) |
36457 | { |
36458 | continue; |
36459 | } |
36460 | |
36461 | // does the EE actually want us to finalize this object? |
36462 | if (!GCToEEInterface::ShouldFinalizeObjectForUnload(pDomain, obj)) |
36463 | { |
36464 | continue; |
36465 | } |
36466 | |
36467 | if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN) |
36468 | { |
36469 | //remove the object because we don't want to |
36470 | //run the finalizer |
36471 | MoveItem (i, Seg, FreeList); |
36472 | //Reset the bit so it will be put back on the queue |
36473 | //if resurrected and re-registered. |
36474 | obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN); |
36475 | } |
36476 | else |
36477 | { |
36478 | if (method_table(obj)->HasCriticalFinalizer()) |
36479 | { |
36480 | finalizedFound = TRUE; |
36481 | MoveItem (i, Seg, CriticalFinalizerListSeg); |
36482 | } |
36483 | else |
36484 | { |
36485 | if (GCToEEInterface::AppDomainIsRudeUnload(pDomain)) |
36486 | { |
36487 | MoveItem (i, Seg, FreeList); |
36488 | } |
36489 | else |
36490 | { |
36491 | finalizedFound = TRUE; |
36492 | MoveItem (i, Seg, FinalizerListSeg); |
36493 | } |
36494 | } |
36495 | } |
36496 | } |
36497 | |
36498 | return finalizedFound; |
36499 | } |
36500 | |
36501 | bool |
36502 | CFinalize::FinalizeAppDomain (void *pDomain, bool fRunFinalizers) |
36503 | { |
36504 | bool finalizedFound = false; |
36505 | |
36506 | unsigned int startSeg = gen_segment (max_generation); |
36507 | |
36508 | EnterFinalizeLock(); |
36509 | |
36510 | for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++) |
36511 | { |
36512 | if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg)) |
36513 | { |
36514 | finalizedFound = true; |
36515 | } |
36516 | } |
36517 | |
36518 | LeaveFinalizeLock(); |
36519 | |
36520 | return finalizedFound; |
36521 | } |
36522 | |
36523 | void |
36524 | CFinalize::MoveItem (Object** fromIndex, |
36525 | unsigned int fromSeg, |
36526 | unsigned int toSeg) |
36527 | { |
36528 | |
36529 | int step; |
36530 | ASSERT (fromSeg != toSeg); |
36531 | if (fromSeg > toSeg) |
36532 | step = -1; |
36533 | else |
36534 | step = +1; |
36535 | // Place the element at the boundary closest to dest |
36536 | Object** srcIndex = fromIndex; |
36537 | for (unsigned int i = fromSeg; i != toSeg; i+= step) |
36538 | { |
36539 | Object**& destFill = m_FillPointers[i+(step - 1 )/2]; |
36540 | Object** destIndex = destFill - (step + 1)/2; |
36541 | if (srcIndex != destIndex) |
36542 | { |
36543 | Object* tmp = *srcIndex; |
36544 | *srcIndex = *destIndex; |
36545 | *destIndex = tmp; |
36546 | } |
36547 | destFill -= step; |
36548 | srcIndex = destIndex; |
36549 | } |
36550 | } |
36551 | |
36552 | void |
36553 | CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC) |
36554 | { |
36555 | ScanContext sc; |
36556 | if (pSC == 0) |
36557 | pSC = ≻ |
36558 | |
36559 | pSC->thread_number = hn; |
36560 | |
36561 | //scan the finalization queue |
36562 | Object** startIndex = SegQueue (CriticalFinalizerListSeg); |
36563 | Object** stopIndex = SegQueueLimit (FinalizerListSeg); |
36564 | |
36565 | for (Object** po = startIndex; po < stopIndex; po++) |
36566 | { |
36567 | Object* o = *po; |
36568 | //dprintf (3, ("scan freacheable %Ix", (size_t)o)); |
36569 | dprintf (3, ("scan f %Ix" , (size_t)o)); |
36570 | #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING |
36571 | if (g_fEnableAppDomainMonitoring) |
36572 | { |
36573 | pSC->pCurrentDomain = GCToEEInterface::GetAppDomainForObject(o); |
36574 | } |
36575 | #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING |
36576 | |
36577 | (*fn)(po, pSC, 0); |
36578 | } |
36579 | } |
36580 | |
36581 | void CFinalize::WalkFReachableObjects (fq_walk_fn fn) |
36582 | { |
36583 | Object** startIndex = SegQueue (CriticalFinalizerListSeg); |
36584 | Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg); |
36585 | Object** stopIndex = SegQueueLimit (FinalizerListSeg); |
36586 | for (Object** po = startIndex; po < stopIndex; po++) |
36587 | { |
36588 | //report *po |
36589 | fn(po < stopCriticalIndex, *po); |
36590 | } |
36591 | } |
36592 | |
36593 | BOOL |
36594 | CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p, |
36595 | gc_heap* hp) |
36596 | { |
36597 | ScanContext sc; |
36598 | sc.promotion = TRUE; |
36599 | #ifdef MULTIPLE_HEAPS |
36600 | sc.thread_number = hp->heap_number; |
36601 | #else |
36602 | UNREFERENCED_PARAMETER(hp); |
36603 | #endif //MULTIPLE_HEAPS |
36604 | |
36605 | BOOL finalizedFound = FALSE; |
36606 | |
36607 | //start with gen and explore all the younger generations. |
36608 | unsigned int startSeg = gen_segment (gen); |
36609 | { |
36610 | m_PromotedCount = 0; |
36611 | for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++) |
36612 | { |
36613 | Object** endIndex = SegQueue (Seg); |
36614 | for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--) |
36615 | { |
36616 | CObjectHeader* obj = (CObjectHeader*)*i; |
36617 | dprintf (3, ("scanning: %Ix" , (size_t)obj)); |
36618 | if (!g_theGCHeap->IsPromoted (obj)) |
36619 | { |
36620 | dprintf (3, ("freacheable: %Ix" , (size_t)obj)); |
36621 | |
36622 | assert (method_table(obj)->HasFinalizer()); |
36623 | |
36624 | if (GCToEEInterface::EagerFinalized(obj)) |
36625 | { |
36626 | MoveItem (i, Seg, FreeList); |
36627 | } |
36628 | else if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN) |
36629 | { |
36630 | //remove the object because we don't want to |
36631 | //run the finalizer |
36632 | |
36633 | MoveItem (i, Seg, FreeList); |
36634 | |
36635 | //Reset the bit so it will be put back on the queue |
36636 | //if resurrected and re-registered. |
36637 | obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN); |
36638 | |
36639 | } |
36640 | else |
36641 | { |
36642 | m_PromotedCount++; |
36643 | |
36644 | if (method_table(obj)->HasCriticalFinalizer()) |
36645 | { |
36646 | MoveItem (i, Seg, CriticalFinalizerListSeg); |
36647 | } |
36648 | else |
36649 | { |
36650 | MoveItem (i, Seg, FinalizerListSeg); |
36651 | } |
36652 | } |
36653 | } |
36654 | #ifdef BACKGROUND_GC |
36655 | else |
36656 | { |
36657 | if ((gen == max_generation) && (recursive_gc_sync::background_running_p())) |
36658 | { |
36659 | // TODO - fix the following line. |
36660 | //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE)); |
36661 | dprintf (3, ("%Ix is marked" , (size_t)obj)); |
36662 | } |
36663 | } |
36664 | #endif //BACKGROUND_GC |
36665 | } |
36666 | } |
36667 | } |
36668 | finalizedFound = !IsSegEmpty(FinalizerListSeg) || |
36669 | !IsSegEmpty(CriticalFinalizerListSeg); |
36670 | |
36671 | if (finalizedFound) |
36672 | { |
36673 | //Promote the f-reachable objects |
36674 | GcScanRoots (pfn, |
36675 | #ifdef MULTIPLE_HEAPS |
36676 | hp->heap_number |
36677 | #else |
36678 | 0 |
36679 | #endif //MULTIPLE_HEAPS |
36680 | , 0); |
36681 | |
36682 | hp->settings.found_finalizers = TRUE; |
36683 | |
36684 | #ifdef BACKGROUND_GC |
36685 | if (hp->settings.concurrent) |
36686 | { |
36687 | hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg)); |
36688 | } |
36689 | #endif //BACKGROUND_GC |
36690 | if (hp->settings.concurrent && hp->settings.found_finalizers) |
36691 | { |
36692 | if (!mark_only_p) |
36693 | GCToEEInterface::EnableFinalization(true); |
36694 | } |
36695 | } |
36696 | |
36697 | return finalizedFound; |
36698 | } |
36699 | |
36700 | //Relocates all of the objects in the finalization array |
36701 | void |
36702 | CFinalize::RelocateFinalizationData (int gen, gc_heap* hp) |
36703 | { |
36704 | ScanContext sc; |
36705 | sc.promotion = FALSE; |
36706 | #ifdef MULTIPLE_HEAPS |
36707 | sc.thread_number = hp->heap_number; |
36708 | #else |
36709 | UNREFERENCED_PARAMETER(hp); |
36710 | #endif //MULTIPLE_HEAPS |
36711 | |
36712 | unsigned int Seg = gen_segment (gen); |
36713 | |
36714 | Object** startIndex = SegQueue (Seg); |
36715 | for (Object** po = startIndex; po < SegQueue (FreeList);po++) |
36716 | { |
36717 | GCHeap::Relocate (po, &sc); |
36718 | } |
36719 | } |
36720 | |
36721 | void |
36722 | CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p) |
36723 | { |
36724 | // update the generation fill pointers. |
36725 | // if gen_0_empty is FALSE, test each object to find out if |
36726 | // it was promoted or not |
36727 | if (gen_0_empty_p) |
36728 | { |
36729 | for (int i = min (gen+1, max_generation); i > 0; i--) |
36730 | { |
36731 | m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)]; |
36732 | } |
36733 | } |
36734 | else |
36735 | { |
36736 | //Look for demoted or promoted plugs |
36737 | |
36738 | for (int i = gen; i >= 0; i--) |
36739 | { |
36740 | unsigned int Seg = gen_segment (i); |
36741 | Object** startIndex = SegQueue (Seg); |
36742 | |
36743 | for (Object** po = startIndex; |
36744 | po < SegQueueLimit (gen_segment(i)); po++) |
36745 | { |
36746 | int new_gen = g_theGCHeap->WhichGeneration (*po); |
36747 | if (new_gen != i) |
36748 | { |
36749 | if (new_gen > i) |
36750 | { |
36751 | //promotion |
36752 | MoveItem (po, gen_segment (i), gen_segment (new_gen)); |
36753 | } |
36754 | else |
36755 | { |
36756 | //demotion |
36757 | MoveItem (po, gen_segment (i), gen_segment (new_gen)); |
36758 | //back down in order to see all objects. |
36759 | po--; |
36760 | } |
36761 | } |
36762 | |
36763 | } |
36764 | } |
36765 | } |
36766 | } |
36767 | |
36768 | BOOL |
36769 | CFinalize::GrowArray() |
36770 | { |
36771 | size_t oldArraySize = (m_EndArray - m_Array); |
36772 | size_t newArraySize = (size_t)(((float)oldArraySize / 10) * 12); |
36773 | |
36774 | Object** newArray = new (nothrow) Object*[newArraySize]; |
36775 | if (!newArray) |
36776 | { |
36777 | // It's not safe to throw here, because of the FinalizeLock. Tell our caller |
36778 | // to throw for us. |
36779 | // ASSERT (newArray); |
36780 | return FALSE; |
36781 | } |
36782 | memcpy (newArray, m_Array, oldArraySize*sizeof(Object*)); |
36783 | |
36784 | //adjust the fill pointers |
36785 | for (int i = 0; i < FreeList; i++) |
36786 | { |
36787 | m_FillPointers [i] += (newArray - m_Array); |
36788 | } |
36789 | delete m_Array; |
36790 | m_Array = newArray; |
36791 | m_EndArray = &m_Array [newArraySize]; |
36792 | |
36793 | return TRUE; |
36794 | } |
36795 | |
36796 | #ifdef VERIFY_HEAP |
36797 | void CFinalize::CheckFinalizerObjects() |
36798 | { |
36799 | for (int i = 0; i <= max_generation; i++) |
36800 | { |
36801 | Object **startIndex = SegQueue (gen_segment (i)); |
36802 | Object **stopIndex = SegQueueLimit (gen_segment (i)); |
36803 | |
36804 | for (Object **po = startIndex; po < stopIndex; po++) |
36805 | { |
36806 | if ((int)g_theGCHeap->WhichGeneration (*po) < i) |
36807 | FATAL_GC_ERROR (); |
36808 | ((CObjectHeader*)*po)->Validate(); |
36809 | } |
36810 | } |
36811 | } |
36812 | #endif //VERIFY_HEAP |
36813 | |
36814 | #endif // FEATURE_PREMORTEM_FINALIZATION |
36815 | |
36816 | |
36817 | //------------------------------------------------------------------------------ |
36818 | // |
36819 | // End of VM specific support |
36820 | // |
36821 | //------------------------------------------------------------------------------ |
36822 | void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) |
36823 | { |
36824 | generation* gen = gc_heap::generation_of (gen_number); |
36825 | heap_segment* seg = generation_start_segment (gen); |
36826 | uint8_t* x = ((gen_number == max_generation) ? heap_segment_mem (seg) : |
36827 | generation_allocation_start (gen)); |
36828 | |
36829 | uint8_t* end = heap_segment_allocated (seg); |
36830 | BOOL small_object_segments = TRUE; |
36831 | int align_const = get_alignment_constant (small_object_segments); |
36832 | |
36833 | while (1) |
36834 | |
36835 | { |
36836 | if (x >= end) |
36837 | { |
36838 | if ((seg = heap_segment_next (seg)) != 0) |
36839 | { |
36840 | x = heap_segment_mem (seg); |
36841 | end = heap_segment_allocated (seg); |
36842 | continue; |
36843 | } |
36844 | else |
36845 | { |
36846 | if (small_object_segments && walk_large_object_heap_p) |
36847 | |
36848 | { |
36849 | small_object_segments = FALSE; |
36850 | align_const = get_alignment_constant (small_object_segments); |
36851 | seg = generation_start_segment (large_object_generation); |
36852 | x = heap_segment_mem (seg); |
36853 | end = heap_segment_allocated (seg); |
36854 | continue; |
36855 | } |
36856 | else |
36857 | { |
36858 | break; |
36859 | } |
36860 | } |
36861 | } |
36862 | |
36863 | size_t s = size (x); |
36864 | CObjectHeader* o = (CObjectHeader*)x; |
36865 | |
36866 | if (!o->IsFree()) |
36867 | |
36868 | { |
36869 | _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point |
36870 | |
36871 | if (!fn (o->GetObjectBase(), context)) |
36872 | return; |
36873 | } |
36874 | x = x + Align (s, align_const); |
36875 | } |
36876 | } |
36877 | |
36878 | void gc_heap::walk_finalize_queue (fq_walk_fn fn) |
36879 | { |
36880 | #ifdef FEATURE_PREMORTEM_FINALIZATION |
36881 | finalize_queue->WalkFReachableObjects (fn); |
36882 | #endif //FEATURE_PREMORTEM_FINALIZATION |
36883 | } |
36884 | |
36885 | void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p) |
36886 | { |
36887 | #ifdef MULTIPLE_HEAPS |
36888 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36889 | { |
36890 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36891 | |
36892 | hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p); |
36893 | } |
36894 | #else |
36895 | walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p); |
36896 | #endif //MULTIPLE_HEAPS |
36897 | } |
36898 | |
36899 | void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context) |
36900 | { |
36901 | uint8_t* o = (uint8_t*)obj; |
36902 | if (o) |
36903 | { |
36904 | go_through_object_cl (method_table (o), o, size(o), oo, |
36905 | { |
36906 | if (*oo) |
36907 | { |
36908 | Object *oh = (Object*)*oo; |
36909 | if (!fn (oh, context)) |
36910 | return; |
36911 | } |
36912 | } |
36913 | ); |
36914 | } |
36915 | } |
36916 | |
36917 | void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type) |
36918 | { |
36919 | gc_heap* hp = (gc_heap*)gc_context; |
36920 | hp->walk_survivors (fn, diag_context, type); |
36921 | } |
36922 | |
36923 | void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p) |
36924 | { |
36925 | gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p); |
36926 | } |
36927 | |
36928 | void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn) |
36929 | { |
36930 | gc_heap* hp = (gc_heap*)gc_context; |
36931 | hp->walk_finalize_queue (fn); |
36932 | } |
36933 | |
36934 | void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc) |
36935 | { |
36936 | #ifdef MULTIPLE_HEAPS |
36937 | for (int hn = 0; hn < gc_heap::n_heaps; hn++) |
36938 | { |
36939 | gc_heap* hp = gc_heap::g_heaps [hn]; |
36940 | hp->finalize_queue->GcScanRoots(fn, hn, sc); |
36941 | } |
36942 | #else |
36943 | pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc); |
36944 | #endif //MULTIPLE_HEAPS |
36945 | } |
36946 | |
36947 | void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context) |
36948 | { |
36949 | UNREFERENCED_PARAMETER(gen_number); |
36950 | GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn); |
36951 | } |
36952 | |
36953 | void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context) |
36954 | { |
36955 | UNREFERENCED_PARAMETER(gen_number); |
36956 | GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn); |
36957 | } |
36958 | |
36959 | // Go through and touch (read) each page straddled by a memory block. |
36960 | void TouchPages(void * pStart, size_t cb) |
36961 | { |
36962 | const uint32_t pagesize = OS_PAGE_SIZE; |
36963 | _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2. |
36964 | if (cb) |
36965 | { |
36966 | VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart); |
36967 | VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) - (((size_t)pStart) & (pagesize-1))); |
36968 | while (p < pEnd) |
36969 | { |
36970 | char a; |
36971 | a = VolatileLoad(p); |
36972 | //printf("Touching page %lxh\n", (uint32_t)p); |
36973 | p += pagesize; |
36974 | } |
36975 | } |
36976 | } |
36977 | |
36978 | #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC) |
36979 | // This code is designed to catch the failure to update the write barrier |
36980 | // The way it works is to copy the whole heap right after every GC. The write |
36981 | // barrier code has been modified so that it updates the shadow as well as the |
36982 | // real GC heap. Before doing the next GC, we walk the heap, looking for pointers |
36983 | // that were updated in the real heap, but not the shadow. A mismatch indicates |
36984 | // an error. The offending code can be found by breaking after the correct GC, |
36985 | // and then placing a data breakpoint on the Heap location that was updated without |
36986 | // going through the write barrier. |
36987 | |
36988 | // Called at process shutdown |
36989 | void deleteGCShadow() |
36990 | { |
36991 | if (g_GCShadow != 0) |
36992 | GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow); |
36993 | g_GCShadow = 0; |
36994 | g_GCShadowEnd = 0; |
36995 | } |
36996 | |
36997 | // Called at startup and right after a GC, get a snapshot of the GC Heap |
36998 | void initGCShadow() |
36999 | { |
37000 | if (!(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK)) |
37001 | return; |
37002 | |
37003 | size_t len = g_gc_highest_address - g_gc_lowest_address; |
37004 | if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) |
37005 | { |
37006 | deleteGCShadow(); |
37007 | g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None); |
37008 | if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len)) |
37009 | { |
37010 | _ASSERTE(!"Not enough memory to run HeapVerify level 2" ); |
37011 | // If after the assert we decide to allow the program to continue |
37012 | // running we need to be in a state that will not trigger any |
37013 | // additional AVs while we fail to allocate a shadow segment, i.e. |
37014 | // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV |
37015 | deleteGCShadow(); |
37016 | return; |
37017 | } |
37018 | |
37019 | g_GCShadowEnd += len; |
37020 | } |
37021 | |
37022 | // save the value of g_gc_lowest_address at this time. If this value changes before |
37023 | // the next call to checkGCWriteBarrier() it means we extended the heap (with a |
37024 | // large object segment most probably), and the whole shadow segment is inconsistent. |
37025 | g_shadow_lowest_address = g_gc_lowest_address; |
37026 | |
37027 | //****** Copy the whole GC heap ****** |
37028 | // |
37029 | // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment()) |
37030 | // can produce a NULL result. This is because the initialization has not completed. |
37031 | // |
37032 | generation* gen = gc_heap::generation_of (max_generation); |
37033 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
37034 | |
37035 | ptrdiff_t delta = g_GCShadow - g_gc_lowest_address; |
37036 | BOOL small_object_segments = TRUE; |
37037 | while(1) |
37038 | { |
37039 | if (!seg) |
37040 | { |
37041 | if (small_object_segments) |
37042 | { |
37043 | small_object_segments = FALSE; |
37044 | seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1))); |
37045 | continue; |
37046 | } |
37047 | else |
37048 | break; |
37049 | } |
37050 | // Copy the segment |
37051 | uint8_t* start = heap_segment_mem(seg); |
37052 | uint8_t* end = heap_segment_allocated (seg); |
37053 | memcpy(start + delta, start, end - start); |
37054 | seg = heap_segment_next_rw (seg); |
37055 | } |
37056 | } |
37057 | |
37058 | #define INVALIDGCVALUE (void*)((size_t)0xcccccccd) |
37059 | |
37060 | // test to see if 'ptr' was only updated via the write barrier. |
37061 | inline void testGCShadow(Object** ptr) |
37062 | { |
37063 | Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)]; |
37064 | if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow) |
37065 | { |
37066 | |
37067 | // If you get this assertion, someone updated a GC pointer in the heap without |
37068 | // using the write barrier. To find out who, check the value of |
37069 | // dd_collection_count (dynamic_data_of (0)). Also |
37070 | // note the value of 'ptr'. Rerun the App that the previous GC just occurred. |
37071 | // Then put a data breakpoint for the value of 'ptr' Then check every write |
37072 | // to pointer between the two GCs. The last one is not using the write barrier. |
37073 | |
37074 | // If the memory of interest does not exist at system startup, |
37075 | // you need to set the data breakpoint right after the memory gets committed |
37076 | // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr' |
37077 | // in the memory window. run until the memory gets mapped. Then you can set |
37078 | // your breakpoint |
37079 | |
37080 | // Note a recent change, we've identified race conditions when updating the gc shadow. |
37081 | // Throughout the runtime, code will update an address in the gc heap, then erect the |
37082 | // write barrier, which calls updateGCShadow. With an app that pounds one heap location |
37083 | // from multiple threads, you can hit this assert even though all involved are using the |
37084 | // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE. |
37085 | // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been |
37086 | // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr, |
37087 | // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting |
37088 | // TODO: erroneous asserts in here. |
37089 | |
37090 | if(*shadow!=INVALIDGCVALUE) |
37091 | { |
37092 | #ifdef FEATURE_BASICFREEZE |
37093 | // Write barriers for stores of references to frozen objects may be optimized away. |
37094 | if (!gc_heap::frozen_object_p(*ptr)) |
37095 | #endif // FEATURE_BASICFREEZE |
37096 | { |
37097 | _ASSERTE(!"Pointer updated without using write barrier" ); |
37098 | } |
37099 | } |
37100 | /* |
37101 | else |
37102 | { |
37103 | printf("saw a INVALIDGCVALUE. (just to let you know)\n"); |
37104 | } |
37105 | */ |
37106 | } |
37107 | } |
37108 | |
37109 | void testGCShadowHelper (uint8_t* x) |
37110 | { |
37111 | size_t s = size (x); |
37112 | if (contain_pointers (x)) |
37113 | { |
37114 | go_through_object_nostart (method_table(x), x, s, oo, |
37115 | { testGCShadow((Object**) oo); }); |
37116 | } |
37117 | } |
37118 | |
37119 | // Walk the whole heap, looking for pointers that were not updated with the write barrier. |
37120 | void checkGCWriteBarrier() |
37121 | { |
37122 | // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment |
37123 | // and the GC shadow segment did not track that change! |
37124 | if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address) |
37125 | { |
37126 | // No shadow stack, nothing to check. |
37127 | return; |
37128 | } |
37129 | |
37130 | { |
37131 | generation* gen = gc_heap::generation_of (max_generation); |
37132 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
37133 | |
37134 | PREFIX_ASSUME(seg != NULL); |
37135 | |
37136 | while(seg) |
37137 | { |
37138 | uint8_t* x = heap_segment_mem(seg); |
37139 | while (x < heap_segment_allocated (seg)) |
37140 | { |
37141 | size_t s = size (x); |
37142 | testGCShadowHelper (x); |
37143 | x = x + Align (s); |
37144 | } |
37145 | seg = heap_segment_next_rw (seg); |
37146 | } |
37147 | } |
37148 | |
37149 | { |
37150 | // go through large object heap |
37151 | int alignment = get_alignment_constant(FALSE); |
37152 | generation* gen = gc_heap::generation_of (max_generation+1); |
37153 | heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); |
37154 | |
37155 | PREFIX_ASSUME(seg != NULL); |
37156 | |
37157 | while(seg) |
37158 | { |
37159 | uint8_t* x = heap_segment_mem(seg); |
37160 | while (x < heap_segment_allocated (seg)) |
37161 | { |
37162 | size_t s = size (x); |
37163 | testGCShadowHelper (x); |
37164 | x = x + Align (s, alignment); |
37165 | } |
37166 | seg = heap_segment_next_rw (seg); |
37167 | } |
37168 | } |
37169 | } |
37170 | #endif //WRITE_BARRIER_CHECK && !SERVER_GC |
37171 | |
37172 | #endif // !DACCESS_COMPILE |
37173 | |
37174 | #ifdef FEATURE_BASICFREEZE |
37175 | void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef) |
37176 | { |
37177 | #ifdef DACCESS_COMPILE |
37178 | UNREFERENCED_PARAMETER(seg); |
37179 | UNREFERENCED_PARAMETER(pvContext); |
37180 | UNREFERENCED_PARAMETER(pfnMethodTable); |
37181 | UNREFERENCED_PARAMETER(pfnObjRef); |
37182 | #else |
37183 | uint8_t *o = heap_segment_mem(seg); |
37184 | |
37185 | // small heap alignment constant |
37186 | int alignment = get_alignment_constant(TRUE); |
37187 | |
37188 | while (o < heap_segment_allocated(seg)) |
37189 | { |
37190 | pfnMethodTable(pvContext, o); |
37191 | |
37192 | if (contain_pointers (o)) |
37193 | { |
37194 | go_through_object_nostart (method_table (o), o, size(o), oo, |
37195 | { |
37196 | if (*oo) |
37197 | pfnObjRef(pvContext, oo); |
37198 | } |
37199 | ); |
37200 | } |
37201 | |
37202 | o += Align(size(o), alignment); |
37203 | } |
37204 | #endif //!DACCESS_COMPILE |
37205 | } |
37206 | #endif // FEATURE_BASICFREEZE |
37207 | |
37208 | #ifndef DACCESS_COMPILE |
37209 | HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout) |
37210 | { |
37211 | #ifdef BACKGROUND_GC |
37212 | if (recursive_gc_sync::background_running_p()) |
37213 | { |
37214 | uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout); |
37215 | if (dwRet == WAIT_OBJECT_0) |
37216 | return S_OK; |
37217 | else if (dwRet == WAIT_TIMEOUT) |
37218 | return HRESULT_FROM_WIN32(ERROR_TIMEOUT); |
37219 | else |
37220 | return E_FAIL; // It is not clear if what the last error would be if the wait failed, |
37221 | // as there are too many layers in between. The best we can do is to return E_FAIL; |
37222 | } |
37223 | #endif |
37224 | |
37225 | return S_OK; |
37226 | } |
37227 | #endif // !DACCESS_COMPILE |
37228 | |
37229 | void GCHeap::TemporaryEnableConcurrentGC() |
37230 | { |
37231 | #ifdef BACKGROUND_GC |
37232 | gc_heap::temp_disable_concurrent_p = false; |
37233 | #endif //BACKGROUND_GC |
37234 | } |
37235 | |
37236 | void GCHeap::TemporaryDisableConcurrentGC() |
37237 | { |
37238 | #ifdef BACKGROUND_GC |
37239 | gc_heap::temp_disable_concurrent_p = true; |
37240 | #endif //BACKGROUND_GC |
37241 | } |
37242 | |
37243 | bool GCHeap::IsConcurrentGCEnabled() |
37244 | { |
37245 | #ifdef BACKGROUND_GC |
37246 | return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p)); |
37247 | #else |
37248 | return FALSE; |
37249 | #endif //BACKGROUND_GC |
37250 | } |
37251 | |
37252 | void GCHeap::SetFinalizeRunOnShutdown(bool value) |
37253 | { |
37254 | g_fFinalizerRunOnShutDown = value; |
37255 | } |
37256 | |
37257 | void PopulateDacVars(GcDacVars *gcDacVars) |
37258 | { |
37259 | #ifndef DACCESS_COMPILE |
37260 | assert(gcDacVars != nullptr); |
37261 | *gcDacVars = {}; |
37262 | gcDacVars->major_version_number = 1; |
37263 | gcDacVars->minor_version_number = 0; |
37264 | gcDacVars->built_with_svr = &g_built_with_svr_gc; |
37265 | gcDacVars->build_variant = &g_build_variant; |
37266 | gcDacVars->gc_structures_invalid_cnt = const_cast<int32_t*>(&GCScan::m_GcStructuresInvalidCnt); |
37267 | gcDacVars->generation_size = sizeof(generation); |
37268 | gcDacVars->max_gen = &g_max_generation; |
37269 | #ifndef MULTIPLE_HEAPS |
37270 | gcDacVars->mark_array = &gc_heap::mark_array; |
37271 | gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment); |
37272 | gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state); |
37273 | gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast<dac_heap_segment**>(&gc_heap::saved_sweep_ephemeral_seg); |
37274 | gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start; |
37275 | gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address; |
37276 | gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address; |
37277 | gcDacVars->alloc_allocated = &gc_heap::alloc_allocated; |
37278 | gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj; |
37279 | gcDacVars->oom_info = &gc_heap::oom_info; |
37280 | gcDacVars->finalize_queue = reinterpret_cast<dac_finalize_queue**>(&gc_heap::finalize_queue); |
37281 | gcDacVars->generation_table = reinterpret_cast<dac_generation**>(&gc_heap::generation_table); |
37282 | #ifdef GC_CONFIG_DRIVEN |
37283 | gcDacVars->gc_global_mechanisms = reinterpret_cast<size_t**>(&gc_global_mechanisms); |
37284 | gcDacVars->interesting_data_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_data_per_heap); |
37285 | gcDacVars->compact_reasons_per_heap = reinterpret_cast<size_t**>(&gc_heap::compact_reasons_per_heap); |
37286 | gcDacVars->expand_mechanisms_per_heap = reinterpret_cast<size_t**>(&gc_heap::expand_mechanisms_per_heap); |
37287 | gcDacVars->interesting_mechanism_bits_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_mechanism_bits_per_heap); |
37288 | #endif // GC_CONFIG_DRIVEN |
37289 | #ifdef HEAP_ANALYZE |
37290 | gcDacVars->internal_root_array = &gc_heap::internal_root_array; |
37291 | gcDacVars->internal_root_array_index = &gc_heap::internal_root_array_index; |
37292 | gcDacVars->heap_analyze_success = &gc_heap::heap_analyze_success; |
37293 | #endif // HEAP_ANALYZE |
37294 | #else |
37295 | gcDacVars->n_heaps = &gc_heap::n_heaps; |
37296 | gcDacVars->g_heaps = reinterpret_cast<dac_gc_heap***>(&gc_heap::g_heaps); |
37297 | #endif // MULTIPLE_HEAPS |
37298 | #endif // DACCESS_COMPILE |
37299 | } |
37300 | |