Confused about gcnew and object lifetime

C

cedgington

I wanted to take advantage of the large set of functionality offered by
the framework, so for my latest project I'm using managed C++ with .NET
v2. I'm using the gcnew operator in two different ways, and I'm
confused about the lifetime of objects and whether or not I should be
calling delete. Here are two examples:

ref class SYMBOL : public IComparable
{
public:
// Constructor / destructor
SYMBOL()
{
name = nullptr;
};
~SYMBOL()
{
if (name != nullptr)
{
delete name;
}
}

//Data
String^ name;
UInt32 relativeAddress;

// Methods
virtual Int32 CompareTo(Object^ obj)
{
MODULE^ mod = dynamic_cast<MODULE^>(obj);

return relative.CompareTo(mod->relativeAddress);
}
};

In the example above, the class is used to create a List
(System.Collections.Generic.List) of SYMBOL objects. For each addition
to the list (symbols), I do a gcnew of a SYMBOL object, add pass the
gcnew'ed handle to the List.Add method. I also do a gcnew of a String
object, and assign the gcnew'ed handle to the name member of the SYMBOL
object. As you can see, I am explicitly deleting the String object in
the SYMBOL destructor. Is this correct?

The other case is this:

void PARSER::ReadLogFile(void)
{
FileInfo^ logFileI = gcnew FileInfo(logFilename);
FileStream^ logFileS = logFileI->OpenRead();
logData = gcnew array<Byte>((int)logFileI->Length);
int bytesRead = logFileS->Read(logData, 0, (int)logFileI->Length);

delete logFileI;
// TODO: Add some error checking, mechanism for returning 0-length
array on error
}

As you can see, I'm manually deleting the gcnew'ed FileInfo. Is this
correct?

Sorry if this is a basic question, but the info I've found on the net
is not consistent. In one place I found an "expert" saying that
"objects created with gcnew should never be deleted." However, that
doesn't make sense to me. I understand that after the app exits,
objects on the managed heap will eventually be gc'ed, but to be
completely right about memory management, shouldn't I be deleting
objects that I create?

Thanks,
-Chris
 
G

Guest

I wanted to take advantage of the large set of functionality offered by
the framework, so for my latest project I'm using managed C++ with .NET
v2. I'm using the gcnew operator in two different ways, and I'm
confused about the lifetime of objects and whether or not I should be
calling delete. Here are two examples:

Yes,

The way I understood it you have to manually delete the object.
C++/CLI != C#

There was a very lively discussion a month ago about this topic in this very
newsgroup (Destructor: not gauranteed to be called?)
http://groups.google.nl/group/micro...d+to+be+called?&rnum=1&hl=nl#93c2007f860c0a46

It had a lot of input from lots of experienced people.
The thread makes a good read if you want to know more about this topic.

--

Kind regards,
Bruno.
(e-mail address removed)
Remove only "_nos_pam"
 
T

Tom Widmer [VC++ MVP]

Bruno said:
Yes,

The way I understood it you have to manually delete the object.
C++/CLI != C#

C# and C++/CLI have equivalent GC AFAIK. The problems with C++/CLI GC
are the exact same ones as those of C# (and Java for that matter).
There was a very lively discussion a month ago about this topic in this very
newsgroup (Destructor: not gauranteed to be called?):
http://groups.google.nl/group/micro...d+to+be+called?&rnum=1&hl=nl#93c2007f860c0a46

It had a lot of input from lots of experienced people.
The thread makes a good read if you want to know more about this topic.

I think you missed the point (not that I read the whole thread) - you do
not have to call delete if you don't care whether your destructor runs
or not in a timely manner (or even ever). This is true for Java (my area
of GC familiarity), C# and C++/CLI. It is only if the object in question
has a finalizer/destructor that frees up a non-memory resource, such as
a DB connection, socket or file, that you should manually "delete" it in
order to guarantee timely clean up.

Basically, the GC is adept at recycling memory when it gets low, but it
obviously doesn't know which objects to finalise in order to recycle
other resources, such as db connections. For example, you could end up
running out of available connections even though you only have 1
"reachable" db connection object, since the GC hasn't bothered to
finalize your old connections (presumably because it isn't low on memory).

Essentially, GC frees you from manual memory management but not from
manual resource management.

Tom
 
G

Guest

I think you missed the point (not that I read the whole thread) - you do
not have to call delete if you don't care whether your destructor runs
or not in a timely manner (or even ever). This is true for Java (my area
of GC familiarity), C# and C++/CLI. It is only if the object in question
has a finalizer/destructor that frees up a non-memory resource, such as
a DB connection, socket or file, that you should manually "delete" it in
order to guarantee timely clean up.

Hi Tom,

I did not miss that point. In the thread I linked to someone already
mentioned that the
GC will eventually (whenever it is in the mood - if ever - as you say)
perform the garbage collection.
However, since lots of classes involve physical resources you have to manually
release them if you want to have some determinism in it to prevent resource
problems.

Since everything in my programming background revolves around some resources
or other,
The GC cannot solve any of my problems. I care as much about resources as
about memory.

Your remark reminded me that this is also true for C#. I just don't use C#
for anything
other than some test programs and private applications, which is why I
didn't think of it.

I agree that I should have been more specific.
Saying 'You have to do it manually If you want to be sure that it is released'
would have been much more precise.

--

Kind regards,
Bruno.
(e-mail address removed)
Remove only "_nos_pam"
 
C

Chris Edgington

So maybe I didn't make it clear why I wanted the questions answered.
I'm not worried about running out of resources, and therefore wanting
to make sure that I'm explicility releasing them, I'm wanting to make
sure that resources aren't going to be GC'ed before I'm done with them.
I guess a better general question would be - does the GC ever
automatically release (delete) an object while the process owning the
object is still active?

Thanks,
-Chris
 
C

Chris Edgington

Here's some info I found on MSDN that provides some insight into this:

Code authored in Visual C++ and compiled with /clr will run a type's
destructor for the following reasons:

* If an object created using stack semantics goes out of scope. For
more information, see C++ Stack Semantics for Reference Types.
* If an exception is thrown during the object's construction.
* If the object is a member in an object whose destructor is
running.
* If you call the delete Operator (C++) on a handle (^ (Handle to
Object on Managed Heap)).
* If you explicitly call the destructor.
 
G

Guest

So maybe I didn't make it clear why I wanted the questions answered.
I'm not worried about running out of resources, and therefore wanting
to make sure that I'm explicility releasing them, I'm wanting to make
sure that resources aren't going to be GC'ed before I'm done with them.
I guess a better general question would be - does the GC ever
automatically release (delete) an object while the process owning the
object is still active?

That should never happen.

Garbage collection only happens if all gc handles to an object on the
managed heap are out of scope. From that time on the GC is free to Dispose of
them at its own leisure.

Until that time your objects are safe.

--

Kind regards,
Bruno.
(e-mail address removed)
Remove only "_nos_pam"
 
C

Carl Daniel [VC++ MVP]

I wanted to take advantage of the large set of functionality offered
by the framework, so for my latest project I'm using managed C++ with
.NET v2. I'm using the gcnew operator in two different ways, and I'm
confused about the lifetime of objects and whether or not I should be
calling delete. Here are two examples:

There's no need to invoke delete in either of the cases that you cited.
However, you touched on a case where you should call delete or use a
stack-allocated object:
void PARSER::ReadLogFile(void)
{
FileInfo^ logFileI = gcnew FileInfo(logFilename);
FileStream^ logFileS = logFileI->OpenRead();
logData = gcnew array<Byte>((int)logFileI->Length);
int bytesRead = logFileS->Read(logData, 0, (int)logFileI->Length);

delete logFileI;
// TODO: Add some error checking, mechanism for returning 0-length
array on error
}

Here, you should be calling delete on logFileS, or at least executing
logFileS->Close() to make sure that the Win32 file handle is closed
deterministicly. Otherwise, that file will remain open (and locked) until
the FileStream object is collected. Unfortunately, the System.IO classes
don't implement copy constructors, so it's not possible to create them as
stack-based objects, so you're left with something like this:

void ReadLogFile(void)
{
FileInfo^ logFileI = gcnew FileInfo(logFilename);
FileStream^ logFileS = logFileI->OpenRead();
try
{
logData = gcnew array<Byte>((int)logFileI->Length);
int bytesRead = logFileS->Read(logData, 0, (int)logFileI->Length);
}
finally
{
delete logFileS;
}
}


-cd
 
C

Chris Edgington

I'm confused by your example:

* You do not delete the FileInfo object gcnew'ed
* You do delete the FileStream object returned from the FileInfo object

If any .NET API returns an object handle, if you want deterministic
resource cleanup should the caller of the API eventually call delete on
the object's handle?

For example, if I do { String^ newText = oldText->Remove(0, someChars)
}, is the API actually doing a gcnew of a String object, and therefore
is it now my responsibility to delete that object (or leave it up to
the GC to delete the object at some point).

Thanks,
-Chris
 
G

Guest

* You do not delete the FileInfo object gcnew'ed
FileInfo only contains data. It does not hold resources. Therefore it does
not really matter how long it takes before it is GC'ed. At least from a
resource point of view.
* You do delete the FileStream object returned from the FileInfo object
The filestream holds a resource (the file handle) so if it is not deleted
explicitly, it will be left open until the GC decides to delete it.
If any .NET API returns an object handle, if you want deterministic
resource cleanup should the caller of the API eventually call delete on
the object's handle? Yes.

For example, if I do { String^ newText = oldText->Remove(0, someChars)
}, is the API actually doing a gcnew of a String object, and therefore
is it now my responsibility to delete that object (or leave it up to
the GC to delete the object at some point).
Indeed.

--

Kind regards,
Bruno.
(e-mail address removed)
Remove only "_nos_pam"
 
C

Chris Edgington

Another example ... if I have a function like I have listed below, do I
need to delete each object returned from StreamReader.ReadLine? If I do
not delete each object, and I end up parsing a very large text file,
how will that affect the GC heap? Is the compiler / runtime smart
enough to see that I'm no longer storing a handle to the object (since
I've stored the handle of a new object in the original container), so
it eventually free's the objects even though the program is still
running (if it needs to do a gc)? Same line of question as previous
post ... if I want deterministic usage of the GC heap, is it safe (or
even proper) to go ahead and call delete on each object returned from
ReadLine?

Thanks,
-Chris

void MODULE::LoadSymbolTable(void)
{
StreamReader^ mapFile = gcnew StreamReader(filename);
String^ line;

while (line = mapFile->ReadLine())
{
// process the line
}

mapFile->Close();
delete mapFile;
}
 
C

Carl Daniel [VC++ MVP]

Chris said:
For example, if I do { String^ newText = oldText->Remove(0,
someChars) }, is the API actually doing a gcnew of a String object,
and therefore is it now my responsibility to delete that object (or
leave it up to the GC to delete the object at some point).

Keep in mind - delete applied to a managed object does nothing unless the
object is IDisposable, in which case it calls Dispose() on it. For objects
like System::String, System::IO::FileInfo, etc., calling delete has no
effect whatsoever. The memory will be reclaimed only when the GC gets
around to it.

For an object such as System::IO::FileStream, which is IDisposable, calling
delete on it results in the unmanaged resources in controls being freed,
which is what you wanted.

-cd
 
C

Carl Daniel [VC++ MVP]

Chris said:
Another example ... if I have a function like I have listed below, do
I need to delete each object returned from StreamReader.ReadLine?
No.

If
I do not delete each object, and I end up parsing a very large text
file, how will that affect the GC heap?

It will grow larger and eventually trigger a collect. This will be a Gen0
collect though, which is very fast.
Is the compiler / runtime
smart enough to see that I'm no longer storing a handle to the object
(since I've stored the handle of a new object in the original
container), so it eventually free's the objects even though the
program is still running (if it needs to do a gc)?

Yes. The CLR implements 100% accurate tracking of the reachability of
memory. The instant you assign a new value to line, the old value is
available for collection. Reachability is based on the concept of roots -
static variables are roots, as are all of the object references on the
stacks of all threads that have entered the CLR, plus any roots that have
been explicitly created (e.g. through the gcroot<T> template). If it's not
possible to reach an object by tracing references starting at a root then
the object is available for collection.
Same line of
question as previous post ... if I want deterministic usage of the GC
heap, is it safe (or even proper) to go ahead and call delete on each
object returned from ReadLine?

Safe and pointless. Since System::String isn't IDisposable, calling delete
on it will have no effect at all.
Thanks,
-Chris

void MODULE::LoadSymbolTable(void)
{
StreamReader^ mapFile = gcnew StreamReader(filename);
String^ line;

while (line = mapFile->ReadLine())
{
// process the line
}

mapFile->Close();
delete mapFile;
}

-cd
 
C

Chris Edgington

Wow. I'm from the device driver / embedded world, where you need to be
diligent about freeing even the smallest allocation. Your explanations
have answered all my questions, but also created many more. Are there
any books written on how this whole GC stuff works in the CLR?

As a side note - this almost seems like it would have the tendency to
create "sloppy" programmers - allocating objects willy nilly, depending
on the system to clean up. Or, maybe some would say that many
programmers are sloppy in the first place, and this type of system
exists to protect the platform from those programmers ;)

Thanks for the quick responses.

-Chris
 
T

Tamas Demjen

Chris said:
As a side note - this almost seems like it would have the tendency to
create "sloppy" programmers - allocating objects willy nilly, depending
on the system to clean up.

In a system that was designed to handle that it's OK. It's a different
story when those programmers later go to an unmanaged environment,
whether they can adapt.

But even in the managed world, there are two issues with this "the
system will clean up for me" attitude. First, resources still need to be
manually disposed. Being sloppy there means scarce system resources may
remain locked indefinitely. Not closing a file is often not such a big
problem, but not closing a mutex is disastrous. IMO, it's not
immediately clear which object is a resource, and it puts an extra
pressure on the programmer. There's no quick way that I know of telling
whether an object needs deterministic destruction or not. GC makes
programmers sloppy in a way that they no longer care and may not even
deallocate resource type objects. This is a much bigger problem, as far
as I can tell, than being sloppy with memory allocations. In fact, in
..NET you can't deallocate memory, only the system can, but you still
MUST deallocate resources. In native C++ there is stack syntax that
always works, and boost::shared_ptr that always guards everything
properly. Ironically, .NET makes you think even harder than a C
programmer, because for every object allocated you have to look up the
help to decide whether it needs to be deleted or not. It makes me
paranoid -- did I miss something that really needs destruction? What
happens if a class doesn't store any resources today, but it may in the
future? That class will be used in 100s of places, without the proper
deallocation. Later a resource will be added for that class, and all of
a sudden every existing code becomes invalid. So classes that MAY behave
as a resoruce should be designed to be a resource from day 1. These
issues make you think just as hard as if you didn't have GC.

What's even worse is that .NET doens't have all the tools to make
guaranteed proper deletion of resources. The unmanaged world has slowly
evolved during the past several decades, and we finally have reference
counted smart pointers and deterministic destruction with very solid
guarantees and strong, stable libraries that really makes you forget
about manual deletion of everything. .NET was designed with the goal
that you no longer have to care about any of this stuff, which is not
entirely true. Maybe 99% of your objects never need to be deleted, but
that remaining 1% is as critical as ever was. So programmers still have
to be alert.

The second (completely different) aspect is that just because all memory
is guaranteed to be deleted it doesn't mean you can't have leaks
anymore. You can still have serious memory leaks even if all your
objects are always deterministically destructed. It's as simple as
forgetting about your items stored in containers. You no longer need
them, but they may still reside in some collection in the memory. If you
forget about removing unused objects from lists, maps, sets, hashed
storages, they occupy precious memory, and it's virtually the same
effect as a memory leak. Even worse, no memory leak detector ever finds
that. These kinds of problems are not automatically solved by any type
of garbage collection or reference counting. There's no magic solution
that enitrely protects the system from sloppy programmers.

Once I had a huge lookup table to speed up calculations, and I forgot to
make it a static member. So every instance of my classes had its own
copy of the table, and systems with 256MB RAM ran out of memory quickly.
No leak detector found anything, and yet my application's memory
consumption was growing well beyond the expected limits. Once again,
there was nothing that didn't get properly freed, and yet I had memory
issues due to a human error.

Tom
 
B

Brandon Bray [MSFT]

Chris said:
So maybe I didn't make it clear why I wanted the questions answered.
I'm not worried about running out of resources, and therefore wanting
to make sure that I'm explicility releasing them, I'm wanting to make
sure that resources aren't going to be GC'ed before I'm done with
them. I guess a better general question would be - does the GC ever
automatically release (delete) an object while the process owning the
object is still active?

You've unconvered a very interesting problem. Short answer is that the CLR
will never collect a managed object while before it has finished use. The
problem is when a managed object represents something else (like an OS file
handle, pointer to a buffer on the native heap, etc.) If the managed object
no longer has references to it, but the resource it manages is still being
used (because it didn't properly abstract the resource), the GC will collect
the manage object which will then clean up the resource.

For more info on this, check out:
http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx

To solve this in C++, just call delete on the handle to the managed object.
The call to destructor will keep the object alive until it is no longer
needed.

Cheerio!
Brandon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top