Commit 7a4f0045 authored by Eric Bruneton's avatar Eric Bruneton

Improve the memory benchmarks by using the JMH framework.

Results:
Benchmark                                          Mode  Cnt        Score      Error  Units
MemoryBenchmark.newClassNode_asm4_0               thrpt    5      196,689 ±  123,711  ops/s
MemoryBenchmark.newClassNode_asm4_0:+memory.used  thrpt    5  2798238,715 ± 3602,377  bytes
MemoryBenchmark.newClassNode_asm5_0               thrpt    5      158,445 ±  111,103  ops/s
MemoryBenchmark.newClassNode_asm5_0:+memory.used  thrpt    5  2693396,799 ± 4985,178  bytes
MemoryBenchmark.newClassNode_asm6_0               thrpt    5      140,053 ±   95,105  ops/s
MemoryBenchmark.newClassNode_asm6_0:+memory.used  thrpt    5  2688716,368 ± 5513,428  bytes
MemoryBenchmark.newClassNode_asm6_1               thrpt    5      165,112 ±   94,423  ops/s
MemoryBenchmark.newClassNode_asm6_1:+memory.used  thrpt    5  2690737,112 ± 4993,252  bytes
MemoryBenchmark.newClass_asm4_0                   thrpt    5      236,938 ±  183,190  ops/s
MemoryBenchmark.newClass_asm4_0:+memory.used      thrpt    5  1823050,423 ± 2051,755  bytes
MemoryBenchmark.newClass_asm5_0                   thrpt    5      266,850 ±    3,303  ops/s
MemoryBenchmark.newClass_asm5_0:+memory.used      thrpt    5  1847376,794 ± 1919,159  bytes
MemoryBenchmark.newClass_asm6_0                   thrpt    5      238,714 ±  197,099  ops/s
MemoryBenchmark.newClass_asm6_0:+memory.used      thrpt    5  1848268,051 ± 2092,874  bytes
MemoryBenchmark.newClass_asm6_1                   thrpt    5      245,291 ±  163,061  ops/s
MemoryBenchmark.newClass_asm6_1:+memory.used      thrpt    5  1844124,244 ± 2064,886  bytes
parent b5e6dc44
Pipeline #537 passed with stage
in 10 minutes and 26 seconds
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm.benchmarks;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
/** A {@link Factory} implemented with the ASM library. */
public class ASMFactory implements Factory {
@Override
public String getVersion() {
for (int i = 6; i >= 4; --i) {
try {
String version = "ASM" + i;
if (Opcodes.class.getField(version) != null) {
return version;
}
} catch (NoSuchFieldException e) {
continue;
}
}
return "";
}
@Override
public Object newClass(final byte[] classFile) {
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
classReader.accept(classWriter, 0);
return classWriter;
}
@Override
public Object newClassNode(final byte[] classFile) {
ClassReader classReader = new ClassReader(classFile);
ClassNode classNode = new ClassNode();
classReader.accept(classNode, 0);
return classNode;
}
}
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm.benchmarks;
/**
* A factory of in-memory Java class file models.
*
* @author Eric Bruneton
*/
public interface Factory {
/** @return the version of this factory. */
String getVersion();
/**
* @param classFile a JVMS ClassFile structure
* @return a "high level" representation of the given class.
*/
Object newClass(final byte[] classFile);
/**
* @param classFile a JVMS ClassFile structure
* @return a tree structure representation of the given class.
*/
Object newClassNode(final byte[] classFile);
}
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm.benchmarks;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
/**
* A benchmark to measure the memory usage of several Java bytecode libraries when reading Java
* classes.
*
* @author Eric Bruneton
*/
@State(Scope.Thread)
public class MemoryBenchmark {
// The directories where the different versions of ASM can be found.
private static final String BUILD_DIR = "/benchmarks/memory/build/";
private static final String ASM4_0 = BUILD_DIR + "asm4.0/";
private static final String ASM5_0 = BUILD_DIR + "asm5.0.1/";
private static final String ASM6_0 = BUILD_DIR + "asm6.0/";
private static final String ASM_CORE_6_1 = "/asm/build/classes/java/main/";
private static final String ASM_TREE_6_1 = "/asm-tree/build/classes/java/main/";
// The fully qualified name of the ASMFactory class.
private static final String ASM_FACTORY = "org.objectweb.asm.benchmarks.ASMFactory";
private Factory asm4_0;
private Factory asm5_0;
private Factory asm6_0;
private Factory asm6_1;
private List<byte[]> classFiles;
/**
* Prepares the benchmark by creating a {@link Factory} for each library to be tested, and by
* loading some test data (i.e. some classes to read).
*
* @throws Exception if an error occurs.
*/
@Setup
public void prepare() throws Exception {
String userDir = System.getProperty("user.dir");
String baseUrl = "file://" + userDir;
asm4_0 = new ASMFactoryFactory(new URL[] {new URL(baseUrl + ASM4_0)}).newAsmFactory();
asm5_0 = new ASMFactoryFactory(new URL[] {new URL(baseUrl + ASM5_0)}).newAsmFactory();
asm6_0 = new ASMFactoryFactory(new URL[] {new URL(baseUrl + ASM6_0)}).newAsmFactory();
asm6_1 =
new ASMFactoryFactory(
new URL[] {new URL(baseUrl + ASM_CORE_6_1), new URL(baseUrl + ASM_TREE_6_1)})
.newAsmFactory();
// Check that the correct versions of ASM have been loaded.
if (!asm4_0.getVersion().equals("ASM4")
|| !asm5_0.getVersion().equals("ASM5")
|| !asm6_0.getVersion().equals("ASM6")
|| !asm6_1.getVersion().equals("ASM6")) {
throw new IllegalStateException();
}
classFiles = new ArrayList<byte[]>();
findClasses(new File(userDir + ASM_CORE_6_1));
findClasses(new File(userDir + ASM_TREE_6_1));
}
private void findClasses(final File directory) throws IOException {
Please register or sign in to reply
for (File file : directory.listFiles()) {
if (file.isDirectory()) {
findClasses(file);
} else if (file.getName().endsWith(".class")) {
classFiles.add(readInputStream(new FileInputStream(file)));
}
}
}
private static byte[] readInputStream(final InputStream inputStream) throws IOException {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[inputStream.available()];
Please register or sign in to reply
int bytesRead;
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, bytesRead);
}
outputStream.flush();
return outputStream.toByteArray();
} finally {
try {
inputStream.close();
} catch (IOException e) {
// Nothing to do.
}
}
}
@Benchmark
public void newClass_asm4_0() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm4_0.newClass(classFile));
}
}
@Benchmark
public void newClass_asm5_0() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm5_0.newClass(classFile));
}
}
@Benchmark
public void newClass_asm6_0() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm6_0.newClass(classFile));
}
}
@Benchmark
public void newClass_asm6_1() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm6_1.newClass(classFile));
}
}
@Benchmark
public void newClassNode_asm4_0() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm4_0.newClassNode(classFile));
}
}
@Benchmark
public void newClassNode_asm5_0() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm5_0.newClassNode(classFile));
}
}
@Benchmark
public void newClassNode_asm6_0() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm6_0.newClassNode(classFile));
}
}
@Benchmark
public void newClassNode_asm6_1() {
for (byte[] classFile : classFiles) {
MemoryProfiler.keepReference(asm6_1.newClassNode(classFile));
}
}
/** A factory of {@link ASMFactory} objects, using a specific version of the ASM library. */
private static class ASMFactoryFactory extends URLClassLoader {
/**
* Constructs an {@link ASMFactoryFactory}.
*
* @param asmDirectories the directories where the ASM library classes can be found.
*/
ASMFactoryFactory(final URL[] asmDirectories) {
super(asmDirectories);
}
/**
* @return a new {@link ASMFactory} instance.
* @throws Exception
*/
public Factory newAsmFactory() throws Exception {
return (Factory) loadClass(ASM_FACTORY).newInstance();
}
protected Class<?> loadClass(final String name, final boolean resolve)
throws ClassNotFoundException {
// Force the loading of the ASMFactory class by this class loader (and not its parent). This
// is needed to make sure that the classes it references (i.e. the ASM library classes) will
// be loaded by this class loader too.
if (name.startsWith(ASM_FACTORY)) {
try {
byte[] classFile =
readInputStream(getResourceAsStream(name.replace('.', '/') + ".class"));
Class<?> c = defineClass(name, classFile, 0, classFile.length);
if (resolve) {
resolveClass(c);
}
return c;
} catch (Exception e) {
throw new ClassNotFoundException(name, e);
}
}
// Look for the specified class *first* in asmDirectories, *then* using the parent class
// loader. This is the reverse of the default lookup order, and is necessary to make sure we
// load the correct version of ASM (the parent class loader may have an ASM version in its
// class path, because some components of the JMH framework depend on ASM).
try {
Class<?> c = findClass(name);
if (resolve) {
resolveClass(c);
}
return c;
} catch (ClassNotFoundException e) {
return super.loadClass(name, resolve);
}
}
}
}
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// Based on org.cache2k.benchmark.jmh.ForcedGcMemoryProfiler, with the
// following license:
//
// Copyright (C) 2013 - 2017 headissue GmbH, Munich
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.objectweb.asm.benchmarks;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.IterationParams;
import org.openjdk.jmh.profile.InternalProfiler;
import org.openjdk.jmh.results.AggregationPolicy;
import org.openjdk.jmh.results.IterationResult;
import org.openjdk.jmh.results.Result;
import org.openjdk.jmh.results.ScalarResult;
/**
* An {@link InternalProfiler} to measure the memory allocated per benchmark iteration.
*
* @author Eric Bruneton
*/
public class MemoryProfiler implements InternalProfiler {
private static Object[] references;
private static int referenceCount;
private static long usedMemoryBeforeIteration;
public static void keepReference(final Object reference) {
references[referenceCount++] = reference;
}
@Override
public String getDescription() {
return "Adds used memory to the result.";
}
@Override
public void beforeIteration(
final BenchmarkParams benchmarkParams, final IterationParams iterationParams) {
references = new Object[100000];
referenceCount = 0;
usedMemoryBeforeIteration = getUsedMemory();
}
@Override
public Collection<? extends Result> afterIteration(
final BenchmarkParams benchmarkParams,
final IterationParams iterationParams,
final IterationResult result) {
long usedMemoryAfterIteration = getUsedMemory();
references = null;
long usedMemoryInIteration = usedMemoryAfterIteration - usedMemoryBeforeIteration;
double usedMemoryPerOp =
((double) usedMemoryInIteration) / result.getMetadata().getMeasuredOps();
List<Result> results = new ArrayList<Result>();
results.add(new ScalarResult("+memory.used", usedMemoryPerOp, "bytes", AggregationPolicy.AVG));
return results;
}
/**
* Triggers a gc, waits for completion and returns the used memory. Inspired from cache2k
* ForcedGcMemoryProfiler, itself inspired from the JMH approach.
*
* @see org.cache2k.benchmark.jmh.ForcedGcMemoryProfiler#getUsedMemory()
* @see org.openjdk.jmh.runner.BaseRunner#runSystemGC()
*/
private static long getUsedMemory() {
Please register or sign in to reply
final int MAX_WAIT_MSEC = 20 * 1000;
List<GarbageCollectorMXBean> gcBeans = new ArrayList<GarbageCollectorMXBean>();
for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
long count = gcBean.getCollectionCount();
if (count != -1) {
gcBeans.add(gcBean);
}
}
if (gcBeans.isEmpty()) {
System.err.println("WARNING: MXBeans can not report GC info. Cannot get memory used.");
return -1;
}
long startGcCount = countGc(gcBeans);
long startTimeMillis = System.currentTimeMillis();
System.gc();
while (System.currentTimeMillis() - startTimeMillis < MAX_WAIT_MSEC) {
try {
Thread.sleep(234);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
if (countGc(gcBeans) > startGcCount) {
return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();
}
}
System.err.println("WARNING: System.gc() was invoked but couldn't detect a GC occurring.");
return -1;
}
private static long countGc(final List<GarbageCollectorMXBean> gcBeans) {
long gcCount = 0;
for (GarbageCollectorMXBean bean : gcBeans) {
gcCount += bean.getCollectionCount();
}
return gcCount;
}
}
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package org.objectweb.asm;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
/*
* Created on Nov 30, 2004-2011 as part of ASMPerf by treffer
*/
/**
* Memory performances tests for tree package.
*
* @author treffer
*/
public class ASMMemTest {
public static void main(final String[] args) {
if (args.length < 2) {
System.out.println("java ASMMemTest <jar-file> <number-of-classes>");
System.exit(1);
}
Runtime runtime = Runtime.getRuntime();
memDown(runtime);
System.out.println("Initial memory load: ".concat(memFormat(getUsedMem(runtime))));
LinkedList<byte[]> fileData = new LinkedList<byte[]>();
int limit = Integer.parseInt(args[1]);
try {
long totalSize = 0;
JarInputStream jar = new JarInputStream(new FileInputStream(args[0]));
JarEntry entry = jar.getNextJarEntry();
while (fileData.size() < limit && entry != null) {
String name = entry.getName();
if (name.endsWith(".class")) {
if (entry.getSize() != -1) {
int len = (int) entry.getSize();
byte[] data = new byte[len];
int l = jar.read(data);
assert l == len;
fileData.add(data);
totalSize += data.length;
} else {
System.err.println(
"No jar-entry size given... " + "Unimplemented, jar file not supported");
}
}
entry = jar.getNextJarEntry();
}
System.out.println(
memFormat(totalSize) + " class data, ~" + memFormat(totalSize / limit) + " per class.");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
ArrayList<ClassNode> result = new ArrayList<ClassNode>(fileData.size());
long startmem;
for (int i = 0; i < 10; i++) {
System.out.println("\n> Run ".concat(Integer.toString(i + 1)));
Iterator<byte[]> files = fileData.iterator();
result.clear();
memDown(runtime);
System.out.println("Empty memory load: ".concat(memFormat(startmem = getUsedMem(runtime))));
long time = -System.currentTimeMillis();
while (files.hasNext()) {
byte data[] = files.next();
ClassReader reader = new ClassReader(data);
ClassNode clazz = new ClassNode();
reader.accept(clazz, 0);
result.add(clazz);
}
time += System.currentTimeMillis();
memDown(runtime);
System.out.println("Time: ".concat(timeFormat(time)));
System.out.println("Final memory load: ".concat(memFormat(getUsedMem(runtime))));
System.out.println("ASM memory load: ".concat(memFormat(getUsedMem(runtime) - startmem)));
for (int j = 0; j < limit; j++) {
ClassNode clazz = result.get(j);
List<MethodNode> l = clazz.methods;
for (int k = 0, lim = l.size(); k < lim; k++) {
MethodNode m = l.get(k);
InsnList insn = m.instructions;
if (insn != null) {
insn.clear();
}
}
}
memDown(runtime);
System.out.println(
"ASM memory load (removed method code): "
.concat(memFormat(getUsedMem(runtime) - startmem)));
}
}
public static final long getUsedMem(final Runtime r) {
return r.totalMemory() - r.freeMemory();
}
public static final String timeFormat(final long time) {
int min = (int) (time / (60 * 1000));
int sec = (int) ((time / 1000) % 60);
int msec = (int) (time % 1000);
StringBuilder sbuf = new StringBuilder(30);
if (min > 0) {
sbuf.append(min);
sbuf.append("min ");
}
if (sec > 0 || min > 0) {
sbuf.append(sec);
sbuf.append("s ");
}
if (msec > 0 || sec > 0 || min > 0) {
sbuf.append(msec);
sbuf.append("ms ");
}
sbuf.append('(');
sbuf.append(time);