我正在开发一个当前创建了数百个对象的应用程序。
是否可以确定(或近似)对象(类实例)分配的内存?
您可以使用内存分析器,例如
.NET 内存分析器 ( http://memprofiler.com/ )
或者
CLR Profiler(免费)(http://clrprofiler.codeplex.com/)
如果您想知道特定对象发生了什么,则可能是一种粗略的方法
// Measure starting point memory use
GC_MemoryStart = System.GC.GetTotalMemory(true);
// Allocate a new byte array of 20000 elements (about 20000 bytes)
MyByteArray = new byte[20000];
// Obtain measurements after creating the new byte[]
GC_MemoryEnd = System.GC.GetTotalMemory(true);
// Ensure that the Array stays in memory and doesn't get optimized away
GC.KeepAlive(MyByteArray);
可以像这样获得过程范围的东西
long Process_MemoryStart = 0;
Process MyProcess = System.Diagnostics.Process.GetCurrentProcess();
Process_MemoryStart = MyProcess.PrivateMemorySize64;
希望这可以帮助 ;)
ANTS内存分析器将准确地告诉您为每个对象/方法/等分配了多少。
这是一篇相关文章,我们在其中讨论了确定引用类型的大小。
您还可以使用 WinDbg 和 SOS 或 SOSEX(例如具有更多命令和改进了一些现有命令的 SOS)WinDbg 扩展。用于分析特定内存地址的对象的命令是 !objsize
要记住的一个非常重要的项目是 !objsize 只给你类本身的大小,并不一定包括类中包含的聚合对象的大小 - 我不知道为什么它不这样做,因为它是有时令人沮丧和误导。
我在 Connect 网站上创建了 2 个功能建议,要求将此功能包含在 VisualStudio 中。请为您希望添加的项目投票!
编辑: 我添加以下内容以澄清 Charles Bretana 提供的答案中的一些信息:
顺便说一句,CLR 使用大约 8 种不同的堆:
高温高压
每个“类”都需要足够的内存来保存运行时调用的所有成员的所有 jit 编译代码,(尽管如果你不调用一个方法很长一段时间,CLR 可以释放该内存并如果您再次调用它,请再次重新编译它......加上足够的内存来保存类中声明的所有静态变量......但是这个内存每个类只分配一次,无论您创建了多少类实例。
对于您创建的类的每个实例(并且尚未被垃圾收集),您可以通过将每个基于实例的声明变量的内存使用量相加来近似内存占用量......(字段)
引用变量(对其他对象的引用)占用 4 或 8 个字节(32/64 位操作系统?) int16、Int32、Int64 分别占用 2、4 或 8 个字节...
字符串变量为一些元数据元素占用额外的存储空间,(加上地址指针的大小)
此外,对象中的每个引用变量也可以被认为“间接”包括它指向的对象在堆上占用的内存,尽管您可能希望将该内存计为属于该对象而不是属于该对象的变量引用它...
等等等等
要大致了解应用程序中的内存分配,请在 WinDbg 中使用以下 sos 命令
!dumpheap -stat
请注意,!dumpheap 只为您提供对象类型本身的字节,不包括它可能引用的任何其他对象类型的字节。
如果您想查看特定对象类型的总持有字节数(对象引用的所有对象的所有字节总和),请使用内存分析器,如 dot Trace - http://www.jetbrains.com/profiler/
如果可以的话 - 序列化它!
Dim myObjectSize As Long
Dim ms As New IO.MemoryStream
Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
bf.Serialize(ms, myObject)
myObjectSize = ms.Position
有一个学术问题是运行时对象的大小是多少?这很有趣,但它只能由附加到正在运行的进程的分析器正确回答。我最近花了很长时间研究这个问题,并确定没有足够准确和快速的通用方法让您想在生产系统中使用它。像数值类型数组这样的简单案例有简单的答案,但除此之外,最好的答案是不要费心去解决它。你为什么想知道这个?是否有其他信息可以达到同样的目的?
就我而言,我最终想回答这个问题,因为我有各种有用的数据,但可以丢弃以释放 RAM 用于更关键的服务。这里的海报男孩是Undo Stack和Cache。
最终我得出结论,管理撤消堆栈和缓存大小的正确方法是查询可用内存量(这是一个 64 位进程,因此可以安全地假设它全部可用),然后允许更多项目如果有足够大的 RAM 缓冲区,则添加,并且如果 RAM 运行不足,则需要删除项目。
对于任何潜伏在寻找答案的 Unity 开发人员,这里有一种比较受@varun 答案启发的两个不同类内存分配的方法:
void Start()
{
var totalMemory = System.GC.GetTotalMemory(false);
var class1 = new Class1[100000];
System.GC.KeepAlive(class1);
for (int i = 0; i < 100000; i++)
{
class1[i] = new Class1();
}
var newTotalMemory = System.GC.GetTotalMemory(false);
Debug.Log($"Class1: {newTotalMemory} - {totalMemory} = {newTotalMemory - totalMemory}");
var class2 = new Class2[100000];
System.GC.KeepAlive(class2);
for (int i = 0; i < 100000; i++)
{
class2[i] = new Class2(10, 10);
}
var newTotalMemory2 = System.GC.GetTotalMemory(false);
Debug.Log($"Class2: {newTotalMemory2} - {newTotalMemory} = {newTotalMemory2 - newTotalMemory}");
}