Preface:
I know UE4 has its own garbage collection and that if you create an object and it is only referenced in a standard library C++ container (such as a vector) and not a UObject it will be garbage collected. I also know you can inherit from FGCObject in order to reference the objects and prevent their garbage collection.
Question:
My question is, if an object is referenced by both a UObject, and in a standard C++ container (e.g. a vector of pointers) will UE4 ever move the objects location in memory making the standard container reference invalid.
Example:
In BP I spawn 4 actors and attach them to different components on another actor. I then pass a TArray of pointers to those actors to a C++ function which puts those pointers into a std::map<std::string, std::vector<myActor*>>.
Problem:
The spawned actors are not garbage collected as they are attached to another actor and referenced there but sometimes after 1 hour or 30 hours of running the program will crash because a pointer to one of the spawned actors in the vector has become invalid. Is it possible that UE4 is moving the object in memory?
The “real” answer here is that you shouldn’t be using any std/stl with UE4, certainly not with pointers to UObject-types. If you must reference objects outside the normal UE4 system, use TWeakObjectPtr, not raw pointers.
AFAIK though, the allocated memory for a UObject instance is not moved. Don’t take that as gospel though, just an intuition.
To follow up with this, it is the UPROPERTY macro that allows for reference tracking - not the container or anything else (except one off things like the RootSet or disabling GC entirely for objects).
class UMyClass : public UObject
{
....
TArray<AMyActor*> UnsafeArray; // Will not count as references to the Actor's it holds as the GC has no insight into this structure, so the pointers can go invalid at any point (when any one who does own an actual reference drops it).
UPROPERTY()
TArray<AMyActor*> SafeArray; // Actor entries will always be valid because the Array is holding on to a reference and the GC system sees it.
}
When the GC system goes to prune things, it uses the UE4 reflection system (e.g., UPROPERTY) to find containers and the references they hold. If you are using naked fields without any reflection markers - it doesn’t see them.
This is only true for UObjects and things that inherit from them (Actor, Pawn, etc). Slate and such use raw pointers but they use TSharedRef / TSharedPtr / TWeakPtr to control object management.
Thank you for all of the replies. I am using child classes from an external C++ library which is why I have mixed UE4 and normal C++. I will try using the TWeakObjectPtr first for any references outside of normal UE4 systems. I also should be able to set up everything to be (or get data directly from) a UPROPERTY. I suspect I will have to make all of my non UE4 classes into UE4 classes, perhaps even the child classes of the library using multiple inheritance.
What I am doing is using scene captures to render to textures and getting the RGBA data. I double buffer that and grab the image data and encode and send it over a network on different threads. The problem always occurs when using the reference to the UE4 actor which holds the render textures. The actor still exists but the pointer to it in the std::vector no longer points to a valid location.
(Just to clarify though, as I stated in the original post, garbage collection is not an issue at all. I just mentioned it because I wanted to make it clear that I wasn’t having garbage collection problems.)
Something like that I would definitely consider doing within the UE4 environment itself (especially as you’re also using rendering resources, which has other threading implications).
UE has the functionality you need to safely export a render target into an array of colour data in a thread-safe way. I have some pretty simple code for writing into an RT but not reading from unfortunately, but you can probably start by looking at the source behind this node: Read Render Target Pixel | Unreal Engine Documentation
Reading render targets is pretty slow when done on the game-thread though
The capture is the only thing I’m doing on the game thread (which is required). I am using two textures for the scene capture to double buffer and grabbing the resources, encoding, and sending out over the network on other threads. The entire pipeline is pretty good except for this crash I get which can happen after 1 hour or 3 days.
The rendering and storage is done in a UE4 class but the encoding and networking is done in a non UE4 class. That non UE4 class uses a pointer to the UE4 class to access the data and rarely and randomly that pointer is no longer valid. I have ascertained that the object in question still exists but it seems to have moved in memory which prompted my initial speculation about UE4 possibly changing the memory location of an object. Thinking about it more it seems entirely possible and even likely that this would happen so storing raw pointers outside of UE4 classes is probably something that shouldn’t be done.
So just in case anyone ever finds this or is interested. The UObjects are now stored in a TArray in a UStructure in a TMap in a UClass. It hasn’t helped at all so it seems there probably wasn’t any weird UE4 memory management thigns going on.
The crash happens much more frequently now and is because the entire TMAP is empty when it shouldn’t be. I may have some kind of weird memory stomping going on somewhere that I haven’t found yet.
A “raw” array of textures visible to the Unreal GC (will prevent collection until you remove the object from the array or exit the application),
without UClass / UProperty macros:
#include "Runtime/CoreUObject/Public/UObject/GCObject.h"
#include "Runtime/Engine/Classes/Engine/Texture2D.h"
class MYMODULE_API IMyMainModule : public FGCObject
{
TArray< UTexture2D* > MyCollection;
public:
virtual void AddReferencedObjects(FReferenceCollector & Collector) override
{
for (auto & Obj : MyCollection)
{
Collector.AddReferencedObject( Obj );
}
}
public:
void register_object( UTexture2D * AddNew); // just a helper function..
void remove_object( UTexture2D * ToRemove); // just a helper function..
}
REMOVE the object from this array before you destroy it at runtime.