# Ram Usage under 64bit doubles?



## Guest (27. Dez 2007)

Hey,

ich bin relativ neu was java angeht habe aber trotzdem ein programm geschrieben, dass mit etwa 20 millionen strings arbeitet und diese nach einem muster bewertet. Die Strings lade ich aus textfiles in das Programm.

Unter WinXP 32Bit habe ich dafür bisher etwa 1,5GB ram gebraucht. Das reicht bald jedoch nicht mehr. Dadurch, dass ich über den -Xmx1600M command nur maximal 1600MB vergeben kann (habe es tausendmal probiert mehr zu vergeben funktioniert nicht mehr) - bin ich dazu über gegangen mir Vista 64Bit zu installieren um meine neu gekauften 4 GB ram zu nutzen.

Mein jetztiges Problem: Mein Programm braucht auf einmal 3,15 GB ram (plus die ~1GB vista verbraucht steht ich am selben punkt wie vorher). Liegt das komplett an der 64Bit architektur? oder ist da die noch schlechte JRE 64bit version schuld? Oder etwas anderes?


----------



## Wildcard (27. Dez 2007)

Bekommst du out of memory, oder wird der allokierte Speicher einfach nicht mehr freigegeben?


----------



## Guest (27. Dez 2007)

Hey, ich bekomme:



```
Error occurred during initialization of VM
Could not reserve enough space for object heap
```


Ich versuche gerade was mit memory Remapping aber klappt bisher auch nicht.

Ich habe es geschafft unter Vista64bit mit 5 GB ram das programm zu starten da verbruacht es 3,5GB ram :-/


----------



## Wildcard (27. Dez 2007)

Anonymous hat gesagt.:
			
		

> ```
> Error occurred during initialization of VM
> Could not reserve enough space for object heap
> ```


Das deutet aber eher darauf hin, dass dein Xmx zu hoch angesetzt ist.


----------



## Guest (27. Dez 2007)

Das ist ja das Problem.

Unter WinXP 32Bit kann ich den -Xmx MAXIMAL auf 1600M setzen. Danach kommt immer ein error. Obwohl ich 3Gb ram (erkannten ram; eigentlich 5Gb drinne) habe.

Daher habe ich es auch mit Vista 64 probiert.


"Aufgegeben" mit 32bit hatte ich nachdem ich hier:

http://confluence.sakaiproject.org/confluence/display/ENC/JVM+Performance+Tuning

Dies gelesen hatte:



> 32 bit OS's (by nature of having only 32 bits to address memory) can only provide your Java process with 4 GB of virtual address space. In reality this may result in a maximum JVM allocation as small as 2 GB, (on some systems the other 2 GB is reserved for the OS) Further, this 2 GB is allocated between various things (e.g. Permgen, Headers, Heap), so the heap may get only about 1.6 GB.


----------



## Wildcard (27. Dez 2007)

Das ist nunmal eine der vielen Beschränkungen deines Betriebssystems.
Wenn es dir darum geht maximal Speicher allokieren zu können, bist du bei Solaris, Linux,... besser aufgehoben.


----------



## kademlia (27. Dez 2007)

Mmh okay.

Also ich hab kein problem damit ein zusätsliches Betriebssystem zu installieren.

Kannst du mir irgendwas empfehlen für den Anfang? Derzeit wirklich NUR um mit eclipse/java zu arbeiten.


Ubuntu/ne LiveCD?


----------



## Wildcard (27. Dez 2007)

Mal anders gefragt:
Wie viel RAM brauchst du denn? Und siehst du noch Optimierungspotential um den Bedarf zu reduzieren?


----------



## kademlia (27. Dez 2007)

Hey, 

vielen vielen dank schonmal für die ganzen antworten!


Also um das ganze mal etwas genauer zu beschreiben:

Ich habe als test eine Art "YoutubeStats" gebaut. Dabei handelt es sich um ein Programm, dass User auf der Plattform Youtube bewertet. DIes tut es anhand der Links die auf den User verweisen.

Das Programm lädt also millionen Youtubeseiten runter - analysiert sie - und bewertet die Usernamen.

Dabei nutze ich mehrere Hashmaps und arbeite mit threads

Ich sortiere alle Videos/User mit weniger als 10k views aus. Diese kommen in die "Banlist" welche derzeit am größten ist mit 10415658 Einträgen. Hinzu kommen noch 1332384 bearbeitete nutzer und 2085917 in der Warteliste nach 2999935 downloads (Es kommen etwa 110 Mb ram auf 1million namen).

Da Warteschnalge seit einiger zeit fällt (http://derfrosch-forum.de/yt1.PNG; und weiterführend: http://derfrosch-forum.de/yt1_1.PNG ). Brauche icht wohl nicht viel mehr als 2GB ram.

Ich glaube es gibt nicht viel einspaarpotential was das Programm an sich angeht. Außer ich baue es um und arbeite mit einer Datenbank. Ich muss da allerdings zugeben, dass ich hier nicht sehr viel ahnung von habe - jedoch weiß das MySQL dem Datenaufkommen niemals gewachsen wäre (es wurde mir mal eine extrem schnelle DB empfohlen mir ist leider der exotische name entfallen).

PS: die ubuntu Desktop-CD (ubuntu-7.10-desktop-amd64.iso) hat gerade bei mit total versagt. Sie ist mehrfach einfach hängen geblieben und selbst die Installierte version lief nicht stabil.


----------



## Wildcard (27. Dez 2007)

Irgendwie kann ich mir nicht vorstellen wie du langfristig um eine DB oder ähnlich herumkommen willst.
Wenn du alle Daten im Speicher halten willst, dann bedeutet das doch, dass du immer aufrüsten musst wenn die Datenmenge deinen Speicher sprengt.
Da YouTube relativ schnell wächst, könnte das ein teurer Spaß werden.


----------



## kademlia (27. Dez 2007)

Ja hast du recht. Aber eigentlich sollte der Ram derzeit ja noch locker reichen...


----------



## Wildcard (27. Dez 2007)

Aber die Hausnummer von 2GB solltest du auf einem 64bit Vista problemlos allokieren können.
Ich weiß nicht genau wo dort die Grenzen verlaufen, aber das lässt sich sicher bei MS in Erfahrung bringen.


----------



## trc (15. Jan 2008)

wenn dir 8G reichen, kannst du dir ja mal hsql ansehen. wird auch von open office verwendet


----------



## trc (15. Jan 2008)

trc hat gesagt.:
			
		

> wenn dir 8G reichen, kannst du dir ja mal hsql ansehen. wird auch von open office verwendet



  meinte hsqldb -> hsqldb.org


----------



## tuxedo (15. Jan 2008)

Also MySQL kann mit den Mengen umgehen. Hab selbst ne MySQL-DB mit etlichen Millionen Zeilen und hab keine Probleme damit.
Benutze aber seit kurzem fast ausschließlich PostgreSQL ...

- Alex


----------



## kademlia (24. Jan 2008)

@alex.

Das stimmt leider nur Halb.

MySQL kann zwar mit den Werten umgehen allerdings nicht in der notwendigen Geschwindigkeit. Wenn ich den Ram benutze ohne DB. Komme ich auf etwa 1,8 Mb/s (mein Max. download) mit DB reduzieren sich die Zugriffe drastisch und damit auch die Downloadgeschwindigkeit auf etwa 250-400 kb/S.

Derzeit versuche ich eine Halb Halb lösung zu bauen, leider nicht all zu viel zeit


----------



## tuxedo (24. Jan 2008)

1,8mbit oder mbyte?

Also ich kann schneller aus meiner DB rauslesen. Wesentlich schneller. 

Kannst du auf deinen Wert mal genauer eingehen?

Hängt die DB lokal im Netz? Oder ist sie auf deinem localhost?
Netzwerk (100Mbit LAN) sollte ja bis zu 10MByte/s schaffen (Samba-Filetransfer kann bei mir so viel, und aus der DB kann ich auch so schnell lesen (Tabelle mit vielen SPalten und extrem vielen Zeilen)).

- Alex


----------



## kademlia (25. Jan 2008)

Haha, ne der Wert bezieht sich auf das Java Programm das ich am Laufen habe.

Es arbeitet mit 30 Downloader Threads, einem "ListHandler" und einer MySQLConnecter klasse. Die 30 Downloader übergeben jeweils die von ihnen erareiteten Informationen an den ListHandler, welcher entscheidet wo die informationen hin gehören.

Anschließend werden die aufgearbeiteten informationen an den mysqlconnecter übergeben. Dieser schreibt die Daten in die DB.

Der speed von 1,8 Megabyte pro sekunde betrifft meinen realen Downloadstream, den die 30 Downloader verursachen.


Und wie gesagt: Mit der Datenbank schrumpft dieser wert auf etwa 250-400 kilobyte/s weil die Datenbank nicht schnellgenug hinterher kommt (Wenn ich mit hashtables arbeite bleibt der download konstant bei 1,8MByte/s).


----------



## tuxedo (30. Jan 2008)

Naja, irgendwie kann da was an deiner DB nicht stimmen. 
Ich schaffs ja mit meinem JDBC-über-HTTP Treiber aus einer MySQL-Datenbank im Internet schneller zu saugen als mit 400kbyte . Upload (schreiben) in die DB geht auch mit full-speed. Vielleicht hast du ein zu komplexes Insert-Statement oder noch irgendwelche "bremsenden" Trigger?

Deine 250..400kbyte/sek kann ich jedenfalls nicht so reproduzieren, sei es beim Insert,  noch beim Select. 

- Alex


----------



## kademlia (31. Jan 2008)

Prinzipiell haben die kb/s ja nicht direkt was mit der DB zu tun.

Wenn man annimmt das eine durschnittliche youtubeseite 100kb an text hat lade ich bei ~1,8 MB/s etwa 18 seiten pro sekunde runter.

Man kommt auf etwa ~10 namen pro seite. Macht 180 namen pro sekunde.

Überprüfung auf vorhandenen eintrag in der banliste (180 abfragen; +~25 inserts)
Überprüfung ob vorhanden in der Userliste (+140 abfragen;+1insert;+60updates)
Falls nirgendwo enthalten update des values in der Warteschlange (+~80;+40inserts;+26deletes;+40updates)

Macht in der sehr wagen Theorie:
400 gets/s
66 inserts/s
100 updates/s
26 deletes/s
__________
knapp 600 anfragen pro sekunde.


glaube da geht mit mysql einfach nicht mehr viel.

Gibts vielleicht ne DB die mehr kann oder ein weg in mysql die DB im ram zu haben solange genug ram da ist?


----------



## tuxedo (31. Jan 2008)

MySQL beherrscht Caching-Techniken. Damit kannst du ne Menge rauskitzeln. Die standard-konfig ist für solche Anfrage-Mengen nicht geeignet.

Wenn ich mit so deinen "DB-Algorithmus" anschaue, würde ich auch sagen: verfeinere das Design so dass weniger Abfragen notwendig sind. Oder "verbessere" deine Querys. Da du ja selbst geschrieben hast dass du da noch nicht so viel Ahnung hast, würde ich drauf tippen dass ein großteil der Querys sind wirklich performant sind.

Es gibt genug Tool da draußen um "langsame" Query's (guckst du z.b. hier) zu identifizieren (mysql bringt da sogar selbst was mit, schau mal in die config).

Da du immer und immer wieder die gleichen Tabellen benutzt würde ich auch die Caches anpassen. Weil: Es gibt sicher noch größere Datenbanken auf dem Planet die auch MySQL benutzen und prima mit +600 Querys/Sekunde zurecht kommen. Es kommt nur auf das Finetuning an (wie bei Apache, dem Webserver eben auch).

- Alex


----------



## sparrow (31. Jan 2008)

> Überprüfung auf vorhandenen eintrag in der banliste (180 abfragen; +~25 inserts)


Richtig: 1 Abfrage, 25 inserts



> Überprüfung ob vorhanden in der Userliste (+140 abfragen;+1insert;+60updates)


Richtig: 1 Abfrage, 1 Insert, 60 Updates
Das mit den Updates kann ich nicht ganz nachvollziehen, also was genau das update bewirkt.
Kann aber sein, dass ein Update ausreicht.



> Falls nirgendwo enthalten update des values in der Warteschlange (+~80;+40inserts;+26deletes;+40updates)


Das versteh ich nicht, glaube aber fest an ein ähnliches Optimierungspotential wie oben!

Gruß
Sparrow


----------



## tuxedo (31. Jan 2008)

Naja, er frägt jeden User einzeln ab. Und da er theoretisch auf 180 Usernamen pro Sekunde kommt, sinds da 180 Abfragen. Aber ganz koscher ist das nicht. Irgendwie lässt sich das sicherlich optimieren.

- Alex


----------



## sparrow (31. Jan 2008)

Wenn er für jeden Benutzer eine Abfrage macht ist das ein riesiger Overhead.
Dann lieber sammeln, das ganze in einer einzigen Abfrage raussammeln (also mit einem WHERE-Rattenschwanz) und dann das Ergebnis (also das Resultset) für die weiteren Sachen kurz im Speicher halten.
Das dürfte insgesamt schneller sein weil so die jeweilige Datenbank ihre Stärken, nämlich das schnelle selektieren von Datensätzen, ausspielen kann.

Bei solch einer Überlegung ist es aber auch wichtig den ganzen Zusammenhang zu sehen.
So gehört der gesamte Ablauf auf verschiedene Controler verteilt die sich jeweils um "ihre" Zuständigkeit kümmern.
Einer dieser Controler sollte die Warteschlange vorrätig halten in denen die zu prüfenden Datensätze stehen und neue hinzugefügt werden. Das Teil des Programms der sich die Daten aus dem Netz spidert sollte hiervon komplett getrennt sein, so kann es auch nicht zu einer Verlangsamung durch die Datenbank kommen.
Sollte es dazu kommen, dass die Datensätze nicht schnell genug abgearbeitet werden können und die Warteschlange überläuft, gibt es ein Signal an den Spider für die Controler der daraufin solange pausiert bis wieder Platz da ist.

Das rumgewühle auf der Datenbank sollte in jedem Fall schneller gehen als das spidern einer Internetseite. Ist ja nicht so, dass HTTP superschnell ist.


----------



## kademlia (1. Feb 2008)

wow, erstma vielen dank für die antworten!

da muss ich mir wohl noch einiges an mysql angucken...

Falls es interessiert :autsch: :

Ich stelle sozusagen ein Rankin der YouTubeUser/Videos her.
Dabei erhält jede Seite(User/Video) einen Punkt wenn sie irgendwo auf einer anderen Seite verlinkt ist.
In der DB steht also z.B:

[...]
YouTube 21
User223 50
Kevin 5
[...]

Seiten unter 10.000 Views kommen in die "Banlist" (Reduziert das Downloadaufkommen um etwa 80%), damit, falls sie erneut gefunden werden (z.B. durch verlinkung auf anderen Seiten) nicht erneut in die Warteschlange gelangen und ich in eine Endlosschleife gerate.
Eine bearbeitete Seite kommt in die "Userlist" bzw "Videolist". Unbearbeitete/Unbekannte Seiten kommen in die Warteschlange.

Ein Downloader-Thread parsed also eine Seite nach Links, speichert diese in einer temporären HashList und übergibt sie an den synchronized "ListHandler", welcher dann die MySQL abfragen über den MySQLHandler tätigt und die Seiten einsortiert.

Ich scheue mich ja etwas meinen "Noob-Code" hier zu posten aber ich wage es einfach mal  :? 

Auszug aus dem "ListHandler", der die anfallenden Seiten bearbeitet.

```
public synchronized void checkhashList(String link, int newcount, boolean isDownloadLink) {
		if (isDownloadLink){
			giveOutNames(link); // Logger um eventuelle synchronitätsfehler zu erkennen bzw.
								// um YouTubeseitige Fehler zu erkennen ("Promoted Videos" ist
								// z.b. eine dieser Sektionen die sich nur Zeitweise sehr oft
								// wiederholen und daher nicht in die Erhebung einfließen darf.
		}
		if (!link.contains("\"") && !link.contains(":") && !link.contains("\\") && !link.contains("/")) {
			int oldCount_userlist = 0;
			int oldCount_queue = 0;
			// Abfrage des in der DB enthaltenen Values einer Seite
			if (newcount < 0){ // Videos haben negative Values
				oldCount_userlist = handler.findLinkInUserlist(link,"vuserlist");
				oldCount_queue = handler.findLinkInQueue(link,"vqueue");
			}else{ // User haben positive Values
				oldCount_userlist = handler.findLinkInUserlist(link,"cuserlist");
				oldCount_queue = handler.findLinkInQueue(link,"cqueue");
			}
			if (newcount < 0){ // Handelt es sich um ein Video...

				if (oldCount_userlist != 0){ // Wenn in der Userliste vorhanden. Value updaten und abbrechen
					handler.updateUserlist(link, oldCount_userlist + newcount,"vuserlist");
				} else if (isDownloadLink == true) { // Wenn DownloadLink in die Userlist einfügen und abbrechen
					handler.insertLinkToUserlist(link, oldCount_queue+newcount,"vuserlist");
					handler.removeLinkFromQueue(link,"vqueue");
				} else if (oldCount_queue != 0) {	// Falls in der Queue vorhanden nur value erneuern
					handler.updateQueue(link, oldCount_queue + newcount,"vqueue");
				} else if (oldCount_queue == 0) { // Falls komplett unbekannt in die QR einfügen
					handler.insertLinkToQueue(link, newcount,"vqueue");
				}
			}else{  // Handelt es sich um ein User...
				if (oldCount_userlist != 0){
					handler.updateUserlist(link, oldCount_userlist + newcount,"cuserlist");
				} else if (isDownloadLink == true) {
					handler.insertLinkToUserlist(link, oldCount_queue+newcount,"cuserlist");
					handler.removeLinkFromQueue(link,"cqueue");
				} else if (oldCount_queue != 0) {
					handler.updateQueue(link, oldCount_queue + newcount,"cqueue");
				} else if (oldCount_queue == 0) {
					handler.insertLinkToQueue(link, newcount,"cqueue");
				}
			}
		[...]
```

Einige Funktionen werden von den Downloader-Threads noch aufgerufen:


```
public synchronized void addtobanList(String link,int count) {
		if (count < 0){ // its a video
			count = handler.findLinkInQueue(link, "vqueue");
			if (count >= 0){
				count = -1;
			}
			handler.insertLinkToBanlist(link, count,"vbanlist"); 
			handler.removeLinkFromQueue(link,"vqueue");
		}else{ // its a channel
			count = handler.findLinkInQueue(link, "cqueue");
			handler.insertLinkToBanlist(link, count,"cbanlist"); 
			handler.removeLinkFromQueue(link,"cqueue");
		}

		pL.remLink(link); // pL = processingList, jeder DownloadThread hat eine
	}
	
	public synchronized void checkRightUsername(String realUserName) {
		// es kann vorkommen, dass Links den falschen Namen enthalten (/user statt /UsEr)
		if (!handler.findLinkInBanlist(realUserName,"cbanlist")
				&& handler.findLinkInQueue(realUserName,"cbanlist") == 0
				&& handler.findLinkInUserlist(realUserName,"cbanlist") == 0 
				&& !realUserName.equals("")) 
		{
			handler.insertLinkToQueue(realUserName, 1,"cbanlist");
		}
	}
	
	public synchronized boolean linkInBanlist(String newlink,int count) {
		if (count < 0){
			if (handler.findLinkInBanlist(newlink,"vbanlist")){
				return true;
			}
		}else{
			if (handler.findLinkInBanlist(newlink,"cbanlist")){
				return true;
			}
		}
		return false;
	}
```


----------

