You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, I'm experiencing a lot of memory allocations when sending (NetworkMessageManager.SendMessage) and receiving messages (NetworkMessageManager.ReceiveMessage). These allocations occur when sending RPCs, as well as due to built-in networking components (e.g., NetworkTransform). Profiling was performed by the host using a debug build of IL2CPP with the deep profiler. There were five people (including the host) logged on to the server at the time.
The middle screenshot is unfortunately a known issue. It's due to the current design of NetworkTransform. We're working on removing it, but unfortunately it requires a pretty deep re-work of NetworkTransform. It's a tricky piece of work that is unfortunately likely to cause some regressions
The other two GC allocs look more solvable.
The first one is inside a call of Activator.CreateInstance. The only NGO code that calls this method is inside Editor code, so it's unlikely to be the culprit here. Do you have any custom serialization configured in your project? Common culprits can be any INetworkSerializable types or NetworkBehaviours with OnSynchronize implemented.
The bottom screenshot is coming from an RPC send. Some of NGO's automatic serialization of RPC parameters will make GC allocs. Are you passing any parameters with that RPC? If you can share the RPC code here we might be able to help figure out how to get the same functionality without the GC allocs.
First Screenshot
Yes, I have many structures and classes that implement INetworkSerializable. What exactly might affect allocations? I've provided the code for some of the implementations of this interface below.
Bottom Screenshot
Yes, I am passing data using Rpc, but I am using a pre-created NativeArray, as well as a structure (TransformData) that contains the structures.
NetworkTransformGroup
usingNetwork.Utilities;usingSimulationCycle;usingSystem;usingUnity.Collections;usingUnity.Netcode;usingUnityEngine;usingUtilities.ComponentsGrabbers;usingUtilities.Interpolation;namespaceNetwork.TransformSynchronization{publicclassNetworkTransformGroup:NetworkBehaviour,IAuthoritative,IGlobalInterpolationCycleSubscriber{[Header("Interpolation Settings")][SerializeField]privatefloat_positionDeltaThreshold=0.0001f;[SerializeField]privatefloat_rotationDeltaThreshold=0.0001f;privateNetworkTransformGroupMember[]_networkTransformGroupMembers=Array.Empty<NetworkTransformGroupMember>();privateNativeArray<TransformData>_nativeTransformDatas;privatebool__isActive=false;publicboolIsActive{get=>__isActive;set{if(__isActive==value)return;__isActive=value;if(value){OnActivate();}else{OnDeactivate();}}}publicintInterpolationExecutionOrder=>-50;publicvirtualboolIsAuthoritative()=>IsSpawned&&IsServer;publicoverridevoidOnNetworkSpawn(){base.OnNetworkSpawn();if(IsAuthoritative()){NetworkManager.NetworkTickSystem.Tick+=OnTick;}}publicoverridevoidOnNetworkDespawn(){base.OnNetworkDespawn();Deinitialize();NetworkManager.NetworkTickSystem.Tick-=OnTick;}publicvoidInitialize(ComponentsGrabbermodelRootGrabber){Deinitialize();_networkTransformGroupMembers=modelRootGrabber.GrabComponents<NetworkTransformGroupMember>();_nativeTransformDatas=newNativeArray<TransformData>(_networkTransformGroupMembers.Length,Allocator.Persistent);foreach(varmemberin_networkTransformGroupMembers){member.Initialize(IsAuthoritative,_positionDeltaThreshold,_rotationDeltaThreshold);}OnDeactivate();}publicvoidDeinitialize(){IsActive=false;foreach(varmemberin_networkTransformGroupMembers){member.Deinitialize();}if(_nativeTransformDatas.IsCreated)_nativeTransformDatas.Dispose();_networkTransformGroupMembers=Array.Empty<NetworkTransformGroupMember>();}publicvoidOnInterpolationUpdate(floatdeltaTime,floatinterpolationFactor){if(!IsActive||IsAuthoritative())return;for(inti=0;i<_networkTransformGroupMembers.Length;i++){_networkTransformGroupMembers[i].TransformInterpolator.Interpolate();}}privatevoidOnActivate(){foreach(varmemberin_networkTransformGroupMembers){member.IsActive=true;}}privatevoidOnDeactivate(){foreach(varmemberin_networkTransformGroupMembers){member.IsActive=false;}}privatevoidOnTick(){if(IsActive==false){return;}for(inti=0;i<_networkTransformGroupMembers.Length;i++){TransformmemberTransform=_networkTransformGroupMembers[i].transform;_nativeTransformDatas[i]=newTransformData(memberTransform.position,memberTransform.rotation);}SetMembersAuthoritativePositionAndRotationNotServerRpc(_nativeTransformDatas);}[Rpc(SendTo.Everyone)]privatevoidSetMembersAuthoritativePositionAndRotationNotServerRpc(NativeArray<TransformData>transformDatas){if(IsServer)return;for(inti=0;i<transformDatas.Length;i++){if(i>=_networkTransformGroupMembers.Length)thrownewIndexOutOfRangeException($"Transform index {i} is out of range.");TransformDatatransformData=transformDatas[i];varmember=_networkTransformGroupMembers[i];TransformInterpolatorinterpolator=member.TransformInterpolator;interpolator.PreSimulationInterpolationUpdate();interpolator.PostSimulationInterpolationUpdate(NetworkManager.ServerTime.FixedDeltaTime);interpolator.UpdateInterpolationTargetData(transformData.Position,transformData.Rotation);}}publicvoidBeforeInterpolationUpdate(floatdeltaTime){}publicvoidAfterInterpolationUpdate(floatdeltaTime){}}}
Thank you for your code! I can confirm that your custom serialization and RPC code looks solid.
I finally had a chance to take another look at this. I think I might have been able to track the issues down. I was able to reproduce the issue with RPC message sending (screenshot 3) and fix that.
The first two screenshots are a known ghost GC issue that we have that we've struggled to pin down and fix. However, with your screenshots I might have finally been able to pin it down. It's potentially related to different C# compilation environments handling new T() on a struct type differently.
Which compilation backend are you using? Mono or IL2CPP?
Feel free to check if the changes in #4119 resolve the issue.
Activity
EmandM commentedon Jul 23, 2026
The middle screenshot is unfortunately a known issue. It's due to the current design of NetworkTransform. We're working on removing it, but unfortunately it requires a pretty deep re-work of NetworkTransform. It's a tricky piece of work that is unfortunately likely to cause some regressions
The other two GC allocs look more solvable.
The first one is inside a call of
Activator.CreateInstance. The only NGO code that calls this method is inside Editor code, so it's unlikely to be the culprit here. Do you have any custom serialization configured in your project? Common culprits can be anyINetworkSerializabletypes or NetworkBehaviours withOnSynchronizeimplemented.The bottom screenshot is coming from an RPC send. Some of NGO's automatic serialization of RPC parameters will make GC allocs. Are you passing any parameters with that RPC? If you can share the RPC code here we might be able to help figure out how to get the same functionality without the GC allocs.
hardsmoke commentedon Jul 25, 2026
Thanks for the clarification.
First Screenshot
Yes, I have many structures and classes that implement INetworkSerializable. What exactly might affect allocations? I've provided the code for some of the implementations of this interface below.
StateInfo
CompressedNormal
Bottom Screenshot
Yes, I am passing data using Rpc, but I am using a pre-created NativeArray, as well as a structure (TransformData) that contains the structures.
NetworkTransformGroup
TransformData
EmandM commentedon Aug 7, 2026
Thank you for your code! I can confirm that your custom serialization and RPC code looks solid.
I finally had a chance to take another look at this. I think I might have been able to track the issues down. I was able to reproduce the issue with RPC message sending (screenshot 3) and fix that.
The first two screenshots are a known ghost GC issue that we have that we've struggled to pin down and fix. However, with your screenshots I might have finally been able to pin it down. It's potentially related to different C# compilation environments handling
new T()on a struct type differently.Which compilation backend are you using? Mono or IL2CPP?
Feel free to check if the changes in #4119 resolve the issue.
hardsmoke commentedon Aug 11, 2026
IP2CPP
Thanks.