std::bad_alloc mit QList<QByteArray>

Alles rund um die Programmierung mit Qt
CKassiopaia
Beiträge: 38
Registriert: 21. April 2009 16:42

std::bad_alloc mit QList<QByteArray>

Beitrag von CKassiopaia »

Hallo,

ich habe ein kleines aber schwerwiegendes Problem:
Ich habe eine

Code: Alles auswählen

QList<QByteArray> buffer
und ein

Code: Alles auswählen

QByteArray element;
element.append(0x01);
element.append(0x02);
...
element.append(xxx);
Wenn das Element fertig ist, möchte ich es der Liste hinzufügen:

Code: Alles auswählen

buffer.append(element)
Kompilieren funktioniert auch wunderbar, aber es kommt eine Exception(?)

"terminate called after throwing an instance of std::bad_alloc"

Ich hab schon herausgefunden, dass das wohl ein Speicherproblem ist: Es wird wohl versucht mit new Speicher zu reservieren, was nicht funktioniert. Aber warum?

Ein Speicherproblem sollte es eigentlich nicht geben, da genug vorhanden sein sollte. Ich frag mich nur, wo das new verwendet wird, da ich es nicht nutze. Und, warum anscheind zu viel Speicher reserviert wird. Kann mir jemand einen Tip geben? Danke!
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Der Code den Du uns zeigst sieht ok aus.
Wir brauchen ein kleines Beispielprogramm damit wir den Fehler reproduzieren können.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Ein Speicherproblem sollte es eigentlich nicht geben, da genug vorhanden sein sollte
Definier mal genug ???

Um welche groessenordnungen handelt es sich bei dir ?

Ich frag mich nur, wo das new verwendet wird
Dynamisch = du weisst zur compilezeit nicht, wieviel speicher du brauchst.
Was machen Container ? Richtig, sie speichern elemente, eine zur laufzeit bestimmten Anzahl.
Nur weil du das QByteArray aufn stack anlegst (die verwaltungsinformationen) heisst das ned, das das QByteArray nicht selber dynamisch speicher allociert. Im gegenteil, es kann ja gar nicht die (oder alle) elemente aufn stack speichern, da es beim erzeugen ja ned weiss wie viele es werden.
Ergo, es wird irgendwo intern speicher allokieren.
Mach mal nen sizeof(QByteArray) da weisst wieviel das aufn stack belegt. dann rechne mal aus wieviel byte an daten drinne sind ....

Das QByteArray wird also beim hinzufuegen von elementen irgendwann ,ueber seinen eigenen allokator gecacht, nen new, replacement new oder ähnliches aufrufen.

Ciao ...
CKassiopaia
Beiträge: 38
Registriert: 21. April 2009 16:42

Danke!

Beitrag von CKassiopaia »

Genug heißt, dass die Elemente nur wenige Byte groß sind (<10 Byte) und ich 1GB habe...
Aber ich habe das Problem lösen können.

Der Fehler hat sich durch die .append(xxx) bemerkbar gemacht, Grund war aber, dass ich die Funktion (innerhalb einer Klasse) aufgerufen hatte, bevor ich eine Instanz der Klasse erzeugt hatte. Dummerweise hat sich Problem vorher nie gezeigt.

Aber Danke für die Hilfe!

Eine kurze Frage habe ich noch: Wenn ich wie im obigen Beispiel ein element der QList hinzufüge, wird das Element kopiert oder nur ein Zeiger übergeben?

Code: Alles auswählen

void function(){
QByteArray element;
element.append(1);
...
element.append(10);

buffer.append(element);
}
In diesem Beispiel wird ja der Speicher des in der Funktion erzeugte QByteArray am Ende wieder freigegeben und kann anderweitig benutzt werden.... wird somit auch der Speicher des Elements in der QList freigegeben? Oder sollte/muss ich hier mit Zeigern arbeiten?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Es wird eine Kopie eingefügt.
Du kannst das selber testen, indem du Kopierkonstruktor und Zuweisungsoperator in ner Testklasse als Private deklarierst.
Du bekommst dann beim Anhängen eines Value (also nicht Pointer) nen compile-Error ;)
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Oder sollte/muss ich hier mit Zeigern arbeiten?
ein deutliches Jein ! :P

heisst theorethisch ja, praktisch nein ^^

wenn du mit der stl arbeiten wuerdest, waere dein code genau so schoen, aber wie du befuerchtest schon inperformant.

du erzeugst ein object eher temporaer, kopierst das, wirfst das alte weg.
macht 2 news und 1 delete um ein object in einem container zu erzeugen ^^

richtig lustig wuerde es erst werden, wenn deinen contaienr sortieren lassen wuerdest.

was macht das QByteArray anderes ?
internes caching. die daten werden referenzgezaehlt. machst du eine copy konstruktion / zuweisung, werden die daten nicht sofort kopiert, sondern erst wenn es unterschiede geben wuerde. passiert ned, weil den ursprung gleich wegwirfst.
deshalb wird das qbytearray aehnlich performant sein, als wie wenn mit zeiger arbeitest ....

Ciao ...
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Also, wenn schon der Tip kommt mit Pointer in ne Liste, sollte man auch auf die Boost Pointer-Container verweisen.
Das Problem mit Pointern in Containern ist, dass man dran denken muss, diese zu löschen. Vergisst du es, fängst du dir ein Memory-Leak ein...

Ich denke nicht dass man Values in Containern so prinzipiell ablehnen sollte. Kopien können natürlich teuer sein, aber müssen nicht. Dafür hat man dann sicheren Code. Ein delete an der falschen Stelle und dein Programm crasht ;)
Außerdem ist der Stack schneller als der Heap.

Und speziell bei Listen (Qlist/std::list/...), die ja nicht direkt den Value wie z.B. std::vector in einem Array ablegen sondern einen Umweg über verkettete Nodes machen, ist Sortieren wieder ein Klacks, denn es werden nur Nodes umgehängt und keine Values kopiert.
Und nur wegen einem _möglichen_ Perfomancegewinn nur noch Pointer in Container zu legen ist auch nicht das Wahre ;)
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Was manche so zwischen zeilen lesen .... :lol:
Ich bin mit Sicherheit kein verfechter von rohen zeigern in c++.

BTW sind Smartpointer auch zeiger, mit nur etwas mehr funktionalitaet.

Sorry wenn ich Zeiger generell verwende ... ich meint damit allgemeine dyn. allokierung.

Und hier auf die boost::pointer verweissen ? Warum ? Der TE warbeit mit Q-COntainern. Soll er doch auch Q-Pointer verwenden, wenn notwendig.
Und grad fuer sein anliegen im spezialfall Qt isses ueberhaupt nicht notwendig. Grad auf diese naive Verwendung der container zielt doch das verdeckte optimieren der qt !

Außerdem ist der Stack schneller als der Heap.

was in diesem falle aber irrelevant iss.
Listen legen ihre nodes immer dyn. an ... und verwenden dafuer nur ihren eigenen allokator. um das (impliziete) new kommst also ned drumherum .

Das einzigste was man verschenkt, wenn man selber allokiert, iss die optimierungsmoeglichkeit des allokierens der daten in den container der eingefuegt wird. Das ist wahrscheinlich schon recht heftig.
Aber auch da kann man selber allokatoren verwenden um das abzuschwaechen.
Aber so ins detail gehen darum gehts gar ned.
Fakt ist, er macht 2 tiefe kopien seiner daten im einzufuegenden container.
Und nur wegen einem _möglichen_ Perfomancegewinn nur noch Pointer in Container zu legen ist auch nicht das Wahre
nein natuerlich nicht, nur als c++ programmierer sollt man sich der ganzen thematik bewusst sein ... man sollt also wissen was man da tut. und vor allem sollt man wissen was qbytearray da tut, sonst koennt man leicht beim manuellen (und vielleicht unnoetigen) nachoptimieren kraeftig auf die nase fallen.

Ciao ...
Thrake
Beiträge: 10
Registriert: 25. November 2007 13:57

Re: Danke!

Beitrag von Thrake »

CKassiopaia hat geschrieben: Der Fehler hat sich durch die .append(xxx) bemerkbar gemacht, Grund war aber, dass ich die Funktion (innerhalb einer Klasse) aufgerufen hatte, bevor ich eine Instanz der Klasse erzeugt hatte. Dummerweise hat sich Problem vorher nie gezeigt.
Ich habe leider gerade genau das gleiche Problem wie du es hattest, allerdings nutze ich kein QByteArray, deshlab meine Frage gleich hier: Wie konntest du denn eine Funktion innerhalb der Klasse erzeugen, ohne dabei die Klasse selbst zu instazieren? Bei mir ist das Problem, dass ich ein Programm geschrieben habe, das auf meinem Laptop gut läuft, allerdings auf einem Atomboard immer diesen Fehler auswirft, außer ich starte das Programm sehr schnell hintereinander bis es dann geht.
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

http://www.cplusplus.com/reference/std/new/bad_alloc/
ich nehme einfach an, dass dir der Speicher ausgeht... Dein Atom ist ja arg beschränkt in seinen Ressourcen.
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

ich nehme einfach an, dass dir der Speicher ausgeht
Denk ich auch ...
Wenn ihr angeblich nur kleine speichermengen allociert, denk ich mal das Problem dann iss, das ihr oft allociert ? und das freigeben ned klappt, bzw ihr euch in ner rekusion verliert.

bad_alloc sagt ja auch nix anderes aus ... als dass Dein (implizietes) new nicht die geforderte Speichermenge liefern kann !

Übrigens, klar iss der Atom lahm. Aber er kann normal schon nen 32bit system ausreizen ... 4GB RAM kann er auch controllieren.
Ab 400er und 500er Serie kann er sogar 64bit ... keine Ahnung was der Speichercontroller maximal schafft.
Iss also eher ne frage des Speicherausbaus aufm Board und was Dein BS draus macht.


Ciao ....
Thrake
Beiträge: 10
Registriert: 25. November 2007 13:57

Beitrag von Thrake »

das Problem liegt allerdings darin, dass auch wenn ich es durch häufiges "starten" endlich zum laufen kriege, es einwandfrei funktioniert. Der benötigt auch nur um die 2,5% des gesammten Arbeitsspeichers. Deswegen wunder ich mich, warum ich den Fehler überhaupt bekomme. Selten tritt er auch auf meinem Laptop auf, der mit 4GB mehr als genug haben sollte für ein Programm, dass einige Widgets hat, aber keine rießen arrays.

EDIT: Ich hab mal das getestet, was franzf mir da aufgebracht hat. Der Arbeitsspeicher ist allerdings dadurch nicht wirklich gefüllt (deutlich <1%), allerindings kriegt er da auch irgendwann ein alloc-problem bei einer etwas höheren zahl an Arrays.

Bevor ich mein Pogramm noch dicker mache als es schon ist, soll ich dann alles, was ich "genewt" habe, einfach so umschreiben und normal instanzieren
also statt

Code: Alles auswählen

QLabel *label = new QLabel("Hello");
zu

Code: Alles auswählen

QLabel label("Hello");
ändern?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Thrake hat geschrieben:also statt

Code: Alles auswählen

QLabel *label = new QLabel("Hello");
zu

Code: Alles auswählen

QLabel label("Hello");
ändern?
Nein, auf keinen Fall! Naja - kommt schon drauf an :P
Wenn die Widgets (allgemein: QObjects) in einer Objekthierarchie stecken, also einen parent haben, musst du das Widget dynamisch allokieren, sonst gibts trouble mit double-free (einmal über die QObject-Hierarchie, das andere mal wenn das Objekt aus dem Scope läuft). Außer du stehst auf Hampel-Kniffe und entfernst die QObjects aus dem object-tree, bevor dieser eingerissen wird (nicht wirklich empfehlenswert :P)

Ich denke dass dein Problem ganz wo anders liegt. Wenn das Programm denn läuft und nur so wenig Speicher braucht, hast du irgendwo im Laufe der Initialisierung eine extreme dynamische Speicherallokation.

Code: Alles auswählen

{
  Type* t = new Type[1000000000];
  // mach was
  delete[] t;
}
{
  std::vector<Type> vec;
  vec.resize(1000000000);
}
{
  // usw
}
Wenn sowas durch ist und wieder bereinigt, merkst du natürlich nix von den Allokationen. Vermeiden kannst du es, indem du nicht unnötig viel Speicher für solche Arrays anforderst, sondern mit einer Liste (std::vector, QList, QVector, ...) arbeitest und diesen nicht auf eine bestimmte Größe vergrößerst, sondern je nach Anforderung wachsen lässt.
Sollte das immer noch nicht funktionieren, musst du deinen Algorythmus ändern ;)
Thrake
Beiträge: 10
Registriert: 25. November 2007 13:57

Beitrag von Thrake »

hmmm... an Arrays habe ich aber kaum was und die, die ich habe, sind in meiner klasse drin und sollten auch nicht wieder deletet werden. und das ist nur einer von der größe von [2000][6].

Ich vermute mal, dass ein Debuger (über Eclipse z.b.) auf dem Atom-Board mir das Problem liefern würde, wo das problem sei, oder?
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Thrake hat geschrieben:hmmm... an Arrays habe ich aber kaum was und die, die ich habe, sind in meiner klasse drin und sollten auch nicht wieder deletet werden. und das ist nur einer von der größe von [2000][6].
Und wie viele Instanzen dieser Klasse erstellst du? Wie groß sind die Objekte in dem Array?
Ich vermute mal, dass ein Debuger (über Eclipse z.b.) auf dem Atom-Board mir das Problem liefern würde, wo das problem sei, oder?
Ja, ein Debugger ist nie verkehrt. Sollte eigentlich das erste sein, das du bei einem Laufzeitproblem versuchst :P
Antworten