前言
前两天和朋友探讨技术的时候有聊到JVM和JDK这一块,聊到这里两个人就像高山流水遇知音那是根本停不下来,事后我想着趁现在印象还比较深刻就把这些东西整理起来分享给大家来帮助更多的人吧。话不多说,满满的干货都整理在下面了!
这个系列小编将分五个篇章来分享
Day1
●JVM探究
●jvm的位置
●类加载器
●双亲委派机制
●沙箱安全机制
JVM探究
jvm的位置
![](https://i-blog.csdnimg.cn/blog_migrate/fa5c6b3ce92e8b7a790dfca6cd5e4e21.png)
jvm的体系结构
![](https://i-blog.csdnimg.cn/blog_migrate/1e6d94d1e42349ea6b66e26e6560837a.png)
堆里面有垃圾,需要被GC回收
栈里面是没有垃圾的,用完就弹出去了,栈里面有垃圾,程序就崩了,执行不完main方法。
Java栈,本地方法栈,程序计数器里面是不可能存在垃圾的。也就不会有垃圾回收。
所谓的jvm调优就是在堆里面调优了,jvm调优99%都是在方法区和堆里面进行调优的。
类加载器
![](https://i-blog.csdnimg.cn/blog_migrate/e2a86866ae9034d84c3cf8c5995de6eb.png)
public class Car {
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
System.out.println(car1.hashCode());
System.out.println(car2.hashCode());
System.out.println(car3.hashCode());
Class<? extends Car> aClass1 = car1.getClass();
Class<? extends Car> aClass2 = car2.getClass();
Class<? extends Car> aClass3 = car3.getClass();
System.out.println(aClass1.hashCode());
System.out.println(aClass2.hashCode());
System.out.println(aClass3.hashCode());
}
}
复制代码
作用:加载class文件 - 类似new Student();
类是一个模板,是抽象的,而new出来的对象是具体的,是对这个抽象的类的实例化
1.虚拟机自带的加载器
2.启动类(根)加载器
3.扩展加载器
4.应用程序(系统类)加载器
ClassLoader classLoader = aClass1.getClassLoader();
System.out.println(classLoader);//AppClassLoader 应用程序加载器
System.out.println(classLoader.getParent());//ExtClassLoader 扩展类加载器
System.out.println(classLoader.getParent().getParent());//null 1.不存在 2.Java程序获取不到
复制代码
1.类加载器收到类加载的请求
2.将这个请求向上委托给父类加载器去完成,一直向上委托,直到根加载器
3.启动类加载器会检查是否能够加载当前这个类,能加载就结束,使用当前加载器,否则,抛出异常,通知子类加载器进行加载。
4.重复步骤3
若都找不到就会报 Class Not Found
null:Java调用不到,可能编程语言是C写的,所以调不到
Java =C+±- 去掉C里面比较繁琐的东西 指针,内存管理(JVM帮你做了)
双亲委派机制
双亲委派机制:安全
APP–>EXC–BOOTStrap(根目录,最终执行)
当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
在src下创建Java.lang包,创建一个String类
package java.lang;
public class String {
public String toString(){
return "hello";
}
public static void main(String[] args) {
String s = new String();
System.out.println(s.getClass().getClassLoader());
s.toString();
}
}
复制代码
执行结果
![](https://i-blog.csdnimg.cn/blog_migrate/8ae5375cd814011e41afcfb17c06400e.png)
它会去最终的BOOTStrap里面的String类里面去执行,找到执行类的位置,发现里面没有要执行的mian方法,所以会报这个错。
在src下创建类Student
public class Student {
public String toString(){
return "HELLO";
}
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.getClass().getClassLoader());
System.out.println(student.toString());
}
}
复制代码
执行结果
![](https://i-blog.csdnimg.cn/blog_migrate/eca8a63ac9fe6f9f79e07601fdf7227a.png)
如上图可见最终是在APP里面执行的,成功输出HELLO语句
双亲委派机制的作用
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。 2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
沙箱安全机制
Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境,沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏,沙箱主要限制系统资源访问,那系统资源包括什么?CPU,内存,文件系统,网格,不同级别的沙箱对这些资源访问的限制也是可以不一样。
所以的Java程序运行都可以指定沙箱,可以定制安全策略。
在Java中将执行程序分为本地代码呵远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信任的,对于授权的本地代码,可以访问一切本地资源,而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱机制,如下图jdk1.0安全模型
![](https://i-blog.csdnimg.cn/blog_migrate/f0dde790b62842a1b32f8b85fa09c6aa.png)
但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现,因此在后续的Java1.1版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码本地资源的访问权限,如下图所示JDK1.1安全模型
![](https://i-blog.csdnimg.cn/blog_migrate/cda89b5d394415443134d693bc74302e.png)
在Java1.2版本中,再次改进了安全机制,增加了代码签名,不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制,如下图所JDK1.2安全模型
![](https://i-blog.csdnimg.cn/blog_migrate/c9f764c77565765729cbec55cd6872b8.png)
当前最新的安全机制实现,则引入域(Domain)的概念,虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来各种需求的资源进行访问,虚拟机中不同的受保护域,对应不一样的权限,存在不同域中的类文件就具有了当前域的全部权限,如下图所示,最新的安全模型
![](https://i-blog.csdnimg.cn/blog_migrate/9d2c8a6779148de394a0dc4b5cd6ca51.png)
组成沙箱的基本组件:
字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范,这样可以帮助Java程序实现内存保护,但并不是所有的类文件都会经过字节码校验,比如核心类。
类加载器(class loader):其中类加载器在3个方面对Java沙箱起作用
它防止恶意代码去干涉善意的代码;//双亲委派机制
它守护了被信任的类库边界;
它将代码归入保护域,确定了代码可以进行哪些操作;
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间由Java虚拟机为每一个类加载器维护的,它们互相甚至不可见。
类加载器采用的机制是双亲委派模式。
虚拟机为不同的类加载开始加载,外层恶意同名类得不到加载从而无法使用;
由于严格通过包来区分了访问域:外层恶意的类通过内置代码也无法获得权限访问到内置类,破坏代码就自然无法生效。
存取控制器(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。
安全管理器 (security manager):是核心API和操作系统之间的主要接口,实现权限控制,比存取控制器优先级高。
安全软件包(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
安全提供者
信息摘要
数字签名 kettools https(需要证书)
加密
鉴别