98

I have a function written some time ago (for .NET 3.5), and now that I have upgraded to 4.0

I can't get it to work.

The function is:

public static class MemoryAddress
{
    public static string Get(object a)
    {
        GCHandle handle = GCHandle.Alloc(a, GCHandleType.Pinned);
        IntPtr pointer = GCHandle.ToIntPtr(handle);
        handle.Free();
        return "0x" + pointer.ToString("X");
    }
}

Now, when I call it - MemoryAddress.Get(new Car("blue"))

public class Car
{
    public string Color;
    public Car(string color)
    {
        Color = color;
    }
}

I get the error:

Object contains non-primitive or non-blittable data.

Why doesn't it work anymore?

How can I now get the memory address of managed objects?

6
  • 17
    I am trying to get the address of the object. It is very helpful (and instructive) when determining whether stuff is actually being copied (i.e. passed by value) or not.
    – lejon
    Commented Feb 14, 2011 at 16:07
  • 1
    I assume (hope?) that he's trying to see whether his own code is cloning the object.
    – SLaks
    Commented Feb 14, 2011 at 16:15
  • 231
    "Because I want to know how it works" is a good enough reason. Commented Apr 14, 2011 at 20:50
  • 16
    I agree that "because I want to know how it works" is a good enough reason -- additionally -- if you are trying to verify that two objects are the same instance (versus exact copies) you can use the object.ReferenceEquals method. It doesn't actually tell you what the reference is, but it does return a boolean indicating if the two objects point to the same heap location or not. (Hopefully that helps someone.) Commented Jun 28, 2014 at 19:59
  • 6
    GCHandle.ToIntPtr returns an internal representation of the handle itself, not the address of the object it points to. If you create multiple handles for the same object, GCHandle.ToIntPtr will return different results for each handle. It is GCHandle.AddrOfPinnedObject that returns the address of the object the handle points to. See GCHandle.ToIntPtr vs. GCHandle.AddrOfPinnedObject for more details.
    – Antosha
    Commented Aug 21, 2016 at 20:24

9 Answers 9

68

You can use GCHandleType.Weak instead of Pinned. On the other hand, there is another way to get a pointer to an object:

object o = new object();
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);

Requires unsafe block and is very, very dangerous and should not be used at all. ☺


Back in the day when by-ref locals weren't possible in C#, there was one undocumented mechanism that could accomplish a similar thing – __makeref.

object o = new object();
ref object r = ref o;
//roughly equivalent to
TypedReference tr = __makeref(o);

There is one important difference in that TypedReference is "generic"; it can be used to store a reference to a variable of any type. Accessing such a reference requires to specify its type, e.g. __refvalue(tr, object), and if it doesn't match, an exception is thrown.

To implement the type checking, TypedReference must have two fields, one with the actual address to the variable, and one with a pointer to its type representation. It just so happens that the address is the first field.

Therefore, __makeref is used first to obtain a reference to the variable o. The cast (IntPtr**)(&tr) treats the structure as an array (represented via a pointer) of IntPtr* (pointers to a generic pointer type), accessed via a pointer to it. The pointer is first dereferenced to obtain the first field, then the pointer there is dereferenced again to obtain the value actually stored in the variable o – the pointer to the object itself.

However, since 2012, I have come up with a better and safer solution:

public static class ReferenceHelpers
{
    public static readonly Action<object, Action<IntPtr>> GetPinnedPtr;

    static ReferenceHelpers()
    {
        var dyn = new DynamicMethod("GetPinnedPtr", typeof(void), new[] { typeof(object), typeof(Action<IntPtr>) }, typeof(ReferenceHelpers).Module);
        var il = dyn.GetILGenerator();
        il.DeclareLocal(typeof(object), true);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Stloc_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Conv_I);
        il.Emit(OpCodes.Call, typeof(Action<IntPtr>).GetMethod("Invoke"));
        il.Emit(OpCodes.Ret);
        GetPinnedPtr = (Action<object, Action<IntPtr>>)dyn.CreateDelegate(typeof(Action<object, Action<IntPtr>>));
    }
}

This creates a dynamic method that first pins the object (so its storage doesn't move in the managed heap), then executes a delegate that receives its address. During the execution of the delegate, the object is still pinned and thus safe to be manipulated via the pointer:

object o = new object();
ReferenceHelpers.GetPinnedPtr(o, ptr => Console.WriteLine(Marshal.ReadIntPtr(ptr) == typeof(object).TypeHandle.Value)); //the first pointer in the managed object header in .NET points to its run-time type info

This is the easiest way to pin an object, since GCHandle requires the type to be blittable in order to pin it. It has the advantage of not using implementation details, undocumented keywords and memory hacking.

12
  • 8
    I find this useful in just one scenario: when I'm about to initiate a self-dump of the process, and before I do I want to print out a bunch of memory addresses to managed objects to make analyzing the dump file easier later. Commented Dec 20, 2014 at 17:50
  • I used it for validating the concept of string interning, it worked well! Commented Oct 13, 2017 at 11:35
  • "is very, very dangerous". Is there anything that could go wrong if you do this only to read the actual address of the object and store it somewhere ? (eg : as a unique identifier). Let's say you want to walk on a hierarchy of objects and want to make sure you visit them only once.
    – tigrou
    Commented Mar 29, 2018 at 17:40
  • @tigrou The garbage collector could quite possibly step in and move the object around in memory, changing its address. The first unique identifier of an object you should think about using is its reference, i.e. simply object. If you want something numerical, both ObjectIDGenerator and GCHandle can be used to produce a unique number for an object.
    – IS4
    Commented Mar 30, 2018 at 10:20
  • Shouldn't the third line be : IntPtr ptr = *(IntPtr*)&tr??
    – momvart
    Commented Mar 31, 2018 at 16:13
30

Instead of this code, you should call GetHashCode(), which will return a (hopefully-)unique value for each instance.

You can also use the ObjectIDGenerator class, which is guaranteed to be unique.

7
  • 45
    GetHashCode is not unique. The object id generator id is unique but it prevents the object from being collected. Commented Feb 14, 2011 at 16:28
  • 4
    @Eric: 1) For practical purposes, I think (but never checked) that it's good enough. 2) Is that documented anywhere? Checking the source, it is true.
    – SLaks
    Commented Feb 14, 2011 at 16:31
  • 7
    Re (1) well, what is practical depends on the problem being solved; There is a >1% chance of collision after only 9300 hashes. Re (2) it is not explicitly documented, but as you note, if you use reflector to take apart the implementation you'll see that all it does is sticks the objects into a hash table. As long as that hash table is alive, so are the objects. The documentation implies that the id generator should be kept alive only as long as the serialization operation is running. Commented Feb 14, 2011 at 16:36
  • 3
    +1 for the object ID generator, I didn't know it existed. Hashes do not work well with large amounts of objects that need to be cached, wouldn't be the first time I'd encounter a Duplicate Hashkey error. Not sure if @EricLippert comments means that collisions can appear with the ID Generator as well.
    – Abel
    Commented Mar 28, 2012 at 18:27
  • 2
    If you override Equals you have to also override GetHashCode -- sometimes you also need reference based hash and not an equality based hash. (i.e. two copies of the same object, should give two different hashes in this instance.) Commented Jun 28, 2014 at 19:57
21

There's a better solution if you don't really need the memory address but rather some means of uniquely identifying a managed object:

using System.Runtime.CompilerServices;

public static class Extensions
{
    private static readonly ConditionalWeakTable<object, RefId> _ids = new ConditionalWeakTable<object, RefId>();

    public static Guid GetRefId<T>(this T obj) where T: class
    {
        if (obj == null)
            return default(Guid);

        return _ids.GetOrCreateValue(obj).Id;
    }

    private class RefId
    {
        public Guid Id { get; } = Guid.NewGuid();
    }
}

This is thread safe and uses weak references internally, so you won't have memory leaks.

You can use any key generation means that you like. I'm using Guid.NewGuid() here because it's simple and thread safe.

Update

I went ahead and created a Nuget package Overby.Extensions.Attachments that contains some extension methods for attaching objects to other objects. There's an extension called GetReferenceId() that effectively does what the code in this answer shows.

2
  • One would use it like so: System.Guid guid1 = Overby.Extensions.Attachments.AttachmentExtensions.GetReferenceId(myObject); Commented Aug 1, 2017 at 17:01
  • 1
    Or as an extension method if you're into the whole brevity thing. Commented Aug 2, 2017 at 12:15
8

When you free that handle, the garbage collector is free to move the memory that was pinned. If you have a pointer to memory that's supposed to be pinned, and you un-pin that memory, then all bets are off. That this worked at all in 3.5 was probably just by luck. The JIT compiler and the runtime for 4.0 probably do a better job of object lifetime analysis.

If you really want to do this, you can use a try/finally to prevent the object from being un-pinned until after you've used it:

public static string Get(object a)
{
    GCHandle handle = GCHandle.Alloc(a, GCHandleType.Pinned);
    try
    {
        IntPtr pointer = GCHandle.ToIntPtr(handle);
        return "0x" + pointer.ToString("X");
    }
    finally
    {
        handle.Free();
    }
}
3
  • I agree with your comment about the possibility of object a being moved after it's unpinned, but I don't think your code example and the OP's behave differently. Both compute pointer before handle.Free().
    – Joh
    Commented Feb 14, 2011 at 16:19
  • @Joh: It does seem that I mis-read his code at first. From what we can see, it looks like both should act the same. I wonder if they do. If mine acts differently, I'd have to wonder what the JIT compiler is doing. Commented Feb 14, 2011 at 16:30
  • 5
    It's the "GCHandle.Alloc(a, GCHandleType.Pinned)" that fails executing.
    – lejon
    Commented Feb 16, 2011 at 8:45
8

Here's a simple way I came up with that doesn't involve unsafe code or pinning the object. Also works in reverse (object from address):

public static class AddressHelper
{
    private static object mutualObject;
    private static ObjectReinterpreter reinterpreter;

    static AddressHelper()
    {
        AddressHelper.mutualObject = new object();
        AddressHelper.reinterpreter = new ObjectReinterpreter();
        AddressHelper.reinterpreter.AsObject = new ObjectWrapper();
    }

    public static IntPtr GetAddress(object obj)
    {
        lock (AddressHelper.mutualObject)
        {
            AddressHelper.reinterpreter.AsObject.Object = obj;
            IntPtr address = AddressHelper.reinterpreter.AsIntPtr.Value;
            AddressHelper.reinterpreter.AsObject.Object = null;
            return address;
        }
    }

    public static T GetInstance<T>(IntPtr address)
    {
        lock (AddressHelper.mutualObject)
        {
            AddressHelper.reinterpreter.AsIntPtr.Value = address;
            T obj = (T)AddressHelper.reinterpreter.AsObject.Object;
            AddressHelper.reinterpreter.AsObject.Object = null;
            return obj;
        }
    }

    // I bet you thought C# was type-safe.
    [StructLayout(LayoutKind.Explicit)]
    private struct ObjectReinterpreter
    {
        [FieldOffset(0)] public ObjectWrapper AsObject;
        [FieldOffset(0)] public IntPtrWrapper AsIntPtr;
    }

    private class ObjectWrapper
    {
        public object Object;
    }

    private class IntPtrWrapper
    {
        public IntPtr Value;
    }
}
2
  • 1
    That's a neat hack. Commented Aug 6, 2020 at 2:01
  • In GetInstance<T> you have a memory leak. You should set the object to null again with something like reinterpreter.AsObject.Object = null before returning the instance. Otherwise this static class will keep a reference to the last instance you where retrieving. Commented Oct 27, 2021 at 7:10
4

For .net core

public static string Get(object a)
{
    var ptr = Unsafe.As<object, IntPtr>(ref a);
    return $"0x{ptr:X}";
}
2
  • Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community? Commented Nov 11, 2023 at 1:24
  • This works great on .NET 8, and is by far the simplest answer. My use case was to find out where the object was being cached and I could pass the output of this to !gcroot in sos.dll to get the answer.
    – EM0
    Commented Mar 18 at 17:09
3

This works for me...

#region AddressOf

    /// <summary>
    /// Provides the current address of the given object.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static System.IntPtr AddressOf(object obj)
    {
        if (obj == null) return System.IntPtr.Zero;

        System.TypedReference reference = __makeref(obj);

        System.TypedReference* pRef = &reference;

        return (System.IntPtr)pRef; //(&pRef)
    }

    /// <summary>
    /// Provides the current address of the given element
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="t"></param>
    /// <returns></returns>
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static System.IntPtr AddressOf<T>(T t)
        //refember ReferenceTypes are references to the CLRHeader
        //where TOriginal : struct
    {
        System.TypedReference reference = __makeref(t);

        return *(System.IntPtr*)(&reference);
    }

    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    static System.IntPtr AddressOfRef<T>(ref T t)
    //refember ReferenceTypes are references to the CLRHeader
    //where TOriginal : struct
    {
        System.TypedReference reference = __makeref(t);

        System.TypedReference* pRef = &reference;

        return (System.IntPtr)pRef; //(&pRef)
    }

    /// <summary>
    /// Returns the unmanaged address of the given array.
    /// </summary>
    /// <param name="array"></param>
    /// <returns><see cref="IntPtr.Zero"/> if null, otherwise the address of the array</returns>
    [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
    public static System.IntPtr AddressOfByteArray(byte[] array)
    {
        if (array == null) return System.IntPtr.Zero;

        fixed (byte* ptr = array)
            return (System.IntPtr)(ptr - 2 * sizeof(void*)); //Todo staticaly determine size of void?
    }

    #endregion
1
  • 3
    AddressOf(object obj) in your code returns the address of the stack-allocated TypedReference, not the object address. To obtain the object address, you have to dereference two times as in this answer.
    – Antosha
    Commented Aug 21, 2016 at 20:58
0

Switch the alloc type:

GCHandle handle = GCHandle.Alloc(a, GCHandleType.Normal);
4
  • If he switches the GCHandleType he wouldn't be able to use the ToIntPtr method. Commented Feb 14, 2011 at 16:50
  • Does this not change the instruction to the GC that I don't want it to move the object around? (Changing the code does however compile and gives no errors during run-time.)
    – lejon
    Commented Feb 15, 2011 at 9:29
  • 1
    You can still call ToIntPtr() on a non pinned object. But the ptr it returns is only really valid within the .NET run-time. It no longer represents a pyhsical address you can write directly to, but within the CLR is can be used as a "reference" to an object Commented Feb 15, 2011 at 13:27
  • For all GCHandle types, pinned or non-pinned, GCHandle.ToIntPtr returns an internal representation of the handle itself, not the address of the object it points to. If you create multiple handles for the same object, GCHandle.ToIntPtr will return different results for each handle. It is GCHandle.AddrOfPinnedObject that returns the address of the object the handle points to, however, you may use that method for pinned handles only.
    – Antosha
    Commented Aug 21, 2016 at 20:38
-4

Getting the address of an arbitrary object in .NET is not possible, but can be done if you change the source code and use mono. See instructions here: Get Memory Address of .NET Object (C#)

1
  • 2
    Not usual yes, not possible... no... see above
    – Jay
    Commented Mar 2, 2018 at 14:42

Not the answer you're looking for? Browse other questions tagged or ask your own question.