MemoryUsage.java revision d24b8183b93e781080b2c16c487e60d51c12da31
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import java.io.Serializable; 18import java.io.IOException; 19import java.io.BufferedReader; 20import java.io.InputStreamReader; 21import java.io.InputStream; 22import java.io.OutputStream; 23import java.util.List; 24import java.util.ArrayList; 25import java.util.Arrays; 26 27/** 28 * Memory usage information. 29 */ 30class MemoryUsage implements Serializable { 31 32 private static final long serialVersionUID = 0; 33 34 static final MemoryUsage NOT_AVAILABLE = new MemoryUsage(); 35 36 static int errorCount = 0; 37 static final int MAXIMUM_ERRORS = 10; // give up after this many fails 38 39 final int nativeSharedPages; 40 final int javaSharedPages; 41 final int otherSharedPages; 42 final int nativePrivatePages; 43 final int javaPrivatePages; 44 final int otherPrivatePages; 45 46 final int allocCount; 47 final int allocSize; 48 final int freedCount; 49 final int freedSize; 50 final long nativeHeapSize; 51 52 public MemoryUsage(String line) { 53 String[] parsed = line.split(","); 54 55 nativeSharedPages = Integer.parseInt(parsed[1]); 56 javaSharedPages = Integer.parseInt(parsed[2]); 57 otherSharedPages = Integer.parseInt(parsed[3]); 58 nativePrivatePages = Integer.parseInt(parsed[4]); 59 javaPrivatePages = Integer.parseInt(parsed[5]); 60 otherPrivatePages = Integer.parseInt(parsed[6]); 61 allocCount = Integer.parseInt(parsed[7]); 62 allocSize = Integer.parseInt(parsed[8]); 63 freedCount = Integer.parseInt(parsed[9]); 64 freedSize = Integer.parseInt(parsed[10]); 65 nativeHeapSize = Long.parseLong(parsed[11]); 66 } 67 68 MemoryUsage() { 69 nativeSharedPages = -1; 70 javaSharedPages = -1; 71 otherSharedPages = -1; 72 nativePrivatePages = -1; 73 javaPrivatePages = -1; 74 otherPrivatePages = -1; 75 76 allocCount = -1; 77 allocSize = -1; 78 freedCount = -1; 79 freedSize = -1; 80 nativeHeapSize = -1; 81 } 82 83 MemoryUsage(int nativeSharedPages, 84 int javaSharedPages, 85 int otherSharedPages, 86 int nativePrivatePages, 87 int javaPrivatePages, 88 int otherPrivatePages, 89 int allocCount, 90 int allocSize, 91 int freedCount, 92 int freedSize, 93 long nativeHeapSize) { 94 this.nativeSharedPages = nativeSharedPages; 95 this.javaSharedPages = javaSharedPages; 96 this.otherSharedPages = otherSharedPages; 97 this.nativePrivatePages = nativePrivatePages; 98 this.javaPrivatePages = javaPrivatePages; 99 this.otherPrivatePages = otherPrivatePages; 100 this.allocCount = allocCount; 101 this.allocSize = allocSize; 102 this.freedCount = freedCount; 103 this.freedSize = freedSize; 104 this.nativeHeapSize = nativeHeapSize; 105 } 106 107 MemoryUsage subtract(MemoryUsage baseline) { 108 return new MemoryUsage( 109 nativeSharedPages - baseline.nativeSharedPages, 110 javaSharedPages - baseline.javaSharedPages, 111 otherSharedPages - baseline.otherSharedPages, 112 nativePrivatePages - baseline.nativePrivatePages, 113 javaPrivatePages - baseline.javaPrivatePages, 114 otherPrivatePages - baseline.otherPrivatePages, 115 allocCount - baseline.allocCount, 116 allocSize - baseline.allocSize, 117 freedCount - baseline.freedCount, 118 freedSize - baseline.freedSize, 119 nativeHeapSize - baseline.nativeHeapSize); 120 } 121 122 int javaHeapSize() { 123 return allocSize - freedSize; 124 } 125 126 int javaPagesInK() { 127 return (javaSharedPages + javaPrivatePages) * 4; 128 } 129 130 int nativePagesInK() { 131 return (nativeSharedPages + nativePrivatePages) * 4; 132 } 133 int otherPagesInK() { 134 return (otherSharedPages + otherPrivatePages) * 4; 135 } 136 137 /** 138 * Was this information available? 139 */ 140 boolean isAvailable() { 141 return nativeSharedPages != -1; 142 } 143 144 /** 145 * Measures baseline memory usage. 146 */ 147 static MemoryUsage baseline() { 148 return forClass(null); 149 } 150 151 private static final String CLASS_PATH = "-Xbootclasspath" 152 + ":/system/framework/core.jar" 153 + ":/system/framework/ext.jar" 154 + ":/system/framework/framework.jar" 155 + ":/system/framework/framework-tests.jar" 156 + ":/system/framework/services.jar" 157 + ":/system/framework/loadclass.jar"; 158 159 private static final String[] GET_DIRTY_PAGES = { 160 "adb", "-e", "shell", "dalvikvm", CLASS_PATH, "LoadClass" }; 161 162 /** 163 * Measures memory usage for the given class. 164 */ 165 static MemoryUsage forClass(String className) { 166 167 // This is a coarse approximation for determining that no device is connected, 168 // or that the communication protocol has changed, but we'll keep going and stop whining. 169 if (errorCount >= MAXIMUM_ERRORS) { 170 return NOT_AVAILABLE; 171 } 172 173 MeasureWithTimeout measurer = new MeasureWithTimeout(className); 174 175 new Thread(measurer).start(); 176 177 synchronized (measurer) { 178 if (measurer.memoryUsage == null) { 179 // Wait up to 10s. 180 try { 181 measurer.wait(30000); 182 } catch (InterruptedException e) { 183 System.err.println("Interrupted waiting for measurement."); 184 e.printStackTrace(); 185 return NOT_AVAILABLE; 186 } 187 188 // If it's still null. 189 if (measurer.memoryUsage == null) { 190 System.err.println("Timed out while measuring " 191 + className + "."); 192 return NOT_AVAILABLE; 193 } 194 } 195 196 System.err.println("Got memory usage for " + className + "."); 197 return measurer.memoryUsage; 198 } 199 } 200 201 static class MeasureWithTimeout implements Runnable { 202 203 final String className; 204 MemoryUsage memoryUsage = null; 205 206 MeasureWithTimeout(String className) { 207 this.className = className; 208 } 209 210 public void run() { 211 MemoryUsage measured = measure(); 212 213 synchronized (this) { 214 memoryUsage = measured; 215 notifyAll(); 216 } 217 } 218 219 private MemoryUsage measure() { 220 String[] commands = GET_DIRTY_PAGES; 221 if (className != null) { 222 List<String> commandList = new ArrayList<String>( 223 GET_DIRTY_PAGES.length + 1); 224 commandList.addAll(Arrays.asList(commands)); 225 commandList.add(className); 226 commands = commandList.toArray(new String[commandList.size()]); 227 } 228 229 try { 230 final Process process = Runtime.getRuntime().exec(commands); 231 232 final InputStream err = process.getErrorStream(); 233 234 // Send error output to stderr. 235 Thread errThread = new Thread() { 236 @Override 237 public void run() { 238 copy(err, System.err); 239 } 240 }; 241 errThread.setDaemon(true); 242 errThread.start(); 243 244 BufferedReader in = new BufferedReader( 245 new InputStreamReader(process.getInputStream())); 246 String line = in.readLine(); 247 if (line == null || !line.startsWith("DECAFBAD,")) { 248 System.err.println("Got bad response for " + className 249 + ": " + line); 250 errorCount += 1; 251 return NOT_AVAILABLE; 252 } 253 254 in.close(); 255 err.close(); 256 process.destroy(); 257 258 return new MemoryUsage(line); 259 } catch (IOException e) { 260 System.err.println("Error getting stats for " 261 + className + "."); 262 e.printStackTrace(); 263 return NOT_AVAILABLE; 264 } 265 } 266 267 } 268 269 /** 270 * Copies from one stream to another. 271 */ 272 private static void copy(InputStream in, OutputStream out) { 273 byte[] buffer = new byte[1024]; 274 int read; 275 try { 276 while ((read = in.read(buffer)) > -1) { 277 out.write(buffer, 0, read); 278 } 279 } catch (IOException e) { 280 e.printStackTrace(); 281 } 282 } 283} 284