我正在尝试追踪单声道运行时中的一个错误,其中变量似乎被分配给一个有效对象,然后被重新分配给一个虚假对象,特别是
//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#中确定单声道对象的地址?很好地链接在非托管代码或其他任何东西。 (关于诊断主要问题的方法的任何其他线索)。
答案 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