Java对象的结构

在HotSpot虚拟机中, 对象在内存中存储的布局可以分为3块区域

  • 对象头Header
  • 实例数据Instance Data
  • 对齐填充Padding

对象头包含的数据有

  1. markword 用于存储对象自身的运行时数据, 如HashCode, GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit, 官方称它为MarkWord
  2. klass, klass类型指针, 即对象指向它的类元数据的指针. 虚拟机通过这个指针来确定这个对象是哪个类的实例.
  3. 只有数组对象有 - 数组长度. 如果对象是一个数组, 那在对象头中还必须有一块数据用于记录数组长度.

32位JVM下的长度

  1. Class指针的空间大小是4字节,MarkWord是4字节,对象头为8字节
  2. 数组增加4字节数组长度, 对象头为12字节

64位JVM下的长度

  1. Class指针的空间大小是8字节,MarkWord是8字节,对象头为16字节
  2. 数组增加4字节数组长度, 对象头为20字节

64位JVM开启指针压缩下的长度

  1. Class指针的空间大小是4字节,MarkWord是8字节,对象头为12字节
  2. 数组增加4字节数组长度, 对象头为16字节

Java 类加载过程?

https://zhuanlan.zhihu.com/p/60328095

https://hesey.wang/2011/04/introduction-to-java-virtual-machine.html

类加载器子系统负责加载编译好的.class字节码文件,并装入内存,使JVM可以实例化或以其它方式使用加载后的类。JVM的类加载子系统支持在运行时的动态加载,动态加载的优点有很多,例如可以节省内存空间、灵活地从网络上加载类,动态加载的另一好处是可以通过命名空间的分隔来实现类的隔离,增强了整个系统的安全性。

1、ClassLoader 的分类:

  • 启动类加载器 BootStrap Class Loader: 负责加载rt.jar文件中所有的Java类, 即Java的核心类都是由该ClassLoader加载. 在Sun JDK中这个类加载器是由C++实现的, 并且在Java语言中无法获得它的引用.
  • 扩展类加载器 Extension Class Loader: 负责加载一些扩展功能的jar包
  • 应用类加载器 Application Class Loader: 负责加载启动参数中指定的Classpath中的jar包及目录, 通常我们自己写的Java类也是由该ClassLoader加载. 在Sun JDK中系统类加载器的名字叫AppClassLoader
  • 用户自定义类加载器 User Defined Class Loader: 由用户自定义类的加载规则, 可以手动控制加载过程中的步骤

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运行时状态中去, 链接分为以下三步:

    • 验证: 校验.class文件的正确性, 确保该文件是符合规范定义的,并且适合当前JVM使用
    • 准备: 为类分配内存, 同时初始化类中的静态变量赋值为默认值
    • 解析(可选): 主要是把类的常量池中的符号引用解析为直接引用, 这一步可以在用到相应的引用时再解析
  • Initialize 初始化

    初始化类中的静态变量, 并执行类中的static代码和构造函数. JVM规范严格定义了何时需要对类进行初始化

    • 通过new关键字, 反射, clone, 反序列化机制实例化对象时
    • 调用类的静态方法时
    • 使用类的静态字段或对其赋值时
    • 通过反射调用类的方法时
    • 初始化该类的子类时, 初始化子类前其父类必须已经被初始化
    • JVM启动时被标记为启动类的类, 简单理解为具有main方法的类

描述一下 JVM 加载 Class 文件的原理机制?

类加载的时机

  1. 遇到new、getstatic、putstatic 等指令时
  2. 对类进行反射调用的时候
  3. 初始化某个类的子类的时候
  4. 虚拟机启动时会先加载设置的程序主类
  5. 使用JDK 1.7 的动态语言支持的时候

JVM 默认用于加载用户程序的ClassLoader为AppClassLoader, 不过无论是什么ClassLoader, 它的根父类都是java.lang.ClassLoader.

loadClass() 方法最终会调用到ClassLoader.definClass1()中, 这是一个 Native 方法.

其他就是上溯委托加载

什么是类加载器?

Java虚拟机把描述类的数据从Class文件加载进内存, 并对数据进行校验, 转换解析和初始化, 最终形成可以被虚拟机直接使用的Java类型, 这就是虚拟机的类加载机制.

虚拟机设计团队把类加载阶段中的通过一个类的全限定名来获取描述此类的二进制字节流这个动作放到Java虚拟机外部去实现, 以便让应用程序自己决定如何去获取所需要的类, 实现这动作的代码模块成为类加载器

类加载器就是根据指定全称将class文件加载到JVM内存, 转为Class对象. 如果站在JVM的角度来看, 只存在两种类加载器:

  • 启动类加载器Bootstrap ClassLoader 由C++语言实现(针对HotSpot), 负责将存放在<JAVA_HOME>\lib 目录或 -Xbootclasspath 参数指定的路径中的类库加载到内存中
  • 其他类加载器 由Java语言实现, 继承自抽象类ClassLoader

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载他的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类命名空间。这句话可以表达的更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来自同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那这个两个类就必定不相等

类加载器有哪些?

  • 启动类加载器 BootStrap Class Loader: 负责加载rt.jar文件中所有的Java类, 即Java的核心类都是由该ClassLoader加载. 在Sun JDK中这个类加载器是由C++实现的, 并且在Java语言中无法获得它的引用.
  • 扩展类加载器 Extension Class Loader: 负责加载一些扩展功能的jar包
  • 应用类加载器 Application/System Class Loader: 负责加载启动参数中指定的Classpath中的jar包及目录, 通常我们自己写的Java类也是由该ClassLoader加载. 在Sun JDK中系统类加载器的名字叫AppClassLoader
  • 用户自定义类加载器 User Defined Class Loader: 由用户自定义类的加载规则, 可以手动控制加载过程中的步骤

什么是类加载器双亲委派模型?

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类加载的要求

  • 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
  • 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机,这是不合理的。
  • web容器也有自己依赖的类库,不能于应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。
  • web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情,所以,web容器需要支持 jsp 修改后不用重启。

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功能

JVM专题1: 类和类加载机制的更多相关文章

  1. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  2. JVM,Tomcat与OSGi类加载机制比较

    首先一个思维导图来看下Tomcat的类加载机制和JVM类加载机制的过程 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加载. 比如JVM启动时,会通过不同的类加载器加载 ...

  3. 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)

    目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...

  4. JVM活学活用——类加载机制

    类的实例化过程 有父类的情况 1. 加载父类静态    1.1 为静态属性分配存储空间并赋初始值     1.2 执行静态初始化块和静态初始化语句(从上至下) 2. 加载子类静态    2.1 为静态 ...

  5. JVM学习笔记(四):类加载机制

    虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 一.类加载的时机1. 类从被加载到虚拟机内存 ...

  6. JVM学习第三天(JVM的执行子系统)之类加载机制补充

    昨晚没看完,今天继续 系统的类加载器 对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间.这句话可以表达得更通俗一些: ...

  7. JVM(14)之 类加载机制

    开发十年,就只剩下这套架构体系了! >>>   从本篇博文开始,我们就进入虚拟机类加载机制的学习了.那么什么是类加载呢?当我们写完一个Java类的时候,并不是直接就可以运行的,它还要 ...

  8. JVM学习第三天(JVM的执行子系统)之类加载机制

    好几天没有学习了,前几天因为导出的事情,一直在忙,今天继续学习, 其实今天我也遇到了一个问题,如果有会的兄弟可以评论留给我谢谢; 问题:fastJSON中JSONObject.parseObject做 ...

  9. JVM加载类的原理机制

    在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载.链接和初始化,其中链接又可以分成校验.准备.解析装载:查找和导入类或接口的二进制数据: 链接:执行下面的校验.准备和解析 ...

随机推荐

  1. 【LeetCode】743. Network Delay Time 解题报告(Python)

    [LeetCode]743. Network Delay Time 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博客: ht ...

  2. Subsequence(hdu3530)

    Subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  3. 关于 TCP/IP

    基于TCP/IP的参考模型将协议分成四个层次,它们分别是链路层.网络层.传输层和应用层. (1)应用层:这里面有http,ftp 等等我们熟悉的协议. (2)传输层:著名的TCP和UDP协议就在这个层 ...

  4. vue 滚动公告

    <!-- 滚动公告 --> <div class="textArr"> <p class="slice-enter-active" ...

  5. Android物联网应用程序开发(智慧园区)—— 登录界面开发

    效果: 布局代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:an ...

  6. 编写Java程序,使用JFrame创建一个窗体

    返回本章节 返回作业目录 需求说明: 使用JFrame创建一个窗体 实现思路: 使用JFrame创建窗体的思路 定义一个窗体对象f,窗体名称为"一个简单窗口" 设置窗体左上角与显示 ...

  7. [学习笔记] RabbitMQ的简单使用

    安装依赖 # composer.json { "require": { "php-amqplib/php-amqplib": ">=2.9.0& ...

  8. 分区命令(大于2TB的分区)

    注意:parted命令在恢复误删除的分区时候,容易失败的几点: (1)只划分一个分区.恢复失败 (2)划分了2个分区,但是没有格式化.直接删除一个分区,恢复也会失败. (3)做删除操作时候,如果同时删 ...

  9. monkey介绍及常用命令

    前置准备: adb:用来连接安卓手机和PC端的桥梁,要有adb作为两者之间的维系,才能在电脑对手机进行全面的操作.(adb push 文件路径 到手机路径  adb pull 从手机拉取到电脑) mo ...

  10. Go语言系列之标准库strconv

    Go语言中strconv包实现了基本数据类型和其字符串表示的相互转换. strconv包 strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数: Atoi().Itia().pa ...