# Deklarierung frisst Rechenzeit



## javampir (30. Jul 2015)

hallo alle zusammen,
ich habe eine frage, die vermutlich richtung memory management bei der jvm geht. in meinem programm verursacht

```
short[][] ret = new short[(b.length - offset) / (numchannels * (bitspersample / 8))][numchannels];
```
einen merklichen zeitverlust (ca. 4 sekunden), wobei man von der größenordnung her

```
short[][] ret = new short[11080700][2];
```
hat (plus minus paar millionen). ich sehe dann auch einen vollausschlag bei der cpu. allerdings werden danach erst werte reingeschrieben, das geht dann ruck zuck in wenigen millisekunden. ich habe mit System.getCurrentTimeMillies diese zeile als übeltäter identifiziert.
gibt es ideen, wie man hier den heap bescheunigen könnte?
javampir


----------



## Dompteur (30. Jul 2015)

Könnte durch den Garbage Collector verursacht werden.


----------



## javampir (30. Jul 2015)

warum das? ich lege doch das array an?


----------



## Dompteur (30. Jul 2015)

War nur eine Idee.
Ich habe einfach einmal angenommen, dass das nicht gleich beim Programmstart passiert. Dann kann die Anforderung eines größeren Speicherblocks dazu führen, dass Java erst einmal den Speicher aufräumen muss.


----------



## javampir (30. Jul 2015)

ah, ok das kann sein. es passiert in der tat erst viel später.
was kann man da tun? ich kann ja nicht mal ne progressbar hinhauen.


----------



## Dompteur (30. Jul 2015)

Als erstes kannst du einmal den Garbage Collector beobachten. Ruf dein Java-Programm mit folgender Option auf "-verbose:gc".
Da siehst du dann unter anderem die Zeitdauer der einzelnen Aufrufen. Das sollte reichen, um den Verdacht zu bestätigen bzw. zu widerlegen.

Vermeiden kannst du GC nicht. 
Wenn du das Optimieren willst, dann kannst du dir hier ein paar Tips holen: http://www.cubrid.org/blog/dev-platform/how-to-tune-java-garbage-collection/


----------



## InfectedBytes (30. Jul 2015)

das Problem ist die dimensionierung des Arrays.

```
short[][] ret = new short[11080700][2];
```
Intern werden quasi 11080700 viele Arrays erzeugt, welche jeweils aus 2 Einträgen bestehen. Das ist natürlich unglaublich rechenintensiv.
Wenn du unbedingt ein 2D array brauchst, dann tausche (wenn möglich) die dimensionen:

```
short[][] ret = new short[2][11080700];
```
Dadurch werden nur zwei Arrays erzeugt, welche jeweils 11080700 viele Einträge beinhalten.
Oder noch besser, nutze nur ein ein dimensionales Array mit doppelter länge


----------



## Bitfehler (30. Jul 2015)

Mal eine Zwischenfrage:
Warum vermutest du den GC als Ursache? 
Der GC räumt doch nicht mehr benutzte/referenzierte Objekte im Speicherbereiche des Heap frei. Durch den GC wird folglich Speicher wieder freigeben. Da ich in dieser Diskussion aber noch nichts von java.outOfMemory gelesen habe, kann doch erstmal davon ausgegangen werden das ausreichend HeapSpace zur Verfügung steht.  

Zurück zum Thema:

Was sind denn deine Werte für -Xms und -Xmx?
Kann es sein, dass du soviel Speicher anforderst, dass der initial bereitgestellte Speicherbereich dauernd erweitert werden muss und dieser Vorgang Zeit kostet? (Testweise: Xms=Xmx=max. 1/4 deines RAM)
Hierbei bin ich mir selber nicht ganz sicher, also mit sehr viel Vorsicht zu genießen.
Nehmen wir mal an, dass es wirklich etwas mit dem Heap (und dem umzu) zu tun hat, ändert sich etwas wenn das Array static ist, damit es evtl. in einem anderen Speicherbereich (nicht in Eden-Space) erzeugt wird. Alternativ kann man einen HeapDump erzeugen, um zu ermitteln, wie der HeapSpace genutzt bzw. belegt ist. Ob das einfach zu analysieren ist oder überhaupt etwas bringt, kann ich dir nicht sagen (da bisher nur einmal gemacht).
Viel Spaß noch dabei ;9


----------



## Tobse (30. Jul 2015)

Kannst du vllt auch mit einem Array dieser Art arbeiten?


```
short[][] ret = new short[2][11080700];
```

Ich vermute mal ins Blaue hinein, dass das schneller geht.

Es sind letztlich auch nur ~50MB, das sollte für die JVM eigentlich kein Problem sein....


----------



## InfectedBytes (30. Jul 2015)

Tobse hat gesagt.:


> Es sind letztlich auch nur ~50MB, das sollte für die JVM eigentlich kein Problem sein....


Das Problem ist nicht der Speicherverbrauch, sondern die Anzahl der Allokationen. Die JVM allokiert eben pro Array speicher. Wie lang dieses Array dabei ist, macht jedoch keinen Unterschied. 
Dementsprechend macht es eben einen extremen Unterschied, ob man mehrere millionen Speicherallokationen hat oder eben nur zwei^^


----------



## Tobse (30. Jul 2015)

InfectedBytes hat gesagt.:


> Das Problem ist nicht der Speicherverbrauch, sondern die Anzahl der Allokationen. Die JVM allokiert eben pro Array speicher. Wie lang dieses Array dabei ist, macht jedoch keinen Unterschied.
> Dementsprechend macht es eben einen extremen Unterschied, ob man mehrere millionen Speicherallokationen hat oder eben nur zwei^^


Das ergibt Sinn, ja 

Habe das jetzt getestet und InfectedBytes' und meine Vermutung hat sich bestätigt:


```
long a = System.currentTimeMillis();

short[][] ret = new short[2][11080700];

long b = System.currentTimeMillis() - a;

System.out.println("[2][XXX]: " + b + "ms");

a = System.currentTimeMillis();

short[][] ret2 = new short[11080700][2];

b = System.currentTimeMillis() - a;

System.out.println("[XXX][2]: " + b + "ms");
```


```
[2][XXX]: 10ms
[XXX][2]: 3137ms
-------------------
[2][XXX]: 9ms
[XXX][2]: 3365ms
-------------------
[2][XXX]: 9ms
[XXX][2]: 3254ms
```


----------



## javampir (1. Aug 2015)

danke!!
ich habe das array mal umgedreht, und tatsächlich, geht wie geschmiert.
ich kann leider nicht ein einzelnes array draus machen, denn wenn statt der 2 ne 4 dasteht und ich ein paar millionen mehr drin hab, bin ich sofort im int overflow bei den indizes der arrays drin.

Vielen Dank euch allen!


----------



## InfectedBytes (1. Aug 2015)

"ein paar millionen mehr" reichen da nicht aus. Bisher bist du bei rund 11 millionen, damit es zum overflow kommt, musst schon mehr als 2 milliarden mehr haben ;-)


----------



## Tobse (1. Aug 2015)

InfectedBytes hat gesagt.:


> "ein paar millionen mehr" reichen da nicht aus. Bisher bist du bei rund 11 millionen, damit es zum overflow kommt, musst schon mehr als 2 milliarden mehr haben ;-)


Richtig. Und selbst bei zwei Dimensionen: solange du die kleinere Zahl (dem ersten Code zu entnehmen die Zahl der Kanäle) in der ersten Dimension hast, wird die Zeit für die Speicherreservierung auch nie über ~20-30ms steigen.


----------



## javampir (1. Aug 2015)

ok, war nur aus dem handgelenk geschrieben. aber es sind (unschwer zu erraten) audiodateien mit raw data, die können auch größer werden. aber das sind dann insgesmt memorygeschichten


----------



## RalleYTN (12. Aug 2015)

javampir hat gesagt.:


> ok, war nur aus dem handgelenk geschrieben. aber es sind (unschwer zu erraten) audiodateien mit raw data, die können auch größer werden. aber das sind dann insgesmt memorygeschichten


Willst du damit sagen, dass du mehrere Audioströme gleichzeitig in den Arbeitsspeicher laden willst?
Ich würde dir davon abraten.


----------



## Tobse (12. Aug 2015)

RalleYTN hat gesagt.:


> Willst du damit sagen, dass du mehrere Audioströme gleichzeitig in den Arbeitsspeicher laden willst?
> Ich würde dir davon abraten.


Und warum? Die größten FLAC und WAV Dateien, die ich je gesehn habe waren bei 100-200MB. Bei zT. 4 - 8GB Arbeitsspeicher ist das absolut 0 Problem.


----------



## RalleYTN (13. Aug 2015)

Tobse hat gesagt.:


> Und warum? Die größten FLAC und WAV Dateien, die ich je gesehn habe waren bei 100-200MB. Bei zT. 4 - 8GB Arbeitsspeicher ist das absolut 0 Problem.


Allerdings nehmen die dann die dann diese 100-200MB weg und der Java Heap Space ist ja nicht genauso groß wie der Arbeitspeicher. Am performantesten ist es jede Datei nur dann zu laden wenn man sie brauch und wenn sie einmal nicht mehr gebraucht wird dann schließt man den Datenstrom wieder. Der einzige Nachteil dabei ist, das etwas Zeit benötigt um die Datei zu laden.


----------



## JStein52 (13. Aug 2015)

Das kann man doch so allgemein nicht sagen. Das hängt davon ab was er mit seinen Daten macht. Wenn er ständig auf alle Kanäle zugreifen muss kann er sie eben nicht immer wieder neu laden.  Und es hängt davon ab wie seine Anwqendung genutzt wird. Von einem Benutzer auf einem einzelnen Rechner, da wäre es zum Beispiel kein Problem wenn er sich grosse Teile seines Speichers voll lädt. Oder von vielen Benutzern auf einem Terminalserver, dann könnte das zu einem Problem werden.


----------



## Tobse (13. Aug 2015)

JStein52 hat gesagt.:


> Das kann man doch so allgemein nicht sagen. Das hängt davon ab was er mit seinen Daten macht. Wenn er ständig auf alle Kanäle zugreifen muss kann er sie eben nicht immer wieder neu laden.  Und es hängt davon ab wie seine Anwqendung genutzt wird. Von einem Benutzer auf einem einzelnen Rechner, da wäre es zum Beispiel kein Problem wenn er sich grosse Teile seines Speichers voll lädt. Oder von vielen Benutzern auf einem Terminalserver, dann könnte das zu einem Problem werden.


So sehe ich das Auch. Audio-Bearbeitungs Software hat bei mittelgroßen Projekten locker mal 10-20 Spuren im RAM (undzwahr in 48Khz, 64bit Qualität). Die JVM macht dne Heap auch größer, wenn mehr RAM gebraucht wird.
Auf einem Server ist das natürlich wieder anders. Aber wenn der Server Audiodaten verarbeiten muss, ist es unumgänglich, einiges an Daten im RAM zu haben (z.B. SoundCloud, YouTube, Voice-Chats).


----------



## javampir (15. Aug 2015)

ich muss immer wieder auf die daten zugreifen. ich lade hier nicht mehrere audioströme. nur mal zum spaß: der herr der ringe soundtrack als wav hat ca. 3 GB
es ist als desktopanwendung konzipiert, mit zentral im netz verwaltetem speicher muss ich mir keine sorgen machen.


----------

