diff -r b1e1559f5a45 -r de2e04fe4897 mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Wed Jul 28 13:45:07 2021 +0300 +++ b/mercurial/hgweb/hgwebdir_mod.py Tue Jul 20 17:20:19 2021 +0200 @@ -285,6 +285,7 @@ self.lastrefresh = 0 self.motd = None self.refresh() + self.requests_count = 0 if not baseui: # set up environment for new ui extensions.loadall(self.ui) @@ -341,6 +342,10 @@ self.repos = repos self.ui = u + self.gc_full_collect_rate = self.ui.configint( + b'experimental', b'web.full-garbage-collection-rate' + ) + self.gc_full_collections_done = 0 encoding.encoding = self.ui.config(b'web', b'encoding') self.style = self.ui.config(b'web', b'style') self.templatepath = self.ui.config( @@ -383,12 +388,27 @@ finally: # There are known cycles in localrepository that prevent # those objects (and tons of held references) from being - # collected through normal refcounting. We mitigate those - # leaks by performing an explicit GC on every request. - # TODO remove this once leaks are fixed. - # TODO only run this on requests that create localrepository - # instances instead of every request. - gc.collect() + # collected through normal refcounting. + # In some cases, the resulting memory consumption can + # be tamed by performing explicit garbage collections. + # In presence of actual leaks or big long-lived caches, the + # impact on performance of such collections can become a + # problem, hence the rate shouldn't be set too low. + # See "Collecting the oldest generation" in + # https://devguide.python.org/garbage_collector + # for more about such trade-offs. + rate = self.gc_full_collect_rate + + # this is not thread safe, but the consequence (skipping + # a garbage collection) is arguably better than risking + # to have several threads perform a collection in parallel + # (long useless wait on all threads). + self.requests_count += 1 + if rate > 0 and self.requests_count % rate == 0: + gc.collect() + self.gc_full_collections_done += 1 + else: + gc.collect(generation=1) def _runwsgi(self, req, res): try: