获取.NET对象的内存地址(C#)

时间:2014-08-20 16:43:27

标签: c# mono garbage-collection

我正在尝试追踪单声道运行时中的一个错误,其中变量似乎被分配给一个有效对象,然后被重新分配给一个虚假对象,特别是

//early in code I allocate, fine
var o = new object(); // valid allocation
// later in code this is called, not fine
lock(o) // <- is triggering bug due to "o" now referencing a nonsense memory location.

我想知道何时对“o”的引用变得无意义,并且这样做是为了找到一种方法来确定C#代码中不同时间点的“o”的地址。我知道与其他问题类似,答案是“不要那样做有GC”,但是GC没有用,所以我需要一个解决方法。

有谁知道如何在C#中确定单声道对象的地址?很好地链接在非托管代码或其他任何东西。 (关于诊断主要问题的方法的任何其他线索)。

5 个答案:

答案 0 :(得分:11)

您应该能够使用GCHandle构造来完成此任务。

GCHandle objHandle = GCHandle.Alloc(obj,GCHandleType.WeakTrackResurrection);
int address = GCHandle.ToIntPtr(objHandle).ToInt32(); 

其中'obj'是您想要获得其地址的对象。

答案 1 :(得分:5)

事实证明这在.NET中是不可能的,但可以通过改变单声道运行时代码来实现。要创建可以读取内存地址的C#方法,请对单声道源代码进行以下更改:

改变gc-internal.h添加

gpointer    ves_icall_System_GCHandle_GetAddrOfObject (MonoObject *obj) MONO_INTERNAL;

改变gc.c添加:

gpointer    ves_icall_System_GCHandle_GetAddrOfObject (MonoObject *obj) {
    return (char*)obj;
}

改变GCHandle.cs添加:

MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern static IntPtr GetAddrOfObject(object obj);

public static IntPtr AddrOfObject(object o)
{
    IntPtr res = GetAddrOfObject(o);
    return res;
}

Alter icall-def.h添加

ICALL(GCH_6, "GetAddrOfObject", ves_icall_System_GCHandle_GetAddrOfObject)

请注意,这些必须按顺序排列,因此请将其添加到GetAddrOfPinnedObject行上方 重建

最后,从C#中调用它

for (int i = 0; i < 100; i++) {
    object o = new object ();
    var ptr = GCHandle.AddrOfObject (o);
    Console.WriteLine ("Address: " + ptr.ToInt64().ToString ("x"));
}

答案 2 :(得分:1)

通常,您无法在托管代码中获取已管理对象的地址。如果对象有一个类似int的字段,你可以使用固定的C#语句获取它的地址,然后在对象内部有一个指针。出于调试目的,您可以做出一些假设并获得对象基址指针的偏移量(在32位平台上,单声道上的对象头大小为8字节,此时64位架构上为16字节)。

你的错误报告声称你正在使用Boehm收集器,并且该收集器不会在内存中移动对象,该错误可能是由于某些无关的内存损坏,对象被错误释放或其他一些逻辑错误引起的在GC中(我不确定你指出的零大小是否相关,因为托管对象至少有8-16字节的标题)。

答案 3 :(得分:1)

查看分配给变量的内存地址的一种快速方法是:

代码

string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");

输出

Memory address:45687608

说明

方法 GCHandle.AddrOfPinnedObject 检索固定手柄中对象的地址。

拆卸

您可以查看分配给每种方法和变量的每个内存地址,都应使用Visual Studio中的反汇编窗口对 JIT编译的代码进行分析。

通过在工具>选项>调试>常规下选择启用地址级调试来启用反汇编。

在应用程序的开头设置刹车点,然后开始调试。一旦应用程序出现故障,请选择 Debug> Windows> Disassembly 打开Disassembly窗口。

--- C:\Users\Ivan Porta\source\repos\ConsoleApp1\Program.cs --------------------
        {
0066084A  in          al,dx  
0066084B  push        edi  
0066084C  push        esi  
0066084D  push        ebx  
0066084E  sub         esp,4Ch  
00660851  lea         edi,[ebp-58h]  
00660854  mov         ecx,13h  
00660859  xor         eax,eax  
0066085B  rep stos    dword ptr es:[edi]  
0066085D  cmp         dword ptr ds:[5842F0h],0  
00660864  je          0066086B  
00660866  call        744CFAD0  
0066086B  xor         edx,edx  
0066086D  mov         dword ptr [ebp-3Ch],edx  
00660870  xor         edx,edx  
00660872  mov         dword ptr [ebp-48h],edx  
00660875  xor         edx,edx  
00660877  mov         dword ptr [ebp-44h],edx  
0066087A  xor         edx,edx  
0066087C  mov         dword ptr [ebp-40h],edx  
0066087F  nop  
            Sealed sealedClass = new Sealed();
00660880  mov         ecx,584E1Ch  
00660885  call        005730F4  
0066088A  mov         dword ptr [ebp-4Ch],eax  
0066088D  mov         ecx,dword ptr [ebp-4Ch]  
00660890  call        00660468  
00660895  mov         eax,dword ptr [ebp-4Ch]  
00660898  mov         dword ptr [ebp-3Ch],eax  
            sealedClass.DoStuff();
0066089B  mov         ecx,dword ptr [ebp-3Ch]  
0066089E  cmp         dword ptr [ecx],ecx  
006608A0  call        00660460  
006608A5  nop  
            Derived derivedClass = new Derived();
006608A6  mov         ecx,584F3Ch  
006608AB  call        005730F4  
006608B0  mov         dword ptr [ebp-50h],eax  
006608B3  mov         ecx,dword ptr [ebp-50h]  
006608B6  call        006604A8  
006608BB  mov         eax,dword ptr [ebp-50h]  
006608BE  mov         dword ptr [ebp-40h],eax  
            derivedClass.DoStuff();
006608C1  mov         ecx,dword ptr [ebp-40h]  
006608C4  mov         eax,dword ptr [ecx]  
006608C6  mov         eax,dword ptr [eax+28h]  
006608C9  call        dword ptr [eax+10h]  
006608CC  nop  
            Base BaseClass = new Base();
006608CD  mov         ecx,584EC0h  
006608D2  call        005730F4  
006608D7  mov         dword ptr [ebp-54h],eax  
006608DA  mov         ecx,dword ptr [ebp-54h]  
006608DD  call        00660490  
006608E2  mov         eax,dword ptr [ebp-54h]  
006608E5  mov         dword ptr [ebp-44h],eax  
            BaseClass.DoStuff();
006608E8  mov         ecx,dword ptr [ebp-44h]  
006608EB  mov         eax,dword ptr [ecx]  
006608ED  mov         eax,dword ptr [eax+28h]  
006608F0  call        dword ptr [eax+10h]  
006608F3  nop  
        }
0066091A  nop  
0066091B  lea         esp,[ebp-0Ch]  
0066091E  pop         ebx  
0066091F  pop         esi  
00660920  pop         edi  
00660921  pop         ebp  

00660922  ret  

答案 4 :(得分:0)

我的替代方案......还有@ This similar question

#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