Open rozbf opened 1 year ago
Thanks!
I'll think about this a bit. I've been scratching my head on how to best approach this too. Repurposing GC.SuppressFinalize
is definitely an out-of-the-box idea. I'm conflicted because on one hand it kind of is in the spirit of zerolib (no new API that doesn't exist in .NET), but on the other hand the meaning is different.
Perhaps this could involve the NativeMemory class (as without a GC, all zerolib memory is native memory), which has dedicated methods for alloc and free. Although the API ergonomics of those functions leave much to be desired..
on one hand it kind of is in the spirit of zerolib (no new API that doesn't exist in .NET), but on the other hand the meaning is different
I don't think there is a solution that satisfies both. The closest thing C# has to manual memory management is the dispose pattern, which uses the GC.SuppressFinalize
method to inform the garbage collector that the object has been disposed. Repurposing this method seems quite natural and together with the using
keyword supported by the language, it produces something similar to std::unique_ptr
in C++ (but without the ownership checks).
Btw, I also considered this approach:
int id = GC.GetGeneration(obj);
GC.Collect(id, GCCollectionMode.Optimized);
It is more explicit, but the implementation is complicated and it has a much higher overhead in .NET than GC.SuppressFinalize
.
Perhaps this could involve the NativeMemory class
While it might be useful to provide access to the already implemented alloc/free functions via the NativeMemory
class, it doesn't solve the problem of memory leaks when using managed reference types.
How about adding a Method to object
called Free
, which can be called on anything, like ToString
, Equals
or GetHashCode
?
Why not just
unsafe static bool Free(void* ptr)
{
...
}
?
Since it ins't .NET, I see no point in creating an "GC" class for this. There is no GC here.
Why not just
See here:
Public API surface that doesn't exist in .NET cannot be added (i.e. source code compilable against zerolib needs to be compilable against .NET).
Shouldn't this be void Free?
Shouldn't this be void Free?
it could return an boolean indicating if the memory was freed or not.
Well if there are memory leaks why not implement a profiler that constantly tracks allocations and deallocations? I mean profilers are possible with native aot now for .NET
Freeing an object that's on the call stack turns your code into a minefield. Your this
pointer now points to freed memory. You can't safely access any class fields or virtual methods at this point.
Also looking through the standard .NET framework, many functions use GC.SuppressFinalize
on objects that aren't ready to actually be freed from memory. For example, the class System.IO.Stream
. Close
calls GC.SuppressFinalize
. Dispose
is implemented by calling the virtual function Close
. So if you call Close
, you can't call Dispose
afterwards without a crash.
Freeing an object that's on the call stack turns your code into a minefield. Your this pointer now points to freed memory. You can't safely access any class fields or virtual methods at this point.
This is no different from C/C++, which also has manual memory management. Originally, I was looking for a dotnet method that takes a ref object
parameter, which could then set the reference to null
, but it's kinda pointless since there may be other references to the deallocated object.
Also looking through the standard .NET framework, many functions use GC.SuppressFinalize on objects that aren't ready to actually be freed from memory. For example, the class System.IO.Stream. Close calls GC.SuppressFinalize. Dispose is implemented by calling the virtual function Close. So if you call Close, you can't call Dispose afterwards without a crash.
Yes, you should not call anything on the freed object. That's a responsibility the developer takes on when working with Zerolib. Zerolib will only work with specially tailored code that follows its constraints. The goal is that the same code should also work perfectly fine when compiled and run with dotnet. The reverse is not necessarily true - you can write code that will work fine with dotnet, but will crash with Zerolib.
Is it possible to use libc's free
function on .NET object?
@iahung2 do you mean consuming windows API implementations? One question that I have idk If it is a practical question or not: But is it possible to reimplement these methods in C# by peeking the windows API implementations?
@iahung2 do you mean consuming windows API implementations? One question that I have idk If it is a practical question or not: But is it possible to reimplement these methods in C# by peeking the windows API implementations?
My question is simple. I have a class called MyClass
. I created an object MyObject
of type MyClass
using new
(heap allocation). As it's said on this thread, any heap allocation is a memory leak because there is no GC. I don't want to have a memory leak. So, I need a way to deallocate the memory. I want to know if something like free(MyObject);
will work or not.
@iahung2 as far as I know, is you can't, allocating in heap isn't preferred and instead of reference types use structs as @MichalStrehovsky suggested to use stack allocation instead of heap, as heap allocation will eventually run out of memory due to no GC, you can try to see if free deallocates or not but this was my understanding from this statement.
https://twitter.com/MStrehovsky/status/1728378901188235301?t=NnuY-TJHNsztAasdfLQAxQ&s=19
@Tajbiul-Rawol My intuition tells me that free
doesn't work. I only asked to be sure. @MichalStrehovsky You must do something. If there is no GC, you must add the ability to do manual memory management. Otherwise, your zerolib
is only a toy, or the use cases of it will be very limited, for example, in resource-constrained environments like UEFI, where heap allocation isn't preferred. But for all other use cases, it's useless.
@iahung2 I agree. The tool feels very limited in what it can do, maybe because it is a brand new tool, but with no manual memory collection system for heap it is severely limiting. @MichalStrehovsky hope you change your decision and implement something to handle deallocations.
I'm going to give heap allocation for nostdlib a shot by overriding the .Net new() operator, as per my comment here: https://github.com/bflattened/bflat/issues/138#issuecomment-194946075
But if anyone wants to beat me to it, please feel free 😉
Here’s another idea: how about overriding GC.KeepAlive() to free an object? (Instead of GC.SuppressFinalize)
It might sound odd at first, but when you think about it, the purpose of KeepAlive is to ensure that the object stays alive until KeepAlive is called. Nobody calling that method should have the expectation that the object would live any longer, so in theory, it should be safe to release at that point.
Currently, when using
--stdlib:zero
, all heap-allocated data are memory leaks since there is no GC.This PR adds the ability to manually free heap-allocated objects using the
GC.SuppressFinalize
method. Why this method?object
.GC.SuppressFinalize
are no-ops unless the type has a finalizer.IDisposable
interface,GC.SuppressFinalize
is typically called at the end when all managed resources have been released. It makes sense to deallocate the object itself at that point.Example:
It is also possible to have nested objects if the
IDisposable
interface is implemented correctly. Arrays also work, but can't implementIDisposable
, so they have to be freed by callingGC.SuppressFinalize
directly.I haven't tested the Linux and UEFI code. Feel free to edit this PR as needed.