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这样的事情。
  • 策略文件
  1. 默认策略文件
    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";
}
;
  1. 参数文件
    这个文件和策略文件在同一个目录下。这个参数文件定义了沙箱的一些参数。
    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类的方式都被看作是对类的被动使用,都不会导致类的初始化。

原文地址:https://www.cnblogs.com/suit000001/p/13562290.html