Please Help me understand how UObject gets pending kill

I am struggling to understand how GC works in UE4 and made a test project for that with 4.25.0, but it gives me unexpected results, and I don’t know why. Can anyone help me understand how UObject gets Pending Kill?

According to the doc, if I declare UPROPERTY() SomeUObjectSubClass* Object1 and just SomeUObjectSubClass* Object2 in an actor class, and initialize both of them using NewObject(), Object1 will not be Pending Kill unless the actor is alive, but Object2. And that is why normally it is recommended to use empty UPROPERTY() macro when you use UObject,even if you don’t want to make it editable/visible in the editor, right?

In order to check if my understanding is correct, I made a project and a simple actor script as below.

// GC_Tester.h

UCLASS()
class UE_GC_CHECKER_4_24_3_API UGCTestObject : public UObject
{
	GENERATED_BODY()

public:
	UGCTestObject();
	int Value;
};

UCLASS()
class UE_GC_CHECKER_4_24_3_API AGC_Tester : public AActor
{
	GENERATED_BODY()

public:
	// Sets default values for this actor's properties
	AGC_Tester();

	UGCTestObject* Non_UP_Object;

	UPROPERTY()
		UGCTestObject* UP_Object;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	UFUNCTION(BlueprintCallable)
		void CheckNonUPObject(float DisplayTime);

	UFUNCTION(BlueprintCallable)
		void CheckUPObject(float DisplayTime);
};

// GC_Tester.cpp

// GC_Tester.cpp
    void AGC_Tester::BeginPlay()
    {
    	Super::BeginPlay();
    
    	Non_UP_Object = NewObject<UGCTestObject>();
    	UP_Object = NewObject<UGCTestObject>();
    }
    
    void AGC_Tester::CheckNonUPObject(float DisplayTime)
    {
    	if (Non_UP_Object == nullptr)
    	{
    		UE_LOG(LogTemp, Warning, TEXT("Non_UP_Object is null"));
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(-1, DisplayTime, FColor::Yellow, "Non_UP_Object is null");
    		}
    	}
    
    	if (!IsValid(Non_UP_Object))
    	{
    		UE_LOG(LogTemp, Error, TEXT("Non_UP_Object is not valid"));
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(-1, DisplayTime, FColor::Red, "Non_UP_Object is not valid");
    		}
    	}
    	else
    	{
    		//UE_LOG(LogTemp, Log, TEXT("Non_UP_Object is valid"));
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(-1, DisplayTime, FColor::White, "Non_UP_Object is valid");
    		}
    	}
    }
    
    void AGC_Tester::CheckUPObject(float DisplayTime)
    {
    	if (UP_Object == nullptr)
    	{
    		UE_LOG(LogTemp, Warning, TEXT("UP_Object is null"));
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(-1, DisplayTime, FColor::Yellow, "UP_Object is null");
    		}
    	}
    
    	if (!IsValid(UP_Object))
    	{
    		UE_LOG(LogTemp, Error, TEXT("UP_Object is not valid"));
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(-1, DisplayTime, FColor::Red, "UP_Object is not valid");
    		}
    	}
    	else
    	{
    		//UE_LOG(LogTemp, Log, TEXT("UP_Object is valid"));
    		if (GEngine)
    		{
    			GEngine->AddOnScreenDebugMessage(-1, DisplayTime, FColor::White, "UP_Object is valid");
    		}
    	}
    }

This actor just initializes Non_UP_Object and UP_Object with NewObject on BeginPlay, and make thier checking functions which use IsValid function to check callable from event graph.

I made a blueprint of this actor, and in its event graph, call CheckNonUPObject and CheckUPObject on every Tick. And I place the blueprint actor in my level. My expected result is that UP_Object is always valid but Non_UP_Object will be invalid in 1 or 2 minutes. But actually both of them will be alive. (by the way Time Between Purging Pending Kill Objects = 61.099998)

I thought this result is related to the change in UE 4.25 for some reason, that UProperty is renamed to FProperty. And I made the exact same project in UE 4.24.3 but the same.

I am not sure what is wrong. Anyone please help me?

Alright, I figured that out by myself. I don’t know why but IsValid(Non_UP_Object) always return true and Non_UP_Object never become null, but if you use Non_UP_Object->IsValidLowLevel(), it will soon return false.