C
C#2y 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
SoundOP2y 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ésh2y 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
ero2y ago
you never even use bg? what's the point? the Dispose method also doesn't do anything
cap5lut
cap5lut2y 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
cap5lut2y 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
SoundOP2y 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
cap5lut2y 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ésh2y ago
A GC doesn't immediately delete after it's no longer used like a RAII smart pointer does.
Sound
SoundOP2y ago
well, apparently it never deletes it
Jaiganésh
Jaiganésh2y ago
If it still leaks after the GC runs, then probably you have a reference to it somewhere that you aren't realising.
cap5lut
cap5lut2y ago
what makes u think that? just the memory usage from taskmgr or which ever tool u use?
Jaiganésh
Jaiganésh2y ago
Try calling GC.Collect and check if it's gone.
cap5lut
cap5lut2y ago
(question was meant for Sound btw)
Jaiganésh
Jaiganésh2y ago
The memory usage window in the Visual Studio debugger can show all the managed objects not collected yet.
Sound
SoundOP2y ago
the memory usage graph in vs
cap5lut
cap5lut2y ago
that? that includes unused heap memory
Jaiganésh
Jaiganésh2y 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
cap5lut2y ago
on 3 a double click, then ya can inspect
cap5lut
cap5lut2y 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ésh2y ago
You could click on the objects and see if an instance of Background is still there.
Accord
Accord2y 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.

Did you find this page helpful?