Microsoft Visual C#2008减少已加载DLL的数量 在Visual C#2008 Express Edition中调试时,如何减少加载的DLL数量?
在调试器中运行visual C#项目时,由于2GB虚拟地址空间的碎片,我得到了OutOfMemoryException,我们假设加载的DLL可能是碎片的原因 布莱恩·拉斯穆森,你让我开心!:) 他提出的“禁用VisualStudio托管进程”解决了这个问题。Microsoft Visual C#2008减少已加载DLL的数量 在Visual C#2008 Express Edition中调试时,如何减少加载的DLL数量?,c#,visual-studio-2008,memory,C#,Visual Studio 2008,Memory,在调试器中运行visual C#项目时,由于2GB虚拟地址空间的碎片,我得到了OutOfMemoryException,我们假设加载的DLL可能是碎片的原因 布莱恩·拉斯穆森,你让我开心!:) 他提出的“禁用VisualStudio托管进程”解决了这个问题。 (有关更多信息,请参阅下面的问题发展历史) 嗨, 我需要在内存中加载两个大的int数组,每个数组大约有1.2亿个元素(~470MB),并且都在一个visualc#project中 当我试图实例化第二个数组时,我得到一个OutOfMe
(有关更多信息,请参阅下面的问题发展历史)
嗨, 我需要在内存中加载两个大的int数组,每个数组大约有1.2亿个元素(~470MB),并且都在一个visualc#project中 当我试图实例化第二个数组时,我得到一个OutOfMemoryException 我确实有足够的总可用内存,在做了一次web搜索之后,我认为我的问题是我的系统上没有足够大的连续可用内存块。 但是当我在一个Visual C#实例中仅实例化一个数组,然后打开另一个Visual C#实例时,第二个实例可以实例化470MB的数组。 (编辑以澄清:在上面的段落中,我指的是在Visual C的调试器中运行它) 任务管理器显示了相应的内存使用量增加,正如您预期的那样。 因此,整个系统上没有足够的连续内存块不是问题所在。然后,我试着运行一个编译后的可执行文件,该文件实例化了两个同样有效的数组(内存使用量1GB) 总结: VisualC中的OutOfMemoryException使用两个大int数组,但运行编译的exe works(内存使用1GB)和两个单独的VisualC实例可以为我的大数组找到两个足够大的连续内存块,但我需要一个VisualC实例才能提供内存
更新: 首先要特别感谢nobugz和Brian Rasmussen,我认为他们的预测是正确的,“进程2GB虚拟地址空间的碎片”是问题所在 按照他们的建议,我使用VMMap和ListDLS进行业余分析,得到:
*为“独立”-exe列出21个DLL。(工作并使用1GB内存的设备。)
*为vshost.exe-version列出了58个DLL。(调试时运行并引发异常且仅使用500MB的版本) VMMap显示调试器版本的最大可用内存块为262175167155108Mbs。
所以VMMap说没有连续的500MB块,根据关于空闲块的信息,我添加了9个较小的int数组,这些数组的内存使用量总计超过了1,2GB,并且确实有效。
因此,我想说,我们可以将“2GB虚拟地址空间的碎片化”称为有罪 从listdll输出中,我创建了一个小的电子表格,将十六进制数转换为十进制数,以检查DLL之间的可用区域,我确实在两个DLL之间为独立版本(21个)找到了很大的可用空间,但没有为vshost调试器版本(58个DLL)找到。我并不是说在这两者之间没有其他的东西,我也不确定我在这里所做的是否有意义,但这似乎与VMMaps分析一致,似乎只有DLL已经为调试器版本分割了内存 因此,如果我能够减少调试器使用的DLL的数量,可能会有一个解决方案。
1.可能吗?
2.如果是,我将如何执行此操作?3rd update:通过禁用Visual Studio宿主进程(项目属性,调试),可以显著减少加载的DLL数量。这样做仍将允许您调试应用程序,但它将去除大量DLL和许多帮助线程 在一个小测试项目中,当我禁用托管进程时,加载的DLL数量从69个增加到34个。我还去掉了10+个线程。总而言之,内存使用显著减少,这也有助于减少堆碎片 有关托管过程的其他信息:
可以在新应用程序中加载第二个阵列的原因是每个进程都获得一个完整的2GB虚拟地址空间。也就是说,操作系统将交换页面,以允许每个进程处理内存总量。当您试图在一个进程中分配两个数组时,运行时必须能够分配两个所需大小的连续块。您在阵列中存储了什么?如果存储对象,则每个对象都需要额外的空间 请记住,应用程序实际上并不请求物理内存。相反,每个应用程序都有一个地址空间,可以从中分配虚拟内存。然后,操作系统将虚拟内存映射到物理内存。这是一个相当复杂的过程(Russinovich在他的Windows Internal书籍中花了100多页的篇幅讲述Windows如何处理内存)。有关Windows如何做到这一点的更多详细信息,请参阅 更新:我思考这个问题已经有一段时间了,听起来确实有点奇怪。通过Visual Studio运行应用程序时,可能会看到根据配置加载的其他模块。在我的设置中,由于profiler和TypeMock(它基本上通过profiler钩子发挥作用),我在调试期间加载了许多不同的DLL 根据它们的大小和加载地址,它们可能会阻止运行时分配连续内存。话虽如此,我还是有点惊讶,因为它们的总大小小于1GB,所以在只分配了两个大型阵列之后,就得到了OOM 您可以使用SysInternals中的
listdll
工具查看加载的DLL。它将显示加载地址和大小。或者,您可以使用WinDbg。lm
命令显示加载的模块。如果还需要大小,则需要为详细输出指定v
选项。WinDbg还将允许您检查
using System;
using NUnit.Framework;
namespace ConsoleApplication5
{
class Program
{
// static int[] a = new int[100 * 1024 * 1024];
static BigArray<int> a = new BigArray<int>(100 * 1024 * 1024);
static void Main(string[] args)
{
int l = a.Length;
for (int index = 0; index < l; index++)
a[index] = index;
for (int index = 0; index < l; index++)
if (a[index] != index)
throw new InvalidOperationException();
}
}
[TestFixture]
public class BigArrayTests
{
[Test]
public void Constructor_ZeroLength_ThrowsArgumentOutOfRangeException()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
new BigArray<int>(0);
});
}
[Test]
public void Constructor_NegativeLength_ThrowsArgumentOutOfRangeException()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
new BigArray<int>(-1);
});
}
[Test]
public void Indexer_SetsAndRetrievesCorrectValues()
{
BigArray<int> array = new BigArray<int>(10001);
for (int index = 0; index < array.Length; index++)
array[index] = index;
for (int index = 0; index < array.Length; index++)
Assert.That(array[index], Is.EqualTo(index));
}
private const int PRIME_ARRAY_SIZE = 10007;
[Test]
public void Indexer_RetrieveElementJustPastEnd_ThrowsIndexOutOfRangeException()
{
BigArray<int> array = new BigArray<int>(PRIME_ARRAY_SIZE);
Assert.Throws<IndexOutOfRangeException>(() =>
{
array[PRIME_ARRAY_SIZE] = 0;
});
}
[Test]
public void Indexer_RetrieveElementJustBeforeStart_ThrowsIndexOutOfRangeException()
{
BigArray<int> array = new BigArray<int>(PRIME_ARRAY_SIZE);
Assert.Throws<IndexOutOfRangeException>(() =>
{
array[-1] = 0;
});
}
[Test]
public void Constructor_BoundarySizes_ProducesCorrectlySizedArrays()
{
for (int index = 1; index < 16384; index++)
{
BigArray<int> arr = new BigArray<int>(index);
Assert.That(arr.Length, Is.EqualTo(index));
arr[index - 1] = 42;
Assert.That(arr[index - 1], Is.EqualTo(42));
Assert.Throws<IndexOutOfRangeException>(() =>
{
arr[index] = 42;
});
}
}
}
public class BigArray<T>
{
const int BUCKET_INDEX_BITS = 13;
const int BUCKET_SIZE = 1 << BUCKET_INDEX_BITS;
const int BUCKET_INDEX_MASK = BUCKET_SIZE - 1;
private readonly T[][] _Buckets;
private readonly int _Length;
public BigArray(int length)
{
if (length < 1)
throw new ArgumentOutOfRangeException("length");
_Length = length;
int bucketCount = length >> BUCKET_INDEX_BITS;
bool lastBucketIsFull = true;
if ((length & BUCKET_INDEX_MASK) != 0)
{
bucketCount++;
lastBucketIsFull = false;
}
_Buckets = new T[bucketCount][];
for (int index = 0; index < bucketCount; index++)
{
if (index < bucketCount - 1 || lastBucketIsFull)
_Buckets[index] = new T[BUCKET_SIZE];
else
_Buckets[index] = new T[(length & BUCKET_INDEX_MASK)];
}
}
public int Length
{
get
{
return _Length;
}
}
public T this[int index]
{
get
{
return _Buckets[index >> BUCKET_INDEX_BITS][index & BUCKET_INDEX_MASK];
}
set
{
_Buckets[index >> BUCKET_INDEX_BITS][index & BUCKET_INDEX_MASK] = value;
}
}
}
}