Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Garbage collection and reflection

Discussion in 'Scripting' started by Colin_Hallett, Sep 19, 2021.

  1. Colin_Hallett

    Colin_Hallett

    Joined:
    May 18, 2021
    Posts:
    1
    I know reflection is less than ideal in terms of performance, but scanning the docs it seems it could cause potentially more serious issues:
    "Mono and IL2CPP internally cache all C# reflection (System.Reflection) objects and by design, Unity does not garbage collect them. The result of this behavior is that the garbage collector continuously scans the cached C# reflection objects during the lifetime of your application, which causes unnecessary and potentially significant garbage collector overhead."
    From here - https://docs.unity3d.com/Manual/overview-of-dot-net-in-unity.html

    Does this mean for example using reflection to handle property attributes could cause significant overhead? Would I need to manually call GC.Collect() when destroying a reflection object?

    It's the only place I've seen this written in regards to garbage collection but seems like something I need to be aware of!

    Was wondering if anyone could shed some more light on it?
     
  2. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,051
    The paragraph specifically talks about objects returned by reflection methods, e.g.
    Type
    or
    MethodInfo
    instances. Once you call a reflection method that returns those objects, they will be cached and never be destroyed. Calling
    GC.Collect()
    will not do anything regarding those objects.

    The paragraph implies that those objects will still be checked by the GC, even if they'll never be collected. The more of those objects are created, the more time the GC will waste each cycle checking them.

    I don't think this is really significant in most cases. But, as the next paragraph points out, some methods like
    Assembly.GetTypes
    and
    Type.GetMethods
    can return a ton of those objects and the overhead can add up.

    As they point out, you can only try to limit the number of reflection methods you call. Don't check every type in every assembly but instead have a pre-defined list of types or dynamically create one during the build.

    If Unity upgrades to a generational garbage collector, this would relieve the situation, as the reflection objects would move into older generations and not be checked on each cycle.
     
    Frank-Cheng and Bunny83 like this.
  3. kailin89

    kailin89

    Joined:
    Dec 16, 2019
    Posts:
    13
    Something like this won't cause much of an overhead right? Only if there aren't a ton of interfaces and properties on that single type? A much simplified example:
    Code (CSharp):
    1.    
    2.    
    3.     interface IHaveAttribute
    4.     {
    5.         [AnAttribute("Name")]
    6.         string Name { get; set; }
    7.     }
    8.  
    9.     internal class AnAttribute : Attribute
    10.     {
    11.         public AnAttribute(string name) { }
    12.     }
    13.  
    14.     public class Foo : IHaveAttribute
    15.     {
    16.         public string Name { get; set; }
    17.     }
    18.  
    19.     public class Test
    20.     {
    21.         void AttTest()
    22.         {
    23.             var aFoo = new Foo();
    24.             var props = aFoo.GetType().GetInterfaces().SelectMany(i => i.GetProperties());
    25.             foreach (var prop in props)
    26.             {
    27.                 var attributes = prop.GetCustomAttributes(false);
    28.                 foreach (var att in attributes)
    29.                 {
    30.                     if (att is AnAttribute dsAtt)
    31.                     {
    32.                         // cache property
    33.                     }
    34.                 }
    35.             }
    36.         }
    37.     }
    38.     }
     
  4. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,051
    I assume the type, the interface types and all their property infos as well as the attribute instances are cached. It's all a matter of magnitude, probably not measurable if you have only a few, starts becoming significant if you have a ton of any of them. Really can't judge the impact without trying it out.

    Again, I suspect the note came from some people e.g. scanning all properties of all types in all assemblies, which conceivably produces a lot of instances and could slow down GC. If you keep your numbers reasonable, there's probably other things in your heap that have a bigger impact.