EditableRoot will not be removed from Memory

EditableRoot will not be removed from Memory

Old forum URL: forums.lhotka.net/forums/t/10827.aspx


t.kehl posted on Sunday, October 30, 2011

  Hi.

I have attached a printscreen which shows my Problem in Redgates-Memory-Profiler.

I have an EditableRoot-Object called Prayon.Library.Activity. For my Tests, I load the Activity-Object like

        private void OnDummy() {
            var a = Activity.GetTypedActivity(new Guid("8A4810EB-F516-11E0-B771-040CCEE2D49D"));
        }

After the Fetch, I call the fallowing (on the Client-Side):

            if (!(FieldManager.FieldExists(GeneralCustomFieldValueListProperty))) {
                Child.CustomFieldValueList.GetCustomFieldValueListAsync(this, true, initial, (o, e) =>
                {
                    if (e.Error == null && e.Object != null) {
                        LoadProperty(GeneralCustomFieldValueListProperty, e.Object);
                    }
                });
            }

CustomFieldValueList is a Child-List of Activity.

The Problem is, that the loaded Activity-Object now stays in the Memory. As you see on the printscreen, it has the fallowing Reference-Path:

- Csla.DataPortal<Child.CustomFieldValueList> (FetchCompleted)
   - System.EventHandler<DataPortalResult<Child.CustomFieldValueList>>
      -  Prayon.Library.Activity

Can anybody help me, how I can solve this problem, that the Activity-Object will be collected of the GC?

Thank you.

Best Regards, Thomas

t.kehl replied on Sunday, October 30, 2011

I have found out, that when I remove the FetchCompleted-Eventhandler after complete from the DataPortal, it is working and my Object will be collected from the Memory.

For doing this, I have created the Helper-Method:

        static EventHandler<DataPortalResult<Child.CustomFieldValueList>> GetEventHandler(object classInstance, string eventName) {
            Type classType = classInstance.GetType();
            FieldInfo eventField = classType.GetField(eventName, BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);

            var eventDelegate = (EventHandler<DataPortalResult<Child.CustomFieldValueList>>)eventField.GetValue(classInstance);

            // eventDelegate will be null if no listeners are attached to the event
            if (eventDelegate == null) {
                return null;
            }

            return eventDelegate;
        }

Which I use there:

            if (!(FieldManager.FieldExists(GeneralCustomFieldValueListProperty))) {
                Child.CustomFieldValueList.GetCustomFieldValueListAsync(this, true, initial, (o, e) =>
                {
                    ((DataPortal<Child.CustomFieldValueList>)o).FetchCompleted -= GetEventHandler(o, "FetchCompleted");

                    if (e.Error == null && e.Object != null) {
                        LoadProperty(GeneralCustomFieldValueListProperty, e.Object);
                    }
                });
            }

BUT: I ask me know, if it is right, that I have to do this? Do I something wrong with the async-DataPortal or perhaps this is a Bug of CSLA?

Best Regards, Thomas

RockfordLhotka replied on Monday, October 31, 2011

Can you post your factory method so we can see what it looks like?

t.kehl replied on Monday, October 31, 2011

Hi Rocky

This is the factory-method of the child-list (Child.CustomFieldValueList.GetCustomFieldValueList()):

        internal static void GetCustomFieldValueListAsync(Activity activity, bool general, bool defaultValues, EventHandler<DataPortalResult<CustomFieldValueList>> callback) {
            DataPortal.BeginFetch(new ActivityCriteria(activity.Id, general, defaultValues), callback);
        }

and here the DataPortal_Fetch-Method:

        private void DataPortal_Fetch(ActivityCriteria criteria) {
            this.RaiseListChangedEvents = false;

            using (var customFieldValueRepository = new CustomFieldValueRepository()) {
                foreach (
                    var customFieldValue in customFieldValueRepository.GetAll().Where(x => x.BusinessObjectId == criteria.Value)) {
                    this.Add(CustomFieldValue.GetCustomFieldValue(customFieldValue));
                }
            }

            this.RaiseListChangedEvents = true;
        }

This is the factory-method of the EditableRoot:

        public static Activity GetTypedActivity(Guid Id) {
            var criteria = new SingleCriteria<Activity, Guid>(Id);
            var ret = DataPortal.Fetch<Activity>(criteria);
            ret.OnFetchComplete(criteria);
            return ret;
        }

The async Load of the Child list will be started in ret.OnFetchComplete(criteria).

Thanks for your help.

Best Regards, Thomas

RockfordLhotka replied on Monday, October 31, 2011

There's nothing we (Sergey, Justin and me) are seeing that would cause this effect.

Justin was wondering if your application makes uses of large images or large byte[] types or some other huge object instances that might fragment memory (and be handled differently by the GC)?

Thinking out loud:

The FetchCompleted event is always declared as an instance event, never static. And the instances of DataPortal<T> are scoped to individual methods. And the FetchCompleted handler (the data portal creates an implicit reference to the handler of the callback) is a lambda right? The lambda is an object at runtime.

It is true that some of these objects might not get GCed quickly, because they may create circular references between themselves.

But even then, none of them should have ever established a reference to the Activity object itself.

Unless you have some code somewhere that establishes an event handler for FetchCompleted that isn't a lambda? But even if you did that, the data portal instance holding that reference would eventually get GCed because the data portal instance is scoped to a specific method.

t.kehl replied on Tuesday, November 01, 2011

Hi Rocky

I do not use large imaged or large byte[] types on this place. I will now build in my application two small classes (Editable-Root and Child) like this two without anything and try this with this two classes. I will back with the results.

Best Regards, Thomas

t.kehl replied on Tuesday, November 01, 2011

Hi Rocky

I have tested this. I think it will work - but when I Load the Child-List, it take a very long time, till the instance will be collected from the GC (about 90 Seconds).

Best Regards, Thomas

RockfordLhotka replied on Tuesday, November 01, 2011

Garbage collection is not immediate, that is absolutely true. And it can take longer or shorter amounts of time depending on whether you are using the server or client GC. In other words, you'll see different behavior on the server from the client.

RockfordLhotka replied on Monday, October 31, 2011

Also, when you call RegisterProperty for your child property declaration, you are marking it as a child as part of the registration right?

t.kehl replied on Tuesday, November 01, 2011

Hi Rocky

The ChildProperty is declared on this way:

        private static readonly PropertyInfo<Child.CustomFieldValueList> GeneralCustomFieldValueListProperty = RegisterProperty(new PropertyInfo<Child.CustomFieldValueList>("GeneralCustomFieldValueList"));

Best Regards, Thomas

Copyright (c) Marimer LLC