Garbage Collection & Dynamic Memory Allocation
Contents
- 1 Overview
- 2 Dynamic Load Object From Asset Path
- 3 Disclaimer
- 4 Dynamic UObject Allocation
- 5 Preventing Garbage Collection
- 6 Using UObject Flag to Prevent Garbage Collection
- 7 Counting UPROPERTY() References To Any Object
- 8 Destroying / Deallocating
- 9 IsValidLowLevel()
- 10 Weak Pointers
- 11
- 12 Dynamic Memory Management
- 13 If You Use Malloc You Must Use Free
- 14 How to Disable GC Verify To Avoid Hitches
- 15 Summary
Overview
Original Author: ( )
Please note this tutorial in its present state reflects only my understanding and is not an Epic tutorial, you should examine the UE4 Source to get a more complete understanding of Dynamic Memory Management and the Garbage Collection System.
What I post here are the basics to get you started :)
Dynamic Load Object From Asset Path
See my Dynamic Load Object Tutorial!
This is how you load objects from an Asset Path, after creating those assets via the Editor!
You can use Dynamic Load Object to load a UStaticMesh into your game and then assign it to a Static Mesh Actor that you spawn, for example.
If you are new to C++ / UE4 C++ I recommend you start with my Dynamic Load Object tutorial.
Disclaimer
Please note that the rest of this tutorial is an advanced tutorial, and you should not attempt any of this until you are comfortable with C++ and UE4 C++.
Dynamic UObject Allocation
//in any class
UMyObjectClass* DynamicObj = NewObject<UMyObjectClass>(this);
this = Outer, if you are looking through the UE4 Source.
Preventing Garbage Collection
UE4 Garbage Collection only counts references to UObjects that are UPROPERTY()
To ensure that your spawned UObjects or objects created with NewObject are not Garbage Collected prematurely, you must have at least 1 reference to the UObject that is UPROPERTY()
.H
UPROPERTY()
UMyObjectClass* MyGCProtectedObj;
.CPP
MyGCProtectedObj = NewObject<UMyObjectClass>(this);
Implications of Not Using UPROPERTY()
If you do not use UPROPERTY() you can
never rely on your dynamic UObject staying in existence!
Make sure you pay close attention to this if you are spawning UObjects or using NewObject())!
Using UObject Flag to Prevent Garbage Collection
You can also prevent an object from being garbage collected by setting the RF_RootSet flag.
YourObjectInstance->AddToRoot();
Counting UPROPERTY() References To Any Object
I have created a wiki that shows you how you can find out exactly who is referring to your UObject/AActor at any time!
Garbage Collection ~ Count References To Any Object
Destroying / Deallocating
Destroying Objects
You can destroy objects created during runtime using:
if(!MyObject) return;
if(!MyObject->IsValidLowLevel()) return;
MyObject->ConditionalBeginDestroy(); //instantly clears UObject out of memory
MyObject = nullptr;
Destroying Actors
You can destroy AActor extending classes using
if(!TheCharacter) return;
if(!TheCharacter->IsValidLowLevel()) return;
TheCharacter->Destroy();
TheCharacter->ConditionalBeginDestroy(); //essential extra step, in all my testing
IsValidLowLevel()
You must always check if a UObject or AActor is valid before deferencing the pointer to it!
If you are wanting to use Dynamic Allocation of UObjects, you really should also use IsValidLowLevel()
//Get the Name of my Dynamic Object
if(!MyGCProtectedObj) return;
if(!MyGCProtectedObj->IsValidLowLevel()) return;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//It is safe to dereference now :)
ClientMessage(MyGCProtectedObj->GetName());
IsValidLowLevel() checks the actual validity of the created UObject and can prevent crashes where a simple pointer check wouldn't, because the pointer is valid but is pointing to an incomplete / partially deconstructed UObject.
Weak Pointers
Quoting Epic Dev Matt
If you know that the lifetime of an object is managed elsewhere and you just want to observe it and not contribute any references to it, you can hold onto a TWeakObjectPtr to that object which will safely be nulled out when the object is destroyed.
TWeakObjectPtr<UStaticMesh> MeshThatCouldBeNull;
Dynamic Memory Management
C++ operator new and delete
Although you can use Malloc and free as I explain below, I personally recommend that you use c++ operator new and delete !
This is because C++ operator new properly initializes the vtable (virtual function table) for any virtual functions in your data type, and also calls the constructor for your data type!
FYourDataType* NewDataPtr = new FYourDataType();
Every New Must Have a Delete
Please note you absolutely must pair every use of new with a delete to avoid memory leaks, as you are now doing your own memory management!
delete NewDataPtr;
UnrealMemory.h / FMemory::
Use the Memory.h functions!
Do not use c++ level memory functions!
UnrealMemory.h contains UE4 C++ versions of C++ memory management functions that are maintained by Epic as the engine evolves.
Templated UE4 C++ Malloc Function
Here is my very own Templated Malloc function which I used to make my
//The purpose of this template is to ensure no typos when malloc-ing
// types that are related and will pass static cast, but if there's a typo could be allocating
// insufficient amount of space
//the name is VStruct because I was mallocing USTRUCTS
template <typename DataType>
FORCEINLINE DataType* VStructMalloc()
{
return static_cast<DataType*>(FMemory::Malloc(sizeof(DataType)));
}
Example Usage
FVictoryUndoDataCreateWall* NewUndo = VStructMalloc<FVictoryUndoDataCreateWall>();
if(!NewUndo) return;
UE4 C++ Free
FMemory::Free(NewUndo);
If You Use Malloc You Must Use Free
Every Malloc must be paired with a Free when you are doing your own Dynamic Memory management, or you will have a Memory Leak.
FMemory::Malloc
FMemory::Free
UE4 new operator and running out of memory
From AnswerHub answer by Jamie Dale:
UObject and UStruct types overload operator new via one of the nested macros within GENERATED_UCLASS_BODY and GENERATED_USTRUCT_BODY. Slate widgets also override this operator, as do modules via REPLACEMENT_OPERATOR_NEW_AND_DELETE.The module level replacement seems to catch all the allocations made within a module, even if you're not allocating a UObject, UStruct, or Slate widget.
Ultimately they call through to FMemory::Malloc, which will forward it onto whichever allocator is active (eg, FMallocTBB). If one of these allocators fails to perform an allocation, they will call an implementation specific OutOfMemory function to log a fatal error.
I tried allocating 0x7fffffffffffffff bytes. With a debugger attached, it broke into the debugger on the failed allocation; without a debugger attached, the application just quit.
How to Disable GC Verify To Avoid Hitches
If you want to disable GC in release builds to avoid hitches (if you are experiencing them)
In the commandline when you run the game:
-NoVerifyGC
AnswerHub Explanation by Epic Dev Robert
Summary
Now you know how to dynamically spawn / create UObjects and also prevent them from getting Garbage Collected!
You also know how to prevent your game from crashing constantly.
It's called IsValidLowLevel() !!!
You also have a very brief intro into Dynamic Memory Management which I plan to let you explore further by studying UnrealMemory.h directly.
Enjoy!
( )