1 // This is adapted from a benchmark written by John Ellis and Pete Kovac
2 // of Post Communications.
3 // It was modified by Hans Boehm of Silicon Graphics.
5 // This is no substitute for real applications. No actual application
6 // is likely to behave in exactly this way. However, this benchmark was
7 // designed to be more representative of real applications than other
8 // Java GC benchmarks of which we are aware.
9 // It attempts to model those properties of allocation requests that
10 // are important to current GC techniques.
11 // It is designed to be used either to obtain a single overall performance
12 // number, or to give a more detailed estimate of how collector
13 // performance varies with object lifetimes. It prints the time
14 // required to allocate and collect balanced binary trees of various
15 // sizes. Smaller trees result in shorter object lifetimes. Each cycle
16 // allocates roughly the same amount of memory.
17 // Two data structures are kept around during the entire process, so
18 // that the measured performance is representative of applications
19 // that maintain some live in-memory data. One of these is a tree
20 // containing many pointers. The other is a large array containing
21 // double precision floating point numbers. Both should be of comparable
24 // The results are only really meaningful together with a specification
25 // of how much memory was used. It is possible to trade memory for
26 // better time performance. This benchmark should be run in a 32 MB
27 // heap, though we don't currently know how to enforce that uniformly.
29 // Unlike the original Ellis and Kovac benchmark, we do not attempt
30 // measure pause times. This facility should eventually be added back
31 // in. There are several reasons for omitting it for now. The original
32 // implementation depended on assumptions about the thread scheduler
33 // that don't hold uniformly. The results really measure both the
34 // scheduler and GC. Pause time measurements tend to not fit well with
35 // current benchmark suites. As far as we know, none of the current
36 // commercial Java implementations seriously attempt to minimize GC pause
39 // Known deficiencies:
40 // - No way to check on memory use
41 // - No cyclic data structures
42 // - No attempt to measure variation with object size
43 // - Results are sensitive to locking cost, but we dont
44 // check for proper locking
49 Node(Node l, Node r) { left = l; right = r; }
53 public class GCBench {
55 public static final int kStretchTreeDepth = 18; // about 16Mb
56 public static final int kLongLivedTreeDepth = 16; // about 4Mb
57 public static final int kArraySize = 500000; // about 4Mb
58 public static final int kMinTreeDepth = 4;
59 public static final int kMaxTreeDepth = 16;
61 // Nodes used by a tree of a given size
62 static int TreeSize(int i) {
63 return ((1 << (i + 1)) - 1);
66 // Number of iterations to use for a given tree depth
67 static int NumIters(int i) {
68 return 2 * TreeSize(kStretchTreeDepth) / TreeSize(i);
71 // Build tree top down, assigning to older objects.
72 static void Populate(int iDepth, Node thisNode) {
77 thisNode.left = new Node();
78 thisNode.right = new Node();
79 Populate (iDepth, thisNode.left);
80 Populate (iDepth, thisNode.right);
84 // Build tree bottom-up
85 static Node MakeTree(int iDepth) {
89 return new Node(MakeTree(iDepth-1),
94 static void PrintDiagnostics() {
95 long lFreeMemory = Runtime.getRuntime().freeMemory();
96 long lTotalMemory = Runtime.getRuntime().totalMemory();
98 System.out.print(" Total memory available="
99 + lTotalMemory + " bytes");
100 System.out.println(" Free memory=" + lFreeMemory + " bytes");
103 static void TimeConstruction(int depth) {
105 long tStart, tFinish;
106 int iNumIters = NumIters(depth);
109 System.out.println("Creating " + iNumIters +
110 " trees of depth " + depth);
111 tStart = System.currentTimeMillis();
112 for (int i = 0; i < iNumIters; ++i) {
113 tempTree = new Node();
114 Populate(depth, tempTree);
117 tFinish = System.currentTimeMillis();
118 System.out.println("\tTop down construction took "
119 + (tFinish - tStart) + "msecs");
120 tStart = System.currentTimeMillis();
121 for (int i = 0; i < iNumIters; ++i) {
122 tempTree = MakeTree(depth);
125 tFinish = System.currentTimeMillis();
126 System.out.println("\tBottom up construction took "
127 + (tFinish - tStart) + "msecs");
131 public static void main(String args[]) {
135 long tStart, tFinish;
139 System.out.println("Garbage Collector Test");
141 " Stretching memory with a binary tree of depth "
142 + kStretchTreeDepth);
144 tStart = System.currentTimeMillis();
146 // Stretch the memory space quickly
147 tempTree = MakeTree(kStretchTreeDepth);
150 // Create a long lived object
152 " Creating a long-lived binary tree of depth " +
153 kLongLivedTreeDepth);
154 longLivedTree = new Node();
155 Populate(kLongLivedTreeDepth, longLivedTree);
157 // Create long-lived array, filling half of it
159 " Creating a long-lived array of "
160 + kArraySize + " doubles");
161 double array[] = new double[kArraySize];
162 for (int i = 0; i < kArraySize/2; ++i) {
163 array[i] = 1.0/(i+1);
167 for (int d = kMinTreeDepth; d <= kMaxTreeDepth; d += 2) {
171 if (longLivedTree == null || array[1000] != 1.0/1001)
172 System.out.println("Failed");
173 // fake reference to LongLivedTree
175 // to keep them from being optimized away
177 tFinish = System.currentTimeMillis();
178 tElapsed = tFinish-tStart;
180 System.out.println("Completed in " + tElapsed + "ms.");