110
111
int need, Eterm *objv, int nobj);
111
112
static void shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj);
112
113
static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj);
113
static void sweep_proc_bins(Process *p, int fullsweep);
114
static void sweep_proc_funs(Process *p, int fullsweep);
115
static void sweep_proc_externals(Process *p, int fullsweep);
114
static void sweep_off_heap(Process *p, int fullsweep);
116
115
static void offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size);
117
116
static void offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size);
118
117
static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size,
147
ASSERT(offsetof(ProcBin,thing_word) == offsetof(struct erl_off_heap_header,thing_word));
148
ASSERT(offsetof(ProcBin,thing_word) == offsetof(ErlFunThing,thing_word));
149
ASSERT(offsetof(ProcBin,thing_word) == offsetof(ExternalThing,header));
150
ASSERT(offsetof(ProcBin,size) == offsetof(struct erl_off_heap_header,size));
151
ASSERT(offsetof(ProcBin,size) == offsetof(ErlSubBin,size));
152
ASSERT(offsetof(ProcBin,size) == offsetof(ErlHeapBin,size));
153
ASSERT(offsetof(ProcBin,next) == offsetof(struct erl_off_heap_header,next));
154
ASSERT(offsetof(ProcBin,next) == offsetof(ErlFunThing,next));
155
ASSERT(offsetof(ProcBin,next) == offsetof(ExternalThing,next));
148
157
erts_smp_spinlock_init(&info_lck, "gc_info");
149
158
garbage_cols = 0;
286
295
offset_heap_ptr(hp, sz, offs, (char *) low, ((char *)high)-((char *)low));
289
299
#define ptr_within(ptr, low, high) ((ptr) < (high) && (ptr) >= (low))
292
302
erts_offset_off_heap(ErlOffHeap *ohp, Sint offs, Eterm* low, Eterm* high)
294
if (ohp->mso && ptr_within((Eterm *)ohp->mso, low, high)) {
295
Eterm** uptr = (Eterm**) (void *) &ohp->mso;
299
#ifndef HYBRID /* FIND ME! */
300
if (ohp->funs && ptr_within((Eterm *)ohp->funs, low, high)) {
301
Eterm** uptr = (Eterm**) (void *) &ohp->funs;
306
if (ohp->externals && ptr_within((Eterm *)ohp->externals, low, high)) {
307
Eterm** uptr = (Eterm**) (void *) &ohp->externals;
304
if (ohp->first && ptr_within((Eterm *)ohp->first, low, high)) {
305
Eterm** uptr = (Eterm**) (void *) &ohp->first;
1392
1379
remove_message_buffers(Process* p)
1394
ErlHeapFragment* bp = MBUF(p);
1398
while (bp != NULL) {
1399
ErlHeapFragment* next_bp = bp->next;
1400
free_message_buffer(bp);
1406
* Go through one root set array, move everything that it is one of the
1407
* heap fragments to our new heap.
1410
collect_root_array(Process* p, Eterm* n_htop, Eterm* objv, int nobj)
1412
ErlHeapFragment* qb;
1417
ASSERT(p->htop != NULL);
1421
switch (primary_tag(gval)) {
1423
case TAG_PRIMARY_BOXED: {
1424
ptr = boxed_val(gval);
1426
if (IS_MOVED(val)) {
1427
ASSERT(is_boxed(val));
1430
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
1431
if (in_area(ptr, qb->mem, qb->size*sizeof(Eterm))) {
1432
MOVE_BOXED(ptr,val,n_htop,objv);
1441
case TAG_PRIMARY_LIST: {
1442
ptr = list_val(gval);
1444
if (is_non_value(val)) {
1447
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
1448
if (in_area(ptr, qb->mem, qb->size*sizeof(Eterm))) {
1449
MOVE_CONS(ptr,val,n_htop,objv);
1381
if (MBUF(p) != NULL) {
1382
free_message_buffer(MBUF(p));
1467
1387
#ifdef HARDDEBUG
1613
1533
switch (primary_tag(val)) {
1614
1534
case TAG_PRIMARY_BOXED:
1615
ptr = (Eterm *) val;
1535
ptr = (Eterm *) EXPAND_POINTER(val);
1616
1536
if (!in_area(ptr, old_heap, old_heap_size)) {
1617
1537
if (in_area(ptr, new_heap, new_heap_size)) {
1620
1540
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
1621
if (in_area(ptr, qb->mem, qb->size*sizeof(Eterm))) {
1541
if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
1627
1547
case TAG_PRIMARY_LIST:
1628
ptr = (Eterm *) val;
1548
ptr = (Eterm *) EXPAND_POINTER(val);
1629
1549
if (!in_area(ptr, old_heap, old_heap_size)) {
1630
1550
if (in_area(ptr, new_heap, new_heap_size)) {
1633
1553
for (qb = MBUF(p); qb != NULL; qb = qb->next) {
1634
if (in_area(ptr, qb->mem, qb->size*sizeof(Eterm))) {
1554
if (in_area(ptr, qb->mem, qb->alloc_size*sizeof(Eterm))) {
1848
* Go through the subset of the root set that is allowed to
1849
* reference data in heap fragments and move data from heap fragments
1854
n_htop = collect_root_array(p, n_htop, objv, nobj);
1856
if (is_not_immed(p->fvalue)) {
1857
n_htop = collect_root_array(p, n_htop, &p->fvalue, 1);
1859
if (is_not_immed(p->ftrace)) {
1860
n_htop = collect_root_array(p, n_htop, &p->ftrace, 1);
1862
if (is_not_immed(p->seq_trace_token)) {
1863
n_htop = collect_root_array(p, n_htop, &p->seq_trace_token, 1);
1865
if (is_not_immed(p->group_leader)) {
1866
n_htop = collect_root_array(p, n_htop, &p->group_leader, 1);
1870
* Go through the message queue, move everything that is in one of the
1871
* heap fragments to our new heap.
1874
for (mp = p->msg.first; mp != NULL; mp = mp->next) {
1876
* In most cases, mp->data.attached points to a heap fragment which is
1877
* self-contained and we will copy it to the heap at the
1878
* end of the GC to avoid scanning it.
1880
* In a few cases, however, such as in process_info(Pid, messages)
1881
* and trace_delivered/1, a new message points to a term that has
1882
* been allocated by HAlloc() and mp->data.attached is NULL. Therefore
1883
* we need this loop.
1885
if (mp->data.attached == NULL) {
1886
n_htop = collect_root_array(p, n_htop, mp->m, 2);
1891
* Now all references in the root set point to the new heap. However,
1892
* many references on the new heap point to heap fragments.
1798
* Move the heap fragments to the new heap. Note that no GC is done on
1799
* the heap fragments. Any garbage will thus be moved as well and survive
1896
while (qb != NULL) {
1897
frag_begin = (char *) qb->mem;
1898
frag_size = qb->size * sizeof(Eterm);
1803
while (qb != NULL) {
1804
frag_size = qb->used_size * sizeof(Eterm);
1899
1805
if (frag_size != 0) {
1900
n_htop = sweep_one_area(n_hstart, n_htop, frag_begin, frag_size);
1806
frag_begin = (char *) qb->mem;
1807
n_htop = move_one_area(n_htop, frag_begin, frag_size);
2082
1989
HEAP_SIZE(p) = new_sz;
2086
next_vheap_size(Uint vheap, Uint vheap_sz) {
2087
if (vheap < H_MIN_SIZE) {
2092
if (vheap > vheap_sz) {
2093
return erts_next_heap_size(2*vheap, 0);
2096
if ( vheap < vheap_sz/2) {
2097
return (Uint)vheap_sz*3/4;
1993
do_next_vheap_size(Uint64 vheap, Uint64 vheap_sz) {
1997
* vheap_sz ======================
2000
* ----------------------
2002
* vheap 25 - 75% same
2003
* ----------------------
2005
* vheap ~ - 25% shrink
2007
* ----------------------
2010
if ((Uint64) vheap/3 > (Uint64) (vheap_sz/4)) {
2011
Uint64 new_vheap_sz = vheap_sz;
2013
while((Uint64) vheap/3 > (Uint64) (vheap_sz/4)) {
2014
/* the golden ratio = 1.618 */
2015
new_vheap_sz = (Uint64) vheap_sz * 1.618;
2016
if (new_vheap_sz < vheap_sz ) {
2019
vheap_sz = new_vheap_sz;
2025
if (vheap < (Uint64) (vheap_sz/4)) {
2026
return (vheap_sz >> 1);
2100
2029
return vheap_sz;
2105
sweep_proc_externals(Process *p, int fullsweep)
2107
ExternalThing** prev;
2112
if (fullsweep == 0) {
2113
oh = (char *) OLD_HEAP(p);
2114
oh_size = (char *) OLD_HEND(p) - oh;
2117
prev = &MSO(p).externals;
2118
ptr = MSO(p).externals;
2121
Eterm* ppt = (Eterm *) ptr;
2123
if (IS_MOVED(*ppt)) { /* Object is alive */
2124
ExternalThing* ro = external_thing_ptr(*ppt);
2126
*prev = ro; /* Patch to moved pos */
2129
} else if (in_area(ppt, oh, oh_size)) {
2131
* Object resides on old heap, and we just did a
2132
* generational collection - keep object in list.
2136
} else { /* Object has not been moved - deref it */
2137
erts_deref_node_entry(ptr->node);
2138
*prev = ptr = ptr->next;
2141
ASSERT(*prev == NULL);
2145
sweep_proc_funs(Process *p, int fullsweep)
2152
if (fullsweep == 0) {
2153
oh = (char *) OLD_HEAP(p);
2154
oh_size = (char *) OLD_HEND(p) - oh;
2157
prev = &MSO(p).funs;
2161
Eterm* ppt = (Eterm *) ptr;
2163
if (IS_MOVED(*ppt)) { /* Object is alive */
2164
ErlFunThing* ro = (ErlFunThing *) fun_val(*ppt);
2166
*prev = ro; /* Patch to moved pos */
2169
} else if (in_area(ppt, oh, oh_size)) {
2171
* Object resides on old heap, and we just did a
2172
* generational collection - keep object in list.
2176
} else { /* Object has not been moved - deref it */
2177
ErlFunEntry* fe = ptr->fe;
2179
*prev = ptr = ptr->next;
2180
if (erts_refc_dectest(&fe->refc, 0) == 0) {
2181
erts_erase_fun_entry(fe);
2185
ASSERT(*prev == NULL);
2034
next_vheap_size(Process* p, Uint64 vheap, Uint64 vheap_sz) {
2035
Uint64 new_vheap_sz = do_next_vheap_size(vheap, vheap_sz);
2036
return new_vheap_sz < p->min_vheap_size ? p->min_vheap_size : new_vheap_sz;
2188
2039
struct shrink_cand_data {
2189
ProcBin* new_candidates;
2190
ProcBin* new_candidates_end;
2191
ProcBin* old_candidates;
2040
struct erl_off_heap_header* new_candidates;
2041
struct erl_off_heap_header* new_candidates_end;
2042
struct erl_off_heap_header* old_candidates;
2192
2043
Uint no_of_candidates;
2193
2044
Uint no_of_active;
2196
2047
static ERTS_INLINE void
2197
2048
link_live_proc_bin(struct shrink_cand_data *shrink,
2049
struct erl_off_heap_header*** prevppp,
2050
struct erl_off_heap_header** currpp,
2202
ProcBin *pbp = *pbpp;
2053
ProcBin *pbp = (ProcBin*) *currpp;
2054
ASSERT(**prevppp == *currpp);
2056
*currpp = pbp->next;
2206
2057
if (pbp->flags & (PB_ACTIVE_WRITER|PB_IS_WRITABLE)) {
2207
2058
ASSERT(((pbp->flags & (PB_ACTIVE_WRITER|PB_IS_WRITABLE))
2208
2059
== (PB_ACTIVE_WRITER|PB_IS_WRITABLE))
2238
/* Not a shrink candidate; keep in original mso list */
2090
/* Not a shrink candidate; keep in original mso list */
2240
2091
*prevppp = &pbp->next;
2246
sweep_proc_bins(Process *p, int fullsweep)
2096
sweep_off_heap(Process *p, int fullsweep)
2248
2098
struct shrink_cand_data shrink = {0};
2099
struct erl_off_heap_header* ptr;
2100
struct erl_off_heap_header** prev;
2103
Uint64 bin_vheap = 0;
2105
int seen_mature = 0;
2256
2108
if (fullsweep == 0) {
2257
oh = (char *) OLD_HEAP(p);
2258
oh_size = (char *) OLD_HEND(p) - oh;
2109
oheap = (char *) OLD_HEAP(p);
2110
oheap_sz = (char *) OLD_HEND(p) - oheap;
2261
2113
BIN_OLD_VHEAP(p) = 0;
2267
* Note: In R7 we no longer force a fullsweep when we find binaries
2268
* on the old heap. The reason is that with the introduction of the
2269
* bit syntax we can expect binaries to be used a lot more. Note that
2270
* in earlier releases a brand new binary (or any other term) could
2271
* be put on the old heap during a gen-gc fullsweep, but this is
2272
* no longer the case in R7.
2275
Eterm* ppt = (Eterm *) ptr;
2277
if (IS_MOVED(*ppt)) { /* Object is alive */
2278
bin_vheap += ptr->size / sizeof(Eterm);
2279
ptr = (ProcBin*) binary_val(*ppt);
2280
link_live_proc_bin(&shrink,
2283
!in_area(ptr, oh, oh_size));
2284
} else if (in_area(ppt, oh, oh_size)) {
2286
* Object resides on old heap, and we just did a
2287
* generational collection - keep object in list.
2289
BIN_OLD_VHEAP(p) += ptr->size / sizeof(Eterm); /* for binary gc (words)*/
2290
link_live_proc_bin(&shrink, &prev, &ptr, 0);
2291
} else { /* Object has not been moved - deref it */
2295
if (erts_refc_dectest(&bptr->refc, 0) == 0)
2296
erts_bin_free(bptr);
2301
if (BIN_OLD_VHEAP(p) >= BIN_OLD_VHEAP_SZ(p)) {
2302
FLAGS(p) |= F_NEED_FULLSWEEP;
2305
BIN_VHEAP_SZ(p) = next_vheap_size(bin_vheap, BIN_VHEAP_SZ(p));
2306
BIN_OLD_VHEAP_SZ(p) = next_vheap_size(BIN_OLD_VHEAP(p), BIN_OLD_VHEAP_SZ(p));
2307
MSO(p).overhead = bin_vheap;
2115
prev = &MSO(p).first;
2118
/* Firts part of the list will reside on the (old) new-heap.
2119
* Keep if moved, otherwise deref.
2122
if (IS_MOVED_BOXED(ptr->thing_word)) {
2123
ASSERT(!in_area(ptr, oheap, oheap_sz));
2124
*prev = ptr = (struct erl_off_heap_header*) boxed_val(ptr->thing_word);
2125
ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
2126
if (ptr->thing_word == HEADER_PROC_BIN) {
2127
int to_new_heap = !in_area(ptr, oheap, oheap_sz);
2128
ASSERT(to_new_heap == !seen_mature || (!to_new_heap && (seen_mature=1)));
2130
bin_vheap += ptr->size / sizeof(Eterm);
2132
BIN_OLD_VHEAP(p) += ptr->size / sizeof(Eterm); /* for binary gc (words)*/
2134
link_live_proc_bin(&shrink, &prev, &ptr, to_new_heap);
2141
else if (!in_area(ptr, oheap, oheap_sz)) {
2143
switch (thing_subtag(ptr->thing_word)) {
2144
case REFC_BINARY_SUBTAG:
2146
Binary* bptr = ((ProcBin*)ptr)->val;
2147
if (erts_refc_dectest(&bptr->refc, 0) == 0) {
2148
erts_bin_free(bptr);
2154
ErlFunEntry* fe = ((ErlFunThing*)ptr)->fe;
2155
if (erts_refc_dectest(&fe->refc, 0) == 0) {
2156
erts_erase_fun_entry(fe);
2161
ASSERT(is_external_header(ptr->thing_word));
2162
erts_deref_node_entry(((ExternalThing*)ptr)->node);
2164
*prev = ptr = ptr->next;
2166
else break; /* and let old-heap loop continue */
2169
/* The rest of the list resides on old-heap, and we just did a
2170
* generational collection - keep objects in list.
2173
ASSERT(in_area(ptr, oheap, oheap_sz));
2174
ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
2175
if (ptr->thing_word == HEADER_PROC_BIN) {
2176
BIN_OLD_VHEAP(p) += ptr->size / sizeof(Eterm); /* for binary gc (words)*/
2177
link_live_proc_bin(&shrink, &prev, &ptr, 0);
2180
ASSERT(is_fun_header(ptr->thing_word) ||
2181
is_external_header(ptr->thing_word));
2188
BIN_OLD_VHEAP_SZ(p) = next_vheap_size(p, BIN_OLD_VHEAP(p) + MSO(p).overhead, BIN_OLD_VHEAP_SZ(p));
2190
BIN_VHEAP_SZ(p) = next_vheap_size(p, bin_vheap, BIN_VHEAP_SZ(p));
2191
MSO(p).overhead = bin_vheap;
2192
BIN_VHEAP_MATURE(p) = bin_vheap;
2310
2195
* If we got any shrink candidates, check them out.
2313
2198
if (shrink.no_of_candidates) {
2314
ProcBin *candlist[] = {shrink.new_candidates, shrink.old_candidates};
2199
ProcBin *candlist[] = { (ProcBin*)shrink.new_candidates,
2200
(ProcBin*)shrink.old_candidates };
2315
2201
Uint leave_unused = 0;
2325
2211
for (i = 0; i < sizeof(candlist)/sizeof(candlist[0]); i++) {
2327
for (ptr = candlist[i]; ptr; ptr = ptr->next) {
2328
Uint new_size = ptr->size;
2213
for (pb = candlist[i]; pb; pb = (ProcBin*)pb->next) {
2214
Uint new_size = pb->size;
2330
2216
if (leave_unused) {
2331
2217
new_size += (new_size * 100) / leave_unused;
2332
2218
/* Our allocators are 8 byte aligned, i.e., shrinking with
2333
2219
less than 8 bytes will have no real effect */
2334
if (new_size + 8 >= ptr->val->orig_size)
2220
if (new_size + 8 >= pb->val->orig_size)
2338
ptr->val = erts_bin_realloc(ptr->val, new_size);
2339
ptr->val->orig_size = new_size;
2340
ptr->bytes = (byte *) ptr->val->orig_bytes;
2224
pb->val = erts_bin_realloc(pb->val, new_size);
2225
pb->val->orig_size = new_size;
2226
pb->bytes = (byte *) pb->val->orig_bytes;
2346
2232
* We now potentially have the mso list divided into three lists:
2347
2233
* - shrink candidates on new heap (inactive writable with unused data)
2348
2234
* - shrink candidates on old heap (inactive writable with unused data)
2349
* - other binaries (read only + active writable ...)
2235
* - other binaries (read only + active writable ...) + funs and externals
2351
2237
* Put them back together: new candidates -> other -> old candidates
2352
2238
* This order will ensure that the list only refers from new
2353
2239
* generation to old and never from old to new *which is important*.
2355
2241
if (shrink.new_candidates) {
2356
if (prev == &MSO(p).mso) /* empty other binaries list */
2242
if (prev == &MSO(p).first) /* empty other binaries list */
2357
2243
prev = &shrink.new_candidates_end->next;
2359
shrink.new_candidates_end->next = MSO(p).mso;
2360
MSO(p).mso = shrink.new_candidates;
2245
shrink.new_candidates_end->next = MSO(p).first;
2246
MSO(p).first = shrink.new_candidates;
2364
2249
*prev = shrink.old_candidates;
2623
2471
Eterm *oheap = (Eterm *) OLD_HEAP(p);
2624
2472
Eterm *ohtop = (Eterm *) OLD_HTOP(p);
2474
union erl_off_heap_ptr u;
2631
for (pb = MSO(p).mso; pb; pb = pb->next) {
2632
Eterm *ptr = (Eterm *) pb;
2633
long refc = erts_refc_read(&pb->val->refc, 1);
2477
for (u.hdr = MSO(p).first; u.hdr; u.hdr = u.hdr->next) {
2479
switch (thing_subtag(u.hdr->thing_word)) {
2480
case REFC_BINARY_SUBTAG:
2481
refc = erts_refc_read(&u.pb->val->refc, 1);
2484
refc = erts_refc_read(&u.fun->fe->refc, 1);
2486
case EXTERNAL_PID_SUBTAG:
2487
case EXTERNAL_PORT_SUBTAG:
2488
case EXTERNAL_REF_SUBTAG:
2489
refc = erts_refc_read(&u.ext->node->refc, 1);
2492
ASSERT(!!"erts_check_off_heap2: Invalid thing_word");
2634
2494
ERTS_CHK_OFFHEAP_ASSERT(refc >= 1);
2495
#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_LIST
2496
ERTS_CHK_OFFHEAP_ASSERT(!(u.hdr->thing_word & ERTS_EXTERNAL_VISITED_BIT));
2497
u.hdr->thing_word |= ERTS_OFFHEAP_VISITED_BIT;
2636
ERTS_CHK_OFFHEAP_ASSERT(oheap <= ptr && ptr < ohtop);
2500
ERTS_CHK_OFFHEAP_ASSERT(oheap <= u.ep && u.ep < ohtop);
2638
else if (oheap <= ptr && ptr < ohtop)
2502
else if (oheap <= u.ep && u.ep < ohtop)
2641
ERTS_CHK_OFFHEAP_ASSERT(within2(ptr, p, htop));
2505
ERTS_CHK_OFFHEAP_ASSERT(within2(u.ep, p, htop));
2646
for (eft = MSO(p).funs; eft; eft = eft->next) {
2647
Eterm *ptr = (Eterm *) eft;
2648
long refc = erts_refc_read(&eft->fe->refc, 1);
2649
ERTS_CHK_OFFHEAP_ASSERT(refc >= 1);
2651
ERTS_CHK_OFFHEAP_ASSERT(oheap <= ptr && ptr < ohtop);
2652
else if (oheap <= ptr && ptr < ohtop)
2655
ERTS_CHK_OFFHEAP_ASSERT(within2(ptr, p, htop));
2659
for (et = MSO(p).externals; et; et = et->next) {
2660
Eterm *ptr = (Eterm *) et;
2661
long refc = erts_refc_read(&et->node->refc, 1);
2662
ERTS_CHK_OFFHEAP_ASSERT(refc >= 1);
2663
#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_EXTERNAL_LIST
2664
ERTS_CHK_OFFHEAP_ASSERT(!(et->header & ERTS_EXTERNAL_VISITED_BIT));
2667
ERTS_CHK_OFFHEAP_ASSERT(oheap <= ptr && ptr < ohtop);
2668
else if (oheap <= ptr && ptr < ohtop)
2671
ERTS_CHK_OFFHEAP_ASSERT(within2(ptr, p, htop));
2672
#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_EXTERNAL_LIST
2673
et->header |= ERTS_EXTERNAL_VISITED_BIT;
2677
#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_EXTERNAL_LIST
2678
for (et = MSO(p).externals; et; et = et->next)
2679
et->header &= ~ERTS_EXTERNAL_VISITED_BIT;
2509
#ifdef ERTS_OFFHEAP_DEBUG_CHK_CIRCULAR_EXTERNAL_LIST
2510
for (u.hdr = MSO(p).first; u.hdr; u.hdr = u.hdr->next)
2511
u.hdr->thing_word &= ~ERTS_OFFHEAP_VISITED_BIT;