Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GC detects leaks for items that are added to the List field of the base class, when the class scope allocated. #2005

Open
Zeon8 opened this issue Aug 6, 2024 · 2 comments

Comments

@Zeon8
Copy link

Zeon8 commented Aug 6, 2024

I have a base class that defines append list and derived class that have a method that allocates new items and adds it to the list.
However, after allocating the class on stack and calling that method, after a few seconds GC detects memory leaks, even though the list field has a destructor that deletes all items.

Code example
class Program
{
	static int Main(String[] args)
	{
		var test = scope Test2();
		test.AddItems();
		while(true)
		{
			Thread.Sleep(500);
		}
	}
}

class Test1
{
	public append List<TestItem> Items ~ ClearAndDeleteItems!(_);
}

class Test2 : Test1
{
	public void AddItems()
	{
		Items.Add(new TestItem());
		Items.Add(new TestItem());
	}
}

class TestItem {}

Output: image

Example project

@Grimelios
Copy link

Think I've found another example of this problem:

public struct A : IDisposable
{
    public List<float> list = new .();

    public void Dispose()
    {
        delete this.list;
    }
}

public struct B : A
{
}

public class C
{
    public B b = .();

    public ~this()
    {
        this.b.Dispose();
    }
}

static
{
    public static void Main()
    {
        C c = new .();

        Console.Write("Sleeping... ");

        System.Threading.Thread.Sleep(3000);

        // Crashes with a memory leak (on A's list) before reaching this call.
        Console.WriteLine("done!");

        delete c;
    }
}

Some additional oddities:

  1. Sleeping for shorter periods of time avoids the crash. From experimentation, 2000 ms seems to be about the threshold where the memory leak stops.
  2. Removing the first Console.Write avoids the crash, even when sleeping for much longer periods of time (say, 10 seconds or longer).
  3. Using an A instance within C (rather than a B instance) avoids the crash:
public class C
{
    // Doesn't leak!
    public A a = .();

    public ~this()
    {
        this.a.Dispose();
    }
}
  1. Composing A within B rather than inheriting avoids the crash:
public struct B : IDisposable
{
    // Doesn't leak!
    public A a = .();

    public void Dispose()
    {
        this.a.Dispose();
    }
}

@bfiete
Copy link
Collaborator

bfiete commented Sep 28, 2024

4589e7e should fix the @Grimelios issue, which was a simpler and higher-priority fix, but the append object issue remains

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants