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

[API Proposal]: Let ALC unload by String Name #111197

Open
aloksharma1 opened this issue Jan 8, 2025 · 7 comments
Open

[API Proposal]: Let ALC unload by String Name #111197

aloksharma1 opened this issue Jan 8, 2025 · 7 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-AssemblyLoader-coreclr untriaged New issue has not been triaged by the area owner

Comments

@aloksharma1
Copy link

aloksharma1 commented Jan 8, 2025

Background and motivation

Hello Team Dotnet,

i want to know if this is feasible to add as feature in dotnet to provide a mechanism in to unload an AssemblyLoadContext (ALC) by specifying its friendly string name (as originally passed to the constructor) without the need to keep a direct reference to the AssemblyLoadContext instance.

as we can do this
var alc = new AssemblyLoadContext("ALC_Key", isCollectible: true);

we would like to have a similar method like AssemblyLoadContext.UnloadByName("ALC_Key"). The intent behind this is if alc is stilll loaded it goes into unloading and we dont have to keep alc reference for the same. which is not good if we are marking isCollectible: true as that makes it strongly referenced, defeating the purpose of future unloading of non-used objects.

so, i am thinking something along this can be implemented:

namespace System.Runtime.Loader
{
    public partial class AssemblyLoadContext
    {
        /// <summary>
        /// Attempts to unload the AssemblyLoadContext whose Name matches <paramref name="name"/>.
        /// If found, calls <c>Unload()</c> on that context.
        /// Returns <c>true</c> if the context was found and unload was triggered, otherwise <c>false</c>.
        /// </summary>
        public static bool UnloadByName(string name);
        
        // ...
    }
}

This proposed API would simplify usage of collectible AssemblyLoadContexts in scenarios where the user only tracks them by a friendly name. It reduces code complexity and eliminates the need for the user to maintain references to the ALC for the sole purpose of calling Unload().

Thank you for considering this proposal!

API Proposal

namespace System.Runtime.Loader
{
    public partial class AssemblyLoadContext
    {
        /// <summary>
        /// Attempts to unload the AssemblyLoadContext whose Name matches <paramref name="name"/>.
        /// If found, calls <c>Unload()</c> on that context.
        /// Returns <c>true</c> if the context was found and unload was triggered, otherwise <c>false</c>.
        /// </summary>
        public static bool UnloadByName(string name);
        
        // ...
    }
}

API Usage

bool success = AssemblyLoadContext.UnloadByName("RazorView_2025");
    if (success)
    {
        Console.WriteLine("Unload triggered for ALC with name: RazorView_2025.");
    }
    else
    {
        Console.WriteLine("No ALC found with that name or it was already collected.");
    }

Alternative Designs

No response

Risks

No response

@aloksharma1 aloksharma1 added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Jan 8, 2025
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jan 8, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Jan 8, 2025
@huoyaoyuan
Copy link
Member

This is not actionable at all. The name of ALC is only informative. It's not unique, nor tracked by runtime.

The intent behind this is if alc is stilll loaded it goes into unloading and we dont have to keep alc reference for the same. which is not good if we are marking isCollectible: true as that makes it strongly referenced, defeating the purpose of future unloading of non-used objects.

The Unload method doesn't trigger the collection immediately. Only after every usages are collected, the unload can actually start. The ALC instance would be still reachable internally from the Type objects.

It reduces code complexity and eliminates the need for the user to maintain references to the ALC for the sole purpose of calling Unload().

You don't really need to call Unload at all. Collectible ALC will be unload when it's finalized.

~AssemblyLoadContext()
{
// Use the _unloadLock as a guard to detect the corner case when the constructor of the AssemblyLoadContext was not executed
// e.g. due to the JIT failing to JIT it.
if (_unloadLock != null)
{
// Only valid for a Collectible ALC. Non-collectible ALCs have the finalizer suppressed.
Debug.Assert(IsCollectible);
// We get here only in case the explicit Unload was not initiated.
Debug.Assert(_state != InternalState.Unloading);
InitiateUnload();
}
}

@aloksharma1
Copy link
Author

aloksharma1 commented Jan 8, 2025

@huoyaoyuan thanks for your reply, but in dynamic scenarios like hot swappable plugins or dynamic razor views, unloading an assembly is required manually (in case of a razor view GC can collect it too if its not in use). So for most user it may not be necessary, but these advance cases require assembly unload on demand as well as keep the collectible:true process.

i am aware that collectible assemblies are collected when all references to it are removed, but in above cases we need to do both sometimes, if we are hot swapping we initiate assembly unload and wait and load newer version and a named ALC can make this process smoother.
Same, in case of razor views/pages changes I would like to unload old assembly and load new once to make update take effect.
I dont care if unload is not immediate, having a mechanism where i can wait for actual unload is fine but keeping reference to collectible assembly (for future unloads) defeats the purpose of auto-flushing it out of memory when it was not in use.

hope my use-case scenario is more clear now.

@huoyaoyuan huoyaoyuan added area-AssemblyLoader-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jan 8, 2025
Copy link
Contributor

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

@huoyaoyuan
Copy link
Member

unloading an assembly is required manually (in case of a razor view GC can collect it too if its not in use)

There's not a concept of "manual unloading" or "on demand". The Unload method only marks the ALC to prevent further loading into it. The actual unloading work is always driven by GC when all usages are gone.

if we are hot swapping we initiate assembly unload and wait and load newer version and a named ALC can make this process smoother.

You don't need to name an ALC either. Just create a new one and throw away the old one.

hope my use-case scenario is more clear now.

This proposal is not actionable. The above are explaining why it won't achieve any business purpose.

@jkotas
Copy link
Member

jkotas commented Jan 8, 2025

You can implement this method yourself if you really need it:

    public static bool UnloadByName(string name)
    {
        foreach (var alc in AssemblyLoadContext.All)
        {
            if (alc.Name == name)
            {
                alc.Unload();
                return true;
            }
        }
        return false;
    }

It is exactly what the runtime implementation would look like.

As @huoyaoyuan explained, it is probably not what you want to do.

@aloksharma1
Copy link
Author

@huoyaoyuan @jkotas thanks for the explanation, i am clear about the functionality now although this makes plugin hot swapping a bit tedious, I hope this can improve in future implementations. Also, from docs, this intent was not so clear, my understanding was once unload was called, assembly was marked for sweeping and all weaker references would go away when calling it, guess it's not the case.
Anyways, i think the code you have given is somewhat useful to me.

@teo-tsirpanis
Copy link
Contributor

This proposed API would simplify usage of collectible AssemblyLoadContexts in scenarios where the user only tracks them by a friendly name. It reduces code complexity and eliminates the need for the user to maintain references to the ALC for the sole purpose of calling Unload().

You have to track something either way, be it a string or an ALC instance. You can track only the ALC and get its name from the Name property, and that does not increase complexity in user code.

If you want to force expedite the unloading of an ALC, you can store it in a weak reference and run GC.Collect until the ALC is collected, or is not collected after a fixed amount of collections, in which case it got rooted somewhere and cannot be unloaded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-AssemblyLoader-coreclr untriaged New issue has not been triaged by the area owner
Projects
Status: No status
Development

No branches or pull requests

4 participants