是否存在最终可能无法在 java 中运行的情况?谢谢。
12 回答
来自Sun 教程
注意:如果在执行 try 或 catch 代码时 JVM 退出,则 finally 块可能不会执行。同样,如果执行 try 或 catch 代码的线程被中断或杀死,即使应用程序作为一个整体继续运行,finally 块也可能不会执行。
我不知道finally块不会执行的任何其他方式......
System.exit关闭虚拟机。
终止当前运行的 Java 虚拟机。该参数用作状态码;按照惯例,非零状态码表示异常终止。
该方法调用
exit
类中的方法Runtime
。此方法永远不会正常返回。
try {
System.out.println("hello");
System.exit(0);
}
finally {
System.out.println("bye");
} // try-finally
“再见”不会在上面的代码中打印出来。
只是为了扩展其他人所说的,任何不会导致诸如 JVM 退出之类的事情都会导致 finally 块。所以下面的方法:
public static int Stupid() {
try {
return 0;
}
finally {
return 1;
}
}
会奇怪地编译并返回 1。
与 System.exit 相关,还有某些类型的灾难性故障,finally 块可能无法执行。如果 JVM 完全耗尽内存,它可能会直接退出而没有捕获或最终发生。
具体来说,我记得一个我们愚蠢地尝试使用的项目
catch (OutOfMemoryError oome) {
// do stuff
}
This didn't work because the JVM had no memory left for executing the catch block.
try { for (;;); } finally { System.err.println("?"); }
In that case the finally will not execute (unless the deprecated Thread.stop
is called, or an equivalent, say, through a tools interface).
The Sun tutorial has been wrongly quoted here in this thread.
Note: If the JVM exits while the try or catch code is being executed, then the finally block will not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block will not execute even though the application as a whole continues.
If you look into sun tutorial closely for finally block, it doesn't say "will not execute" but "may not execute" Here is the correct description
Note: If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.
The apparent reason for this behavior is, call to system.exit() is processed in a runtime system thread which may take time to shutdown the jvm, meanwhile thread scheduler can ask finally to execute. So finally is designed to always execute, but if you are shutting down jvm, it may happen that jvm shuts down prior to finally getting being executed.
Also if a deadlock/livelock happens inside try
block.
Here's the code that demonstrates it:
public class DeadLocker {
private static class SampleRunnable implements Runnable {
private String threadId;
private Object lock1;
private Object lock2;
public SampleRunnable(String threadId, Object lock1, Object lock2) {
super();
this.threadId = threadId;
this.lock1 = lock1;
this.lock2 = lock2;
}
@Override
public void run() {
try {
synchronized (lock1) {
System.out.println(threadId + " inside lock1");
Thread.sleep(1000);
synchronized (lock2) {
System.out.println(threadId + " inside lock2");
}
}
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
}
public static void main(String[] args) throws Exception {
Object ob1 = new Object();
Object ob2 = new Object();
Thread t1 = new Thread(new SampleRunnable("t1", ob1, ob2));
Thread t2 = new Thread(new SampleRunnable("t2", ob2, ob1));
t1.start();
t2.start();
}
}
This code produces the following output:
t1 inside lock1
t2 inside lock1
and "finally" never gets printed
If the JVM exits while the try or catch code is being executed, then the finally block may not execute. (source)
Normal Shutdown - this occurs either when the last non-daemon thread exits OR when Runtime.exit() (source)
When a thread exits, the JVM performs an inventory of running threads, and if the only threads that are left are daemon threads, it initiates an orderly shutdown. When the JVM halts, any remaining daemon threads are abandoned finally blocks are not executed, stacks are not unwound the JVM just exits. Daemon threads should be used sparingly few processing activities can be safely abandoned at any time with no cleanup. In particular, it is dangerous to use daemon threads for tasks that might perform any sort of I/O. Daemon threads are best saved for "housekeeping" tasks, such as a background thread that periodically removes expired entries from an in-memory cache. (source)
Last non-daemon thread exits example:
public class TestDaemon {
private static Runnable runnable = new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("Is alive");
Thread.sleep(10);
// throw new RuntimeException();
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
System.out.println("This will never be executed.");
}
}
};
public static void main(String[] args) throws InterruptedException {
Thread daemon = new Thread(runnable);
daemon.setDaemon(true);
daemon.start();
Thread.sleep(100);
// daemon.stop();
System.out.println("Last non-daemon thread exits.");
}
}
Output:
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive
In following cases, finally block will not be executed :-
- When
System.exit(0)
is invoked fromtry
block. - When JVM runs out of memory
- When your java process is killed forcefully from task mgr or console
- Deadlock condition in your
try
block - When your machine shuts down due power failure
There may also be other fringe cases, where finally block will not be executed.
There are two ways to stop finally block code execution:
1. Use System.exit();
2. If somehow execution control don't reach to try block.
See:
public class Main
{
public static void main (String[]args)
{
if(true){
System.out.println("will exceute");
}else{
try{
System.out.println("result = "+5/0);
}catch(ArithmeticException e){
System.out.println("will not exceute");
}finally{
System.out.println("will not exceute");
}
}
}
}
I've come across a very specific case of the finally block not executing related specifically to the play framework.
I was surprised to find that the finally block in this controller action code only got called after an Exception, but never when the call actually succeeded.
try {
InputStream is = getInputStreamMethod();
renderBinary(is, "out.zip");
catch (Exception e) {
e.printStackTrace();
} finally {
cleanUp();
}
Perhaps the thread is terminated or something when renderBinary() is called. I would suspect the same thing happens for other render() calls, but I didn't verify it.
I solved the problem by moving the renderBinary() to after the try/catch. Further investigation revealed that play provides an @Finally annotation to create a method that gets executed after a controller action executes. The caveat here is that this will get called after the execution of ANY action in the controller, so it may not always be a good choice.
//If ArithmeticException Occur Inner finally would not be executed
class Temp
{
public static void main(String[] s)
{
try
{
int x = 10/s.length;
System.out.println(x);
try
{
int z[] = new int[s.length];
z[10] = 1000;
}catch(ArrayIndexOutOfBoundsException e)
{
System.out.println(e);
}
finally
{
System.out.println("Inner finally");
}
}
catch(ArithmeticException e)
{
System.out.println(e);
}
finally
{
System.out.println("Outer Finally");
}
System.out.println("Remaining Code");
}
}