Java erhält verfügbaren Speicher

77

Gibt es eine gute Möglichkeit, den verbleibenden Speicher zur Laufzeit für die JVM verfügbar zu machen? Der Anwendungsfall hierfür wäre, Webdienste zu haben, die ordnungsgemäß ausfallen, wenn sie sich ihren Speichergrenzen nähern, indem sie neue Verbindungen mit der netten Fehlermeldung "Zu viele Benutzer verwenden dies, versuchen Sie es später erneut" ablehnen, anstatt abrupt mit einem OutOfMemory-Fehler zu sterben .

Beachten Sie, dass dies nichts mit der vorherigen Berechnung / Schätzung der Kosten für jedes Objekt zu tun hat. Im Prinzip könnte ich anhand dieser Schätzung abschätzen, wie viel Speicher meine Objekte belegen und neue Verbindungen ablehnen, aber das scheint irgendwie hackig / fragil zu sein.

Li Haoyi
quelle

Antworten:

96

Dieses Beispiel von William Brendel kann von Nutzen sein.

BEARBEITEN: Ich habe dieses Beispiel ursprünglich bereitgestellt (Link zu William Brendels Antwort zu einem anderen Thema). Der Ersteller dieses Themas (Steve M) wollte eine plattformübergreifende Java-Anwendung erstellen. Insbesondere versuchte der Benutzer, ein Mittel zu finden, mit dem die Ressourcen des laufenden Computers (Speicherplatz, CPU- und Speichernutzung) bewertet werden können.

Dies ist eine Inline-Abschrift der Antwort in diesem Thema. Zu diesem Thema wurde jedoch darauf hingewiesen, dass dies nicht die ideale Lösung ist, obwohl meine Antwort als akzeptiert markiert ist.

public class Main {
  public static void main(String[] args) {
  /* Total number of processors or cores available to the JVM */
  System.out.println("Available processors (cores): " + 
  Runtime.getRuntime().availableProcessors());

  /* Total amount of free memory available to the JVM */
  System.out.println("Free memory (bytes): " + 
  Runtime.getRuntime().freeMemory());

  /* This will return Long.MAX_VALUE if there is no preset limit */
  long maxMemory = Runtime.getRuntime().maxMemory();
  /* Maximum amount of memory the JVM will attempt to use */
  System.out.println("Maximum memory (bytes): " + 
  (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));

  /* Total memory currently in use by the JVM */
  System.out.println("Total memory (bytes): " + 
  Runtime.getRuntime().totalMemory());

  /* Get a list of all filesystem roots on this system */
  File[] roots = File.listRoots();

  /* For each filesystem root, print some info */
  for (File root : roots) {
    System.out.println("File system root: " + root.getAbsolutePath());
    System.out.println("Total space (bytes): " + root.getTotalSpace());
    System.out.println("Free space (bytes): " + root.getFreeSpace());
    System.out.println("Usable space (bytes): " + root.getUsableSpace());
  }
 }
}

Benutzer Christian Fries weist darauf hin, dass es falsch ist anzunehmen, dass Runtime.getRuntime().freeMemory()Sie die Menge an Speicher erhalten, die zugewiesen werden kann, bis ein Fehler aufgrund von Speichermangel auftritt.

Aus der Dokumentation geht hervor , dass die Unterschrift Runtime.getRuntime().freeMemory()als solche zurückgegeben wird:

Rückgabe: Eine Annäherung an die derzeit für zukünftige zugewiesene Objekte verfügbare Gesamtmenge an Speicher, gemessen in Byte.

Benutzer Christian Fries behauptet jedoch, dass diese Funktion möglicherweise falsch interpretiert wird. Er behauptet, dass die ungefähre Speichermenge, die zugewiesen werden kann, bis ein Fehler aufgrund von Speichermangel auftritt (der freie Speicher), wahrscheinlich gegeben ist durch:

long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;

Mit allocatedMemorygegeben von:

long allocatedMemory = 
  (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());

Der Schlüssel hier ist eine Diskrepanz zwischen dem Konzept des freien Speichers. Eine Sache ist der Speicher, den das Betriebssystem der Java Virtual Machine zur Verfügung stellt. Eine andere ist die Gesamtmenge an Bytes, die die Blöcke von Speicherblöcken umfasst, die tatsächlich von der Java Virtual Machine selbst verwendet werden.

In Anbetracht der Tatsache, dass der für Java-Anwendungen bereitgestellte Speicher von der Java Virtual Machine in Blöcken verwaltet wird, stimmt die für die Java Virtual Machine verfügbare Menge an freiem Speicher möglicherweise nicht genau mit dem für eine Java-Anwendung verfügbaren Speicher überein.

Insbesondere bezeichnet Christian Fries die Verwendung der Flags -mxoder -Xmx, um die maximale Speichermenge festzulegen, die für die Java Virtual Machine verfügbar ist. Er stellt folgende Funktionsunterschiede fest:

/* Returns the maximum amount of memory available to 
   the Java Virtual Machine set by the '-mx' or '-Xmx' flags. */
Runtime.getRuntime().maxMemory();

/* Returns the total memory allocated from the system 
   (which can at most reach the maximum memory value 
   returned by the previous function). */
Runtime.getRuntime().totalMemory();

/* Returns the free memory *within* the total memory 
   returned by the previous function. */
Runtime.getRuntime().freeMemory();

Christian schließt seine Antwort mit der Feststellung, dass Runtime.getRuntime().freeMemory()tatsächlich das zurückgegeben wird, was man als vermutliches freies Gedächtnis bezeichnen kann; Selbst wenn eine zukünftige Speicherzuweisung den von dieser Funktion zurückgegebenen Wert nicht überschreitet, java.lang.OutOfMemoryErrorkann möglicherweise noch ein vom Hostsystem zugewiesener Speicherblock erzeugt werden, wenn die Java Virtual Machine noch nicht den tatsächlichen vom Hostsystem zugewiesenen Speicherblock empfangen hat .

Letztendlich hängt die richtige Methode in unterschiedlichem Maße von den Besonderheiten Ihrer Anwendung ab.

Ich biete einen weiteren Link an, der nützlich sein kann. Diese Frage wurde vom Benutzer Richard Dormand gestellt und von stone333 zur Bestimmung der verwendeten Standard-Java-Heap-Größe beantwortet .

Blitzkoder
quelle
1
@LeonardoGaldioli was meinst du? Die aufgeführten Methoden werden nicht schnell genug ausgeführt, um das zu erreichen, was Sie benötigen. Was ist der Kontext?
Blitzkoder
1
Ich möchte nur wissen, ob diese Methoden die Anwendung erheblich verlangsamen
Leonardo Galdioli
2
@LeonardoGaldioli weiß ich zur Zeit nicht. Warum versuchen Sie nicht, dieses Beispiel auszuführen mit: long startTime = System.currentTimeMillis (); / * ORIGINAL MEMORY CODE HIER EINFÜGEN * / long stopTime = System.currentTimeMillis (); long elapsedTime = stopTime - startTime;
Blitzkoder
3
Die Frage war, welche Anzeige für die Speichermenge verwendet werden soll, bis ein Fehler aufgrund von Speichermangel auftritt. Beachten Sie, dass `Runtime.getRuntime (). FreeMemory ());` NICHT die Antwort auf diese Frage ist. - (Ich habe die Frage genauer beantwortet).
Christian Fries
72

Hinweis: Alle bisherigen Antworten, auch die akzeptierte, scheinen die Frage zu beantworten, indem sie sagen, dass Runtime.getRuntime().freeMemory()Sie die Menge an Speicher erhalten, die zugewiesen werden kann, bis ein Fehler aufgrund von Speichermangel auftritt. Allerdings: das ist falsch.

Die ungefähre Speichermenge, die zugewiesen werden kann, bis ein Fehler aufgrund von Speichermangel auftritt, dh der freie Speicher ist wahrscheinlich

long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;

wo

long allocatedMemory      = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());

Erläuterung: Wenn Sie die JVM über einen Parameter -mx (oder -Xmx) starten, geben Sie die maximale Menge an, die der JVM zur Verfügung steht. Runtime.getRuntime().maxMemory()wird Ihnen diesen Betrag geben. Aus dieser Menge an Systemspeicher weist die JVM Speicher in Blöcken zu, beispielsweise Blöcke von 64 MB. Zu Beginn weist die JVM nur einen solchen Block aus dem System zu und nicht den vollen Betrag. Runtime.getRuntime().totalMemory()gibt den vom System zugewiesenen Gesamtspeicher an, während Runtime.getRuntime().freeMemory()Sie den freien Speicher innerhalb des zugewiesenen Gesamtspeichers erhalten.

Daher:

long definitelyFreeMemory = Runtime.getRuntime().freeMemory();

ist der freie Speicher, der bereits von der JVM reserviert wurde, aber wahrscheinlich nur eine kleine Menge. Und Sie werden wahrscheinlich bekommen presumableFreeMemory. Natürlich kann es zu einer Ausnahme wegen Speichermangels kommen, selbst wenn Sie versucht haben, einen Betrag zuzuweisen, der kleiner als ist presumableFreeMemory. Dies kann passieren, wenn die JVM nicht den nächsten Speicherblock vom System erhält. Auf den meisten Systemen wird dies jedoch niemals passieren und das System wird eher mit dem Austausch beginnen - eine Situation, die Sie gerne vermeiden. Zur ursprünglichen Frage: Wenn -mx auf einen vernünftigen Wert eingestellt ist, presumableFreeMemoryist dies ein guter Indikator für den freien Speicher.

Christian Fries
quelle
1
@ Vasili Ich nehme an, ich leide unter Doppel-Ismus ... arbeite zu viel mit Doppel. Vielen Dank für den Hinweis, ich habe die Antwort bearbeitet.
Christian Fries
Die Dokumente für maxMemory()state: "Wenn es keine inhärente Grenze gibt, wird der Wert Long.MAX_VALUE zurückgegeben." Es lohnt sich wahrscheinlich, dies zu überprüfen, da die "kostenlose" Nummer, die es Ihnen geben wird, Unsinn sein wird.
mrec
Ich habe dieses voll funktionsfähige Beispiel für eine Anwendung erstellt, die Speicherinformationen basierend auf den Erklärungen von Christian in seiner Antwort erhält: github.com/luciopaiva/java-simple-system-info
Lucio Paiva
8

Zusätzlich zur Verwendung der Runtime-Methoden können Sie mithilfe von zusätzliche Speicherinformationen abrufen

MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heap = memBean.getHeapMemoryUsage();
MemoryUsage nonheap = memBean.getNonHeapMemoryUsage();

Jede MemoryUsage bietet Init- , Used- , Commit- und Max-Werte. Dies kann hilfreich sein, wenn Sie einen Speichermonitor-Thread erstellen, der den Speicher abfragt und protokolliert und Ihnen einen Verlauf der Speichernutzung im Laufe der Zeit liefert. Manchmal ist es hilfreich, die Speichernutzung im Laufe der Zeit zu sehen, die zu den Fehlern führt.

Wenn Sie dies wirklich auf die Spitze treiben möchten, erstellen Sie einen Heap-Dump-Thread. Überwachen Sie Ihre Speichernutzung im Laufe der Zeit und gehen Sie wie folgt vor, wenn bestimmte Schwellenwerte überschritten werden (dies funktioniert unter JBoss 5.0 - Ihr Kilometerstand kann variieren):

// init code
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean diagBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class); 

// loop code
// add some code to figure if we have passed some threshold, then

File heapFile = new File(outputDir, "heap-" + curThreshold + ".hprof");
log.info("Dumping heap file " + heapFile.getAbsolutePath());
diagBean.dumpHeap(heapFile.getAbsolutePath(), true);

Später können Sie diese Heap-Dump-Dateien mit dem Eclipse Memory Analyzer oder ähnlichen Tools überprüfen, um nach Speicherlecks usw. zu suchen.

Guido Simone
quelle
6

Neben der anderen Antwort möchte ich darauf hinweisen, dass dies nicht unbedingt eine gute Idee ist, da Ihre App möglicherweise einen Cache enthält, der SoftReferences verwendet .

Ein solcher Cache würde Speicher freigeben, sobald die JVM ihre Speichergrenzen erreicht. Das Zuweisen von Speicher, selbst wenn nicht genügend freier Speicher vorhanden ist, würde zuerst dazu führen, dass Speicher durch die weichen Referenzen freigegeben wird, und ihn für die Zuweisung verfügbar machen.

JB Nizet
quelle
6

OSHIFügen Sie mithilfe der folgenden Maven-Abhängigkeit hinzu, um den für das Betriebssystem verfügbaren Speicher zu erhalten :

<dependency>
    <groupId>com.github.oshi</groupId>
    <artifactId>oshi-core</artifactId>
    <version>LATEST</version>
</dependency>

Verwenden Sie dann in Java den folgenden Code:

SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
long availableMemory = hal.getMemory().getAvailable();
Oleksii Kyslytsyn
quelle
Dadurch erhält ich den verfügbaren betriebssystemweiten Speicher, nach dem ich gesucht habe. :)
BullyWiiPlaza
0

Sie können jederzeit anrufen Runtime.getRuntime().freeMemory().

Die andere Hälfte des Problems, die Kosten für Objekte zu ermitteln, erscheint mir problematischer.

Ich denke, eine bessere Lösung wäre es, herauszufinden, wie Sie Ihre Webdienste gruppieren und skalieren können, damit sie 150% der Nennlast ordnungsgemäß akzeptieren können, ohne neue Verbindungen zu verweigern. Klingt so, als würde eine Größenübung eine bessere Lösung bringen als ein Code-Hack.

Duffymo
quelle
0
long freeMemory = Runtime.getRuntime().freeMemory();
Ryan Anderson
quelle
0

Runtime.getRuntime().freeMemory()ist eine Möglichkeit, in diesem Moment zur Laufzeit freien Speicher für JVM zu erhalten. Ist es ein guter Weg (oder nicht), hängt es vollständig von Ihrer Anwendung ab.

kosa
quelle
0

Um die Gesamtzahl , den verwendeten und den freien Speicher der physischen Maschine abzurufen , können Sie diese auch mithilfe von Java Runtime.exec()mit dem Befehlsargument abrufen free -mund dann wie folgt interpretieren:

Runtime runtime = Runtime.getRuntime();

BufferedReader br = new BufferedReader(
    new InputStreamReader(runtime.exec("free -m").getInputStream()));

String line;
String memLine = "";
int index = 0;
while ((line = br.readLine()) != null) {
  if (index == 1) {
    memLine = line;
  }
  index++;
}
//                  total        used        free      shared  buff/cache   available
//    Mem:          15933        3153        9683         310        3097       12148
//    Swap:          3814           0        3814

List<String> memInfoList = Arrays.asList(memLine.split("\\s+"));
int totalSystemMemory = Integer.parseInt(memInfoList.get(1));
int totalSystemUsedMemory = Integer.parseInt(memInfoList.get(2));
int totalSystemFreeMemory = Integer.parseInt(memInfoList.get(3));

System.out.println("Total system memory in mb: " + totalSystemMemory);
System.out.println("Total system used memory in mb: " + totalSystemUsedMemory);
System.out.println("Total system free memory in mb: "   + totalSystemFreeMemory);
krishna Prasad
quelle