1.沙箱安全机制
Java安全模型的核心就是Java沙箱(sandbox)。
沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——
CPU、内存、文件系统、网络
。不同级别的沙箱对这些资源访问的限制也可以不一样。所有的Java程序运行都可以指定沙箱,可以定制安全策略。
本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱 (Sandbox) 机制。如下图所示 JDK1.0安全模型
但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的 Java1.1 版本中,针对安全机制做了改进,增加了
安全策略
,允许用户指定代码对本地资源的访问权限。如下图所示 JDK1.1安全模型
在 Java1.2 版本中,再次改进了安全机制,增加了
代码签名
。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。如下图所示 JDK1.2安全模型
当前最新的安全机制实现,则引入了域
组成沙箱的基本组件:
字节码校验器
(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。类装载器
(class loader):其中类装载器在3个方面对Java沙箱起作用
- 它防止恶意代码去干涉善意的代码;
- 它守护了被信任的类库边界;
- 它将代码归入保护域,确定了代码可以进行哪些操作。
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。
类装载器采用的机制是双亲委派模式。
- 从最内层JVM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
- 由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。
存取控制器
(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。安全管理器
(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。安全软件包
(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
- 安全提供者
- 消息摘要
- 数字签名(keytools https)
- 加密
- 鉴别
2.Native
package com.newer;
public class Demo1 {
public static void main(String[] args) {
new Thread(() -> {
},"my thread name").start();
}
// native:凡是带了native关键字的,说明java的作用范围达不到了,会去调用底层C语言的库
// 会进入本地方法栈
// 调用本地方法本地接口 JNI
// JNI作用:扩展Java的使用,融合不同的编程语言为Java所用!
// Java诞生时,C,C++横行,想要立足,必须要有调用C,C++的程序
// 在内存区域中专门开辟了一块标记区域:Native Method Stack,登记native方法
// 在最终执行时,加载本地方法库中的方法通过JNI
// Java程序驱动打印机,管理系统。在企业级应用中较为少见!
private native void start0();
// 调用其他接口:Socket,WebService,http
}
start方法的源代码
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
- 凡是带了native关键字的,说明java的作用范围达不到,去调用底层C语言的库
- JNI:Java Native Interface(Java 本地方法接口)
- 凡是带了native关键字的方法就会进入本地方法栈,其他的就是Java栈
本地方法栈(Native Method Stack)
它的具体做法是Native Method Stack中登记native方法,在(Execution Engine)执行引擎执行时,加载Native Libraies(本地库)。
3.PC寄存器
程序计数器:Program Counter register
- 每个线程都有一个程序计数器,是线程私有的。就是一个指针,指向方法区的方法字节码(用来存储指向一条指令的地址,即将要执行的指令代码)。在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。
- 这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令。
- 如果执行的是一个Native方法,那这个计数器是空的。
使用PC寄存器存储字节码指令地址有什么用
- 因为CPU需要不停的切换各个线程,这时候切换回来之后,就得知道接着从哪开始继续执行。
- JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令;
PC寄存器为什么会被设定为线程私有
- 为了能够准确的记录各个线程证在执行的当前字节码指令地址,最好的办法自然是为每个线程都分配一个PC寄存器,这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况!
- 由于CPU时间片论限制,众多线程在并发执行过程中,任何一个确定的时刻,一个处理器或者多核处理器中的一个内核,只会执行某个线程中的一条指令。
- 每个线程在创建之后,都会产生自己的程序计数器和栈帧,程序计数器在各个线程之间互不影响;
4.方法区(Method Area)
- 方法区是被所有线程共享。所有字段和方法字节码,以及一些特殊方法(构造方法,接口代码)也在此定义。简单来说,所有定义的方法的信息都保存在该区域。这个区域属于共享区间。
- 当类加载器加载完成类之后,会将类信息、运行时常量池、静态变量(此处指的是指针,如果是一个对象对象的分配还是在堆中)等存储在方法区;但在JDK不同版本对字符串常量和静态变量的存储有所不同
静态变量,常量,类信息(构造方法,接口定义),运行的常量池存在方法区中,但是实例变量存在堆内存中,与方法区无关
方法区存在static,final,Class,常量池等
package com.newer;
public class Test {
private int a;
private String name="小灰灰";
public static void main(String[] args) {
Test test1 = new Test();
test1.a=1;
test1.name="156";
}
}