JVM-类加载器
类加载系统
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域,如图所示。
类加载器
类加载子系统作用
类加载子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识即16进制CA FE BA BE;加载后的Class类信息存放于一块成为方法区的内存空间。除了类信息之外,方法区还会存放运行时常量池信息,可能还包括字符串字面量和数字常量ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定
功能细分
加载模块
- 通过一个类的全限定明获取定义此类的二进制字节流;
- 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据;
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
链接模块
- 验证(保证字节流中包含信息符合虚拟机要求,文件格式、源数据、字节码、符号引用)
- 准备(类变量的分配内存及初始化,final修饰的静态变量早已分配,实例的变量会在堆中分配)
- 解析(常量池中的符号引用转换为直接引用的过程,针对类、接口、字段、类方法、接口方法、方法类型等)
初始化模块
就是执行类构造器方法clinit()的过程,只有静态变量存在时才会有,按照在源文件中出现的顺序执行且父类的clinit先执行,在多线程情况下一个类的clinit方法被同步加锁
类加载器分类
引导类加载器
java核心类库都是使用引导类加载器BootStrapClassLoader加载的;并不继承自java.lang.ClassLoader,没有父加载器;加载拓展类和应用程序类加载器,并指定为他们的父加载器;BootStrap启动类加载器只加载包名为java、javax、sun等开头的类
自定义类加载器
所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
其中用户自定义类加载器,可以隔离加载类、修改类加载的方式、拓展加载源、防止源码泄露
- 拓展类加载器
从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会由拓展类加载器自动加载 - 应用程序类加载器
它负责加载环境变量classpath或系统属性 java.class.path指定路径下的类库该类加载器是程序中默认的类加载器。一般来说,java应用的类都是由它来完成加载;通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。
/**
* 虚拟机自带加载器
*/
public class ClassLoaderTest1 {
public static void main(String[] args) {
System.out.println("********启动类加载器*********");
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
//获取BootStrapClassLoader能够加载的api路径
for (URL e:urls){
System.out.println(e.toExternalForm());
}
//从上面的路径中随意选择一个类 看看他的类加载器是什么
//Provider位于 /jdk1.8.0_171.jdk/Contents/Home/jre/lib/jsse.jar 下,引导类加载器加载它
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader);
//null
System.out.println("********拓展类加载器********");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")){
System.out.println(path);
}
//从上面的路径中随意选择一个类 看看他的类加载器是什么:拓展类加载器
ClassLoader classLoader1 = CurveDB.class.getClassLoader();
System.out.println(classLoader1);
//sun.misc.Launcher$ExtClassLoader@4dc63996
}
}
- 换句话说,在jvm中,即使这两个类对象(class对象)来源同一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的.
classloader的常用方法及获取方式
双亲委派机制
如果一个类加载器受到类加载请求,不会自己先去加载,而是把请求委托给父类加载器执行,直到顶层的启动类加载器,先父后子;
优势在于:
避免类的重复加载、保护程序安全,防止核心API被随意篡改
沙箱安全机制
Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。
java中的安全模型
-
在java的将执行程序分成本地代码和远程代码,其中远程代码是不受信任的。当用户希望远程代码访问本地系统的文件时,需要权限授予。
-
当前最新的安全机制,引入了域的概念,虚拟机会把所有的代码加载到不同的系统域和应用域中,其中系统域专门负责与关键资源进行交互,应用域通过系统域的部分代理对资源进行访问。
-
API等更复杂的用法能够使得一段受信任代码获得更大权限,甚至比调用它的应用程序更多,来满足一些特殊的应用需要。
组成沙箱的基本组件
- 字节码校验器
确保java类文件遵循java语言规范,帮助java程序实现内存保护,但并不是所有类文件都会经历字节码校验。 - 类装载器
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见 - 存取控制器
存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定 - 安全管理器
核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。 - 安全软件包
ava.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性
沙箱包含的要素
- 权限
包括:权限类型、权限名、允许的操作
permission java.security.AllPermission; //权限类型 permission java.lang.RuntimePermission "stopThread"; //权限类型+权限名 permission java.io.FilePermission "/tmp/foo" "read"; //权限类型+权限名+允许的操作
- 代码源
代码源是类所在的位置,表示为以URL地址 - 保护域
保护域用来组合代码源和权限,这是沙箱的基本概念。保护域就在于声明了比如由代码A可以做权限B这样的事情。 - 策略文件
- 默认策略文件
grant授权允许操作某个权限。这个默认的策略文件就指明了jdk扩展包可以有全部权限,允许代码stop线程,允许监听1099端口(1099号端口,是默认的服务器端RMI监听端口等等
// Standard extensions get all permissions by default
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
}
;
// default permissions granted to all domains
grant {
// Allows any thread to stop itself using the java.lang.Thread.stop()
// method that takes no argument.
// Note that this permission is granted by default only to remain
// backwards compatible.
// It is strongly recommended that you either remove this permission
// from this policy file or further restrict it to code sources
// that you specify, because Thread.stop() is potentially unsafe.
// See the API specification of java.lang.Thread.stop() for more
// information.
permission java.lang.RuntimePermission "stopThread";
// allows anyone to listen on dynamic ports
permission java.net.SocketPermission "localhost:0", "listen";
// permission for standard RMI registry port
permission java.net.SocketPermission "localhost:1099", "listen";
// "standard" properies that can be read by anyone
permission java.util.PropertyPermission "java.version", "read";
permission java.util.PropertyPermission "java.vendor", "read";
permission java.util.PropertyPermission "java.vendor.url", "read";
permission java.util.PropertyPermission "java.class.version", "read";
permission java.util.PropertyPermission "os.name", "read";
permission java.util.PropertyPermission "os.version", "read";
permission java.util.PropertyPermission "os.arch", "read";
permission java.util.PropertyPermission "file.separator", "read";
permission java.util.PropertyPermission "path.separator", "read";
permission java.util.PropertyPermission "line.separator", "read";
permission java.util.PropertyPermission "java.specification.version", "read";
permission java.util.PropertyPermission "java.specification.vendor", "read";
permission java.util.PropertyPermission "java.specification.name", "read";
permission java.util.PropertyPermission "java.vm.specification.version", "read";
permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
permission java.util.PropertyPermission "java.vm.specification.name", "read";
permission java.util.PropertyPermission "java.vm.version", "read";
permission java.util.PropertyPermission "java.vm.vendor", "read";
permission java.util.PropertyPermission "java.vm.name", "read";
}
;
- 参数文件
这个文件和策略文件在同一个目录下。这个参数文件定义了沙箱的一些参数。
policy.url.这个属性指明了使用的策略文件,如上文所述,默认的两个位置就在这里配置,用户可以自行更改顺序和存储位置。而policy.allowSystemProperty指明是否允许用户自行通过命令行指定policy文件。
- 密钥库
保存密钥证书的地方
如何使用
通过Java命令行启动的Java应用程序,默认不启用沙箱。要想启用沙箱,启动命令需要做如下形式的变更:
java -Djava.security.manager <other args>
沙箱启动后,安全管理器会使用两个默认的策略文件来确定沙箱启动参数。当然也可以通过命令指定:
java -Djava.security.policy=<URL>
如果要求启动时只遵循一个策略文件,那么启动参数要加个等号,如下
java -Djava.security.policy==<URL>
详情链接:
https://www.cnblogs.com/MyStringIsNotNull/p/8268351.html
类的主动使用和被动使用
主动使用,分为七种情况
-
创建类的实例
-
访问某各类或接口的静态变量,或者对静态变量赋值
-
调用类的静态方法反射 比如Class.forName(com.dsh.jvm.xxx)
-
初始化一个类的子类
-
java虚拟机启动时被标明为启动类的类
-
JDK 7 开始提供的动态语言支持:
java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化 -
除了以上七种情况,其他使用java类的方式都被看作是对类的被动使用,都不会导致类的初始化。