Skip to content

Commit

Permalink
[JENKINS-16341] INSANE testing shows that a RenderOnDemandClosure bin…
Browse files Browse the repository at this point in the history
…ding adds about 380k, with or wothout 08efdf5.(cherry picked from commit f178f0c)

Conflicts:
	test/src/main/java/org/jvnet/hudson/test/MemoryAssert.java
	test/src/test/java/lib/layout/RenderOnDemandTest.java
  • Loading branch information
jglick authored and vjuranek committed Jan 26, 2013
1 parent 47d6811 commit 5fe6280
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 0 deletions.
131 changes: 131 additions & 0 deletions test/src/main/java/org/jvnet/hudson/test/MemoryAssert.java
@@ -0,0 +1,131 @@
/*
* The MIT License
*
* Copyright 2012 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jvnet.hudson.test;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import jenkins.model.Jenkins;
import static org.junit.Assert.*;
import org.netbeans.insane.scanner.CountingVisitor;
import org.netbeans.insane.scanner.Filter;
import org.netbeans.insane.scanner.ScannerUtils;

/**
* Static utility methods for verifying heap memory usage.
* Uses the <a href="http://performance.netbeans.org/insane/">INSANE library</a>
* to traverse the heap from within your test.
* <p>Object sizes are in an idealized JVM in which pointers are 4 bytes
* (realistic even for modern 64-bit JVMs in which {@code -XX:+UseCompressedOops} is the default)
* but objects are aligned on 8-byte boundaries (so dropping an {@code int} field does not always save memory).
* <p>{@code import static org.jvnet.hudson.test.MemoryAssert.*;} to use.
*/
public class MemoryAssert {

private MemoryAssert() {}

/**
* Verifies that an object and its transitive reference graph occupy at most a predetermined amount of memory.
* The referents of {@link WeakReference} and the like are ignored.
* <p>To use, run your test for the first time with {@code max} of {@code 0};
* when it fails, use the reported actual size as your assertion maximum.
* When improving memory usage, run again with {@code 0} and tighten the test to both demonstrate
* your improvement quantitatively and prevent regressions.
* @param o the object to measure
* @param max the maximum desired memory usage (in bytes)
*/
public static void assertHeapUsage(Object o, int max) throws Exception {
// XXX could use ScannerUtils.recursiveSizeOf here
CountingVisitor v = new CountingVisitor();
ScannerUtils.scan(ScannerUtils.skipNonStrongReferencesFilter(), v, Collections.singleton(o), false);
int memoryUsage = v.getTotalSize();
assertTrue(o + " consumes " + memoryUsage + " bytes of heap, " + (memoryUsage - max) + " over the limit of " + max, memoryUsage <= max);
}

/**
* @see #increasedMemory
* @since 1.500
*/
public static final class HistogramElement implements Comparable<HistogramElement> {
public final String className;
public final int instanceCount;
public final int byteSize;
HistogramElement(String className, int instanceCount, int byteSize) {
this.className = className;
this.instanceCount = instanceCount;
this.byteSize = byteSize;
}
@Override public int compareTo(HistogramElement o) {
int r = o.byteSize - byteSize;
return r != 0 ? r : className.compareTo(o.className);
}
@Override public boolean equals(Object obj) {
if (!(obj instanceof HistogramElement)) {
return false;
}
HistogramElement o = (HistogramElement) obj;
return o.className.equals(className);
}
@Override public int hashCode() {
return className.hashCode();
}
}

/**
* Counts how much more memory is held in Jenkins by doing some operation.
* @param callable an action
* @param filters things to exclude
* @return a histogram of the heap delta after running the operation
* @since 1.500
*/
public static List<HistogramElement> increasedMemory(Callable<Void> callable, Filter... filters) throws Exception {
Filter f = ScannerUtils.skipNonStrongReferencesFilter();
if (filters.length > 0) {
Filter[] fs = new Filter[filters.length + 1];
fs[0] = f;
System.arraycopy(filters, 0, fs, 1, filters.length);
f = ScannerUtils.compoundFilter(fs);
}
CountingVisitor v1 = new CountingVisitor();
ScannerUtils.scan(f, v1, Collections.singleton(Jenkins.getInstance()), false);
Set<Class<?>> old = v1.getClasses();
callable.call();
CountingVisitor v2 = new CountingVisitor();
ScannerUtils.scan(f, v2, Collections.singleton(Jenkins.getInstance()), false);
List<HistogramElement> elements = new ArrayList<HistogramElement>();
for (Class<?> c : v2.getClasses()) {
int delta = v2.getCountForClass(c) - (old.contains(c) ? v1.getCountForClass(c) : 0);
if (delta > 0) {
elements.add(new HistogramElement(c.getName(), delta, v2.getSizeForClass(c) - (old.contains(c) ? v1.getSizeForClass(c) : 0)));
}
}
Collections.sort(elements);
return elements;
}

}
43 changes: 43 additions & 0 deletions test/src/test/java/lib/layout/RenderOnDemandTest.java
Expand Up @@ -46,4 +46,47 @@ public void testBehaviour() throws Exception {
ScriptResult r = p.executeJavaScript("var r=document.getElementsBySelector('DIV.a'); r[0].innerHTML+r[1].innerHTML+r[2].innerHTML");
assertEquals("AlphaBravoCharlie",r.getJavaScriptResult().toString());
}

/*
public void testMemoryConsumption() throws Exception {
createWebClient().goTo("self/testBehaviour"); // prime caches
int total = 0;
for (MemoryAssert.HistogramElement element : MemoryAssert.increasedMemory(new Callable<Void>() {
@Override public Void call() throws Exception {
createWebClient().goTo("self/testBehaviour");
return null;
}
}, new Filter() {
@Override public boolean accept(Object obj, Object referredFrom, Field reference) {
return !obj.getClass().getName().contains("htmlunit");
}
})) {
total += element.byteSize;
System.out.println(element.className + " ×" + element.instanceCount + ": " + element.byteSize);
}
System.out.println("total: " + total);
}
*/

/**
* Makes sure that scripts get evaluated.
*/
public void testScript() throws Exception {
HtmlPage p = createWebClient().goTo("self/testScript");
assertNull(p.getElementById("loaded"));

p.getElementById("button").click();
// all AJAX calls complete before the above method returns

assertNotNull(p.getElementById("loaded"));
ScriptResult r = p.executeJavaScript("x");
assertEquals("xxx",r.getJavaScriptResult().toString());

r = p.executeJavaScript("y");
assertEquals("yyy",r.getJavaScriptResult().toString());

// if you want to test this in the browser
System.out.println("Try http://localhost:"+localPort+"/self/testScript");
interactiveBreak();
}
}

0 comments on commit 5fe6280

Please sign in to comment.