0

I am attempting to load a java agent to use java.lang.instrument.Instrumentation to measure the size of an object. Here is my Java:

package com.example.memory.usage;

import java.lang.instrument.Instrumentation;
import java.util.*;


public class MemoryUsage {

    public static void main(String[] args) throws Exception {

        Random random = new Random();

        Set<Integer> integerSet = new HashSet<>();

        for(int i = 0; i < pixels; i++) {
            if(random.nextDouble() < 0.20) {
                integerSet.add(i);
            }
        }

        System.out.println(ObjectSizeFetcher.getObjectSize(integerSet));
    }

    public static class ObjectSizeFetcher {
        private static Instrumentation instrumentation;

        public static void premain(String args, Instrumentation inst) {
            System.out.println("Premain ... " + inst);
            instrumentation = inst;
        }

        public static long getObjectSize(Object o) {
            return instrumentation.getObjectSize(o);
        }
    }
}

As this is a Maven project, here is my pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>memory-usage</artifactId>
<version>1.0-SNAPSHOT</version>

<build>
    <finalName>memory-usage</finalName>
    <plugins>
        <!-- Compiler -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.0</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>

        <!-- Jar -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.example.memory.usage.MemoryUsage</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>

        <!-- Assembly -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>attached</goal>
                    </goals>
                    <phase>package</phase>
                    <configuration>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                        <archive>
                            <manifest>
                                <mainClass>com.example.memory.usage.MemoryUsage</mainClass>
                            </manifest>
                            <manifestEntries>
                                <Premain-Class>java.lang.instrument.Instrumentation</Premain-Class>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
</project>

No I do not know what to do next. Please help. I ran

$ mvn clean package

and then

$ java -jar target/memory-usage-jar-with-dependencies.jar

which gives me this exception:

Exception in thread "main" java.lang.NullPointerException
    at com.example.memory.usage.MemoryUsage$ObjectSizeFetcher.getObjectSize(MemoryUsage.java:42)
    at com.example.memory.usage.MemoryUsage.main(MemoryUsage.java:29)

What should I do to run this?

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
David Williams
  • 8,388
  • 23
  • 83
  • 171

2 Answers2

1

The launch mechanism is considered JVM/JRE specific. However, the package-summary of the instrumentation API tells you:

Command-Line Interface

An implementation is not required to provide a way to start agents from the command-line interface. On implementations that do provide a way to start agents from the command-line interface, an agent is started by adding this option to the command-line:

-javaagent:jarpath[=options]

jarpath is the path to the agent JAR file. options is the agent options. This switch may be used multiple times on the same command-line, thus creating multiple agents. More than one agent may use the same jarpath. An agent JAR file must conform to the JAR file specification.


The launcher documentation also provides a hint:

-javaagent:jarpath[=options]

Loads the specified Java programming language agent. For more information about instrumenting Java applications, see the java.lang.instrument package description in the Java API documentation at http://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html


But note that you have to specify the right agent class in your manifest. As far as I can see you’re specifying java.lang.instrument.Instrumentation as premain class which is nonsense. In your code, the class containing the premain method is com.example.memory.usage.MemoryUsage$ObjectSizeFetcher and hence you should specify that class…

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Does this get me closer to actually analyzing the size of objects? – David Williams Mar 24 '15 at 04:01
  • Sure it does. It tells you how to ensure that you `premain` method gets called with a JVM-provided `Instrumentation` instance before `main` gets called. So your field will not be `null` anymore. That’s what your question is about. But if you abandoned the idea of implementing an agent by yourself, just run `jvisualvm` (which is even included in the jdk) and let it do the work… – Holger Mar 24 '15 at 10:27
0

You call getObjectSize statically. The variable instrumentation needs to be initialized (i.e. instrumentation = new Instrumentation();)

Dando18
  • 622
  • 9
  • 22
  • New Instrumentation requires a large number of overrides. Plus its implied thats its passed in `public static void premain(String args, Instrumentation inst)` – David Williams Feb 25 '15 at 01:52
  • @DavidWilliams I don't think premain is getting called. – Dando18 Feb 25 '15 at 02:27
  • I think you're right, I put an `System.out.println there and didn't see anything. But this is following online examples. If I create a new one the method getObjectSize is stubbed to return 0. Clearly I need an existing implementation, right? – David Williams Feb 25 '15 at 20:30
  • It appears to be possible to do this from the command line via javaagent. Do you know hoe to do that, and where I might download the right agent to size an object? – David Williams Feb 26 '15 at 03:06