JVM笔记

img

Java代码执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-smm9bR4f-1628844016582)(JVM.assets/image-20210805171335842.png)]

任何一个环节失败都不能正确生成字节码文件。

JVM的生命周期

  • 虚拟机的启动
    • 是通过引导类加载器创建一个初始类来完成的。
  • 虚拟机的执行
    • 程序开始就运行,程序结束就停止
    • 执行一个java程序时,实际上执行的是java虚拟机的进程
  • 虚拟机的退出
    • 程序正常执行结束
    • 遇到异常或者错误
    • 操作系统的错误
    • 调用Runtime类或者System类exit方法

手写一个JVM的需要类加载器和执行引擎

类加载器

java字节码文件起始的[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APaVDV21-1628844016584)(JVM.assets/image-20210805194224618.png)]

类加载的过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GMM7vVxd-1628844016589)(JVM.assets/image-20210805193039442.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aj9nRtx7-1628844016591)(JVM.assets/image-20210805193901572.png)]

类加载器的分类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9bsCaxHG-1628844016593)(JVM.assets/image-20210805203313401.png)]

  • 引导类加载器(Bootstrap Class Loader)
    • C/C++实现,嵌套在JVM内部
    • 加载核心类库
    • 出于安全考虑只加载Java、Javax、Sun等开头的类
  • 扩展类加载器(Extension Class Loader)
    • 派生于Class Loader类
  • 系统类加载器(App Class Loader)
    • 派生于Class Loader类
    • 负责加载环境变量classpath或系统属性
    • 该加载器时程序中默认的加载器,Java引用类都是由它完成
  • 自定义加载器(User-Defined Class Loader)
    • 直接或者间接继承了Bootstrap Class Loader
    • 扩展类加载器和系统类加载器都是自定义加载类
  • 不是继承关系,并且一般的类不能获取到引导类加载器
  • Java的核心类库都是使用引导类加载器加载的
类加载过程
  1. 加载阶段
    1. 通过一个类的全限定名获取此类二进制字节流
    2. 将这个字节流所代表静态存储结构转化为方法区的运行时数据结构
    3. 在内存中生成Class对象,作为方法区这个类的各种数据的访问入口
  2. 链接阶段
    1. 验证(Verify)
      • 确保Class文件的正确性
      • 四种验证(文件格式验证、元数据验证、字节码验证、符号引用验证)
    2. 准备(Prepare)
      • 分配内存、赋初始值(零值或者null)
      • 不包含final修饰的static,因为final在编译时就分配了值(显式初始化)
    3. 解析(Resolve)
      • 将常量池中的符号引用转化为直接引用的过程
  3. 初始化阶段
    • 初始化阶段就是执行类构造器方法<clinit>()的过程
    • 此方法不需要定义,自动收集类中所有类变量的赋值动作和静态代码块中的语句
    • 任何一个类声明以后,内部的至少有一个构造器<init>()

双亲委派机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ohifaCGu-1628844016594)(JVM.assets/image-20210805210156482.png)]

  1. 类加载器收到类加载请求,它并不会自己区加载,而是把这个请求委托给父类加载器去执行
  2. 如果父类加载器还有父类,继续委托达到引导类加载器
  3. 随后,若当前加载器能加载完成则返回,若不能,则让子类加载器去完成。

**沙箱安全机制:**不能在java.lang包自定义类,或者重写其他类

运行时数据区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81hrEMNe-1628844016594)(JVM.assets/image-20210806092834925.png)]

PC寄存器

存储运行的下一条指令

问:使用PC寄存器字节码指令地址有什么用?

答:因为CPU需要不停的切换线程,切换回来的时候就知道接着从哪里继续执行

问:为什么使用PC寄存器记录当前线程的执行地址?

答:JVM的字节码编译器就需要通过改变PC寄存器的值来明确下一条字节码指令

JVM栈

栈帧包含(局部变量表,操作数栈,动态链接,方法返回地址,附加信息)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aPYqbw2B-1628844016596)(JVM.assets/image-20210806102046692.png)]

局部变量表(数字数组):方法参数,局部变量,数据基本类型,对象引用。

操作数栈(主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间)

动态链接(链接到本地方法栈)

栈是不存在垃圾回收的问题,但存在OOM(内存溢出)

-Xss 1024k(设置栈的大小)

问:举例栈溢出的情况?

答:StackOverflowError、OOM

问:调整栈大小,就能保证不出现溢出吗?

答:不能,如果使用递归没有终止的情况

问:分配的栈内存越大越好吗?

答:不是,挤占别的空间

问:垃圾回收会涉及到栈吗?

答:不会,因为只有进栈和出栈,不存在垃圾

问:方法中定义的局部变量是否线程安全?

答:具体问题具体分析(对象内部产生内部消亡那个的是安全的,否则不是)

本地方法栈

与JVM栈发挥的作用相似,区别:虚拟机栈为虚拟机执行java方法服务,而本地方法栈使用的是Native方法服务。

  1. 本地方法栈就是管理本地方法的
  2. 本地方法是使用C语言实现的
  3. 与本地方法库打交道
  • 所有的对象实例以及数组都应当运行时分配在堆上
  • 数组和对象永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置
  • 方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除
  • 堆时GC执行垃圾回收的重点区域
  • -Xms(起始内存)【年轻代和老年代】
  • -Xmx(最大内存)
内存细分
  • java7之前:新生区+养老区+永久区(方法区)
  • java8之后:新生区+养老区+元空间
  • 新生区=伊甸园区+s0+s1(比例8:1:1)
  • 1/3新生代,2/3老年区
  • 当伊甸园区满了触发MinorGC
  • 当达到阈值15的时候,晋升(Promotion)到老年代
  • 大对象直接进入老年代

MinorGC、MajorGC、FullGC

  • MinorGC:只是新生代的垃圾回收
    • 只有伊甸园区满了才会触发,幸存区满不会
  • MajorGC:只是老年代的垃圾回收
    • 执行之前至少执行一次MinorGC
    • 目前只有CMS GC会有单独收集老年代的行为
  • Mixd GC:收集整个新生代以及部分老年代的垃圾回收
    • 目前,只有G1 GC会有这种行为
  • Full GC:收集整个java堆和方法区的垃圾收集
方法区(原空间)
  • 独立于堆的内存空间
  • 方法区的大小决定了系统可以保存多少个类
    • 加载大量的第三方jar包
    • 部署工程过多
    • 大量动态生成的反射类
    • 以上都会造成方法区溢出
  • 类型信息、运行时常量池、静态变量、即时编译后的代码缓存
    • 运行时常量池
      • 是方法区的一部分
      • 常量池表存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中
      • 具备动态性
  • 回收
    • 只要常量池中的常量没有被任何地方引用,就可以被回收
    • 判断类是否可以被回收
      • 该类的所有实例被回收
      • 加载该类的加载器被回收(很难达成)
      • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

执行引擎

java是半编译半解释型语言

  • 解释器(解析执行)
    • 逐行执行翻译成机器指令,效率低下,但响应速度快
    • 字节码解释器、模板解释器(与模板函数相关联)
  • JIT编译器(编译执行)
    • 字节码编译程机器指令(翻译成机器指令耗时)
    • 将热点代码缓存
    • 栈上替换
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qAwnMf6h-1628844016597)(JVM.assets/image-20210807103553597.png)]
    • 热点代码缓存在元空间,在jdk8以后元空间使用的是本地内存,故本地内存也可以
    • 热度衰减:超过一定时间限度,这个方法的调用计数器就会被减少一半

本地方法库

  • 与java环境外的交互
  • 与操作系统交互
  • Sun’s Java
  • native关键字

对象实例化

  • 创建对象的方式
    • new
    • Class的newInstance():jdk8能用,jdk9过时(使用起来苛刻,只能调用无参构造,权限必须是public)
    • Constructor的newInstance(XXX):反射,可以调用无参构造和有参构造,权限没有要求
    • 使用clone():不调用任何构造器,当前类需要实现Cloneable接口,实现clone方法
    • 使用反序列化:从文件或者网络中获取对象的二进制流
    • 第三方库Objenesis
  • 创建对象的步骤
    1. 判断对象对应的类是否被加载、链接、初始化
    2. 为对象分配内存
    3. 处理并发安全问题
    4. 初始化分配到的空间
    5. 设置对象头
    6. 执行init方法进行初始化

四大垃圾回收算法

标记阶段
  1. 引用计数算法
    • 有引用计数器就+1,引用失效计数器就-1,只要计数器为0就回收
    • 没法处理循环引用的情况,导致java的垃圾回收器没用使用这一算法
  2. 可达性分析算法
    • 以根对象(GC Roots)集合为起始点,从上之下搜索对象是否可达,不可达的就是垃圾
    • GC Roots:
      • 虚拟机栈中引用的对象
      • 本地方法栈内引用的对象
      • 方法区中类静态属性引用的对象
      • 方法区中常量引用的对象
    • 解决了循环引用的问题
清除阶段
  1. 标记-清除算法(Mark-Sweep)
    • 停止整个程序(STW stop the world),需要停顿,所以体验不好,效率也不高
    • 标记:从根节点出发,标记到非垃圾对象
    • 清除:对堆内存从头到尾进行遍历,如果发现某个对象在其Header中没有被标记,将其回收
  2. 复制算法(Copying)
    • 准备两块相同的空间AB,A存数据,清除的时候把有用的复制到B,随后清除A,然后交换
    • 运行高效,没有碎片
    • 但是需要两倍的内存空间,开销大
    • 适用于年轻代的垃圾回收
  3. 标记-压缩算法(Mark-Compact)
    • 从根节点出发,标记可用对象
    • 将存活对象压缩到内存的一段,按顺序存放(连续存储)
    • 清除其余空间的所有无用对象
    • 适用于老年代的垃圾回收
    • 效率比较低
  4. 分代收集算法
    • 新生代采用复制算法
    • 老年代采用标记-清除和标记-压缩算法

七大垃圾回收器

  1. Serial单线程垃圾回收器
    • 要停止所有工作线程
    • 默认新生代收集器
  2. ParNew垃圾收集器
    • 是Serial的多线程版
    • 新生代
  3. Parallel Scavenge并行垃圾回收器
    • 新生代收集器
    • 使用的复制算法
    • 多线程
  4. Serial Old垃圾回收器
    • 老年代收集器
    • 单线程
    • 标记-压缩算法
  5. Parallel Old垃圾回收器
    • 老年代收集器
    • 多线程
    • 标记-压缩算法
  6. CMS垃圾回收器
    • 标记-清除算法
    • 并发收集、低停顿
  7. G1垃圾收集器
    • 标记-压缩算法
    • 精确的控制停顿,明确指定一个时间段内执行
      rial的多线程版
    • 新生代
  8. Parallel Scavenge并行垃圾回收器
    • 新生代收集器
    • 使用的复制算法
    • 多线程
  9. Serial Old垃圾回收器
    • 老年代收集器
    • 单线程
    • 标记-压缩算法
  10. Parallel Old垃圾回收器
    • 老年代收集器
    • 多线程
    • 标记-压缩算法
  11. CMS垃圾回收器
    • 标记-清除算法
    • 并发收集、低停顿
  12. G1垃圾收集器
    • 标记-压缩算法
    • 精确的控制停顿,明确指定一个时间段内执行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三万少女的梦中刺客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值