C# and GC.KeepAlive()

Today, while browsing some C++/CLI code, I stumbled upon several calls to GC.KeepAlive(someObj).

Immediately I thought memory leak – because I thought KeepAlive() would keep the object alive indefinitely.

Fortunately, this turned out to be wrong, though.

After reading the documentation of GC.KeepAlive() (couldn’t really figure it out), I did some decompiling and found out that GC.KeepAlive() looks like this:

1
2
3
4
[MethodImpl(MethodImplOptions.NoInlining)]
public static void KeepAlive(object obj)
{
}

It just does nothing. So what’s its purpose?

It’s there to fake an access to a variable.

Why? The .NET garbage collector may collect a variable directly after its last use – and not necessarily, contrary to common belief, at the end of the variable’s scope.

Consider this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class SomeClass
{
  // This field is initialized somewhere 
  // in the constructor (not shown here).
  public SomeOtherClass Value;
 
  ...
}
 
...
 
void MyMethod()
{
  SomeClass obj = new SomeClass();
  SomeOtherMethod(obj.Value);
  YetAnotherMethod();
  // obj still alive here? Possibly not.
}

The garbage collector may collect obj just after the runtime has retrieved obj.Value (line 15), i.e. before SomeOtherMethod() is even called.

Note: The exact line where obj will be marked for collection is up to the JIT compiler. The behavior describe above seems to be called “lookahead optimization”.

Usually this optimization not a problem. It becomes a problem, however, if SomeClass has a finalizer:

1
2
3
4
5
6
7
8
9
10
11
class SomeClass
{
  public SomeOtherClass Value;
 
  ~SomeClass()
  {
     // "Value" can't be used anymore 
     // after Dispose() has been called.
     this.Value.Dispose();
  }
}

So, if obj‘s finalizer is executed before SomeOtherMethod() is called, SomeOtherMethod() won’t be able to use obj.Value anymore.

To solve this problem, add GC.KeepAlive() after the call to SomeOtherMethod(), like this (line 5):

1
2
3
4
5
6
7
void MyMethod()
{
  SomeClass obj = new SomeClass();
  SomeOtherMethod(obj.Value);
  GC.KeepAlive(obj);
  YetAnotherMethod();
}

This way, the garbage collector won’t collect obj (and thus run its finalizer) before line 5 has been reached.

Notes:

  • The implementation of the finalizer of SomeClass is flawed – as the examples in this article show. The user shouldn’t need to worry about Value being disposed too early.

    • Rule of thumb: A finalizer should only dispose the resources of its own class, not resources of some member (by calling Dispose() on members).
    • The problem with the finalizer persists if Value is an unmanaged resource/pointer that’s being passed to SomeOtherMethod(). This is always possible in C++/CLI. In C# Value could be of type IntPtr.
    • In the examples above, consider implementing and using IDisposable for SomeClass instead of GC.KeepAlive(), if you need a finalizer.
    • You still need to use GC.KeepAlive() if you can’t change the implementation of SomeClass.
  • Using GC.KeepAlive() is like using GCHandle, just more light-weight and faster.
  • GC.KeepAlive() only works because it can’t be inlined by the compiler (MethodImplOptions.NoInlining).

Passing native pointers across C++/CLI assembly boundaries

C++/CLI allows you to mix native C++ code with managed .NET code (which is extremly nice). Mixing such code also allows you to create methods in a .NET class that take or return pointers to native (C++) classes. Unfortunately, this doesn’t work out of the box across assemblies (read: DLLs). If you define a .NET class in one assembly and this class has a method that returns a pointer, you may not be able to use this method from within another C++/CLI assembly.

This article describes the problem and shows solutions.

Read more →

P/Invoke Tutorial: Basics (Part 1)

P/Invoke is a way of calling C/C++ functions from a .NET program. It’s very easy to use. This article will cover the basics of using P/Invoke.

Note: This tutorial will focus on Windows and thus use Visual Studio. If you’re developing on another platform or with another IDE, adopting the things in this article should be easy enough.

Read more →

CSS clearfix with LESS

The idea of CSS is to separate design from structure (which is represented by HTML). So much for the theory. In pratice, however, this doesn’t always work – or at least the solution isn’t very obvious.

One of the most prominent examples is the so called clearfix. It solves the floating elements problem (described below) but usually requires you to change your HTML code.

Fortunately, with LESS (a better CSS) this is no longer necessary.

Read more →

Triggering a build when file changed in Visual Studio

In Visual Studio when a source code file is changed, the project it belongs to will be built when the whole solution is being built. So far, so good. But what happens if you want some other files (read: non source code files) to have the same behavior?

In my case, I had some translation files (XML files), and a custom build event that converted them into a binary representation. So when one of translation files changed, I wanted this change to trigger a build for the project the translation files were contained in, so that they could be converted into binary form (ie. so that the build event was executed).

Visual Studio (at least version 2010) provides an easy but hidden way to achieve exactly this. After you’ve added the file(s) to your project, right-click them and choose Properties (Alt+Return).

The Properties menu item

Note: You need to open the properties for the files you want to trigger the build – don’t open the project’s properties. Also, you need to open the “Properties” dialog. You can’t do this from the “Properties” panel (which usually opens up when hitting F4).

Note 2: You can have selected multiple files to make them all triggering a build when changed in one step.

In the “Properties” dialog under Configuration Properties –> General –> Item Type you select Custom Build Tool. Then you hit “OK”. And that’s it.

Selecting "Custom Build Tool" as "Item Type".

Changing the file(s) should now trigger a build when you build the solution (via menu Build –> Build Solution).