我正在试图理解为什么当我似乎有足够的(虚拟?)内存可用时,我得到std :: bad_alloc异常。 基本上我有一个素数生成器(Eratosthenes筛(尚未分段)),我正在为指标数组添加bool,然后在我在命令行指定的边界下找到的素数的新的整数。
我有1GB内存(其中一些会被我的操作系统(ubuntu 10.04)占用,可能其中一些不作为堆内存(我在这里错了?))和2.8 GB的交换空间(我相信这是在安装Ubuntu时为我自动设置的)
如果我设置600000000的上限,那么我要求为我的指标数组提供0.6 GB的内存,并为我的素数数组提供大约30000000 * 4字节(略微超过估计,因为有26355867素数小于500000000),以及这里和那里有一些变数;这意味着我要求大约.72(+可忽略不计)GB的内存,我认为应该由我可用的交换空间覆盖(我知道触摸那些东西会让我的程序放慢脚步)。但是我得到了std :: bad_allocs。
有人能指出我在这里缺少的东西吗? (在粘贴我的最后一个错误之前,最后一次改变了很长时间的注意事项是一个段错误(我的数字低于2 ^ 31但是我看不到我在哪里溢出) - 仍然试图找出那个)
我的代码如下(并且没有从我身上带走我自己对更快算法等的调查的好处。我会感谢这里的任何代码改进!(即如果我正在进行重大禁止))
的main.cpp
#include <iostream>
#include <cmath>
#include "Prime.hpp"
#include <ctime>
#include <stdio.h>
#include <cstring>
//USAGE: execute program with the nth prime you want and an upper bound for finding primes --too high may cause bad alloc
int main(int argc, const char *argv[])
{
int a = strlen(argv[1]);
clock_t start = clock();
if(argc != 2)
{
std::cout << "USAGE: Enter a positive inputNumber <= 500000000.\n"
<< "This inputNumber is an upper bound for the primes that can be found\n";
return -1;
}
const char* primeBound = argv[1];
int inputNum = 0;
for(int i = 0; i < strlen(argv[1]); i++)
{
if(primeBound[i] < 48 || primeBound[i] > 57 || primeBound[0] == 48)
{
std::cout << "USAGE: Enter a positive inputNumber <= 500000000.\n"
<< "This inputNumber is an upper bound for the primes that can be found\n";
return -1;
}
inputNum = (int)(primeBound[i]-48) + (10 * inputNum);
}
if(inputNum > 600000000)//getting close to the memory limit for this machine (1GB - memory used by the OS):
//(each bool takes 1 byte and I'd be asking for more than 500 million of these
//and I'd also asking for over 100000000 bytes to store the primes > 0.6 GB)
{
std::cout << "USAGE: Enter a positive inputNumber <= 500000000.\n"
<< "This inputNumber is an upper bound for the primes that can be found\n";
return -1;
}
Prime p(inputNum);
std::cout << "the largest prime less than " << inputNum << " is: " << p.getPrime(p.getNoOfPrimes()) << "\n";
std::cout << "Number of primes: " << p.getNoOfPrimes() << "\n";
std::cout << ((double)clock() - start) / CLOCKS_PER_SEC << "\n";
return 0;
}
Prime.hpp
#ifndef PRIME_HPP
#define PRIME_HPP
#include <iostream>
#include <cmath>
class Prime
{
int lastStorageSize;
bool* primeIndicators;
int* primes;
int noOfPrimes;
void allocateIndicatorArray(int num);
void allocatePrimesArray();
void generateIndicators();
void generatePrimeList();
Prime(){}; //forcing constructor with param = size
public:
Prime(int num);
int getNoOfPrimes();
int getPrime(int nthPrime);
~Prime(){delete [] primeIndicators; delete [] primes;}
};
#endif
Prime.cpp
#include "Prime.hpp"
#include <iostream>
//don't know how much memory I will need so allocate on the heap
void Prime::allocateIndicatorArray(int num)
{
try
{
primeIndicators = new bool[num];
}
catch(std::bad_alloc ba)
{
std::cout << "not enough memory :[";
//if I'm looking for a particular prime I might have over-allocated here anyway...might be worth
//decreasing num and trying again - if this is possible!
}
lastStorageSize = num;
}
void Prime::allocatePrimesArray()
{
//could probably speed up generateIndicators() if, using some prime number theory, I slightly over allocate here
//since that would cut down the operations dramatically (a small procedure done many times made smaller)
try
{
primes = new int[lastStorageSize];
}
catch(std::bad_alloc ba)
{
std::cout << "not enough memory :[";
//if I'm looking for a particular prime I might have over-allocated here anyway...might be worth
//decreasing num and trying again - if this is possible!
}
}
void Prime::generateIndicators()
{
//first identify the primes -- if we see a 0 then start flipping all elements that are multiples of i starting from i*i (these will not be prime)
int numPrimes = lastStorageSize - 2; //we'll be starting at i = 2 (so numPrimes is at least 2 less than lastStorageSize)
for(int i=4; i < lastStorageSize; i+=2)
{
primeIndicators[i]++; //dispense with all the even numbers (barring 2 - that one's prime)
numPrimes--;
}
//TODO here I'm multiple counting the same things...not cool >;[
//may cost too much to avoid this wastage unfortunately
for(int i=3; i < sqrt(double(lastStorageSize)); i+=2) //we start j at i*i hence the square root
{
if(primeIndicators[i] == 0)
{
for(int j = i*i; j < lastStorageSize; j = j+(2*i)) //note: i is prime, and we'll have already sieved any j < i*i
{
if(primeIndicators[j] == 0)
{
numPrimes--;//we are not checking each element uniquely yet :/
primeIndicators[j]=1;
}
}
}
}
noOfPrimes = numPrimes;
}
void Prime::generatePrimeList()
{
//now we go and get the primes, i.e. wherever we see zero in primeIndicators[] then populate primes with the value of i
int primesCount = 0;
for(int i=2;i<lastStorageSize; i++)
{
if(primeIndicators[i] == 0)
{
if(i%1000000 = 0)
std::cout << i << " ";
primes[primesCount]=i;
primesCount++;
}
}
}
Prime::Prime(int num)
{
noOfPrimes = 0;
allocateIndicatorArray(num);
generateIndicators();
allocatePrimesArray();
generatePrimeList();
}
int Prime::getPrime(int nthPrime)
{
if(nthPrime < lastStorageSize)
{
return primes[nthPrime-1];
}
else
{
std::cout << "insufficient primes found\n";
return -1;
}
}
int Prime::getNoOfPrimes()
{
return noOfPrimes;
}
虽然我在读书时有没有人对此有任何见解?
编辑出于某种原因,我决定用lastStorageSize整数而不是noOfPrime开始新建我的素数列表!感谢David Fischer发现那一个!
我现在可以超过600000000作为上限
答案 0 :(得分:4)
您可以在程序中使用的内存量受到两者中较小者的限制:1)可用的虚拟内存,2)可用的地址空间。
如果您在具有平面内存模型的平台上将程序编译为32位可执行文件,则单个进程的可寻址空间绝对限制为4GB。在这种情况下,您可以使用多少交换空间是完全无关紧要的。即使您仍有大量的可用交换空间,您也无法在平面内存32位程序中分配超过4GB的内存。此外,这些4GB可用地址中的大部分将被保留用于系统需求。
在这样的32位平台上分配大量交换空间确实有意义,因为它可以让你一次运行多个进程。但它无法克服每个特定进程的4GB地址空间障碍。
基本上,将其视为电话号码可用性问题:如果某个地区使用7位数电话号码,那么一旦您用完该地区可用的7位数电话号码,就不再为该地区制造更多电话号码没有任何意义 - 它们将无法使用。通过添加交换空间,您基本上可以“制造电话”。但是你已经没有可用的“电话号码”了。
当然,同样的问题正式存在于平面内存模型64位平台上。但是,64位平台的地址空间如此巨大,以至于它不再是瓶颈(你知道,“64位对每个人都应该足够了”:))
答案 1 :(得分:1)
由于程序的内存使用非常容易分析,因此只需完全修复内存布局。不要动态分配任何。使用std::bitset
获取固定大小的位向量,并将其设为全局变量。
std::bitset< 600000000 > indicators; // 75 MB
这不占用磁盘空间。当您沿着阵列前进时,操作系统将只分配零页。它可以更好地利用每一位。
当然,尽管只有一个偶数素数,但是一半的比特代表偶数。 Here是一对优化这类事物的素数生成器。
顺便说一句,最好避免在可能的情况下显式写new
,避免从构造函数中调用函数,并重新抛出std::bad_alloc
以避免允许将对象构造为无效状态。
答案 2 :(得分:1)
分配筛子时,
void Prime::allocateIndicatorArray(int num)
{
try
{
primeIndicators = new bool[num];
}
catch(std::bad_alloc ba)
{
std::cout << "not enough memory :[";
}
lastStorageSize = num;
}
您将lastStorageSize
设置为num
,即素数的给定界限。然后你永远不会改变它,
void Prime::allocatePrimesArray()
{
try
{
primes = new int[lastStorageSize];
}
catch(std::bad_alloc ba)
{
std::cout << "not enough memory :[";
}
}
尝试分配int
个lastStorageSize
元素数组。
如果num
大约为5亿,那么您要求的大约为2 GB。根据操作系统/过度使用策略,即使您实际上只需要一小部分空间,也很容易导致bad_alloc
。
筛选完成后,将noOfPrimes
设置为找到的素数计数 - 使用该数字分配素数数组。
答案 3 :(得分:1)
第一个问题是“正在运行的其他进程是什么?”该
2.87 GB的交换空间在运行的 all 之间共享
流程;这不是每个过程。坦率地说,就现代而言
系统,2.8 GB听起来对我来说相当低。我不会试图跑
最新版本的Windows或Linux,内存小于2GB
4GB交换。 (Linux的最新版本,至少在Ubuntu中
特别是,分发似乎启动了许多守护进程
占用内存。)你可能想尝试top
,排序
虚拟内存大小,只是为了看看其他进程有多少
服用。
cat /proc/meminfo
也会给你很多宝贵的东西
有关实际使用内容的信息。 (在我的系统上,
我只使用xterm
和bash
运行了几个Firefox
只有3623776 kB免费,在一个8GB的系统上。某些
计算使用的内存可能是磁盘缓存,
如果应用程序请求,系统可以缩减
存储器。)
其次,关于你的seg故障:默认情况下,Linux没有
始终报告正确报告分配失败;它
经常撒谎,告诉你,你有记忆,当你
别。试试cat /proc/sys/vm/overcommit_memory
。如果它
显示零,然后您需要更改它。如果是这种情况,
尝试echo 2 > /proc/sys/vm/overcommit_memory
(并执行此操作)
其中一个rc文件)。你可能要改变
/proc/sys/vm/overcommit_ratio
以及获得可靠的行为
来自sbrk
(malloc
和operator new
都依赖)。