Bug 60168 - SGen fails to trigger major heap collections
Summary: SGen fails to trigger major heap collections
Status: RESOLVED FIXED
Alias: None
Product: Runtime
Classification: Mono
Component: GC ()
Version: 5.4 (2017-06)
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2017-10-13 03:08 UTC by ecroyd
Modified: 2017-11-01 19:43 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description ecroyd 2017-10-13 03:08:17 UTC
There appears to be a memory leak (of sorts) in SGen. I was attempting to track down a memory leak and found this issue instead. 

The C# code below was compiled using VS2015 with a Target framework of 4.6.1. It was then run against mono-5.2.0.224-x64. It appears that if we hold on to an object, perform a GC.Collect(0) and then remove the reference to the object, no GC_MAJORs are triggered.

After adding these commands:
SET MONO_LOG_LEVEL=debug
SET MONO_LOG_MASK=gc

the only output is in the format of these lines, without any mention of a GC_MAJOR:

Mono: GC_MINOR: (user request) time 0.08ms, stw 0.08ms promoted 1K major size: 71312K in use: 69724K los size: 0K in use: 0K
Mono: GC_MINOR: (user request) time 0.08ms, stw 0.09ms promoted 1K major size: 71312K in use: 69726K los size: 0K in use: 0K
Mono: GC_MINOR: (user request) time 0.08ms, stw 0.08ms promoted 1K major size: 71328K in use: 69727K los size: 0K in use: 0K
Mono: GC_MINOR: (user request) time 0.12ms, stw 0.12ms promoted 1K major size: 71328K in use: 69728K los size: 0K in use: 0K

I am unaware of any code I include in my real world app calling GC.Collect(0). However I feel there might be a another similar scenario where Mono triggers a minor heap collection without checking if a major heap collection is required. 




// ------------- Source Code ------------- //
using System;
using System.Collections.Generic;

namespace ExampleApp {
	class Program {
		static void Main(string[] args) {
			Queue<byte[]> queue = new Queue<byte[]>();
			queue.Enqueue(new byte[1000]);

			while (true) {
				queue.Enqueue(new byte[1000]);
				queue.Dequeue();
				GC.Collect(0);
			}
		}
	}
}
Comment 1 Vlad Brezae 2017-10-30 20:20:32 UTC
Objects are collected when the heap gets too big. If no objects are promoted then there is no need to collect major objects.
Comment 2 ecroyd 2017-10-30 21:44:55 UTC
Your comment implies that no objects were promoted. After re-running it on Mono Windows 5.4.0. The lines below were outputted. They show:

- "promoted 1K" indicating that 1K was promoted. 
- "major size: 1052800K" indicating that one Gigabyte of memory was used by Mono

It is also worthwhile to note that the code above should only ever have a max of two byte[1000] instances in memory at any time.

GC log lines:

Mono: GC_MINOR: (user request) time 0.69ms, stw 0.69ms promoted 1K major size: 1052800K in use: 1033004K los size: 0K in use: 0K
Mono: GC_MINOR: (user request) time 0.73ms, stw 0.74ms promoted 1K major size: 1052800K in use: 1033005K los size: 0K in use: 0K
Mono: GC_MINOR: (user request) time 0.75ms, stw 0.75ms promoted 1K major size: 1052800K in use: 1033006K los size: 0K in use: 0K
Mono: GC_MINOR: (user request) time 0.68ms, stw 0.68ms promoted 1K major size: 1052800K in use: 1033007K los size: 0K in use: 0K

Version: 

Mono JIT compiler version 5.4.0 (Visual Studio built mono)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
        TLS:           normal
        SIGSEGV:       normal
        Notification:  Thread + polling
        Architecture:  amd64
        Disabled:      none
        Misc:          softdebug
        LLVM:          supported, not enabled.
        GC:            sgen
Comment 3 Vlad Brezae 2017-10-30 23:02:47 UTC
I put the GC.Collect between the Enqueue and the Dequeue and I can reproduce the issue of unbounded memory growth due to no majors, for which there is a fix.

Just because the object is logically dead as far as you are concerned, doesn't mean that the gc sees it as it being dead. A temporary reference to it might still be around somewhere, which would even lead to its promotion. Objects that are no longer alive will be swept in the end once the heap reaches certain sizes (for default configuration, first step would be 16MB). You should not rely on objects being immediately reclaimed.
Comment 4 Vlad Brezae 2017-11-01 19:43:50 UTC
Fixed on master by https://github.com/mono/mono/commit/32d2f28518ecfad2e8f3556f9c120418418fc4ec.

Thanks for the report. Please let me know if you encounter any additional issues.