ProgramingTip

C #에서 개체의 메모리 주소

bestdevel 2020. 11. 15. 11:35
반응형

C #에서 개체의 메모리 주소


얼마로 전에 함수 (.NET 3.5 용)가 있는데 이제 4.0 업그레이드했습니다.

작동 작동 수 없습니다.

기능은 다음과 가변합니다.

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");
    }
}

이제 내가 그것을 호출하면 -MemoryAddress.Get (new Car ( "blue"))

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

오류가 발생합니다.

객체에 원시 데이터가 아니거나 블리트 할 수없는 데이터가 있습니다.

왜 더 이상 작동하지 않습니까?

이제 관리 대상 개체의 메모리 주소를 어떻게 관리합니까?


고정 된 대신 GCHandleType.Weak을 사용할 수 있습니다. 반면에 다른 방법에 대한 포인터를 얻을 수 있습니다.

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

안전하지 않은 블록이 필요하며 매우 위험합니다.


C #에서 by-ref 로컬을 사용할 수 없습니다 __makeref.

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

TypedReference 가 "generic" 이라는 점에는 한 가지 중요한 차이점이 있습니다 . 모든 유형의 변수에 대한 참조를 저장하는 데 사용할 수 있습니다. 일치하지 참조에 액세스하려는 해당 유형 (예 :)을 지정 __refvalue(tr, object)해야합니다.

유형 검사를 구현 비용 TypedReference에 두 개의 필드가 있어야합니다. 하나에는 변수에 대한 실제 주소가 있고 다른 하나에는 유형 표현에 대한 포인터가 있습니다. 주소가 첫 번째 필드 인 경우에도 마찬가지입니다.

따라서 __makeref는 변수에 대한 참조를 먼저 사용 o합니다. 캐스트 (IntPtr**)(&tr)는 구조를 IntPtr*포인터를 통해 액세스되는 (일반 포인터 유형에 대한 포인터)의 배열 ( 포인터를 통해 표시됨)로 처리됩니다. 포인터는 첫 번째 필드를 다시 위해 먼저 역 참조되고, 그 다음에는 변수에 실제로 필요한 값 ( o자체에 포인터 포인터)을 위해 참조 됩니다.

그러나 2012 년부터 더 안전한 솔루션을 찾았습니다.

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>>));
    }
}

이렇게하면 먼저 생성되는 개체를 고정한 다음 (관리되는 힙에서 저장소가 이동하지 않도록) 해당 주소를받는 대리 튼튼 실행하는 동적 메서드가됩니다. 대리 튼튼 실행하는 동안 개체는 여전히 고정되어 있으므로 포인터를 통해 안심하고 조작 할 수 있습니다.

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

GCHandle개체 를 고정하기 위해 유형을 blittable로 설정해야하는 방법 개체를 고정하는 가장 쉬운 방법 입니다. 구현 세부 사항, 문서화되지 않은 키워드 및 메모리 해킹을 사용하지 않는 이점이 있습니다.


이 코드 대신 호출해야합니다 GetHashCode(). 각 인스턴스에 대해 고유 한 값을 반환합니다.

고유 한 ObjectIDGenerator클래스를

사용할 수도 있습니다 .


메모리 주소가 필요하지 않고 관리 대상 개체를 고유하게 포착하는 수단이 필요한 경우 더 나은 솔루션이 있습니다.

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();
    }
}

이것은 사용되는 메모리 누수가 발생하는 안전하며 내부적으로 약한 참조를 사용합니다.

원하는 키 생성 수단을 사용할 수 있습니다. Guid.NewGuid()간단하고 코드로부터 안전하기 때문에 여기서 사용 하고 있습니다.

최신 정보

계속해서 해서 다른 개체에 개체를 연결하기위한 일부 확장 메서드가 포함 된 Nuget 패키지 Overby.Extensions.Attachments만들었습니다 . GetReferenceId()이 문장의 확장 이 있습니다.


해당 핸들을 해제하면 가비지 수집기가 고정 된 메모리를 자유롭게 사용할 수 있습니다. 고정되어야하는 메모리에 대한 포인터가 있고 해당 메모리를 고정 해제하면 모든 것이 해제됩니다. 이것은 3.5에서 작동하지 않는 것이 아마도 운이 좋을 것입니다. JIT 컴파일러와 4.0 용 가동은 아마도 개체 수명 분석 작업을 더 잘 수행 할 것입니다.

이 작업을 수행하려는 경우 사용 try/finally하여 개체가 고정 해제되지 않도록 할 수 있습니다 .

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();
    }
}

이것은 나를 위해 작동합니다 ...

#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

할당 유형을 전환합니다.

GCHandle handle = GCHandle.Alloc(a, GCHandleType.Normal);

안전하지 않은 코드 나 객체 고정을 포함하지 않는 간단한 방법이 있습니다. 또한 역으로 작동합니다 (주소의 객체) :

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;
            return (T)AddressHelper.reinterpreter.AsObject.Object;
        }
    }

    // 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;
    }
}

.NET에서 임의의 객체의 주소를 얻는 것은 불가능하지만 소스 코드를 변경하고 mono를 사용하면 가능합니다. 여기 지침을 참조하십시오. .NET 개체의 메모리 주소 가져 오기 (C #)

참고 URL : https://stackoverflow.com/questions/4994277/memory-address-of-an-object-in-c-sharp

반응형