Ermittlung des Physikalischen Hauptspeichers

Status
Nicht offen für weitere Antworten.

xquadrat

Mitglied
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

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

hdi

Top Contributor
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

Mitglied
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

Aktives Mitglied
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.

Code:
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

Mitglied
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?
 

mahe

Aktives Mitglied
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

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

tuxedo

Gast
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

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

Code:
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.

Code:
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;

Code:
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

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


Kernel32.java
Code:
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
Code:
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
Code:
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
 
T

tuxedo

Gast
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

Top Contributor
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
 
T

tuxedo

Gast
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

Top Contributor
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
 
T

tuxedo

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

- Alex
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben