Комментарии 41
System.IO.MemoryStream stream = new System.IO.MemoryStream();
BinaryFormatter objFormatter = new BinaryFormatter();
objFormatter.Serialize(stream, new X());
stream.Length — будет его размером?
зы:
[Serializable]
class X { public int Val; }
BinaryFormatter objFormatter = new BinaryFormatter();
objFormatter.Serialize(stream, new X());
stream.Length — будет его размером?
зы:
[Serializable]
class X { public int Val; }
-1
Сильно подозреваю, что неправильно, но:
повесить на класс атрибут [StructLayout(LayoutKind.Sequential)] и:
int size = Marshal.SizeOf(new X());
А вообще такие вещи профайлер считает :)
повесить на класс атрибут [StructLayout(LayoutKind.Sequential)] и:
int size = Marshal.SizeOf(new X());
А вообще такие вещи профайлер считает :)
0
Marshal.SizeOf вернет в данном случае объем, занимаемый полями, т.е. 4 байта
профайлер я пытался вчера дернуть из шарпа, но не очень получилось, да и неточные он данные дает
профайлер я пытался вчера дернуть из шарпа, но не очень получилось, да и неточные он данные дает
0
или тут надо получать ссылку на обьект IntPtr через:
GCHandle gch = GCHandle.Alloc(new X());
IntPtr unmanagedPtr = GCHandle.ToIntPtr(gch);
потом передать её unmanaged коду на подсчет?
GCHandle gch = GCHandle.Alloc(new X());
IntPtr unmanagedPtr = GCHandle.ToIntPtr(gch);
потом передать её unmanaged коду на подсчет?
0
Эта тема вроде хорошо раскрыта в CLR via C#.
А вообще правильный ответ 12?
А вообще правильный ответ 12?
+1
long aaa = GC.GetTotalMemory(true);
X t = new X();
t.Val = 5;
long bbb = GC.GetTotalMemory(true);
long ccc = bbb — aaa;
System.Windows.Forms.MessageBox.Show(ccc.ToString());
ответ: 24?
X t = new X();
t.Val = 5;
long bbb = GC.GetTotalMemory(true);
long ccc = bbb — aaa;
System.Windows.Forms.MessageBox.Show(ccc.ToString());
ответ: 24?
0
Экземпляр этого класса занимает 12 байт. 4 байта отводится на ссылку на элемент в таблице синхронизации (если объект не участвует в блокирующих операциях, эти 4 байта равны 0), 4 на ссылку на тип объекта, последние 4 на значение Val.
Как это проверить не знаю =)
Как это проверить не знаю =)
+1
вы не могли бы перенести этюд в блог .net? топики из личных блогов не выходят на главную страницу Хабра
-1
мне как раз и не нужно, чтоб он выходил на главную страницу, потому что тогда меня минусуют те, кому дотнет по барабану — похоже, я зачастил своими этюдами )
а делать закрытым тоже не хочу, так как он скорее предназначен для тех, у кого нет инвайта на хабр, нежели для тех, у кого есть
а делать закрытым тоже не хочу, так как он скорее предназначен для тех, у кого нет инвайта на хабр, нежели для тех, у кого есть
+2
а вы на gotdotnet.ru участвуете? туда бы тоже кросспостить подобные этюды, аудитория соответствующая
0
нет, не участвую
но если там такие монстры, как на rsdn.ru, то я пока воздержусь
там у них такие этюды, которые с решением не сразу-то поймешь
но если там такие монстры, как на rsdn.ru, то я пока воздержусь
там у них такие этюды, которые с решением не сразу-то поймешь
0
unsafe
{
object obj = new X();
RuntimeTypeHandle tHandle = obj.GetType().TypeHandle;
int size = *(*(int**)&tHandle + 1);
}
для работы надо разрешить запуск небезопасного кода в проекте.
{
object obj = new X();
RuntimeTypeHandle tHandle = obj.GetType().TypeHandle;
int size = *(*(int**)&tHandle + 1);
}
для работы надо разрешить запуск небезопасного кода в проекте.
+1
и да и нет
да — потому что Вы первый догадались применить ансейф код, и Ваш метод даже дает правильные резулататы
нет — потому что Вы используете то, что истинный размер указан где-то в типе. это специфическая деталь реализации, и она может менятся, а может и вообще врать.
ну и вдобавок требуется указать содержимое полей
да — потому что Вы первый догадались применить ансейф код, и Ваш метод даже дает правильные резулататы
нет — потому что Вы используете то, что истинный размер указан где-то в типе. это специфическая деталь реализации, и она может менятся, а может и вообще врать.
ну и вдобавок требуется указать содержимое полей
-1
А что должно быть на выходе — размер экземпляра, или значения полей?
0
и то и другое
0
Я знаю как можно «не совсем честно» добраться до экземпляра класса:
X obj = new X();
fixed (int* ptr = &obj.Val)
{
int val = ptr[0];
int typeHandle = ptr[-1];
int syncBlock = ptr[-2];
}
сойдёт за ответ? =)
X obj = new X();
fixed (int* ptr = &obj.Val)
{
int val = ptr[0];
int typeHandle = ptr[-1];
int syncBlock = ptr[-2];
}
сойдёт за ответ? =)
+1
нет, по нескольким причинам
во-1, Вы полагаете, что объект внутри содержит три 32битных поля, а это еще надо доказать
во-2, Вы почему-то решили, что дополнительные поля находятся перед «явным» полем, а это весьма сомнительное утверждение
но направление выбрано правильное, продолжайте думать )
во-1, Вы полагаете, что объект внутри содержит три 32битных поля, а это еще надо доказать
во-2, Вы почему-то решили, что дополнительные поля находятся перед «явным» полем, а это весьма сомнительное утверждение
но направление выбрано правильное, продолжайте думать )
0
совершенно не понятно, каким образом я смогу доказать, что syncBlock — это именно 4-хбайтовое значение. Имхо, об этом можно только прочитать, либо догадаться, проведя некоторое количество экспериментов. Или где-то в глубине typehandle-ов всё-таки есть полные метаданные о class-layout-е, в т.ч. и о вспомогательных полях?
0
похоже, Вы правы, а я ошибся
действительно, ptr[-1] и ptr[-2] будут искомыми полями
я опирался не на адрес поля &obj.Val, а на значение, содержаееся непосредственно в ссылке obj. но похоже, она указывает в «середину» объекта, а именно — в ptr[-1] в Вашей нотации
размер реальной памяти Вы не сосчитали, но это уже сделано в комментариях ниже.
но Вы оба опирались на адрес переменной Val. если бы переменная была приватной и имела геттер/сеттер, или если бы даже ее вообще не было, Вы бы смогли тогда достучаться до этих полей?
действительно, ptr[-1] и ptr[-2] будут искомыми полями
я опирался не на адрес поля &obj.Val, а на значение, содержаееся непосредственно в ссылке obj. но похоже, она указывает в «середину» объекта, а именно — в ptr[-1] в Вашей нотации
размер реальной памяти Вы не сосчитали, но это уже сделано в комментариях ниже.
но Вы оба опирались на адрес переменной Val. если бы переменная была приватной и имела геттер/сеттер, или если бы даже ее вообще не было, Вы бы смогли тогда достучаться до этих полей?
0
Таксс =)) ясно (правда так и не пришла мысль, как же верно реализовать)… Жалко мне пора уходить и нет времени дальше эксперементировать. Надеюсь все-таки увидеть решение, когда вернусь. Ещё раз спасибо за этюды, в день раз их просматривать-самое оно =)
+1
Думаю нужно сделать 2 new и вычесть ссылки. Желательно сделать это много раз и взять минимум из-за возможности сборки мусора и создания объектов в других потоках.
0
код давайте, код! ))
0
unsafe { X x1 = new X() { Val = 1 }; X x2 = new X() { Val = 2 }; fixed (int *p1 = &x1.Val) fixed (int *p2 = &x2.Val) { long fieldCount = p2 - p1; long size = fieldCount * sizeof(int); Console.WriteLine(size); int *pointer = p1 + 1; while (pointer <= p2) { Console.WriteLine(*pointer); pointer++; } } }
Правда тут ограничения есть.
1. Предполагается, что объекты будут расположены подряд (если запускать этот код отдельно, не создавая ни чего до него и во время него, то так оно и будет).
2. По поводу количества полей. Опять же предполагается, что они int.
А так, размер и содержимое памяти выводятся.
+1
очень хорошая попытка, но к сожалению, не засчитывается
размер Вы посчитали верно (т.е. такой способ тоже годится — допущение, что два объекта одного типа имеют одинаковую внутреннюю структуру — достаточно логичное)
необходимое условие — запускать код в отдельной программе, не создавая других объектов — тоже верно
а вот содержимое выведено неправильно
см. iaroshenko.habrahabr.ru/blog/77275/#comment_2250134
размер Вы посчитали верно (т.е. такой способ тоже годится — допущение, что два объекта одного типа имеют одинаковую внутреннюю структуру — достаточно логичное)
необходимое условие — запускать код в отдельной программе, не создавая других объектов — тоже верно
а вот содержимое выведено неправильно
см. iaroshenko.habrahabr.ru/blog/77275/#comment_2250134
0
да, смотрите мое дополнение: iaroshenko.habrahabr.ru/blog/77275/#comment_2253737
похоже, Вы тоже выведите искомые поля объекта x2
похоже, Вы тоже выведите искомые поля объекта x2
0
Казалось бы, никто не гарантирует, что объекты в памяти будут расположены последовательно, разве нет?
0
а давайте подумаем, откуда могут появится дырки между объектами? только от удаленных объектов, не так ли?
кроме того, разве сборщик мусора не перемещает объекты в начало кучи?
кроме того, разве сборщик мусора не перемещает объекты в начало кучи?
0
Т.е. аллокатор памяти выделяет её всегда последовательно? Буду знать.
0
вопрос динамического выделения памяти — отдельная большая тема, в двух словах ее никак не опишешь
если вкратце, основная проблема — утилизация уже освобожденной памяти (т.е. повторное использование участков, которые были ранее выделены и потом освободились), поэтому объекты желательно держать как можно плотнее. в неуправляемых языках, типа с или с++ (ну также и собственно на уровне ос происходит, хотя с++ дублирует этот механизм) выделяемая память имеет указатель размера, занимаемого объектом. при освобождении указатель вместе с размером добавляется в т.н. список свободных областей. есть хорошая статья джоэля, затрагивающая эту тему: russian.joelonsoftware.com/Articles/BacktoBasics.html. она будет полезна любому, на мой взгляд
в управляемой среде .NET ситуация принципиально другая. поскольку инфраструктура .NET контролирует каждую ссылку на любой объект, имеется возможность перемещать объекты из одной области памяти в другую прозрачно для пользователя, обновляя при этом все ссылки. сборщик мусора действует именно так, уплотняя объекты как можно сильнее — чтобы свободная память была участком максимального размера.
кроме того, следует упомянуть о поколениях сборщика мусора (объекты в пределах поколения «группируются»), о выравнивании памяти (для эффективности границы объектов должны быть выровнены по некоторому размеру, обычно размеру указателя), о больших объектах (в .NET большие объекты не переносятся с места на место, ибо это накладно, а располагаются в отдельной области управляемой кучи).
все это очень большая тема )
если вкратце, основная проблема — утилизация уже освобожденной памяти (т.е. повторное использование участков, которые были ранее выделены и потом освободились), поэтому объекты желательно держать как можно плотнее. в неуправляемых языках, типа с или с++ (ну также и собственно на уровне ос происходит, хотя с++ дублирует этот механизм) выделяемая память имеет указатель размера, занимаемого объектом. при освобождении указатель вместе с размером добавляется в т.н. список свободных областей. есть хорошая статья джоэля, затрагивающая эту тему: russian.joelonsoftware.com/Articles/BacktoBasics.html. она будет полезна любому, на мой взгляд
в управляемой среде .NET ситуация принципиально другая. поскольку инфраструктура .NET контролирует каждую ссылку на любой объект, имеется возможность перемещать объекты из одной области памяти в другую прозрачно для пользователя, обновляя при этом все ссылки. сборщик мусора действует именно так, уплотняя объекты как можно сильнее — чтобы свободная память была участком максимального размера.
кроме того, следует упомянуть о поколениях сборщика мусора (объекты в пределах поколения «группируются»), о выравнивании памяти (для эффективности границы объектов должны быть выровнены по некоторому размеру, обычно размеру указателя), о больших объектах (в .NET большие объекты не переносятся с места на место, ибо это накладно, а располагаются в отдельной области управляемой кучи).
все это очень большая тема )
0
пролистал Рихтера (CLS via C#). он прямым текстом пишет «в управляемой куче последовательно созданные объекты гарантированно будут расположены друг за другом»
конечно, ремарки насчет выравнивания, поколений и больших объектов остаются в силе
конечно, ремарки насчет выравнивания, поколений и больших объектов остаются в силе
0
using System;
using System.Runtime.InteropServices;
public static class ObjectExtensions
{
[StructLayout(LayoutKind.Explicit)]
internal struct MyStruct
{
[FieldOffset(0)]
public Int32 Value1;
[FieldOffset(4)]
public object Value2;
}
public static unsafe int GetAddr(this object o)
{
MyStruct ms = new MyStruct();
ms.Value2 = o;
int* ptr = &ms.Value1;
return ptr[1];
}
public static unsafe void PrintObject(this int ptr, int offset, int count, string descr)
{
Console.WriteLine("Printing {0} object at addr : {1}", descr, ptr);
int* ptr2 = (int*)ptr;
for (int i = 0; i < count; ++i)
{
Console.WriteLine("-- {0}", ptr2[offset + i]);
}
}
}
class X
{
public int Val {get; set; }
}
class App
{
static unsafe void Main()
{
const int INT_BYTES = 4;
object x = new X() { Val = 101 };
{
object y = new X() { Val = 257 };
int size = y.GetAddr() - x.GetAddr();
Console.WriteLine("Size is {0} bytes", size);
x.GetAddr().PrintObject(-1, size/INT_BYTES, "x");
y.GetAddr().PrintObject(-1, size/INT_BYTES, "y");
}
}
}
* This source code was highlighted with Source Code Highlighter.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
C#: Этюды, часть 4