Unity: What's allocating memory in my function? - c#

I am relatively new to memory and optimization. I was tasked to optimize an existing project and while looking at the profiler in Unity, I saw that there was a function producing a bunch of stuff for the GC to clean up.
The function is as such:
public bool AnyCanvasOpen()
{
for (int i = 0; i < canvasList.Count; i++)
if (canvasList[i].activeSelf && canvasList[i].name != "MainUICanvas")
return true;
return false;
}
where canvasList is just a list of GameObjects. What could be the cause of my 282B allocation?

The canvasList variable is a List of GameObject and Unity's GameObject derives from UnityEngine.Object which has the name property you are using.
The memory allocation came from that: canvasList[i].name != "MainUICanvas"
This is because the Object.name property needs to return a string from the native side and this means that a new string is created each time that property is accessed. This is why the GameObject.CompareTag function was added, to make it possible to compare Object name without allocation memory. The GameObject.CompareTag function will compare the object name on the native side without the need to create or return a new string on the C# side. This removes the memory allocation.
Unfortunately, most Unity code examples on their site and elsewhere uses Object.name instead of the GameObject.CompareTag function which causes more people to use the Object.name property more often.
This shouldn't allocate memory:
List<GameObject> canvasList = new List<GameObject>();
public bool AnyCanvasOpen()
{
for (int i = 0; i < canvasList.Count; i++)
if (canvasList[i].activeSelf && canvasList[i].CompareTag("MainUICanvas"))
return true;
return false;
}

Related

C#: Looping through member objects of nested structs

Hi all you c# wizards!
I need to store all the memory offset values of (packed) nested structs within these respective structs.
Recusively looping through all the members works fine so far. Also, i get the appropriate memory offset values.
This struct contraption might contain several dozends of structs, and several hundreds of other members in the end.
But i do this whole thing at initialization time, so CPU performance won't be an issue here.
But:
In this iteration process, it seems i have trouble accessing the actual instances of those structs. As it turns out, when i try to store these offset values, they don't end up where i need them (of course, i need them in the instance "SomeStruct1" and its containing other struct instances, but the debugger clearly shows me the init values (-1)).
I suspect "field_info.GetValue" or "obj_type.InvokeMember" is not the proper thing to get the object reference? Is there any other way to loop through nested struct instances?
Please help! I've desperately debugged and googled for three days, but i'm so out of ideas now...
Thanks for your efforts!
-Albert
PS - the reason i do this unusual stuff:
I communicate between two embedded CPU cores via the mentioned nested struct (both are mixed c/c++ projects). This works like a charm, as both cores share the same memory, where the struct resides.
Additionally, i have to communicate between a c# host application and theses embedded cores, so i thought it could be a neat thing, if i implement a third instance of this struct. Only this time, i oviously can't use shared RAM. Instead, i implement value setters and getters for the data-holding members, find out the memory offset as well as the lenght of the data-holding members, and feed this information (along with the value itself) via USB or Ethernet down to the embedded system - so the "API" to my embedded system will simply be a struct. The only maintenance i have to do every thime i change the struct: i have to copy the holding .h file (of the embedded project) to a .cs file (host project).
I know it's crazy - but it works now.
Thanks for your interest. -Albert
This is a simplified (buggy, see below) example that should compile and execute (WinForms, c#7.3):
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace CodingExample
{
public interface Interf
{
Int32 Offset {get; set; }
}
[StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct sSomeStruct2 : Interf
{
public sSomeStruct2 (bool dummy)
{
Offset = -1;
SomeMember3 = 0;
}
public Int32 Offset {get; set; }
public Int32 SomeMember3;
// much more various-typed members (e. g. nested structs)...
}
[StructLayout (LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct sSomeStruct1 : Interf
{
public sSomeStruct1 (bool dummy)
{
Offset = -1;
SomeMember1 = 0;
SomeStruct2 = new sSomeStruct2 (true);
SomeMember2 = 0;
}
public Int32 Offset {get; set; }
public Int32 SomeMember1;
public sSomeStruct2 SomeStruct2;
public Int16 SomeMember2;
// much more various-typed members...
}
public partial class Form1 : Form
{
void InitializeOffsets (object obj)
{
Console.WriteLine ("obj: {0}", obj);
Type obj_type = obj.GetType ();
foreach (FieldInfo field_info in obj_type.GetFields ())
{
string field_name = field_info.Name;
Int32 offset = (Int32) Marshal.OffsetOf (obj_type, field_name);
Type field_type = field_info.FieldType;
bool is_leafe = field_type.IsPrimitive;
// none of theses three options seem to give me the right reference:
// object node_obj = field_info.GetValue (obj);
// object node_obj = field_info.GetValue (null);
object node_obj = obj_type.InvokeMember (field_name, BindingFlags.GetField, null, obj, null);
Console.WriteLine ("field: {0}; field_type: {1}; is_leafe: {2}; offset: {3}", field_name, field_type, is_leafe, offset);
if (! is_leafe)
{
// this writes not as expected:
(node_obj as Interf).Offset = offset;
InitializeOffsets (node_obj);
}
}
}
sSomeStruct1 SomeStruct1;
public Form1 ()
{
InitializeComponent ();
SomeStruct1 = new sSomeStruct1 (true);
InitializeOffsets (SomeStruct1);
}
}
}
Meanwhile i found out, what i did wrong:
i have to do boxing, so i can use "ref" when i call my initialize function:
// instead of this:
SomeStruct1 = new sSomeStruct1 (true);
// i have to do it this way:
object boxed_SomeStruct1 = new sSomeStruct1 (true);
InitializeOffsets (ref boxed_SomeStruct1);
SomeStruct1 = (sSomeStruct1) boxed_SomeStruct1;
Within the "InitializeOffsets" function, "field_info.GetValue (obj)" delivers a copy of my member object. That's why i have to copy the modified copy back at the very end of the foreach loop:
field_info.SetValue (obj, node_obj);
After these changes, the code works as intended.
Thanks for your interest. -Albert

How to dispose resources in third-party dll?

I have a console application that needs to create multiple objects of type <T> and T is inside another dll that I don’t own.
When an object of type T is created, it loads a XML in memory, but it never releases it.
So if you create too many objects of type T, an OutOfMemoryException is thrown.
The dll doesn't provide a dispose method for that Object and I can’t interact with the XML directly.
Is there a way to dispose of objects of a certain type that were created by a dll that I don’t own ?
I'm using .NET 4.6
The third-party dll is the dll of Trados Studio, for the people who know the program.
Just set the instance of the 3rd part object to null and create a new instance. The garbage collector will eventually clean up the object that you set to null and you wont get an out of memory exception anymore.
public class Class1
{
private StringBuilder sb = new StringBuilder();
public void loadFile()
{
using(StreamReader sr = new StreamReader("C:\\test.txt")) // Loads large text file.
{
sb.Append(sr.ReadToEnd());
}
}
}
static void Main()
{
fileloader.Class1 inst = new fileloader.Class1(); // Assume this is the instance of your 3rd party object.
do
{
if(inst == null)
{
inst = new fileloader.Class1();
}
for (int i = 0; i < 100; i++)
{
inst.loadFile();
}
inst = null; // allows the object to be GC'ed. Without this i get the OutOfMemoryException
Thread.Sleep(1000);
} while (true);
}
Calling GC.Collect() during runtime might solve the problem.
Ref: https://learn.microsoft.com/en-us/dotnet/api/system.gc.collect?view=net-5.0

Session.Remove("session_variable"); not working?

Hi in the following stuff am trying to remove the session after completing the certain tasks. but the session.remove() not working here.
if (Session["CaseNumber"] != "" || Session["CaseNumber"] != null)
{
casenumber = Session["CaseNumber"].ToString();
guid = Session["guid"].ToString();
_duration = bal.viewServiceActivity(guid);
case1 = bal.viewCaseDetail(casenumber);
dt = bal.getRelatedNotes(guid);
Session.Remove("CaseNumber");
Session.Remove("guid");
}
else
{
casenumber = Session["searchCase"].ToString();
guid = Session["caseGuid"].ToString();
_duration = bal.viewServiceActivity(guid);
case1 = bal.viewCaseDetail(casenumber);
dt = bal.getRelatedNotes(guid);
Session.Remove("searchCase");
Session.Remove("caseGuid");
}
There's nothing really wrong with portion of your code shown above. Just understanding is required about how it works.
Session.Remove(key) removes memory used for the key in the session dictionary. When using second approach of : Session["searchCase"]=null; , the key will still exist in the Session although the value is null.
Try setting the Session["Key_Name"] to null. For example:
if (Session["CaseNumber"] != "" || Session["CaseNumber"] != null)
{
casenumber = Session["CaseNumber"].ToString();
guid = Session["guid"].ToString();
_duration = bal.viewServiceActivity(guid);
case1 = bal.viewCaseDetail(casenumber);
dt = bal.getRelatedNotes(guid);
Session["CaseNumber"] = null; // Set NULL
Session["guid"] = null; // Set NULL
}
Session.Remove() doesn't destroys the object bypassing all basic .net rules of object referencing and memory management. What you need to understand here is that Session.Remove() does exactly what it suggests: It removes the reference to the object from its internal collection. Nothing more to expect.
If you need something destroyed, you need to implement IDisposable, and furthermore, you may instruct the garbage collector to collect if you are in a hurry using GC.Collect().
Check this SO question: Proper use of the IDisposable interface
Microsoft Forum confirms the same.
It seems more like an optimization OR so called standard way of implementation.
When you remove an object it is 'marked for removal' but not really removed...unless space is required.
It's how garbage collection works. Just because an object goes out of scope doesn't mean it's destructor gets called and the memory freed immediately. May be GC can remove an object much later.
Still, as a last check, do this :
Make sure you are not viewing a cached version of the page, or you don't add the item to the Session object again somewhere in your application.

Creating an array of objects using initializers seems to fail

I have a GameObject we'll call the GM. Attached to it is a script that's meant to be the primary logical controller for a game.
In that script, somewhere, I have:
private dbEquipment equipment_database = new dbEquipment();
The relevant snippet from dbEquipment.cs:
public class dbEquipment {
private int total_items = 13;
private clEquipment[] _master_equipment_list;
public dbEquipment() {
_master_equipment_list = new clEquipment[total_items];
_master_equipment_list[0] = new clEquipment {
... //large amount of object initializing here
};
... //etc, for all 13 items
}
}
When I run Unity, I get:
NullReferenceException: Object reference not set to an instance of an object
Pointed at the line:
_master_equipment_list[0] = new clEquipment { ...
I tried running through the array and initializing every clEquipment object to an empty clEquipment() first:
for(int x = 0; x < total_items; x++) { _master_equipment_list[x] = new clEquipment(); }
just to be totally sure that the array was actually filled, but I got the same result.
I've also tried changing it to be a List<clEquipment>, and changing everything appropriately -- no dice.
Any ideas?
My guess is that you may been including a null reference in the section that says //large amount of object initializing here when you create a new clEquipment.
_master_equipment_list[0] = new clEquipment {
... //check for nulls here
};
You might want to post the code for the clEquipment class. You say you tried initializing every object...did you do that before the break line? If it didn't break, thats a good sign.
Also, hard to tell from your code, but do you need a "()" in the initialization where it breaks? Just a thought
_master_equipment_list[0] = new clEquipment () {

GC collecting...what?

I am trying to optimize my engine (C# + SlimDX) to make as less allocations as possible (to prevent the GC from firing too often) using as guide a profiler that gives me where the garbaged object are generated. Its going pretty well (going down from like 20 MB garbaged every 5s to 8 MB garbaged every 1 minute and half (yep, it was very little optimized XD))
There is a method where I can't find anything declarated and i don't know what's to do. It seems this method generate 2 garbaged object per execution in its body (not on a called function) :
Can somebody guide me to understand why this function generate object to be garbaged? I really don't have a clue.
public override void Update()
{
base.Update();
if (LastCheckInstancesNumber != Instances.Count)
{
LastCheckInstancesNumber = Instances.Count;
_needToRegenerateUpdate = true;
}
// Crea byte array da usare nel prossimo draw.
if (_needToRegenerateUpdate)
{
Int32 PrimitivesCount = Instances.Count;
Int32 Size = PrimitivesCount * 80;
if ((ByteUpdateTemp != null) && (ByteUpdateTemp.Length < Size))
ByteUpdateTemp = new byte[Size];
int offset = 0;
PrimitivesCount = 0;
Int32 Count = Instances.Count;
for (int i = 0; i < Count; i++)
{
InstancedBase3DObjectInstanceValues ib = Instances[i];
if (ib.Process)
{
MathHelper.CopyMatrix(ref ib._matrix, ref MatrixTemp);
MathHelper.CopyVector(ref ib._diffuseColor, ref ColorTemp);
ObjectUpdateTemp[0] = MatrixTemp.M11;
ObjectUpdateTemp[1] = MatrixTemp.M12;
ObjectUpdateTemp[2] = MatrixTemp.M13;
ObjectUpdateTemp[3] = MatrixTemp.M14;
ObjectUpdateTemp[4] = MatrixTemp.M21;
ObjectUpdateTemp[5] = MatrixTemp.M22;
ObjectUpdateTemp[6] = MatrixTemp.M23;
ObjectUpdateTemp[7] = MatrixTemp.M24;
ObjectUpdateTemp[8] = MatrixTemp.M31;
ObjectUpdateTemp[9] = MatrixTemp.M32;
ObjectUpdateTemp[10] = MatrixTemp.M33;
ObjectUpdateTemp[11] = MatrixTemp.M34;
ObjectUpdateTemp[12] = MatrixTemp.M41;
ObjectUpdateTemp[13] = MatrixTemp.M42;
ObjectUpdateTemp[14] = MatrixTemp.M43;
ObjectUpdateTemp[15] = MatrixTemp.M44;
ObjectUpdateTemp[16] = ColorTemp.X;
ObjectUpdateTemp[17] = ColorTemp.Y;
ObjectUpdateTemp[18] = ColorTemp.Z;
ObjectUpdateTemp[19] = ColorTemp.W;
ByteConverter.WriteSingleArrayToByte(ref ObjectUpdateTemp, ref ByteUpdateTemp, offset);
offset += 20;
PrimitivesCount++;
}
}
SynchronizedObject so = SynchronizationEventWriter.LockData();
so.Synchronizedobject = ByteUpdateTemp;
SynchronizationEventWriter.Update();
SynchronizationEventWriter.UnlockData();
_needToRegenerateUpdate = false;
so = SynchronizationEventWriterNum.LockData();
so.Synchronizedobject = PrimitivesCount;
SynchronizationEventWriterNum.Update();
SynchronizationEventWriterNum.UnlockData();
}
}
Notes :
The new byte[Size] is NEVER called due to caching.
The MathHelper function simply copy each element (Single) from one object to another without creating anything.
The base.Update() does almost nothing (and anyway is derived from ALL object in my engine, but only here i have the garbage object)
Thanks!!!
EDIT:
internal void GetLock()
{
Monitor.Enter(InternalLock);
Value.Locked = true;
Value.LockOwner = Thread.CurrentThread;
}
public SynchronizedObject LockData()
{
Parent.GetLock();
return Parent.Value;
}
Here's the code of the LockData(). I don't think it generates anything :|
I've resolved!!!
The problem was that the so.Synchronizedobject = PrimitivesCount; was assigning an Int32 to an Object class. It seems that this replaces every time the object causing the old object to be garbaged.
I resolved by using a box class to enclose the Int32 object and simply change the value inside.
What's in base.Update(), anything?
Can your profiler dump the heap? If so why I'd put a breakpoint just before this method and dump the heap and then again straight afterwards. That way you'll be able to see what type of object has been created.
Short of that a brute force approach of commenting out line by line is another (horrible) idea.
Do your MathHelper methods create a temp object?
I'm just guessing, but it looks like you're creating two SynchronizedObject objects in the bottom nine lines of that function:
SynchronizedObject so = SynchronizationEventWriter.LockData();
and
so = SynchronizationEventWriterNum.LockData();
No detailed knowledge of SynchronizedObject or whether LockData() actually creates anything, but it's the only choice I can see in your code...

Categories

Resources