JVM 知识点总结

一、内存模型

  1. 程序计数器:线程相关,记录当前线程执行代码的行号位置信息,以便线程切换后回到正确的执行位置;
  2. 虚拟机栈:线程相关,存储虚拟机方法执行信息,每一个方法的执行过程对应一次的入栈和出栈,栈内存储局部变量表,操作数栈,动态链接,方法返回地址,编译时可确定栈大小。局部变量表存放了编译期可知的基本数据类型和引用类型的地址指针;
  3. 本地方法栈:线程相关native 方法的栈信息;
  4. 直接内存:不受虚拟机内存大小限制,供 jdk nio 使用,主要避免数据在java内存和系统内存间的相互复制;
  5. 堆:java中最大的一块内存区域,所有的数组和对象的实例都需要在堆内存中分配;
  6. 方法区:存储已被虚拟机加载的类信息,常量,静态变量,(JIT)即时编译后的代码数据;
  7. 运行时常量池:归属方法区,存放编译期生成的各种字面量和符号引用,这些数据在类加载后放入;
字面量:int a = 8; 8就是a的字面量
符号引用:User u; 以User 为例,会产生一个"LUser;"的字符串,不包括引号。
在编译代码后,数据类型,属性名称,以及基本数据类型的值都会存储在字节码常量池中,然后在属性区域,方法区域对这些值进行引用,产生对应的虚拟机代码。

二、垃圾回收算法

  1. 标记-清除,会产生内存碎片
    把内存分成一个个小格,对正在使用的打上标记,并清空未标记的区域。
  2. 标记-复制,浪费空间,优化 eden:from:to=8:1:1
    把内存进行切分,例如分成A,B两块,每块有n格小格,假设当前正在使用区域A;对正在使用的打上标记,全部转移到区域B,清空A区域,下一次角色互换;
  3. 标记-整理,整理内存需要花费时间
    把内存分成一个个小格,对正在使用的打上标记,把所有使用中的区域移动至内存的开头或结尾,然后清空未使用的区域。

三、垃圾收集器分类

  1. serial new : 采用标记-复制算法,单线程,需要停顿用户线程
  2. parallel new:采用标记-复制算法,多线程,需要停顿用户线程,吞吐量优先;
  3. parnew : 采用标记-复制算法,多线程,需要停顿用户线程
  4. serial old 标记-整理算法,单线程
  5. parallel old 标记-整理算法,多线程,吞吐量优先
  6. CMS 老年代回收器,采用标记-清除算法,多线程;初始标记(需停顿用户线程),并发标记,重新标记(需停顿用户线程),清除;会产生内存碎片,需要定期整理内存, 低停顿
  7. G1
  8. 其他问题:对象存活死亡状态分析,引用计数,可达性分析

四、工具

  1. jps:查询java进程pid
  2. jstat:虚拟机统计信息监视工具,例如gc,内存情况,类加载信息
  3. jinfo: 查询虚拟机配置参数
  4. jmap:转储堆内存
  5. jhat:虚拟机堆快照分析工具
  6. jstack:java线程堆栈跟踪工具
  7. jconsole java监视,管理控制台
  8. visual vm 功能类似jconsole

五、类信息Class

  1. 类格式:两种基本数据类型
    1. 无符号数,可表示1,2,4,8字节的数据;可以描述数字、索引引用,数值,字符串
    2. 表,可以表示多个无符号数或其他表构成的数据结构;

六、类加载机制

符号引用:是用一组字符串描述所引用的目标;引用的对象不一定存在;
直接引用:直接引用时直接指向目标对象的指针,偏移量或	是一个能定位到目标的句柄。引用的类型必须在内存中存在;

1. 类生命周期

​ 加载-->链接(验证-->准备-->解析)-->初始化-->使用-->卸载;

2. 初始化

​ 何时必须初始化,有且仅有这5种情况:

1. 遇到new (new 关键字),getstatic,读取static字段,putstatic,设置static字段,invokestatic 调用静态方法
2.  使用reflect包对类进行反射,且类未初始化时
3. 初始化时,如果父类未初始化,需先初始化父类
4. 虚拟机启动时的启动类
5. 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
6. 子类引用父类静态字段,不会触发子类初始化

3. 类加载过程

  1. 加载:获取类的二进制字节流
    1. 通过类的全限定名获取二进制字节流
    2. 将字节流代表的数据结构转化为方法区运行时数据结构
    3. 在内存中生成代表这个类的Class对象,作为方法区这个类的访问入口
  2. 验证:验证是为了验证类字节流的合法性,保护虚拟机自身安全。
    1. 文件格式验证:验证字节流是否符合当前版本虚拟机规范,例如:魔数,版本号,常量池,符号引用是否存在
    2. 元数据验证:是否存在父类,父类是否允许继承,重写方法,是否实现父类接口方法,抽象方法
    3. 字节码验证:校验语法语意是否合法
    4. 符号引用验证:时机,符号引用转换直接引用时,通过符号引用是否能查找到对应的类,是否存在相应的方法,字段,访问性校验
  3. 准备:为类变量分配内存并设置初始值
    1. 为类变量分配内存并设置初始值(0值)(static 变量,不包括类实例变量);final 变量会被设置对应的常量值;
  4. 解析:虚拟机把常量池中的符号引用转换为直接引用;仅有常量池的?答:(所有的符号引用都在常量池存储)
    1. 虚拟机要求在执行anewarray、 checkcast、getfield、getstatic、instanceof、invokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、multianewarray、new、putfield和putstatic这16个用于 操作符号引用的字节码指令之前,先对它们所使用的符号引用进行解析
    2. 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点 限定符7类符号引用进行;
      1. 类或接口的解析:如果有一类D,需要把一个符号引用解析为对类或接口C的直接引用需要的3个步骤
        1. 如果C不是一个数组类型,虚拟机会把代表C的全限定名传递给类加载器去加载这个类,同时会验证这个类,且又可能触发其他类的加载,例如父类,父接口。
        2. 如果C是一个数组类型,并且数组的元素类型为对象,那么首先会加载解析类,接着虚拟机生成一个代表此数组的对象;
        3. 验证类D对类C的访问权限
      2. 字段解析:解析类C的一个未被解析过的字段符号引用
        1. 要解析一个未被解析过的字段符号引用,首先将会对字段表内class_index[2]项中索引的 CONSTANT_Class_info符号引用进行解析,也就是字段所属的类或接口的符号引用
        2. 如果类C包含了简单的名称和字段描述都与目标相匹配的字段,则返回这个字段的直接引用。
        3. 如果类C不是Object类时,会按照继承关系查找父类对应的字段;
        4. 查找成功后会进行访问权限验证,查找失败抛出NoSuchFieldError;
      3. 类方法解析:解析类C的一个方法引用
        1. 要解析一个未被解析过的字段符号引用,首先将会对字段表内class_index[2]项中索引的 CONSTANT_Class_info符号引用进行解析,也就是字段所属的类或接口的符号引用
        2. 检查类方法表的class_index项,类方法和接口方法符号引用表是分开的,如果该项是一个接口时,直接抛出java.lang.IncompatibleClassChangeError异常。
        3. 在类C中查找名称和描述符都匹配的方法,如果存在直接返回对应的引用
        4. 如果类C中不存在则递归查询父类对象对应的方法,存在直接返回
        5. 如果不存在,则在类C实现的接口列表及查父接口中递归查找,如果存在说明时一个C是抽象类,抛出AbstractMethodError
        6. 查找失败抛出NoSuchMethodError,查找成功需要进行访问权限验证
      4. 接口方法解析:解析接口C的一个符号引用
        1. 要解析一个未被解析过的字段符号引用,首先将会对字段表内class_index[2]项中索引的 CONSTANT_Class_info符号引用进行解析,也就是字段所属的类或接口的符号引用
        2. 检查接口方法表中的class_index中的索引C是一个类,而不是接口时抛出java.lang.IncompatibleClassChangeError异常
        3. 在接口C中查找匹配的方法,有则返回
        4. 如果没有则递归查找父类接口方法,有则返回
        5. 查找失败抛出:NoSuchMethodError。
        6. 接口不需要进行访问权限验证
    3. 初始化:初始化才开始执行java代码
      1. Static 代码块
      2. 构造函数
      3. 父类优先子类
      4. 实际的代码赋值

七、类加载器

  1. Bootstrap ClassLoader
  2. Extension ClassLoader
  3. Application ClassLoader
  4. 优先使用父类加载器加载,父类加载器加载不到时再自己加载类。
posted @ 2023-02-20 11:26  铵铵静静  阅读(22)  评论(0编辑  收藏  举报