Permalänk
Medlem

C++ Dynamisk minneshantering

hej!

jag ska undersöka hur mycket minne jag kan allokera innan OS säger ifrån genom att först
- allokera minne i 1GB-bitar

jag ska även undersöka vad som händer i OS då program läcker minne genom att
- allokera minne i 1GB-bitar

någon vänlig själ som kan förklara lite enklare språk hur det fungerar och ska gå till vid allokering med 1GB i taget samt 1KB i taget

även om jag loopar igenom för att addressera upp en integer så är ju det ändå inte 1GB i taget, utan det är en integer som är värd 4byte (inte alltid) som loopas igenom, det är väl ändå inte samma sak som att allokera upp 1GB i taget? kan ett program läcka minne även om jag använder delete?

lite hjälp på vägen tack

Permalänk
Medlem

Du behöver allokera något som är i den storleks ordningen. Antingen object eller arrays, så t.ex
typedef int testType;
int size = 1024*1024*1024/sizeof(testType);
auto b = new testType[size];
ger dig 1 GB allokerat, du kan då välja vilken vilket object eller typ som du vill istället för int och det kommer bli runt 1 GB

Om du använder
delete[] b;
kommer allt minne för b frigöras inga rester är då låsta.

Skrivet av Lexdale:

hej!

jag ska undersöka hur mycket minne jag kan allokera innan OS säger ifrån genom att först
- allokera minne i 1GB-bitar

jag ska även undersöka vad som händer i OS då program läcker minne genom att
- allokera minne i 1GB-bitar

int main () { try{ new och delete här } catch ( std::bad_alloc e ) { cerr << e.what () << endl; } return 0; }

någon vänlig själ som kan förklara lite enklare språk hur det fungerar och ska gå till vid allokering med 1GB i taget samt 1KB i taget

även om jag loopar igenom för att addressera upp en integer så är ju det ändå inte 1GB i taget, utan det är en integer som är värd 4byte (inte alltid) som loopas igenom, det är väl ändå inte samma sak som att allokera upp 1GB i taget? kan ett program läcka minne även om jag använder delete?

lite hjälp på vägen tack

[] på delete saknades
Permalänk
Medlem
Skrivet av MrGorgar:

Om du använder
delete b;
kommer allt minne för b frigöras inga rester är då låsta.

Minne som allokerats med new[] måste frigöras med delete[], inte delete. Det är odefinierat i C++ vad som händer om man använder delete för att frigöra minne som allokerats med new[], och vice versa, så precis vad som helst kan hända då. Här är en kort genomgång av new/delete.

Permalänk
Medlem

Helt korekt att man ska använda delete[] för god still. Var lite väl snabb med min ändring att tillåta annat än grund typer, så updaterar det.

Dock för lite nöje så kan jag nämna att det inte är odefinierat om du kör det på grund typer då delete tar bort en array men kallar endast destruktor på första elementet. Detta är ju självklart ett problem på objekt...

Skrivet av perost:

Minne som allokerats med new[] måste frigöras med delete[], inte delete. Det är odefinierat i C++ vad som händer om man använder delete för att frigöra minne som allokerats med new[], och vice versa, så precis vad som helst kan hända då. Här är en kort genomgång av new/delete.

Permalänk
Medlem
Skrivet av MrGorgar:

Dock för lite nöje så kan jag nämna att det inte är odefinierat om du kör det på grund typer då delete tar bort en array men kallar endast destruktor på första elementet. Detta är ju självklart ett problem på objekt...

C++-specifikationen (sektion 5.3.5) håller inte med:

Citat:

In the first alternative (delete object), the value of the operand of delete may be a null pointer value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject representing a base class of such an object. If not, the behavior is undefined. In the second alternative (delete array), the value of the operand of delete may be a null pointer value or a pointer value that resulted from a previous array new-expression. If not, the behavior is undefined.

De flesta kompilatorer hanterar det dock på sättet som du beskriver, men det är ju ingen garanti för att det alltid fungerar.

Permalänk
Medlem

va duktiga ni är!

jag tror att jag börjar att förstå detta nu.

eftersom jag ska kolla hur många GB jag kan allokera innan det blir stopp (catch bad_alloc) så tänkte jag skapa en loop, men jag får inte till det, utan det kastas bad_alloc exception direkt på 2a varvet

jag skulle kunna göra det manuellt och skapa 16st manuellt och se vad det tar stopp, men loopen kommer väl till användning senare när jag ska utföra samma sak fast där jag allokerar 1KB i taget istället

tips?

här är vad jag har hittils:

try { int *gigabyte; int sizeGb = 1024*1024*1024/sizeof(gigabyte); for(int i = 0; i < 15; i++) { gigabyte = new int[sizeGb]; cout << "1GB + "; _getch(); } delete[] gigabyte; } catch (bad_alloc e ) { cerr << "Error: memory could not be allocated"; }

En fundering var ifall jag på istället något sätt ändra värdet på sizeGb så att det plussas på 1GB för vare loop varv? därmed skulle jag bara behöva lägga detta i loopen och så att minnesallokeringen även dubblas på något sätt vilket jag ej lyckats med hittils

Permalänk
Medlem

Jag tänkte mig nånting åt detta hållet:

int integer; int sizeGb = 1024*1024*1024/sizeof(integer); for(int i = 0; i < 16; i++) { int *gigabyte; gigabyte = new int[sizeGb]; cout << "1GB : "; _getch(); delete[] gigabyte; cout << "Deleted\n"; _getch(); sizeGb+= ( 1024*1024*1024/sizeof(integer) ); }

men som sagt det fungerar inte, bad_alloc händer efter 1a varvet

Permalänk
Hedersmedlem
Skrivet av Lexdale:

men som sagt det fungerar inte, bad_alloc händer efter 1a varvet

Varför tror du att det inte fungerar? Det skulle ju kunna vara så att du inte får allokera så mycket...

Permalänk
Medlem
Skrivet av Elgot:

Varför tror du att det inte fungerar? Det skulle ju kunna vara så att du inte får allokera så mycket...

Provade det och det fungerade inte, jag måste väl få allokera mer än 1GB minne? jag har 16GB minne i datorn, borde få allokera upptill 11-12 GB minne. Jag har mätt upp windows7 till att den körs med 3,68 GB minne, säg 4GB.

varför blir detta fel tror du?

Permalänk
Medlem

Elgot, du hade rätt!

jag provade att göra det enkelt för mig:

int integer; int sizeGb = 1024*1024*1024/sizeof(integer); int *gigabyte1; gigabyte1 = new int[sizeGb]; cout << "1GB + "; _getch(); int *gigabyte2; gigabyte2 = new int[sizeGb]; //ERROR KASTAS HÄR cout << "1GB + "; _getch();

Vid den 2a allokeringen får jag bad_alloc. Men varför är det så? jag borde väl få allokera minne bäst jag vill förutsatt att det finns tillgängligt? upptill 11GB iallafall

Samt: jag ska även prova att allokera 1KB i taget istället för 1GB. Förslag på hur jag gör detta till en loop då min egen loop inte fungerade?

Permalänk
Hedersmedlem
Skrivet av Lexdale:

Men varför är det så? jag borde väl få allokera minne bäst jag vill förutsatt att det finns tillgängligt? upptill 11GB iallafall

Kompilerar du som 64-bitarsprogram? Annars behöver man kanske ange några extra flaggor för att få tillgång till mer än 2 GB minne.

Skrivet av Lexdale:

jag ska även prova att allokera 1KB i taget istället för 1GB. Förslag på hur jag gör detta till en loop då min egen loop inte fungerade?

Det borde bara vara att ändra

sizeGb+= ( 1024*1024*1024/sizeof(integer) );

till något mindre, t.ex.

sizeGb+= ( 1024/sizeof(integer) );

(och kanske öka till fler än 16 iterationer...)

Permalänk
Medlem
Skrivet av Elgot:

Kompilerar du som 64-bitarsprogram? Annars behöver man kanske ange några extra flaggor för att få tillgång till mer än 2 GB minne.

Hur ser jag ifall jag kompilerar som 64-bits program? jag använder Visual studio 2012 och och jag har windows7 64bit.
Visual studio är installerat till c:\programmering\VS

ifall du tänkte på programmappens namn c:\program (x86)

Permalänk
Hedersmedlem
Skrivet av Lexdale:

Hur ser jag ifall jag kompilerar som 64-bits program? jag använder Visual studio 2012 och och jag har windows7 64bit.

Till höger om release/debug-rutan i verktygsfältet kan man välja. Standard är Win32, som innebär 32-bitar. Välj "Configuration Manager", "<New...>" i "Platform"-fältet och sedan "x64" för att kunna välja även "x64".

Permalänk
Medlem
Skrivet av Elgot:

Till höger om release/debug-rutan i verktygsfältet kan man välja. Standard är Win32, som innebär 32-bitar. Välj "Configuration Manager", "<New...>" i "Platform"-fältet och sedan "x64" för att kunna välja även "x64".

Tack! detta verkade göra susen!

for(int i = 1; i < 13; i++) { int *gigabyte; gigabyte = new int[sizeGb]; cout << i << " GB : "; _getch(); delete[] gigabyte; cout << "Deleted\n"; _getch(); sizeGb+= ( 1024*1024*1024/sizeof(integer) ); }

Jag kom upp i 7GB minne, men en konstig sak som jag inte förstår mig på, varför kan jag inte se i aktivitetshanteraren i windows att mer minne används? den står stadigt kvar på 25% minne oavsett vad som händer. Innan jag ändrade till x64 så kunde den hoppa upp från 25% till 31% nånstans efter 1GB

Sedan så tycker jag att jag borde kunna allokera åtminstone ett par GB till, ca 10-11 GB borde fungera?

Permalänk
Medlem

här allokerar jag 20GB utan error med 16GB minne, vad gör jag fel?

int integer; int sizeKb = 1024/sizeof(integer); for(int i = 1; i < (1024*20); i++) { int *kilobyte; kilobyte = new int[sizeKb]; cout << i << " KB\n"; delete[] kilobyte; sizeKb+= ( 1024/sizeof(integer) ); }

Permalänk
Medlem
Skrivet av Lexdale:

här allokerar jag 20GB utan error med 16GB minne, vad gör jag fel?

int integer; int sizeKb = 1024/sizeof(integer); for(int i = 1; i < (1024*20); i++) { int *kilobyte; kilobyte = new int[sizeKb]; cout << i << " KB\n"; delete[] kilobyte; sizeKb+= ( 1024/sizeof(integer) ); }

Du har ju en swap-fil också.

Permalänk
Medlem

I vissa operativsystem kan man allokera hur mycket minne som helst så länge det finns plats i adressrymden. Det är först när man skriver till minnet som det görs en copy-on-write allokering. Jag vet inte om det funkar så i windows. Prova att fylla hela minnet du allokerar.

Optimeringar i kompilatorn kan också få oväntade konsekvenser. Du allokerar minne utan att använda det så en smart kompilator kanske optimerar bort hela allokeringen. Jag vet inte om det är tillåtet i C++ men för att vara på den säkra sidan bör du stänga av optimering.

Permalänk
Medlem
Skrivet av teejee:

Du har ju en swap-fil också.

swapfilen är avstängd

om jag kan allokera 40.000 KB minne genom att göra det i 1-KB åt gången så borde jag kunnat ha gjort det med att allokera på samma sätt fast i 1-GB i taget

det verkar inte finnas något stopp på hur många KB minne jag kan allokera

sedan att minnesmätaren i aktivitetshanteraren inte rör sig alls förstår jag inte överhuvudtaget, som sagt den hoppade från 25% till 31% förut när jag allokerade 1GB minne innan jag ändrade platformen till x64

Permalänk
Medlem
Skrivet av Emaku:

I vissa operativsystem kan man allokera hur mycket minne som helst så länge det finns plats i adressrymden. Det är först när man skriver till minnet som det görs en copy-on-write allokering. Jag vet inte om det funkar så i windows. Prova att fylla hela minnet du allokerar.

Optimeringar i kompilatorn kan också få oväntade konsekvenser. Du allokerar minne utan att använda det så en smart kompilator kanske optimerar bort hela allokeringen. Jag vet inte om det är tillåtet i C++ men för att vara på den säkra sidan bör du stänga av optimering.

Det låter faktiskt vettigt! hur stänger jag av denna sorts optimering i VS 2012 ?

Permalänk
Medlem

Aktivera /d för optimering, alternativt under options C/C++ optimaztions.
Skriv också till din del av minnet du allokerat.
Samt så deallokerar du i varje loop, vad är tanken?

Visa signatur

Corsair 16GB (4x4096MB) CL9 1600Mhz | Asus P8Z77-V PRO |
Samsung SSD Basic 830-Series 256GB | Intel Core i7 3770K 3,5Ghz |
Asus Xonar Essence STX | Noctua NH-U9B SE2 | Antec Performance One P280 | Corsair HX 850W 80+ Gold Modulär | MSI GTX 770

Permalänk
Hedersmedlem

Ett annat roligt fenomen är att din stackars int (sizeKb) sannolikt endast är 4 byte stor och därmed maximalt kan ha värdet 2147483647 (innan den slår om och blir negativ). Med sizeof(int) = 4 borde detta ske redan vid ~8 GB.

Permalänk
Medlem
Skrivet av NoPaiN^:

Aktivera /d för optimering, alternativt under options C/C++ optimaztions.
Skriv också till din del av minnet du allokerat.
Samt så deallokerar du i varje loop, vad är tanken?

jag har av-aktiverat optimization
och skrivit till minnet jag allokererar enligt nedan
tanken är att kolla om 1 KB går att allokera , okej, prova då allokera 2KB, 3KB, 4KB, etc tillls det tar stopp och jag får error så jag kan se vart det tar stopp
Har du någon bättre idé på att göra detta? det är inte det "snyggaste" att demontera varje varv

Skrivet av Elgot:

Ett annat roligt fenomen är att din stackars int (sizeKb) sannolikt endast är 4 byte stor och därmed maximalt kan ha värdet 2147483647 (innan den slår om och blir negativ). Med sizeof(int) = 4 borde detta ske redan vid ~8 GB.

Mycket lustigt om jag använder long int borde det vara safe

long int integer;
long int sizeKb = 1024/sizeof(integer);

for(int i = 1; i < (1024*40); i++) { long int *kilobyte; kilobyte = new long int[sizeKb]; *kilobyte = 1; //skriver till minnet cout << i << " KB\n"; delete[] kilobyte; sizeKb+= ( 1024/sizeof(integer) ); }

Men jag kan fortfarande allokera otroliga mängder minne utan stopp.
samt vad tror ni att det beror på att minnesmätaren i aktivitetshanteraren aldrig nånsin rubbas nu efter jag bytte till x64 ?

Permalänk
Hedersmedlem
Skrivet av Lexdale:

Har du någon bättre idé på att göra detta? det är inte det "snyggaste" att demontera varje varv

Enklast är ju att bara glömma delete[] (men komma ihåg hur mycket man tidigare har allokerat). Annars skulle man ju till exempel kunna spara pekare till minnet i en lista.

Skrivet av Lexdale:

Mycket lustigt om jag använder long int borde det vara safe

long int integer;
long int sizeKb = 1024/sizeof(integer);

Nja, det är inte säkert att det verkligen är skillnad på int och long int. Däremot skulle unsigned int räcka lite längre, men för tillfällen som dessa finns typen std::size_t. Du behöver inte heller ändra typen i ditt new-anrop (den kan fortfarande vara int).

std::size_t sizeKb = 1024/sizeof(int);

(Man skulle också kunna slippa alla dessa divisioner genom att använda en kortare datatyp än int

std::size_t sizeKb = 1024; char* kilobyte = new char[sizeKb];

)

Skrivet av Lexdale:

samt vad tror ni att det beror på att minnesmätaren i aktivitetshanteraren aldrig nånsin rubbas nu efter jag bytte till x64 ?

Med den där loopen kommer du ju dock aldrig längre än 1024*40 Kb, dvs. 40 MB...

Permalänk
Medlem

Oj vilket slarvfel av mig med loopen och några futtiga megabyte, fixat nu

try { cout << "Uppgift: allokera i 1-KB åt gången\n\n"; int integer; size_t sizeKb = 1024/sizeof(integer); cout << "utskrift för varje Gigabyte allokerat\n--------------------------------\n"; for(int i = 1; i < (1024*1024*15); i++) { int *kilobyte; kilobyte = new int[sizeKb]; *kilobyte = 1; //skriver till minnet if( (i == 1024*1024) || (i == 1024*1024*2) || (i == 1024*1024*3) || (i == 1024*1024*4) || (i == 1024*1024*5) || (i == 1024*1024*6) || (i == 1024*1024*7) || (i == 1024*1024*8) || (i == 1024*1024*9) || (i == 1024*1024*10) || (i == 1024*1024*11) || (i == 1024*1024*12) || (i == 1024*1024*13) || (i == 1024*1024*14) || (i == 1024*1024*15) ) { cout << i << " KB\n"; } delete[] kilobyte; sizeKb+= ( 1024/sizeof(integer) ); }//end for }//end try catch (bad_alloc e ) { _getch(); cerr << "Error: memory could not be allocated"; }

Vid detta så allokerar jag upptill 11 gigabyte (med 1-kB i taget) och så får jag en vacker varning i windows att minnet är lågt och jag rekomenderas att stänga av programmet...vilket jag inte gör, efter det så stängs tyvärr mitt C++ console rutan av utan förvarning, jag får inte ens bad_alloc. Varför får jag inte det?

Jag är fortfarande konfunderad på varför jag inte kan allokera 11GB minne i föregående, genom allokera 1GB i taget där det tar stopp efter 7GB

Permalänk
Hedersmedlem
Skrivet av Lexdale:

|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

Ett bra trick för att slippa skriva långa jämförelser av den där typen är att använda %-operatorn, som returnerar resten vid heltalsdivision. Det du vill testa är om i är en jämn multipel av 1024*1024, vilket sammanfaller med att resten av i/(1024*1024) = 0. If-satsen skulle alltså istället kunna skrivas

if((i % (1024*1024)) == 0) cout << i << " KB\n";

Skrivet av Lexdale:

Jag är fortfarande konfunderad på varför jag inte kan allokera 11GB minne i föregående, genom allokera 1GB i taget där det tar stopp efter 7GB

Och det var inte den där negativa storleken som ställde till det?

Permalänk
Medlem
Skrivet av Elgot:

Ett bra trick för att slippa skriva långa jämförelser av den där typen är att använda %-operatorn, som returnerar resten vid heltalsdivision. Det du vill testa är om i är en jämn multipel av 1024*1024, vilket sammanfaller med att resten av i/(1024*1024) = 0. If-satsen skulle alltså istället kunna skrivas

if((i % (1024*1024)) == 0) cout << i << " KB\n";

Och det var inte den där negativa storleken som ställde till det?

Nice touch med modulus Elgot
jo jag hade glömt att ändra till size_t vid den första delen (allokering av 1-GB i taget)

Del 1: allokering med 1-GB åt gången, landar på 11GB och sedan bad_alloc
Del 2: allokering med 1-KB åt gången, landar på 11GB och sedan lågt minnes-varning i windows, sedan stängs console av utan förvarning

att console stängs av helt utan förvarning kan väl vara rimligt antar jag, men jag hade förväntat mig bad_alloc istället för att console bara stängs av

Permalänk
Medlem

Tack så mycket för hjälpen alla

En sista sak som jag är fundersam på,

vid allokerandet av 1-KB i taget,

säg att jag vill att programmet ska läcka minne, då struntar jag bara i att använda delete?

Hur kan jag göra om föregående så att den och loopen resulterar i minnes-läckage? jag kommer inte på något bra sätt att göra loopen. Samt vad betyder minnesläckage/varför är det dåligt?

Permalänk
Medlem
Skrivet av Lexdale:

Hur kan jag göra om föregående så att den och loopen resulterar i minnes-läckage? jag kommer inte på något bra sätt att göra loopen. Samt vad betyder minnesläckage/varför är det dåligt?

Du har svaret på din fråga i denna tråd, ta en titt till!
Ang minnesläckage, förutom att att ordet är väldigt självbeskrivande, varför tror du man har minne, både innanför pannbenet samt i datorer?

Permalänk
Medlem
Skrivet av Dalton Sleeper:

Du har svaret på din fråga i denna tråd, ta en titt till!

ja och samtidigt så vet jag inte hur jag gör detta, om jag visste skulle jag inte fråga? självklart har jag provat

Skrivet av Dalton Sleeper:

Ang minnesläckage, förutom att att ordet är väldigt självbeskrivande, varför tror du man har minne, både innanför pannbenet samt i datorer?

lågt blodsocker?

Permalänk
Datavetare

Ditt program gör fortfarande inte vad du tror att det gör. När man allokerar minne så kommer moderna operativsystem bara göra en notering att du vill ha en viss mängd minne, den adress du får tillbaka från new[] är en s.k. virtuell adress och varje process har (på AMD/Intel 64-bitars CPUer) 2^48 (256 TB) bytes att tillgå där oavsett hur mycket RAM du har.

Minnet är sedan uppdelat i något som kallas "pages", storleken på dessa är i Windows 4kB (i riktigt moderna Linux kan de vara 4kB, 2MB eller 1GB lite beroende på vilken CPU-modell man har och hur man använder RAM). Du måste därför läsa/skriva till varje sådan "page" för att operativsystemet verkligen ska "commit memory", d.v.s skapa en virtuell till fysisk mappning av din "page", först när detta görs är minnet allokerat "på riktigt" (i.e. det tar plats i RAM och/eller swap).

Har skrivit om ditt program lite och i en virtual box instans med Linux som har 4GB RAM och 2GB swap så går det fort fram till 4GB är allokerat, sedan går det segt nästa 2GB (när swap måste användas, hyfsat snabbt på mitt system då allt ligger på SSD) för att vid 6GB misslyckas med "Error: memory could not be allocated"

Kör jag samma program så Windows så går det snabbt fram till 16GB (så mycket RAM jag har i min laptop), men vetesjutton hur mycket swap Windows anser är vettigt, för programmet bara fortsätter.

Anledningen till att program inte anses ta något RAM i Windows taskmanager (samma sak gäller t.ex. "top" under Linux) är att dessa program mäter allokerat minne filtrerat över tid och programmet kommer spendera allt mer tid i alla allokera/fria samt skriva till "pages" och det är bara precis innan delete[] som processen verkligen drar allt minne. I genomsnitt drar den långt mindre, gissningsvis för att new[]/delete[] tar rätt lång tid efter ett tag men minne lämnas rent "bokföringsmässigt" tillbaka till operativsystemet i delete[] som en av de första sakerna som händer.

#include <iostream> using namespace std; #define kB * 1024 #define MB * (1024 kB) #define kB_OF_INTS (1 kB / sizeof(int)) int main() { try { cout << "Uppgift: allokera i 1-KB åt gången\n\n"; int integer; size_t sizeKb = kB_OF_INTS; cout << "utskrift för varje Gigabyte allokerat\n--------------------------------\n"; for(;;) { int *kilobyte; kilobyte = new int[sizeKb]; // Datorminne på en x86 (och på de flesta andra moderna // CPU-arkitekturer) är uppdelat i 4kB "memory pages". // När man allokerar minne så reserveras bara det minnet // i s.k. "virtual address space", är först när man // skriver/läser första gången som operativsystemet // gör en "virtuell to physical" översättning for (int pg = 0; pg < sizeKb; pg += 4096) kilobyte[pg / sizeof *kilobyte] = 1; if(sizeKb % (1 MB) == 0) { cout << sizeKb*sizeof integer << " kB\n"; } delete[] kilobyte; sizeKb += kB_OF_INTS; }//end for }//end try catch (bad_alloc e ) { cerr << "Error: memory could not be allocated"; return -1; } return 0; }

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer