/*
* Copyright ( C ) 2009 The Android Open Source Project
*
* 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 .
*/
import java.io.File;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static volatile boolean quit = false ;
public static final boolean DEBUG = false ;
private static final boolean WRITE_HPROF_DATA = false ;
private static final int TEST_TIME = 10 ;
private static final String OUTPUT_FILE = "gc-thrash.hprof" ;
public static void main(String[] args) {
// dump heap before
System.out.println("Running (" + TEST_TIME + " seconds) ..." );
runTests();
Method dumpHprofDataMethod = null ;
String dumpFile = null ;
if (WRITE_HPROF_DATA) {
dumpHprofDataMethod = getDumpHprofDataMethod();
if (dumpHprofDataMethod != null ) {
dumpFile = getDumpFileName();
System.out.println("Sending output to " + dumpFile);
}
}
System.gc();
System.runFinalization();
System.gc();
if (WRITE_HPROF_DATA && dumpHprofDataMethod != null ) {
try {
dumpHprofDataMethod.invoke(null , dumpFile);
} catch (IllegalAccessException iae) {
System.out.println(iae);
} catch (InvocationTargetException ite) {
System.out.println(ite);
}
}
System.out.println("Done." );
}
/**
* Finds VMDebug . dumpHprofData ( ) through reflection . In the reference
* implementation this will not be available .
*
* @ return the reflection object , or null if the method can ' t be found
*/
private static Method getDumpHprofDataMethod() {
ClassLoader myLoader = Main.class .getClassLoader();
Class <?> vmdClass;
try {
vmdClass = myLoader.loadClass("dalvik.system.VMDebug" );
} catch (ClassNotFoundException cnfe) {
return null ;
}
Method meth;
try {
meth = vmdClass.getMethod("dumpHprofData" , String.class );
} catch (NoSuchMethodException nsme) {
System.out.println("Found VMDebug but not dumpHprofData method" );
return null ;
}
return meth;
}
private static String getDumpFileName() {
File tmpDir = new File("/tmp" );
if (tmpDir.exists() && tmpDir.isDirectory()) {
return "/tmp/" + OUTPUT_FILE;
}
File sdcard = new File("/sdcard" );
if (sdcard.exists() && sdcard.isDirectory()) {
return "/sdcard/" + OUTPUT_FILE;
}
return null ;
}
/**
* Run the various tests for a set period .
*/
public static void runTests() {
Robin robin = new Robin();
Deep deep = new Deep();
Large large = new Large();
/* start all threads */
robin.start();
deep.start();
large.start();
/* let everybody run for 10 seconds */
sleep(TEST_TIME * 1000 );
quit = true ;
try {
/* wait for all threads to stop */
robin.join();
deep.join();
large.join();
} catch (InterruptedException ie) {
System.out.println("join was interrupted" );
}
}
/**
* Sleeps for the " ms " milliseconds .
*/
public static void sleep(int ms) {
try {
Thread .sleep(ms);
} catch (InterruptedException ie) {
System.out.println("sleep was interrupted" );
}
}
/**
* Sleeps briefly , allowing other threads some CPU time to get started .
*/
public static void startupDelay() {
sleep(500 );
}
}
/**
* Allocates useless objects and holds on to several of them .
*
* Uses a single large array of references , replaced repeatedly in round - robin
* order .
*/
class Robin extends Thread {
private static final int ARRAY_SIZE = 40960 ;
int sleepCount = 0 ;
public void run() {
Main.startupDelay();
String strings[] = new String[ARRAY_SIZE];
int idx = 0 ;
while (!Main.quit) {
strings[idx] = makeString(idx);
if (idx % (ARRAY_SIZE / 4 ) == 0 ) {
Main.sleep(400 );
sleepCount++;
}
idx = (idx + 1 ) % ARRAY_SIZE;
}
if (Main.DEBUG)
System.out.println("Robin: sleepCount=" + sleepCount);
}
private String makeString(int val) {
try {
return new String("Robin" + val);
} catch (OutOfMemoryError e) {
return null ;
}
}
}
/**
* Allocates useless objects in recursive calls .
*/
class Deep extends Thread {
private static final int MAX_DEPTH = 50 ;
private static String strong[] = new String[MAX_DEPTH];
private static WeakReference weak[] = new WeakReference[MAX_DEPTH];
public void run() {
int iter = 0 ;
boolean once = false ;
Main.startupDelay();
while (!Main.quit) {
dive(0 , iter);
once = true ;
iter += MAX_DEPTH;
}
if (!once) {
System.out.println("not even once?" );
return ;
}
checkStringReferences();
/*
* Wipe " strong " , do a GC , see if " weak " got collected .
*/
for (int i = 0 ; i < MAX_DEPTH; i++)
strong[i] = null ;
Runtime.getRuntime().gc();
for (int i = 0 ; i < MAX_DEPTH; i++) {
if (weak[i].get() != null ) {
System.out.println("Deep: weak still has " + i);
}
}
if (Main.DEBUG)
System.out.println("Deep: iters=" + iter / MAX_DEPTH);
}
/**
* Check the results of the last trip through . Everything in
* " weak " should be matched in " strong " , and the two should be
* equivalent ( object - wise , not just string - equality - wise ) .
*
* We do that check in a separate method to avoid retaining these
* String references in local DEX registers . In interpreter mode ,
* they would retain these references until the end of the method
* or until they are updated to another value .
*/
private static void checkStringReferences() {
for (int i = 0 ; i < MAX_DEPTH; i++) {
if (strong[i] != weak[i].get()) {
System.out.println("Deep: " + i + " strong=" + strong[i] +
", weak=" + weak[i].get());
}
}
}
/**
* Recursively dive down , setting one or more local variables .
*
* We pad the stack out with locals , attempting to create a mix of
* valid and invalid references on the stack .
*/
private String dive(int depth, int iteration) {
try {
String str0;
String str1;
String str2;
String str3;
String str4;
String str5;
String str6;
String str7;
String funStr = "" ;
switch (iteration % 8 ) {
case 0 :
funStr = str0 = makeString(iteration);
break ;
case 1 :
funStr = str1 = makeString(iteration);
break ;
case 2 :
funStr = str2 = makeString(iteration);
break ;
case 3 :
funStr = str3 = makeString(iteration);
break ;
case 4 :
funStr = str4 = makeString(iteration);
break ;
case 5 :
funStr = str5 = makeString(iteration);
break ;
case 6 :
funStr = str6 = makeString(iteration);
break ;
case 7 :
funStr = str7 = makeString(iteration);
break ;
}
weak[depth] = new WeakReference(funStr);
strong[depth] = funStr;
if (depth+1 < MAX_DEPTH)
dive(depth+1 , iteration+1 );
else
Main.sleep(100 );
return funStr;
} catch (OutOfMemoryError e) {
// Silently ignore OOME since gc stress mode causes them to occur but shouldn't be a
// test failure.
}
return "" ;
}
private String makeString(int val) {
try {
return new String("Deep" + val);
} catch (OutOfMemoryError e) {
return null ;
}
}
}
/**
* Allocates large useless objects .
*/
class Large extends Thread {
public void run() {
byte [] chunk;
int count = 0 ;
int sleepCount = 0 ;
Main.startupDelay();
while (!Main.quit) {
try {
chunk = new byte [100000 ];
pretendToUse(chunk);
count++;
if ((count % 500 ) == 0 ) {
Main.sleep(400 );
sleepCount++;
}
} catch (OutOfMemoryError e) {
}
}
if (Main.DEBUG)
System.out.println("Large: sleepCount=" + sleepCount);
}
public void pretendToUse(byte [] chunk) {}
}
Messung V0.5 in Prozent C=85 H=97 G=91
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-29)
¤
*© Formatika GbR, Deutschland