the code you’ve shown does not show how you are creating or referencing the UContentBrowserAssetDataSource.
Could you show your startup/shutdownmodule implementation?
In general you’ll need to make sure you’re not keeping any UPROPERTY pointer or TStrongObjectPtr or similar reference if you want the object to be destroyed.
If you have a pointer in the module you might need to reset it manually, since the module would still exist, even if you manually call ShutdownModule() on it.
Also, why do you want to force destroy the data source?
Would shutting it down manually and then waiting for the next GC be an appropriate solution here?
Usually it’s a bit of a code smell trying to force delete a UObject, calling a some shutdown code to let the object clean up and unregister itself as a data source might be a better approach.
You can dump info on the references that keep an object alive via the “obj refs name=/Engine/Transient.AssetData” console command.
This produces an output like this:
LogReferenceChain: Display: InitialGather memory usage: 3.26 LogReferenceChain: (refcounted<1>) (root) ContentBrowserAssetDataSource /Engine/Transient.AssetData is not currently reachable but it does have some of EInternalObjectFlags_RootFlags set. LogReferenceChain: Display: Post-search memory usage: 1.44The important part are the “(refcounted<1>) (root)” parts before the object.
There are no regular references to the object (i.e. via UProperties), but it is using refcounting and is part of the root set.
After running your example code the “(refcounted<1>)” part disappears, so at this point the StrongObjectPtr is correctly reset and the refcount is 0.
However, the object is also still rooted, which will keep it alive indefinitely, regardless of any references existing or not.
I haven’t been able to track down where the object is being rooted, but you can clear that flag like this and it should properly remove the object on the next GC:
I’ve done a short test and this seems to remove the regular assets from the content browser. I don’t think this source was designed to be runtime removable, so there’s still a chance for problems down the road.
I’ve added a check in UObjectBasUtility::AddToRoot() and that finally solved the mystery.
The object is rooted during the FUObjectArray::CloseDisregardForGC() call in FEngineLoop::PreInitPostStartupScreen().
The “Disregard for GC” set is a list of all UObjects that are created during Engine startup, especially in CDO (ClassDefaultObject) constructors, but it also includes objects created in any other way during startup.
The assumption is that any objects created that early or in a CDO need to be kept alive for the whole lifetime of the application, since CDOs itself also never get destroyed.
I think in this specific case it’s ok to remove the object from the root set, since the lifetime is managed by the FContentBrowserAssetDataSourceModule and quite clear (even if the StrongObjectPtr is essentially useless due to the object being rooted anyway).
In my own module, I didn’t create or reference AssetDataSource.
It’s an engine module, which was created in the engine start.
I wan to create my own datasource to customize content browser, so I need to destroy AssetDataSource firstly and immediately, or it will have side effects on my custom data source.