Schätzen von Java-Objektgrößen mit Instrumentierung

Die meisten Java-Entwickler mit C / C ++ - Hintergrund haben sich wahrscheinlich einmal ein Java-Äquivalent von sizeof () gewünscht. Obwohl Java kein echtes sizeof () -Äquivalent aufweist, kann die mit J2SE5 eingeführte Instrumentation-Schnittstelle verwendet werden, um über die Methode getObjectSize (Object) eine Schätzung der Größe eines bestimmten Objekts zu erhalten. Obwohl dieser Ansatz nur das Objekt unterstützt, das selbst betrachtet wird, und die Größe der Objekte, auf die er verweist, nicht berücksichtigt, kann Code erstellt werden, um diese Verweise zu durchlaufen und eine geschätzte Gesamtgröße zu berechnen.

Die Instrumental-Oberfläche bietet verschiedene Methoden, aber der Schwerpunkt dieses Beitrags liegt auf der Methode getObjectSize (Object). Die Javadoc-Dokumentation dieser Methode beschreibt die Methode:

Gibt eine implementierungsspezifische Annäherung an die vom angegebenen Objekt verbrauchte Speichermenge zurück. Das Ergebnis kann einen Teil oder den gesamten Overhead des Objekts enthalten und ist daher für den Vergleich innerhalb einer Implementierung nützlich, jedoch nicht zwischen Implementierungen. Die Schätzung kann sich während eines einzelnen Aufrufs der JVM ändern.

Diese Beschreibung gibt Auskunft darüber, was die Methode tut (liefert eine "implementierungsspezifische Annäherung" an die Größe des angegebenen Objekts), ihre mögliche Einbeziehung von Overhead in die angenäherte Größe und ihre möglicherweise unterschiedlichen Werte während eines einzelnen JVM-Aufrufs.

Es ist ziemlich offensichtlich, dass man Instrumentation.getObjectSize(Object)ein Objekt aufrufen kann , um seine ungefähre Größe zu erhalten, aber wie greift man überhaupt auf eine Instanz von zu Instrumentation? Die Paketdokumentation für das Paket java.lang.instrument enthält die Antwort (und ist ein Beispiel für eine effektive Beschreibung des Javadoc-Pakets).

In der Dokumentation auf Paketebene für das Paket java.lang.instrument werden zwei Möglichkeiten beschrieben, wie eine Implementierung die Verwendung von JVM-Instrumenten ermöglichen kann. Der erste Ansatz (und der in diesem Beitrag hervorgehobene) besteht darin, einen Instrumentierungsagenten über die Befehlszeile anzugeben. Der zweite Ansatz besteht darin, einen Instrumentierungsagenten mit einer bereits ausgeführten JVM zu verwenden. In der Paketdokumentation wird ein allgemeiner Überblick über die Verwendung der einzelnen Ansätze erläutert. Bei jedem Ansatz ist ein bestimmter Eintrag in der Manifestdatei der Agenten-JAR erforderlich, um die Agentenklasse anzugeben: Premain-Classfür den Befehlszeilenansatz und Agent-Classfür den Post-JVM-Startansatz. Für die Agentenklasse muss in beiden Fällen eine bestimmte Methode implementiert werden: premainfür den Befehlszeilenstart oder für den agentmainPost-JVM-Start.

Die nächste Codeliste enthält den Java-Code für den Instrumentierungsagenten. Die Klasse enthält sowohl eine premainMethode (Befehlszeilenagent) als auch eine Methode agentmain(Post-JVM-Startagent). premainIn diesem Beitrag wird jedoch nur die Methode demonstriert.

package dustin.examples; import static java.lang.System.out; import java.lang.instrument.Instrumentation; /** * Simple example of an Instrumentation Agent adapted from blog post * "Instrumentation: querying the memory usage of a Java object" * (//www.javamex.com/tutorials/memory/instrumentation.shtml). */ public class InstrumentationAgent { /** Handle to instance of Instrumentation interface. */ private static volatile Instrumentation globalInstrumentation; /** * Implementation of the overloaded premain method that is first invoked by * the JVM during use of instrumentation. * * @param agentArgs Agent options provided as a single String. * @param inst Handle to instance of Instrumentation provided on command-line. */ public static void premain(final String agentArgs, final Instrumentation inst) { out.println("premain..."); globalInstrumentation = inst; } /** * Implementation of the overloaded agentmain method that is invoked for * accessing instrumentation of an already running JVM. * * @param agentArgs Agent options provided as a single String. * @param inst Handle to instance of Instrumentation provided on command-line. */ public static void agentmain(String agentArgs, Instrumentation inst) { out.println("agentmain..."); globalInstrumentation = inst; } /** * Provide the memory size of the provided object (but not it's components). * * @param object Object whose memory size is desired. * @return The size of the provided object, not counting its components * (described in Instrumentation.getObjectSize(Object)'s Javadoc as "an * implementation-specific approximation of the amount of storage consumed * by the specified object"). * @throws IllegalStateException Thrown if my Instrumentation is null. */ public static long getObjectSize(final Object object) { if (globalInstrumentation == null) { throw new IllegalStateException("Agent not initialized."); } return globalInstrumentation.getObjectSize(object); } } 

Die obige Agentenklasse macht eine statisch verfügbare Methode für den Zugriff verfügbar Instrumentation.getObjectSize(Object). Die nächste Codeliste zeigt eine einfache 'Anwendung', die davon Gebrauch macht.

package dustin.examples; import static java.lang.System.out; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Calendar; import java.util.List; /** * Build up some sample objects and throw them at the Instrumentation example. * * Might run this class as shown next: * java -javaagent:dist\agent.jar -cp dist\agent.jar dustin.examples.InstrumentSampleObjects * * @author Dustin */ public class InstrumentSampleObjects { public enum Color { RED, WHITE, YELLOW } /** * Print basic details including size of provided object to standard output. * * @param object Object whose value and size are to be printed to standard * output. */ public static void printInstrumentationSize(final Object object) { out.println( "Object of type '" + object.getClass() + "' has size of " + InstrumentationAgent.getObjectSize(object) + " bytes."); } /** * Main executable function. * * @param arguments Command-line arguments; none expected. */ public static void main(final String[] arguments) { final StringBuilder sb = new StringBuilder(1000); final boolean falseBoolean = false; final int zeroInt = 0; final double zeroDouble = 0.0; final Long zeroLong = 0L; final long zeroLongP = 0L; final Long maxLong = Long.MAX_VALUE; final Long minLong = Long.MIN_VALUE; final long maxLongP = Long.MAX_VALUE; final long minLongP = Long.MIN_VALUE; final String emptyString = ""; final String string = "ToBeOrNotToBeThatIsTheQuestion"; final String[] strings = {emptyString, string, "Dustin"}; final String[] moreStrings = new String[1000]; final List someStrings = new ArrayList(); final EmptyClass empty = new EmptyClass(); final BigDecimal bd = new BigDecimal("999999999999999999.99999999"); final Calendar calendar = Calendar.getInstance(); printInstrumentationSize(sb); printInstrumentationSize(falseBoolean); printInstrumentationSize(zeroInt); printInstrumentationSize(zeroDouble); printInstrumentationSize(zeroLong); printInstrumentationSize(zeroLongP); printInstrumentationSize(maxLong); printInstrumentationSize(maxLongP); printInstrumentationSize(minLong); printInstrumentationSize(minLongP); printInstrumentationSize(maxLong); printInstrumentationSize(maxLongP); printInstrumentationSize(emptyString); printInstrumentationSize(string); printInstrumentationSize(strings); printInstrumentationSize(moreStrings); printInstrumentationSize(someStrings); printInstrumentationSize(empty); printInstrumentationSize(bd); printInstrumentationSize(calendar); printInstrumentationSize(Color.WHITE); } } 

Um den Instrumentierungsagenten über den Befehlszeilenstart zu verwenden, muss sichergestellt werden, dass eine einfache Metadatei in der Agenten-JAR enthalten ist. In diesem Fall könnte es so aussehen, wie es in der nächsten Codeliste für die Agentenklasse folgt ( dustin.examples.InstrumentationAgent). Obwohl ich nur den Premain-classEintrag für den Befehlszeilenstart des Agenten benötige , habe ich Agent-classals Beispiel für die Verwendung des Post-JVM-Startagenten angegeben. Es schadet nichts, wenn beide vorhanden sind, genauso wie es nichts schadet, wenn beide premainund agentmainMethoden in der Objektklasse definiert sind. Es gibt vorgeschriebene Regeln, für die zuerst versucht wird, basierend auf der Art des verwendeten Mittels.

Premain-class: dustin.examples.InstrumentationAgent Agent-class: dustin.examples.InstrumentationAgent 

Um diese Manifestdatei in der JAR abzulegen, könnte ich die jar cmfmit dem Namen der Manifestdatei und den Java-Klassen verwenden, die in der JAR archiviert werden sollen. Es ist jedoch wohl einfacher, mit Ant umzugehen, und es wird sicherlich bevorzugt, dies wiederholt zu tun. Als nächstes wird eine einfache Verwendung der Aufgabe Ant jar mit dem Unterelement manifest gezeigt.

Mit der erstellten JAR kann ich sie problemlos mit dem Java-Launcher ausführen und den Java-Agenten ( -javaagent) angeben :

java -javaagent:dist\Instrumentation.jar -cp Instrumentation.jar dustin.examples.InstrumentSampleObjects 

Der nächste Screenshot zeigt die Ausgabe.

Die obige Ausgabe zeigt einige der geschätzten Größen verschiedener Objekte wie BigDecimal, Calendar und andere.

Es gibt mehrere nützliche Ressourcen zum Thema dieses Beitrags. Das java.sizeOf-Projekt ist "ein kleiner Java-Agent, der das in Java 5 eingeführte und unter der GPL-Lizenz veröffentlichte Paket java.lang.Instrument verwendet". Der Instrumentation Memory Counter von Dr. Heinz M. Kabutz bietet ein wesentlich komplexeres Beispiel als mein Beitrag zur Verwendung der Instrumentation-Schnittstelle zur Schätzung von Objektgrößen. Instrumentierung: Das Abfragen der Speichernutzung eines Java-Objekts bietet einen schönen Überblick über diese Schnittstelle und einen Link zum Classmexer-Agenten, einem einfachen Java-Instrumentierungsagenten, der einige praktische Anforderungen zum Messen der Speichernutzung von Java-Objekten in einer Anwendung bietet. "" Die Beiträge Wie viel Speicher verbrauchen die Java-Objekte? und Schätzen der Speichernutzung eines Java-Objekts hängen ebenfalls zusammen.

Originalbeitrag verfügbar unter //marxsoftware.blogspot.com/ (Inspiriert von tatsächlichen Ereignissen)

Diese Geschichte "Schätzen von Java-Objektgrößen mit Instrumentierung" wurde ursprünglich von JavaWorld veröffentlicht.