# Ermittlung des Physikalischen Hauptspeichers



## xquadrat (9. Jan 2009)

Hallo,
Da es sich bei meiner Anwendung um eine sehr Speicherintensive Applikation handelt, soll bei der Installation eine Überprüfung stattfinden, ob der *Physikalische Hauptspeicher* des Systems (also *nicht* der Speicher, der der virtuellen Maschine zur Verfügung steht) über einer gewissen Grenze liegt.

Ist diese Überprüfung aus Java heraus möglich? Ich habe in vielen Foren gelesen, dass es nicht möglich ist. Was ist der Grund für diese Limitierung?

Gibt es eine elegantere Lösung als über Java Native Interface in C komplierte Programme aufzurufen?


----------



## Wolfgang Lenhard (9. Jan 2009)

Das könnte in Hinblick auf die Plattformunabhängigkeit von Java schwierig sein. Kannst Du die Anwendung auf ein bestimmtes Computersystem einschränken?


----------



## hdi (10. Jan 2009)

Wie wärs einfach mit einer Meldung bei der Installation



> Please note: This program requires 5000000 Terrabyte of free RAM in order to work properly.
> Do you wish to continue install?



Man kann die Meldung ja etwas hervorheben damit sie nicht jeder gleich wegglickt, oder eine
Freeze-Time von 3 sek einstellen bis man das kann. Zugegeben nicht die tollste Lösung, aber bevor
man da anfängt irgendwelche C Programme einzuschleusen nur für sowas..


----------



## xquadrat (11. Jan 2009)

In der Einschränkung der Plattform besteht genau die Problematik. Dieser Check muss sowohl auf Windows / Linux und Unix Systemen funktionieren. Deshalb wäre eine Ermittlung der Gesamt-Hauptspeichergröße via Java am komfortabelsten.


----------



## mahe (11. Jan 2009)

Das Betriebssystem sollte man ermitteln können.
Dann könnte man je nach Betriebssystem spezifischen Code ausführen der über externe Programme den Hauptspeicher ermittelt.

Auf unixoiden Systemen sollte dies sehr einfach sein weil man sich auf bestimmt Programme verlassen kann.
Windows ist etwas blöder weil sich die Versionen unterscheiden.

[edit]So gut finde ich die Idee inzwischen nicht mehr.
Besonders unter Windows ist es wohl nicht besonders empfehlenswert.
Trotzdem hab ichs mal mit einem Mac und Windows 2003 (eigentlich XP 64 Bit) ausprobiert.


```
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

abstract public class RamInfo {

	protected int used = -1;
	protected int free = -1;

	abstract public boolean update();

	public int getUsedRam() {
		return used;
	}
	public int getFreeRam() {
		return free;
	}
	public int getTotalRam() {
		return used + free;
	}

	public static RamInfo getInstance() {
		String os = System.getProperty("os.name");
		if (os.startsWith("Mac")) {
			return new MacRamInfo();
		} else if (os.startsWith("Windows 2003")) {
			return new Win2k3RamInfo();
		}
		return null;
	}
}

class MacRamInfo extends RamInfo {
	protected MacRamInfo() {}

	public boolean update() {
		used = -1;
		free = -1;

		Pattern usedPattern = Pattern.compile("[0-9]+M used");
		Pattern freePattern = Pattern.compile("[0-9]+M free");

		Process proc = null;
		try {
			proc = Runtime.getRuntime().exec("top -l 1");
			proc.waitFor();
		} catch (Exception e) {
			return false;
		}

		Scanner procScanner = new Scanner(proc.getInputStream());

		while ((used == -1 || free == -1) && procScanner.hasNext()) {
			String line = procScanner.nextLine();
			Matcher m;
			m = usedPattern.matcher(line);
			if (m.find(0)) {
				used = Integer.parseInt(line.substring(m.start(), m.end() - 6));
			}
			m = freePattern.matcher(line);
			if (m.find(0)) {
				free = Integer.parseInt(line.substring(m.start(), m.end() - 6));
			}
		}
		return true;
	}
}

class Win2k3RamInfo extends RamInfo {
	protected Win2k3RamInfo() {}

	public boolean update() {
		int total = -1;
		int available = -1;

		Process proc = null;
		try {
			proc = Runtime.getRuntime().exec("systeminfo");
		} catch (Exception e) {
			return false;
		}

		Scanner procScanner = new Scanner(proc.getInputStream());

		while ((total == -1 || available == -1) && procScanner.hasNext()) {
			String line = procScanner.nextLine();
			if (line.startsWith("Total Physical Memory:")) {
				total = 0;
				for (int i = 22; i < line.length(); ++i) {
					char c = line.charAt(i);
					if (c >= '0' && c <= '9') {
						total *= 10;
						total += (int) (c - '0');
					}
				}
			}

			if (line.startsWith("Available Physical Memory:")) {
				available = 0;
				for (int i = 26; i < line.length(); ++i) {
					char c = line.charAt(i);
					if (c >= '0' && c <= '9') {
						available *= 10;
						available += (int) (c - '0');
					}
				}
			}
		}
		used = total - available;
		free = available;
		return true;
	}
}
```


----------



## xquadrat (13. Jan 2009)

Danke für den Tip. Das 





> Runtime.getRuntime().exec(systeminfo)


 für windows funktioniert wunderbar.
ein kleiner negativer beigeschmack ist jedoch unter windows noch vorhanden.
bei dem systemcall öffnet sich eine dosbox. gibt es eine möglichkeit diese zu unterdrücken?


----------



## Capasso (13. Jan 2009)

geht das nicht mit der Option /C?

also so:
Runtime.getRuntime().exec("cmd /C systeminfo");


----------



## ARadauer (13. Jan 2009)

klappt aber nicht überall oder?
Available Physical Memory kommt bei mir auf deutsch...


----------



## mahe (13. Jan 2009)

Ach Mist. Das auch noch.
Ich habe nicht bedacht, dass ich ein englisches Windows habe (Studentenlizenz, nicht geklaut!).

Wenn das in sämtliche Sprachen übersetzt wurde, müsste man die Zeile anders ermitteln (zählen oder so) oder den Ansatz wieder verwerfen ???:L


----------



## xquadrat (13. Jan 2009)

ja leider... der wahr zu einfach um wahr zu werden 
jetzt muss ich mich doch als letzte lösung mit visual c++ rumschlagen.


----------



## tuxedo (15. Jan 2009)

Nicht unbedingt. Wenn die Windows API schon einen Befehl dazu liefert (keine Ahnung, hab nicht nachgesehen) kommst du mit JNA auch ohne eine Zeile C/C++ Code aus. 

--> http://jna.dev.java.net

Hab damit unter anderem einige Zahlen extrahieren können die auch der Windows TaskManager anzeigt. Aber eben Prozessbasiert (um den Speicherverbrauch zu tracken und memory leaks in nativem Code aufzudecken) und nicht für's gesamte System. 

- Alex


----------



## Saxony (16. Jan 2009)

Für die hier gewünschte Anwendung müsste halt


```
void WINAPI GlobalMemoryStatus(
  __out  LPMEMORYSTATUS lpBuffer
);
```

verwendet werden. Dazu muss man mit JNA auf die Kernel32.dll mappen. Zusätzlich muss noch die Memory Struktur in einer Java Klasse abgebildet werden.


```
typedef struct _MEMORYSTATUS {
  DWORD dwLength;
  DWORD dwMemoryLoad;
  SIZE_T dwTotalPhys;
  SIZE_T dwAvailPhys;
  SIZE_T dwTotalPageFile;
  SIZE_T dwAvailPageFile;
  SIZE_T dwTotalVirtual;
  SIZE_T dwAvailVirtual;
} MEMORYSTATUS, 
 *LPMEMORYSTATUS;
```


```
public static class LPMEMORYSTATUS {

  public int dwLength; // DWORD ist usigned long -> Java int
  ...
  public int dwTotalPhys; // SIZE_T ist unsigned int -> Java int
...
}
```

Steht aber alles nochmal genauer auf der von Tuxedo verlinkten JNA Seite.
Viel Erfolg! 

bye Saxony

Achso: GlobalMemoryStatus erst ab W2k Pro oder höher.


----------



## Saxony (16. Jan 2009)

So, da mich das selber interessiert hat hier mal mein Test dazu.


Kernel32.java

```
import com.sun.jna.Native;
import com.sun.jna.win32.StdCallLibrary;

public interface Kernel32 extends StdCallLibrary { 
    
	Kernel32 INSTANCE = (Kernel32)Native.loadLibrary("kernel32", Kernel32.class);
	
	void GlobalMemoryStatus(MEMORYSTATUS result);
}
```

MEMORYSTATUS.java

```
import com.sun.jna.Structure;

public class MEMORYSTATUS extends Structure {

	public int dwLength;
	public int dwMemoryLoad;
	public int dwTotalPhys;
	public int dwAvailPhys;
	public int dwTotalPageFile;
	public int dwAvailPageFile;
	public int dwTotalVirtual;
	public int dwAvailVirtual;
}
```

JNATest.java

```
public class JNATest {

	public static void main(String[] args) {
		
		Kernel32 lib = Kernel32.INSTANCE;
		MEMORYSTATUS mem = new MEMORYSTATUS();
		lib.GlobalMemoryStatus(mem);
		
		System.out.println("Length: " + mem.dwLength);
		System.out.println("MemoryLoad: " + mem.dwMemoryLoad);
		System.out.println("TotalPhys: " + mem.dwTotalPhys);
		System.out.println("AvailPhys: " + mem.dwAvailPhys);
		System.out.println("TotalPageFile: " + mem.dwTotalPageFile);
		System.out.println("AvailPageFile: " + mem.dwAvailPageFile);
		System.out.println("TotalVirtual: " + mem.dwTotalVirtual);
		System.out.println("AvailVirtual: " + mem.dwAvailVirtual);
	}
}
```

bye Saxony


----------



## tuxedo (16. Jan 2009)

Ach ja. JNA hat eine "Macke" .. Zumindest in der Version in der ich es benutze:

Die notwendige DLL die JNA braucht um "so einfach" auf andere DLLs zuzugreifen ist in der JAR enthalten. Diese wird dann beim ersten Laden entpackt und das Temp-verzeichnis des aktuellen Users entpackt.

Das ist eigentlich recht schick. Dumm ist nur dass die entpackte File eine eindeutige Nummer enthält. Und diese ist eben bei jedem Entpacken "schon wieder" eindeutig, so dass irgendwann das Temp-Verzeichnis voll läuft wenn man die Anwendung oft genug gestartet hat.

Hab die entpackte File einfach in "jna.dll" umbenannt und die wird jetzt mit Systen.load("jna.dll"); einmal beim Programmstart geladen. Dann ist JNA nämlich so schlau und entpackt nix...

- Alex


----------



## Saxony (16. Jan 2009)

Hmm wo genau sollen denn die Temp Files gespeichert werden? Die einzige dll, welche ich in meiner jna.jar habe ist jnidispatch.dll aber von einem entpacken bzw. dem verbleiben in einem Verzeichnis merk ich nix.

Vielleicht liegts auch an der Version: ich verwende 3.0.4 b42

bye Saxony


----------



## tuxedo (16. Jan 2009)

Also bei mir liegen die immer in 

C:\Documents and Settings\<username>\Local Settings\Temp

Heissen dann irgenwie so:

jna<eine Nummer>.<hab ich vergessen>

Weiß gerade nicht welche Version ich benutze. Ist aber etwa 3-4 Monate alt.

- Alex


----------



## Saxony (16. Jan 2009)

Aha, bei mir legt JNA dort ein Verzeichnis an

hsperfdata_<username>

und schreibt dort eine Datei rein "1099" oder "2047" oder ... ohne Endung und wenn meine JavaApp terminiert wird diese gelöscht - das Verzeichnis bleibt aber -  wenn auch leer - im temp ordner zurück. Mein JNA ist vom 05.07.2008.

bye Saxony


----------



## tuxedo (16. Jan 2009)

Das ist was anderes. Das ist die PID File für den Zugriff mit JConsole.

- Alex


----------



## Saxony (16. Jan 2009)

Ah ok - aber mehr hinterläßt meine Version nicht an Spuren.

bye Saxony


----------



## tuxedo (16. Jan 2009)

Dann scheinen sie's geändert zu haben.

- Alex


----------

