在HotSpot虚拟机中, 对象在内存中存储的布局可以分为3块区域
对象头包含的数据有
32位JVM下的长度
64位JVM下的长度
64位JVM开启指针压缩下的长度
https://zhuanlan.zhihu.com/p/60328095
https://hesey.wang/2011/04/introduction-to-java-virtual-machine.html
类加载器子系统负责加载编译好的.class字节码文件,并装入内存,使JVM可以实例化或以其它方式使用加载后的类。JVM的类加载子系统支持在运行时的动态加载,动态加载的优点有很多,例如可以节省内存空间、灵活地从网络上加载类,动态加载的另一好处是可以通过命名空间的分隔来实现类的隔离,增强了整个系统的安全性。
1、ClassLoader 的分类:
2、ClassLoader的工作原理
类加载分为装载、链接、初始化三步
Load 装载
ClassLoader通过类的全称加载类, 将指定的.class文件加载至JVM. 当类被加载以后, 在JVM内部就以类的全称+ ClassLoader实例ID
来标明类. ClassLoader实例和类的实例都位于堆中, 它们的类信息都位于方法区.
装载过程采用了一种被称为双亲委派模型(Parent Delegation Model)
的方式, 即上溯委托, 当一个ClassLoader要加载类时, 它会先请求它的上一级ClassLoader去加载, 而它的上一级ClassLoader会继续把加载请求提给再上一级的ClassLoader, 直到启动类加载器. 只有其所有上级ClassLoader无法加载指定的类时, 它才会自己去加载.
Parent Delegation Model保证了类的安全加载, 这里同时依赖了类加载器隔离的原理: 不同类加载器加载的类之间是无法直接交互的, 即使是同一个类, 被不同的ClassLoader加载, 它们也无法感知到彼此的存在. 这样即使有恶意的类冒充自己在核心包(例如java.lang)下, 由于它无法被启动类加载器加载, 也造成不了危害. 由此也可见, 如果用户自定义了类加载器, 也必须自己保障类加载过程中的安全.
Link 链接
链接的任务是把二进制的类型信息合并到JVM运行时状态中去, 链接分为以下三步:
Initialize 初始化
初始化类中的静态变量, 并执行类中的static代码和构造函数. JVM规范严格定义了何时需要对类进行初始化
类加载的时机
JVM 默认用于加载用户程序的ClassLoader为AppClassLoader, 不过无论是什么ClassLoader, 它的根父类都是java.lang.ClassLoader.
loadClass() 方法最终会调用到ClassLoader.definClass1()中, 这是一个 Native 方法.
其他就是上溯委托加载
Java虚拟机把描述类的数据从Class文件加载进内存, 并对数据进行校验, 转换解析和初始化, 最终形成可以被虚拟机直接使用的Java类型, 这就是虚拟机的类加载机制.
虚拟机设计团队把类加载阶段中的通过一个类的全限定名来获取描述此类的二进制字节流
这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类, 实现这动作的代码模块成为类加载器
类加载器就是根据指定全称将class文件加载到JVM内存, 转为Class对象. 如果站在JVM的角度来看, 只存在两种类加载器:
类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载他的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类命名空间。这句话可以表达的更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来自同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那这个两个类就必定不相等
The Java platform uses a delegation model for loading classes. The basic idea is that every class loader has a parent
class loader. When loading a class, a class loader first delegates
the search for the class to its parent class loader before attempting to find the class itself.
双亲委派的原文是parents delegate
. 实际上这个模型中, 只是表达“上一辈”的class loader而已, 并不是说真的有两个父辈的class loader, 翻译成上溯委托
是更合适的.
ClassLoader通过类的全称加载类, 将指定的.class文件加载至JVM. 当类被加载以后, 在JVM内部就以类的全称+ ClassLoader实例ID
来标明类. ClassLoader实例和类的实例都位于堆中, 它们的类信息都位于方法区.
装载过程采用了一种被称为双亲委派模型(Parent Delegation Model)
的方式, 即上溯委托, 当一个ClassLoader要加载类时, 它会先请求它的上一级ClassLoader去加载, 而它的上一级ClassLoader会继续把加载请求提给再上一级的ClassLoader, 直到启动类加载器. 只有其所有上级ClassLoader无法加载指定的类时, 它才会自己去加载.
Parent Delegation Model保证了类的安全加载, 这里同时依赖了类加载器隔离的原理: 不同类加载器加载的类之间是无法直接交互的, 即使是同一个类, 被不同的ClassLoader加载, 它们也无法感知到彼此的存在. 这样即使有恶意的类冒充自己在核心包(例如java.lang)下, 由于它无法被启动类加载器加载, 也造成不了危害. 由此也可见, 如果用户自定义了类加载器, 也必须自己保障类加载过程中的安全.
Tomcat类加载的要求
Tomcat加载器的实现
最上级3个类加载和默认的一致, 还是Bootstrap, Extension和Application, 但是新增加了四个Tomcat自己定义的类加载器: CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader, 它们分别加载/common/、/server/、/shared/ (在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/中的Java类库. 其中WebApp类加载器和Jsp类加载器通常会存在多个实例, 每一个Web应用程序对应一个WebApp类加载器, 每一个JSP文件对应一个Jsp类加载器.
tomcat 为了实现隔离性, 没有遵守上溯委托
的约定, 每个webapp ClassLoader加载自己的目录下的class文件, 不会传递给父类加载器.
Common ClassLoader
Tomcat最基本的类加载器, 加载路径中的class可以被Tomcat容器本身以及各个Webapp访问. CommonClassLoader能加载的类都可以被 Catalina ClassLoader和SharedClassLoader使用, 从而实现了公有类库的共用, 而Catalina ClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离.
Catalina ClassLoader
Tomcat容器私有的类加载器, 加载路径中的class对于Webapp不可见
Shared ClassLoader
各个Webapp共享的类加载器, 加载路径中的class对于所有Webapp可见, 但是对于Tomcat容器不可见
Webapp ClassLoader
各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见. WebAppClassLoader可以使用SharedClassLoader加载到的类, 但各个WebApp ClassLoader实例之间相互隔离.
Jasper ClassLoader
这个ClassLoader 加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件, 它出现的目的就是为了被丢弃, 当Web容器检测到JSP文件被修改时, 会替换掉目前的JasperLoader的实例, 并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能
手机扫一扫
移动阅读更方便
你可能感兴趣的文章