Created
March 26, 2013 04:34
-
-
Save dimo414/5243162 to your computer and use it in GitHub Desktop.
GC Churn Java application, intended to cause high amounts of garbage collection for benchmarking
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
/** | |
* Runnable which causes a heavy yet stable amount of GC; useful for | |
* benchmarking applications under load. | |
* | |
* Intended to do the following: | |
* * Spend a decent amount (say, 20-50%) of time in GC | |
* * Do an approximately consistent amount of work over time, | |
* and create a similarly consistent amount of work for the GC | |
* * Avoid flooding the heap and triggering a Java heap space error | |
* * Avoid overloading the GC and triggering a GC overhead limit exceeded error | |
*/ | |
public class GCChurn implements Runnable { | |
private static final int DUP_RATE = 75; | |
private final int churn; | |
private final int size; | |
private final int runFor; | |
private StringGenerator sg; | |
/** | |
* Constructs a GCChurn object with reasonable defaults | |
* @param runFor seconds to run for, 0 to run forever | |
*/ | |
public GCChurn(int runFor) { | |
this(25, 500, runFor); | |
} | |
/** | |
* @param churn percent of data to churn each iteration (try 25%) | |
* @param size target data size (try 500) | |
* @param runFor seconds to run for, 0 to run forever | |
*/ | |
public GCChurn(int churn, int size, int runFor) { | |
if(churn < 0 || churn > 100) | |
throw new IllegalArgumentException("Churn expects a valid percent, 0-100, was "+churn); | |
if(size < 0) | |
throw new IllegalArgumentException("Size cannot be negative, was "+size); | |
if(runFor < 0) | |
throw new IllegalArgumentException("RunFor cannot be negative, was "+runFor); | |
this.churn = churn; | |
this.size = size; | |
this.runFor = runFor; | |
sg = new StringGenerator(DUP_RATE,size*10); | |
} | |
@Override | |
/** | |
* Loops over a map of lists, adding and removing elements rapidly | |
* in order to cause GC, for runFor seconds, or until the thread is | |
* terminated. | |
*/ | |
public void run() { | |
HashMap<String,ArrayList<String>> map = new HashMap<>(); | |
long stop = System.currentTimeMillis() + 1000l * runFor; | |
while(runFor == 0 || System.currentTimeMillis() < stop) { | |
churn(map); | |
} | |
} | |
/** | |
* Three steps to churn the garbage collector: | |
* 1. Remove churn% of keys from the map | |
* 2. Remove churn% of strings from the lists in the map | |
* 3. Fill lists back up to size | |
* 4. Fill map back up to size | |
* @param map | |
*/ | |
protected void churn(Map<String,ArrayList<String>> map) { | |
removeKeys(map); | |
churnValues(map); | |
addKeys(map); | |
} | |
/** | |
* Calculates the number of items to churn over, given a size | |
*/ | |
private int churnSize(int size) { | |
return (int)Math.round(size * churn/100.0); | |
} | |
/** | |
* Selects churn% random keys from the map and removes them | |
*/ | |
private void removeKeys(Map<String,ArrayList<String>> map) { | |
List<String> keys = new ArrayList<>(map.keySet()); | |
Collections.shuffle(keys); | |
keys = keys.subList(0, churnSize(keys.size())); | |
for(String key : keys) { | |
map.remove(key); | |
} | |
} | |
/** | |
* Removes churn% items from the front of each list (to force an array copy) | |
* Then adds new strings to the front of the list until list is large enough | |
*/ | |
private void churnValues(Map<String,ArrayList<String>> map) { | |
for(String key : map.keySet()) { | |
ArrayList<String> ls = map.get(key); | |
for(int i = 0; i < churnSize(ls.size()); i++) { | |
ls.remove(0); | |
} | |
while(ls.size() < size) { | |
ls.add(0, sg.next()); | |
} | |
} | |
} | |
/** | |
* Adds new lists to the map using random keys until the map is large enough | |
* high likelyhood that the keys used have been seen before and will | |
* override existing values in the map, causing more GC | |
* @param map | |
*/ | |
private void addKeys(Map<String,ArrayList<String>> map) { | |
while(map.size() < size) { | |
map.put(sg.next(), new ArrayList<String>()); | |
} | |
} | |
public static void main(String[] args) { | |
// Runs a churn for 60 seconds | |
new GCChurn(25, 1000, 60).run(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.Random; | |
/** Generates random strings with a percentage likelyhood the string has been seen before */ | |
public class StringGenerator { | |
private static final String chars; | |
static { | |
StringBuilder charBuilder = new StringBuilder(); | |
for(char c = 'a'; c <= 'z'; c++) { | |
charBuilder.append(c); | |
} | |
for(char c = 'A'; c <= 'Z'; c++) { | |
charBuilder.append(c); | |
} | |
chars = charBuilder.toString(); | |
} | |
private Random rnd = new Random(); | |
private ArrayList<String> dupLs; | |
private int dupRate; | |
/** | |
* Constructs a new StringGenerator | |
* @param dups Percent (0-100) of strings which should be duplicates | |
* @param dupSize number of unique strings to use as duplicate values | |
*/ | |
public StringGenerator(int dups, int dupSize) { | |
dupRate = dups; | |
dupLs = new ArrayList<String>(dupSize); | |
for(int i = 0; i < dupSize; i++) { | |
dupLs.add(genRandString()); | |
} | |
} | |
/** Returns a random string with a percentage likelyhood of having been seen before */ | |
public String next() { | |
if(rnd.nextInt(100) < dupRate) { | |
return dupLs.get(rnd.nextInt(dupLs.size())); | |
} | |
return genRandString(); | |
} | |
private String genRandString() { | |
// 5 char minimum ensures reasonable likelihood of unique string | |
int strLen = rnd.nextInt(11)+5; | |
StringBuilder sb = new StringBuilder(); | |
for(int i = 0; i < strLen; i++) { | |
sb.append(chars.charAt(rnd.nextInt(chars.length()))); | |
} | |
return new String(sb.toString()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
XRef: https://stackoverflow.com/q/15629794/113632