C
C#14mo ago
Sound

✅ Memory leaks and idk how to solve them

Why does the following code produce memory leaks? Shouldn't bg get deleted and then reallocated?
Background bg = new();

while (!Raylib.WindowShouldClose())
{
Raylib.BeginDrawing();

bg.Dispose();
bg=new();

Raylib.DrawText(Raylib.GetFPS().ToString(), 10, 10, 20, Color.RED);

Raylib.EndDrawing();
}
Background bg = new();

while (!Raylib.WindowShouldClose())
{
Raylib.BeginDrawing();

bg.Dispose();
bg=new();

Raylib.DrawText(Raylib.GetFPS().ToString(), 10, 10, 20, Color.RED);

Raylib.EndDrawing();
}
bg.Dispose();
bg=new();
bg.Dispose();
bg=new();
Why do these constantly allocate new memory and the old one doesn t get freed up? And how do i make it work?
21 Replies
Sound
Sound14mo ago
//Background.cs

using Raylib_cs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace Sky_Soarer.src
{
class Background : IDisposable
{
// Flag to track whether Dispose has already been called
private bool disposed = false;

// Dispose method to release resources
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

// Dispose(bool disposing) method to release resources conditionally
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources (if any)
}

// Dispose unmanaged resources (if any)

disposed = true;
}
}

// Finalizer (destructor) to handle disposal as a fallback
~Background()
{
Dispose(false);
}

// Constants
private const int Speed = 50;

// Fields
private Color color;
private Texture2D texture;
private List<Rectangle> lbounds;
private float m_Scale;

// Properties
private bool IsOffScreen(Rectangle bounds)
{
return bounds.x + bounds.width < 0;
}

private Rectangle Reset(Rectangle bounds)
{
return new Rectangle(texture.width * m_Scale - 1f, 0, texture.width * m_Scale, texture.height * m_Scale);
}
//Background.cs

using Raylib_cs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace Sky_Soarer.src
{
class Background : IDisposable
{
// Flag to track whether Dispose has already been called
private bool disposed = false;

// Dispose method to release resources
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

// Dispose(bool disposing) method to release resources conditionally
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources (if any)
}

// Dispose unmanaged resources (if any)

disposed = true;
}
}

// Finalizer (destructor) to handle disposal as a fallback
~Background()
{
Dispose(false);
}

// Constants
private const int Speed = 50;

// Fields
private Color color;
private Texture2D texture;
private List<Rectangle> lbounds;
private float m_Scale;

// Properties
private bool IsOffScreen(Rectangle bounds)
{
return bounds.x + bounds.width < 0;
}

private Rectangle Reset(Rectangle bounds)
{
return new Rectangle(texture.width * m_Scale - 1f, 0, texture.width * m_Scale, texture.height * m_Scale);
}
// Constructor
public Background()
{
color = Color.BEIGE;
texture = Raylib.LoadTexture("./resources/background.png");
m_Scale = Raylib.GetRenderHeight() / (float)texture.height;
lbounds = new();
lbounds.Add(new Rectangle(0, 0, texture.width * m_Scale, texture.height * m_Scale));
lbounds.Add(new Rectangle(texture.width * m_Scale, 0, texture.width * m_Scale, texture.height * m_Scale));
}

// Update method
public void Update(float deltaTime)
{
for(int i = 0; i < lbounds.Count; i++)
{
if (IsOffScreen(lbounds[i]))
{
lbounds[i] = Reset(lbounds[i]);
}
lbounds[i] = new Rectangle(lbounds[i].x - Speed * deltaTime, 0, texture.width * m_Scale, texture.height * m_Scale);
}
}

// Draw method
public void Draw()
{
foreach(Rectangle r in lbounds)
{
if (texture.width == 0)
{
Raylib.DrawRectangleRec(r, color);
}

//Raylib.DrawTexture(texture, (int)r.x, (int)(r.y), Color.WHITE);
Raylib.DrawTextureEx(texture, new Vector2(r.x, (r.y)), 0, m_Scale, Color.WHITE);
}

}
}
}
// Constructor
public Background()
{
color = Color.BEIGE;
texture = Raylib.LoadTexture("./resources/background.png");
m_Scale = Raylib.GetRenderHeight() / (float)texture.height;
lbounds = new();
lbounds.Add(new Rectangle(0, 0, texture.width * m_Scale, texture.height * m_Scale));
lbounds.Add(new Rectangle(texture.width * m_Scale, 0, texture.width * m_Scale, texture.height * m_Scale));
}

// Update method
public void Update(float deltaTime)
{
for(int i = 0; i < lbounds.Count; i++)
{
if (IsOffScreen(lbounds[i]))
{
lbounds[i] = Reset(lbounds[i]);
}
lbounds[i] = new Rectangle(lbounds[i].x - Speed * deltaTime, 0, texture.width * m_Scale, texture.height * m_Scale);
}
}

// Draw method
public void Draw()
{
foreach(Rectangle r in lbounds)
{
if (texture.width == 0)
{
Raylib.DrawRectangleRec(r, color);
}

//Raylib.DrawTexture(texture, (int)r.x, (int)(r.y), Color.WHITE);
Raylib.DrawTextureEx(texture, new Vector2(r.x, (r.y)), 0, m_Scale, Color.WHITE);
}

}
}
}
Jaiganésh
Jaiganésh14mo ago
Does bg have other references? Also note that the old one won't get deleted immediately when you set bg = new(), it will get deleted when the GC runs the next time. Not a good idea generally but could try forcing a garbage collection immediately by calling GC.Collect to check if it does get deleted.
ero
ero14mo ago
you never even use bg? what's the point? the Dispose method also doesn't do anything
cap5lut
cap5lut14mo ago
u probably have a memory leak in loading the texture on every frame when the GC releases the memory for the not rooted Background instance (as in its not referenced anywhere anymore) is up to the GC itself, it could be hundres and thousand frames after unreferencing it. but even that releasing memory, doesnt mean the physcally used memory will shrink. the .net runtime uses its own memory management model on top of the "normal" memory, so even if that heap is mostly empty, ur application could still consume like 2 gb worth of ram from the OS' perspective https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector#high-memory-percent explains quite some stuff about it
Garbage collector config settings - .NET
Learn about run-time settings for configuring how the garbage collector manages memory for .NET Core apps.
cap5lut
cap5lut14mo ago
i assumed that that code runs somewhere every frame its also not good to create new instances every frame, try to reuse them
u probably have a memory leak in loading the texture on every frame
Raylib.UnloadTexture(Texture2D) (https://github.com/ChrisDill/Raylib-cs/blob/master/Raylib-cs/interop/Raylib.cs#L1587) seems to be the method u would need to use free the unmanaged resources
Sound
Sound14mo ago
look. i have a game class that is the whole match itself. it uses background, pipes and bird (these are created new every time i create a new instance of game) when i want to start a new game, i just want to reinitialize that instance game = new Game(); but this causes memory leaks i need a way to delete the memory used by game, and then reallocate it in cpp i would do
delete game;
game = new Game();
delete game;
game = new Game();
or i would use std::unique_ptr
cap5lut
cap5lut14mo ago
as explained u cant really force the GC to do it when u want to, thats the whole point of managed memory systems. as soon as the instance isnt referenced anymore, the GC will sooner or later collect it. when, is up to the GC
Game game;
game = new Game(); // instance A is created and referenced, so not ready for collection
game = new Game(); // instance B is created and referenced, so not ready for collection. BUT instance A is not referenced anymore so it will sooner or later will be collected
Game game;
game = new Game(); // instance A is created and referenced, so not ready for collection
game = new Game(); // instance B is created and referenced, so not ready for collection. BUT instance A is not referenced anymore so it will sooner or later will be collected
if u want to free underlying resources (because they somewhere down the line use native memory), u should use the IDisposable pattern
Jaiganésh
Jaiganésh14mo ago
A GC doesn't immediately delete after it's no longer used like a RAII smart pointer does.
Sound
Sound14mo ago
well, apparently it never deletes it
Jaiganésh
Jaiganésh14mo ago
If it still leaks after the GC runs, then probably you have a reference to it somewhere that you aren't realising.
cap5lut
cap5lut14mo ago
what makes u think that? just the memory usage from taskmgr or which ever tool u use?
Jaiganésh
Jaiganésh14mo ago
Try calling GC.Collect and check if it's gone.
cap5lut
cap5lut14mo ago
(question was meant for Sound btw)
Jaiganésh
Jaiganésh14mo ago
The memory usage window in the Visual Studio debugger can show all the managed objects not collected yet.
Sound
Sound14mo ago
the memory usage graph in vs
cap5lut
cap5lut14mo ago
that? that includes unused heap memory
Jaiganésh
Jaiganésh14mo ago
At the bottom you should see a memory usage tab. And then you can try hitting collect or snapshot (don't remember the name exactly).
cap5lut
cap5lut14mo ago
on 3 a double click, then ya can inspect
cap5lut
cap5lut14mo ago
im not sure if Raylib.LoadTexture("./resources/background.png"); allocates RAM down the line, but its at least a VRAM memory leak as explained earlier
Jaiganésh
Jaiganésh14mo ago
You could click on the objects and see if an instance of Background is still there.
Accord
Accord14mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.