[sgen] Improve memory usage with the concurrent collector
authorVlad Brezae <brezaevlad@gmail.com>
Wed, 3 Feb 2016 22:54:12 +0000 (00:54 +0200)
committerVlad Brezae <brezaevlad@gmail.com>
Mon, 8 Feb 2016 14:22:12 +0000 (16:22 +0200)
commit69915c0c988fae02b7f8fa80a0f99d61a9b710bf
tree1fa6799b951586191594627edd737e35354fd3e8
parent35889d4bc2be1f5e7a68623e3fc33cf24080c2ee
[sgen] Improve memory usage with the concurrent collector

The concurrent collector typically uses more memory because more memory is allocated during the concurrent phase, which also leads to more objects surviving the major collections and higher allowances. We improved the memory situation by forcing synchronous finishing of the concurrent mark if too much memory is allocated during the concurrent mark phase. Still, this doesn't tackle the underlying problem. Even if we have less memory allocated during the CMS phase, some is still allocated and leads to increased overall memory usage. And how much memory growth should we allow in the first place ? The even greater problem is that it appears to force a lot of synchronous finishes, significantly increasing finishing pause times.

This commit attempts to solve this problem by following an ideal mathematical approach. We use the following notations : H - heap size at start of major collection, Gs - heap growth during sync collection, Gc - heap growth during conc collection, s - survival rate of the collection, a - allowance ratio (0.33), Ts - trigger for next major sync, Tc - trigger for next major conc.

Assume we have the heap size H at the start of a major collection. For the two types of majors we would have :
Ts = (H + Gs) * s * (a + 1)
Tc = (H + Gc) * s * (a + 1)

Obviously the problem is that Gc > Gs, which leads to Tc > Ts and higher memory usage. In order to have the two triggers equal we need to find an X that we can subtract from Tc to obtain Ts. Considering Gs = 0 (only a few nursery objects are promoted during a major sync collection) and similar survival rates for the two types of collection, we get X = Gc * s * (a + 1). In order to get equal triggers we would need to subtract this value from the allowance computed after a concurrent collection.

The more the heap grows, the more X grows. If X gets bigger than the available starting allowance ((H + Gc) * s * a), it would mean that we are trying to set a trigger that it is lower than the actual heap size at the end of the collection (which means we already exceeded the target memory usage). In order to avoid this problem we need to enforce Tc - X > (H + Gc) * s. Doing the substitutions we get Gc < a * H. When the heap growth exceeds this value it means we need to force the finish of the concurrent collection.
mono/sgen/sgen-conf.h
mono/sgen/sgen-gc.c
mono/sgen/sgen-memory-governor.c
mono/sgen/sgen-memory-governor.h