Java: System.exit() 与安全策略

说明

System.exit() 的本质是通知 JVM 关闭。

一般来说。有两种禁用 System.exit() 的办法:

  • 安全管理器
  • 安全策略

本质都是JRE 提供的本地实现,在执行之前进行权限推断。

由于System.exit() 是一种非常暴力的手段。假设在 Client 模式下自己写个小程序无所谓,可是在 Server 上多个程序、或者多线程时就会有非常大的麻烦。

底层源代码

1.先来看看静态方法 System.exit() 的源代码:

// System.exit()
public static void exit(int status) {
    Runtime.getRuntime().exit(status);
}

应该说非常easy, 仅仅是简单地调用执行时的 exit 方法.

2.然后我们看执行时的实例方法 exit:

// Runtime.exit()
public void exit(int status) {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkExit(status);
    }
    Shutdown.exit(status);
}

假设有安全管理器,那么就让安全管理器执行 checkExit退出权限检查。

假设检查不通过,安全管理器就会抛出异常(这就是约定!

)。

然后当前线程就会往外一路抛异常,假设不捕获,那么该线程就会退出。

此时假设没有其它的前台线程正在执行,那么JVM也会跟着退出。

3.Shutdownjava.lang 包以下的一个类。

訪问权限是 default, 所以我们在API中是不能调用的。

// Shutdown.exit()
static void exit(int status) {
    boolean runMoreFinalizers = false;
    synchronized (lock) {
        if (status != 0) runFinalizersOnExit = false;
        switch (state) {
        case RUNNING:       /* Initiate shutdown */
            state = HOOKS;
            break;
        case HOOKS:         /* Stall and halt */
            break;
        case FINALIZERS:
            if (status != 0) {
                /* Halt immediately on nonzero status */
                halt(status);
            } else {
                /* Compatibility with old behavior:
                 * Run more finalizers and then halt
                 */
                runMoreFinalizers = runFinalizersOnExit;
            }
            break;
        }
    }
    if (runMoreFinalizers) {
        runAllFinalizers();
        halt(status);
    }
    synchronized (Shutdown.class) {
        /* Synchronize on the class object, causing any other thread
         * that attempts to initiate shutdown to stall indefinitely
         */
        sequence();
        halt(status);
    }
}

当中有一些同步方法进行锁定。

退出逻辑是调用了 halt 方法。

// Shutdown.halt()
static void halt(int status) {
    synchronized (haltLock) {
        halt0(status);
    }
}

static native void halt0(int status);

然后就是调用 native 的 halt0() 方法让 JVM “自杀“了。

演示样例

使用安全管理器的实现代码例如以下所看到的:

1.定义异常类, 继承自 SecurityException

ExitException.java

package com.cncounter.security;

public class ExitException extends SecurityException {
    private static final long serialVersionUID = 1L;
    public final int status;

    public ExitException(int status) {
        super("忽略 Exit方法调用!");
        this.status = status;
    }
}

2.定义安全管理器类, 继承自 SecurityManager

NoExitSecurityManager.java

package com.cncounter.security;

import java.security.Permission;

public class NoExitSecurityManager extends SecurityManager {
    @Override
    public void checkPermission(Permission perm) {
        // allow anything.
    }

    @Override
    public void checkPermission(Permission perm, Object context) {
        // allow anything.
    }

    @Override
    public void checkExit(int status) {
        super.checkExit(status);
        throw new ExitException(status);
    }
}

当中直接拒绝系统退出。

3.添加一个辅助和測试类,实际使用时你也能够自己进行控制。

NoExitHelper.java

package com.cncounter.security;

public class NoExitHelper {

    /**
     * 设置不同意调用 System.exit(status)
     *
     * @throws Exception
     */
    public static void setNoExit() throws Exception {
        System.setSecurityManager(new NoExitSecurityManager());
    }

    public static void main(String[] args) throws Exception {
        setNoExit();
        testNoExit();
        testExit();
        testNoExit();
    }

    public static void testNoExit() throws Exception {
        System.out.println("Printing works");
    }

    public static void testExit() throws Exception {
        try {
            System.exit(42);
        } catch (ExitException e) {
            //
            System.out.println("退出的状态码为: " + e.status);
        }
    }
}

在当中。使用了一个 main 方法来做简单的測试。 控制台输出结果例如以下:

Printing works
退出的状态码为: 42
Printing works

原问题

原来的问题例如以下:

I’ve got a few methods that should call System.exit() on certain inputs. Unfortunately, testing these cases causes JUnit to terminate! Putting the method calls in a new Thread doesn’t seem to help, since System.exit() terminates the JVM, not just the current thread. Are there any common patterns for dealing with this? For example, can I subsitute a stub for System.exit()?

大意是:

有一些方法须要測试, 可是在某些特定的输入时就会调用 System.exit()。这就杯具了,这时候 JUnit 測试也跟着退出了! 用一个新线程来调用这样的方法也没什么用, 由于 System.exit() 会停止JVM , 而不是退出当前线程。

有什么通用的模式来处理这样的情况吗?

比如,我能替换掉 System.exit() 方法吗?

建议例如以下:

Instead of terminating with System.exit(whateverValue), why not throw an unchecked exception?

In normal use it will drift all the way out to the JVM’s last-ditch catcher and shut your script down (unless you decide to catch it somewhere along the way, which might be useful someday).

In the JUnit scenario it will be caught by the JUnit framework, which will report that such-and-such test failed and move smoothly along to the next.

翻译例如以下:

在程序中调用 System.exit(whateverValue) 是一种非常不好的编程习惯, 这样的情况为什么不抛出一个未检測的异常(unchecked exception)呢? 假设程序中不进行捕获(catch), 抛出的异常会一路漂移到 JVM , 然后就会退出程序(仅仅有主线程的话)。

在 JUnit 的測试场景中异常会被 JUnit 框架捕获, 然后就会报告说某某某測试执行失败,然后就继续下一个单元測试了。

当然,给出的解决方式就是前面的那段代码. 你还能够阅读以下的參考文章,查找其它的解决方式。

參考文章:

日期: 2015年08月25日

人员: 铁锚 http://blog.csdn.net/renfufei

时间: 2024-08-29 12:30:51

Java: System.exit() 与安全策略的相关文章

Java 中的System.exit

在java 中退出程序,经常会使用System.exit(1) 或 System.exit(0). 查看System.exit()方法的源码,如下 1 /** 2 * Terminates the currently running Java Virtual Machine. The 3 * argument serves as a status code; by convention, a nonzero status 4 * code indicates abnormal terminati

如何禁止某些代码调用 System.exit()

如何禁止调用 System.exit() 说明 System.exit() 的本质是通知 JVM 关闭. 一般来说,有两种禁用 System.exit() 的办法: 安全管理器 安全策略 本质都是JRE 提供的本地实现,在执行之前进行权限判断. 因为System.exit() 是一种很暴力的手段,如果在 Client 模式下自己写个小程序无所谓,但是在 Server 上多个程序.或者多线程时就会有很大的麻烦. 示例 使用安全管理器的实现代码如下所示: 1.定义异常类, 继承自 SecurityE

Java System类

1 package demo04; 2 3 //因为构造方法被private修饰,System类不能创建对象,但是可以通过类名访问其静态方法 4 public class SystemDemo { 5 public static void main(String[] args) { 6 //static long currentTimeMillis() 返回以毫秒为单位的当前时间 7 8 //获取程序运行时间 end-start 9 long start = System.currentTime

System.exit(0);和finish();,push原理

今天师姐问我安卓后台的问题,想起几年前做进制转换的时候特意研究了一下怎么才能「不驻留内存地退出」.虽然Android不推荐用户手动关闭进程,但是在那个内存捉襟见肘的年代,不得不考虑内存. 首先直接按back键肯定是会驻留内存的,其次finish()也可以结束Activity,但是也驻留内存(我还清楚地记得当时做过实验,业界良心没有没).试了很多方法,最后找到一种方法可以退出: 在底层的Activity上添加back键监听,然后在onClick()里面用这个方法: System.exit(0);

Java System、Runtime类

package com.fish.other; import java.util.Arrays; import java.util.Properties; /* System  系统类 主要用于获取系统的属性数据.   System类常用的方法:  arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 一般      src - 源数组.       srcPos - 源数组中的起始位置.       de

android开发两种退出程序方式(killProcess,System.exit)

KillProcess: 在android中我们如果想要程序的进程结束可以这样写: android.os.Process.killProcess(android.os.Process.myPid()); 这样就可以从操作系统中结束掉当前程序的进程. 注意:android中所有的activity都在主进程中,在Androidmanifest.xml中可以设置成启动不同进程,Service不是一个单独的进程也不是一个线程. 当你Kill掉当前程序的进程时也就是说整个程序的所有线程都会结束,Servi

system.exit(0) vs system.exit(1)

2.解析 查看java.lang.System的源代码,我们可以找到System.exit(status)这个方法的说明,代码如下: /** * Terminates the currently running Java Virtual Machine. The * argument serves as a status code; by convention, a nonzero status * code indicates abnormal termination. * <p> * Th

System.exit和Runtime halt区别

看到RM在处理异常的时候使用了两种退出方式,而且是针对不同的异常.特意查询了一些资料来看看,两种方式有什么不同. System.exit 终止当前正在运行的Java虚拟机.参数作为状态代码,按照惯例,一个非零状态码表示异常终止. 用线程描述,在多线程情况下,可能更准确一些 1.调用方法后,线程会退出 2.未捕获的异常被线程抛出,但如果有其他非守护线程,程序将继续运行. 3.反馈状态码,一般在脚本中有用. 4.线程退出,还是做一些清理动作 -----------------------------

System.exit(-1)和return 的区别

对于只有一个单一方法的类或者系统来说是一样的,但是对于含有多个类和方法,且调用关系比较复杂时就不一样了. System.exit(-1)是指所有程序(方法,类等)停止,系统停止运行. return只是这一个方法停止,并不影响其他方法的顺序运行.比如: void a(){ b(); c(); d(); }其中b()中是return.c()是System.exit(-1);那么 b()会执行,c()会执行,而d()不会被执行. System.exit(int status) public stati