8

I have looked at this and this and I have the following question to see if I understand correctly. Given the code

using System;

namespace returnObject
{
    class myObject
    {
        public int number { get; set; }

    }

    class Program
    {
        static void Main(string[] args)
        {

            myObject mainObj = make();
            mainObj.number = 7;
        }

        public static myObject make()
        {
            myObject localObj = new myObject();
            localObj.number = 4;
            return localObj;
        }

    }
}

I would expect localObj to go out of scope at the end of the make method and, therefore, the setting of obj.number to 7 in the main function to fail. It doesn't. I think I am correct in stating that:

  • localObj is a reference to an object
  • localObj is created on the stack
  • localObj goes out of scope at the end of the make method.
  • the object that localObj refers to is on the heap.

So, am I right in thinking that ordinarily the object that localObj refers to would have been marked for deletion by the garbage collector at the end of the make method but since the reference value has been passed back to mainObj, the object is referenced and therefore not eligible for deletion?

In addition is object creation in this way considered good practice?

3
  • +1 good question. I would all objects are in heap, and only references to them are on the stack. Commented Apr 3, 2011 at 14:25
  • To answer your last question, not only is it a good practice, it's a fundamental pattern. It's called a factory method and it's used to decouple the creation of a dependency from the class using the dependency. en.wikipedia.org/wiki/Factory_method_pattern Commented Apr 3, 2011 at 14:34
  • To be precise, the instance is eligible for deletion when it is disconnected from the "Roots", i.e. no longer used by the application. The circular referencing is an example where the instance is referenced, yet might be eligible for garbage collection. Commented Apr 3, 2011 at 14:38

5 Answers 5

16

Am I right in thinking that ordinarily the object that localObj refers to would have been marked for deletion by the garbage collector at the end of the make method but since the reference value has been passed back to mainObj, the object is referenced and therefore not eligible for deletion?

That is an extremely complex question, and yet it is of a form that admits only a yes or no answer. Rather than trying to answer that, let me break it down into points following on your original points.

localObj is a reference to an object

Better: localObj is a local variable. A local variable is a storage location of reference type. The storage location contains a reference to an object, or null.

localObj is created on the stack

Correct, though this is an implementation detail. The storage location associated with the variable is allocated from the temporary pool. As an implementation detail, the CLR uses the call stack as a temporary pool. It need not; the stack is just a cheap, convenient way to get a temporary pool.

localObj goes out of scope at the end of the make method.

Correct. The scope of a variable is defined as the region of program text in which it may be used via its unqualified name. The scope of this particular variable is the entire body of the method. (I note also that in C# it is traditional to begin methods, properties and types with a capital letter, as you have not done.)

the object that localObj refers to is on the heap.

Correct. As an implementation detail, all references are either null, or refer to an object in the long-term storage, aka, the managed heap. An implementation of the CLR could use flow analysis to determine that a particular object does not escape the lifetime of the local variable which hold the sole reference to it, and therefore allocate it on the temporary pool. In practice, this does not happen in any implementation I'm aware of.

had it not been returned, the object that localObj refers to would have been marked for deletion by the garbage collector at the end of the make method

No. Here is your first major mistaken impression. The GC is not deterministic. It does not see immediately that a local variable has gone out of scope and therefore an object has been orphaned. The object lives on until some policy triggers a garbage collection. And even then, the object may have survived a previous collection and been promoted to a later generation. Later generations are collected less frequently. All you know is that the object will no longer be marked for survival. Remember, a mark-n-sweep garbage collector marks objects for survival, not for deletion.

Furthermore, it is perfectly legal for an object to be cleaned up by the GC before the make method ends. This is a common cause of bugs when dealing with managed/unmanaged code interop. Consider the following scenario:

void M() 
{
    FileThingy f = GetFileThingy();
    MyUnmanagedLibrary.ConsumeFileThingy(f);
}

When is f eligable for collection? As soon as the GC can determine that no managed code ever consumes the reference again. But no managed code ever consumes this reference the instant after the unmanaged code gets its copy of the reference, and therefore the GC is within its rights to collect the object on another thread immediately after the call is invoked but before the managed code runs. To solve this problem you need to use a KeepAlive to keep the object alive.

You cannot rely on the GC reclaiming storage at precisely the moment when a local variable goes out of scope. The lifetime of the object is highly likely to be longer than the lifetime of the variable, and it is legal for it to be shorter if the GC can prove that managed code can't tell the difference.

since the reference value has been passed back to mainObj, the object is referenced and therefore not eligible for deletion

Correct. But again, suppose the mainObj routine then did not use the object passed back for anything. The jitter is within its rights to notice that fact and optimize away the tramping around of the unused data, and thereby make the object eligable for early collection.

What I'm getting at here is the garbage collector should be thought of as smarter than you are about managing your memory. The object will go away no sooner than it needs to, and probably later than it could, but possibly earlier than you'd think. Stop worrying and learn to love the uncertainty; the GC is managing things for you and it does a great job.

Sign up to request clarification or add additional context in comments.

1 Comment

I really enjoyed reading that answer.. I'm a long long way from being able to participate in discussion at this level but even so I learned and was able to understand and comprehend most of what you wrote. +1
3

C# does not work like C or C++; objects are never allocated on the stack.

All objects (reference types) live on the garbage-collected heap; the garbage collector will collect objects some time after they are no longer referenced.

Without using weak references, it is fundamentally impossible to observe a managed object after it has been GC'd.

Comments

3

You should't think too much to memory management in .net framework:
it has been created and it's called managed code mainly for that reason.

This framework is smart enough to create a long-living object if its reference is shared among different scopes and a short-living object if is used just in a limited scope.

Stack, heap etc. are implementation details and you shoudn't care about them, as stated in several blog posts by Eric Lippert e.g.:

Then, if you need to create an object in a place (e.g. a method) an use in another, don't worry about memory allocation... just do it.

Comments

1

localObj is created on the heap. All reference types in C# are. Whenever you use the new keyword, that means the created object is headed to the heap. So that is why the reference to the object is still valid even after returning from the method. That object will stick around until the garbage collector comes and grabs it.

EDIT: after rereading your question, I get where you are coming from. Yes the reference itself, localObj is on the stack. That is correct. However the reference is pointing to an object that is on the heap. You returned a copy of that reference, and so the new reference still points to the same object on the heap.

Comments

1

Although the localObj variable goes out of scope on the stack, it references the object on the heap, and you've passed the reference as a return value, therefore the Main method now references the same object on the heap.

The garbage collector won't clean up an object unless it's no longer being used by your program.

If comparing this scenario to C++, think of localObj being a pointer - the object still remains allocated despite the pointer to it being returned. Note: C# managed code and references are not the same as C/C++, so this is only a way by which to remember a similar behaviour.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.