Runtime.java revision 64c6c367497c7fcf88e7527022234043e4460758
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17/* 18 * Copyright (C) 2008 The Android Open Source Project 19 * 20 * Licensed under the Apache License, Version 2.0 (the "License"); 21 * you may not use this file except in compliance with the License. 22 * You may obtain a copy of the License at 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, 28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 * See the License for the specific language governing permissions and 30 * limitations under the License. 31 */ 32 33package java.lang; 34 35import dalvik.system.VMDebug; 36import dalvik.system.VMStack; 37import java.io.File; 38import java.io.IOException; 39import java.io.InputStream; 40import java.io.InputStreamReader; 41import java.io.OutputStream; 42import java.io.OutputStreamWriter; 43import java.util.ArrayList; 44import java.util.List; 45import java.util.StringTokenizer; 46import libcore.io.Libcore; 47import static libcore.io.OsConstants._SC_NPROCESSORS_ONLN; 48 49/** 50 * Allows Java applications to interface with the environment in which they are 51 * running. Applications can not create an instance of this class, but they can 52 * get a singleton instance by invoking {@link #getRuntime()}. 53 * 54 * @see System 55 */ 56public class Runtime { 57 58 /** 59 * Holds the Singleton global instance of Runtime. 60 */ 61 private static final Runtime mRuntime = new Runtime(); 62 63 /** 64 * Holds the library paths, used for native library lookup. 65 */ 66 private final String[] mLibPaths; 67 68 /** 69 * Holds the list of threads to run when the VM terminates 70 */ 71 private List<Thread> shutdownHooks = new ArrayList<Thread>(); 72 73 /** 74 * Reflects whether finalization should be run for all objects 75 * when the VM terminates. 76 */ 77 private static boolean finalizeOnExit; 78 79 /** 80 * Reflects whether we are already shutting down the VM. 81 */ 82 private boolean shuttingDown; 83 84 /** 85 * Reflects whether we are tracing method calls. 86 */ 87 private boolean tracingMethods; 88 89 /** 90 * Prevent this class from being instantiated. 91 */ 92 private Runtime(){ 93 String pathList = System.getProperty("java.library.path", "."); 94 String pathSep = System.getProperty("path.separator", ":"); 95 String fileSep = System.getProperty("file.separator", "/"); 96 97 mLibPaths = pathList.split(pathSep); 98 99 // Add a '/' to the end so we don't have to do the property lookup 100 // and concatenation later. 101 for (int i = 0; i < mLibPaths.length; i++) { 102 if (!mLibPaths[i].endsWith(fileSep)) { 103 mLibPaths[i] += fileSep; 104 } 105 } 106 } 107 108 /** 109 * Executes the specified command and its arguments in a separate native 110 * process. The new process inherits the environment of the caller. Calling 111 * this method is equivalent to calling {@code exec(progArray, null, null)}. 112 * 113 * @param progArray 114 * the array containing the program to execute as well as any 115 * arguments to the program. 116 * @return the new {@code Process} object that represents the native 117 * process. 118 * @throws IOException 119 * if the requested program can not be executed. 120 */ 121 public Process exec(String[] progArray) throws java.io.IOException { 122 return exec(progArray, null, null); 123 } 124 125 /** 126 * Executes the specified command and its arguments in a separate native 127 * process. The new process uses the environment provided in {@code envp}. 128 * Calling this method is equivalent to calling 129 * {@code exec(progArray, envp, null)}. 130 * 131 * @param progArray 132 * the array containing the program to execute as well as any 133 * arguments to the program. 134 * @param envp 135 * the array containing the environment to start the new process 136 * in. 137 * @return the new {@code Process} object that represents the native 138 * process. 139 * @throws IOException 140 * if the requested program can not be executed. 141 */ 142 public Process exec(String[] progArray, String[] envp) throws java.io.IOException { 143 return exec(progArray, envp, null); 144 } 145 146 /** 147 * Executes the specified command and its arguments in a separate native 148 * process. The new process uses the environment provided in {@code envp} 149 * and the working directory specified by {@code directory}. 150 * 151 * @param progArray 152 * the array containing the program to execute as well as any 153 * arguments to the program. 154 * @param envp 155 * the array containing the environment to start the new process 156 * in. 157 * @param directory 158 * the directory in which to execute the program. If {@code null}, 159 * execute if in the same directory as the parent process. 160 * @return the new {@code Process} object that represents the native 161 * process. 162 * @throws IOException 163 * if the requested program can not be executed. 164 */ 165 public Process exec(String[] progArray, String[] envp, File directory) throws IOException { 166 // ProcessManager is responsible for all argument checking. 167 return ProcessManager.getInstance().exec(progArray, envp, directory, false); 168 } 169 170 /** 171 * Executes the specified program in a separate native process. The new 172 * process inherits the environment of the caller. Calling this method is 173 * equivalent to calling {@code exec(prog, null, null)}. 174 * 175 * @param prog 176 * the name of the program to execute. 177 * @return the new {@code Process} object that represents the native 178 * process. 179 * @throws IOException 180 * if the requested program can not be executed. 181 */ 182 public Process exec(String prog) throws java.io.IOException { 183 return exec(prog, null, null); 184 } 185 186 /** 187 * Executes the specified program in a separate native process. The new 188 * process uses the environment provided in {@code envp}. Calling this 189 * method is equivalent to calling {@code exec(prog, envp, null)}. 190 * 191 * @param prog 192 * the name of the program to execute. 193 * @param envp 194 * the array containing the environment to start the new process 195 * in. 196 * @return the new {@code Process} object that represents the native 197 * process. 198 * @throws IOException 199 * if the requested program can not be executed. 200 */ 201 public Process exec(String prog, String[] envp) throws java.io.IOException { 202 return exec(prog, envp, null); 203 } 204 205 /** 206 * Executes the specified program in a separate native process. The new 207 * process uses the environment provided in {@code envp} and the working 208 * directory specified by {@code directory}. 209 * 210 * @param prog 211 * the name of the program to execute. 212 * @param envp 213 * the array containing the environment to start the new process 214 * in. 215 * @param directory 216 * the directory in which to execute the program. If {@code null}, 217 * execute if in the same directory as the parent process. 218 * @return the new {@code Process} object that represents the native 219 * process. 220 * @throws IOException 221 * if the requested program can not be executed. 222 */ 223 public Process exec(String prog, String[] envp, File directory) throws java.io.IOException { 224 // Sanity checks 225 if (prog == null) { 226 throw new NullPointerException(); 227 } else if (prog.length() == 0) { 228 throw new IllegalArgumentException(); 229 } 230 231 // Break down into tokens, as described in Java docs 232 StringTokenizer tokenizer = new StringTokenizer(prog); 233 int length = tokenizer.countTokens(); 234 String[] progArray = new String[length]; 235 for (int i = 0; i < length; i++) { 236 progArray[i] = tokenizer.nextToken(); 237 } 238 239 // Delegate 240 return exec(progArray, envp, directory); 241 } 242 243 /** 244 * Causes the VM to stop running and the program to exit. If 245 * {@link #runFinalizersOnExit(boolean)} has been previously invoked with a 246 * {@code true} argument, then all objects will be properly 247 * garbage-collected and finalized first. 248 * 249 * @param code 250 * the return code. By convention, non-zero return codes indicate 251 * abnormal terminations. 252 */ 253 public void exit(int code) { 254 // Make sure we don't try this several times 255 synchronized(this) { 256 if (!shuttingDown) { 257 shuttingDown = true; 258 259 Thread[] hooks; 260 synchronized (shutdownHooks) { 261 // create a copy of the hooks 262 hooks = new Thread[shutdownHooks.size()]; 263 shutdownHooks.toArray(hooks); 264 } 265 266 // Start all shutdown hooks concurrently 267 for (Thread hook : hooks) { 268 hook.start(); 269 } 270 271 // Wait for all shutdown hooks to finish 272 for (Thread hook : hooks) { 273 try { 274 hook.join(); 275 } catch (InterruptedException ex) { 276 // Ignore, since we are at VM shutdown. 277 } 278 } 279 280 // Ensure finalization on exit, if requested 281 if (finalizeOnExit) { 282 runFinalization(); 283 } 284 285 // Get out of here finally... 286 nativeExit(code, true); 287 } 288 } 289 } 290 291 /** 292 * Returns the amount of free memory resources which are available to the 293 * running program. 294 * 295 * @return the approximate amount of free memory, measured in bytes. 296 */ 297 public native long freeMemory(); 298 299 /** 300 * Indicates to the VM that it would be a good time to run the 301 * garbage collector. Note that this is a hint only. There is no guarantee 302 * that the garbage collector will actually be run. 303 */ 304 public native void gc(); 305 306 /** 307 * Returns the single {@code Runtime} instance. 308 * 309 * @return the {@code Runtime} object for the current application. 310 */ 311 public static Runtime getRuntime() { 312 return mRuntime; 313 } 314 315 /** 316 * Loads and links the dynamic library that is identified through the 317 * specified path. This method is similar to {@link #loadLibrary(String)}, 318 * but it accepts a full path specification whereas {@code loadLibrary} just 319 * accepts the name of the library to load. 320 * 321 * @param pathName 322 * the absolute (platform dependent) path to the library to load. 323 * @throws UnsatisfiedLinkError 324 * if the library can not be loaded. 325 */ 326 public void load(String pathName) { 327 load(pathName, VMStack.getCallingClassLoader()); 328 } 329 330 /* 331 * Loads and links a library without security checks. 332 */ 333 void load(String filename, ClassLoader loader) { 334 if (filename == null) { 335 throw new NullPointerException("library path was null."); 336 } 337 String error = nativeLoad(filename, loader); 338 if (error != null) { 339 throw new UnsatisfiedLinkError(error); 340 } 341 } 342 343 /** 344 * Loads and links the library with the specified name. The mapping of the 345 * specified library name to the full path for loading the library is 346 * implementation-dependent. 347 * 348 * @param libName 349 * the name of the library to load. 350 * @throws UnsatisfiedLinkError 351 * if the library can not be loaded. 352 */ 353 public void loadLibrary(String libName) { 354 loadLibrary(libName, VMStack.getCallingClassLoader()); 355 } 356 357 /* 358 * Loads and links a library without security checks. 359 */ 360 void loadLibrary(String libraryName, ClassLoader loader) { 361 if (loader != null) { 362 String filename = loader.findLibrary(libraryName); 363 if (filename == null) { 364 throw new UnsatisfiedLinkError("Couldn't load " + libraryName + ": " + 365 "findLibrary returned null"); 366 } 367 String error = nativeLoad(filename, loader); 368 if (error != null) { 369 throw new UnsatisfiedLinkError(error); 370 } 371 return; 372 } 373 374 String filename = System.mapLibraryName(libraryName); 375 List<String> candidates = new ArrayList<String>(); 376 String lastError = null; 377 for (String directory : mLibPaths) { 378 String candidate = directory + filename; 379 candidates.add(candidate); 380 if (new File(candidate).exists()) { 381 String error = nativeLoad(candidate, loader); 382 if (error == null) { 383 return; // We successfully loaded the library. Job done. 384 } 385 lastError = error; 386 } 387 } 388 389 if (lastError != null) { 390 throw new UnsatisfiedLinkError(lastError); 391 } 392 throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates); 393 } 394 395 private static native void nativeExit(int code, boolean isExit); 396 397 private static native String nativeLoad(String filename, ClassLoader loader); 398 399 /** 400 * Provides a hint to the VM that it would be useful to attempt 401 * to perform any outstanding object finalization. 402 */ 403 public void runFinalization() { 404 try { 405 Daemons.waitUntilFinalizerIsIdle(); 406 } catch (InterruptedException e) { 407 Thread.currentThread().interrupt(); 408 } 409 } 410 411 /** 412 * Sets the flag that indicates whether all objects are finalized when the 413 * VM is about to exit. Note that all finalization which occurs 414 * when the system is exiting is performed after all running threads have 415 * been terminated. 416 * 417 * @param run 418 * {@code true} to enable finalization on exit, {@code false} to 419 * disable it. 420 * @deprecated This method is unsafe. 421 */ 422 @Deprecated 423 public static void runFinalizersOnExit(boolean run) { 424 finalizeOnExit = run; 425 } 426 427 /** 428 * Returns the total amount of memory which is available to the running 429 * program. 430 * 431 * @return the total amount of memory, measured in bytes. 432 */ 433 public native long totalMemory(); 434 435 /** 436 * Switches the output of debug information for instructions on or off. 437 * On Android, this method does nothing. 438 * 439 * @param enable 440 * {@code true} to switch tracing on, {@code false} to switch it 441 * off. 442 */ 443 public void traceInstructions(boolean enable) { 444 } 445 446 /** 447 * Switches the output of debug information for methods on or off. 448 * 449 * @param enable 450 * {@code true} to switch tracing on, {@code false} to switch it 451 * off. 452 */ 453 public void traceMethodCalls(boolean enable) { 454 if (enable != tracingMethods) { 455 if (enable) { 456 VMDebug.startMethodTracing(); 457 } else { 458 VMDebug.stopMethodTracing(); 459 } 460 tracingMethods = enable; 461 } 462 } 463 464 /** 465 * Returns the localized version of the specified input stream. The input 466 * stream that is returned automatically converts all characters from the 467 * local character set to Unicode after reading them from the underlying 468 * stream. 469 * 470 * @param stream 471 * the input stream to localize. 472 * @return the localized input stream. 473 * @deprecated Use {@link InputStreamReader}. 474 */ 475 @Deprecated 476 public InputStream getLocalizedInputStream(InputStream stream) { 477 String encoding = System.getProperty("file.encoding", "UTF-8"); 478 if (!encoding.equals("UTF-8")) { 479 throw new UnsupportedOperationException("Cannot localize " + encoding); 480 } 481 return stream; 482 } 483 484 /** 485 * Returns the localized version of the specified output stream. The output 486 * stream that is returned automatically converts all characters from 487 * Unicode to the local character set before writing them to the underlying 488 * stream. 489 * 490 * @param stream 491 * the output stream to localize. 492 * @return the localized output stream. 493 * @deprecated Use {@link OutputStreamWriter}. 494 */ 495 @Deprecated 496 public OutputStream getLocalizedOutputStream(OutputStream stream) { 497 String encoding = System.getProperty("file.encoding", "UTF-8"); 498 if (!encoding.equals("UTF-8")) { 499 throw new UnsupportedOperationException("Cannot localize " + encoding); 500 } 501 return stream; 502 } 503 504 /** 505 * Registers a VM shutdown hook. A shutdown hook is a 506 * {@code Thread} that is ready to run, but has not yet been started. All 507 * registered shutdown hooks will be executed when the VM 508 * terminates normally (typically when the {@link #exit(int)} method is called). 509 * 510 * <p><i>Note that on Android, the application lifecycle does not include VM termination, 511 * so calling this method will not ensure that your code is run</i>. Instead, you should 512 * use the most appropriate lifecycle notification ({@code Activity.onPause}, say). 513 * 514 * <p>Shutdown hooks are run concurrently and in an unspecified order. Hooks 515 * failing due to an unhandled exception are not a problem, but the stack 516 * trace might be printed to the console. Once initiated, the whole shutdown 517 * process can only be terminated by calling {@code halt()}. 518 * 519 * <p>If {@link #runFinalizersOnExit(boolean)} has been called with a {@code 520 * true} argument, garbage collection and finalization will take place after 521 * all hooks are either finished or have failed. Then the VM 522 * terminates. 523 * 524 * <p>It is recommended that shutdown hooks do not do any time-consuming 525 * activities, in order to not hold up the shutdown process longer than 526 * necessary. 527 * 528 * @param hook 529 * the shutdown hook to register. 530 * @throws IllegalArgumentException 531 * if the hook has already been started or if it has already 532 * been registered. 533 * @throws IllegalStateException 534 * if the VM is already shutting down. 535 */ 536 public void addShutdownHook(Thread hook) { 537 // Sanity checks 538 if (hook == null) { 539 throw new NullPointerException("Hook may not be null."); 540 } 541 542 if (shuttingDown) { 543 throw new IllegalStateException("VM already shutting down"); 544 } 545 546 if (hook.hasBeenStarted) { 547 throw new IllegalArgumentException("Hook has already been started"); 548 } 549 550 synchronized (shutdownHooks) { 551 if (shutdownHooks.contains(hook)) { 552 throw new IllegalArgumentException("Hook already registered."); 553 } 554 555 shutdownHooks.add(hook); 556 } 557 } 558 559 /** 560 * Unregisters a previously registered VM shutdown hook. 561 * 562 * @param hook 563 * the shutdown hook to remove. 564 * @return {@code true} if the hook has been removed successfully; {@code 565 * false} otherwise. 566 * @throws IllegalStateException 567 * if the VM is already shutting down. 568 */ 569 public boolean removeShutdownHook(Thread hook) { 570 // Sanity checks 571 if (hook == null) { 572 throw new NullPointerException("Hook may not be null."); 573 } 574 575 if (shuttingDown) { 576 throw new IllegalStateException("VM already shutting down"); 577 } 578 579 synchronized (shutdownHooks) { 580 return shutdownHooks.remove(hook); 581 } 582 } 583 584 /** 585 * Causes the VM to stop running, and the program to exit. 586 * Neither shutdown hooks nor finalizers are run before. 587 * 588 * @param code 589 * the return code. By convention, non-zero return codes indicate 590 * abnormal terminations. 591 * @see #addShutdownHook(Thread) 592 * @see #removeShutdownHook(Thread) 593 * @see #runFinalizersOnExit(boolean) 594 */ 595 public void halt(int code) { 596 // Get out of here... 597 nativeExit(code, false); 598 } 599 600 /** 601 * Returns the number of processors available to the VM, at least 1. 602 */ 603 public int availableProcessors() { 604 return (int) Libcore.os.sysconf(_SC_NPROCESSORS_ONLN); 605 } 606 607 /** 608 * Returns the maximum amount of memory that may be used by the virtual 609 * machine, or {@code Long.MAX_VALUE} if there is no such limit. 610 * 611 * @return the maximum amount of memory that the VM will try to 612 * allocate, measured in bytes. 613 */ 614 public native long maxMemory(); 615} 616