JVM知识整理

jvm

类的加载过程

类的加载与卸载

  • loading

    • 使用双亲委派机制把class文件按二进制读取到内存中
  • linking

    • verificaltion

      • 校验是否属于class标准格式
    • prepartion

      • 给静态变量赋值默认值,static i=8,在这里只先给默认值0
    • resolution

      • 把class文件常量池里面用到的符号引用转换为直接内存地址,即可直接访问到内容
  • Initalizing

    • 静态变量在这时候才会赋初值

    • JVM规则必须初始化的5种情况

      • new getstatic putstatic invokestatic指令,访问final变量除外
      • java.lang.reflect对类进行反射调用时
      • 初始化子类的时候,父类首先初始化
      • 虚拟机启动时,被执行的主类必须初始化
      • 使用动态语言,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_putStatic、REF_getStatic、REF_invokeStatic的方法句柄,该类初始化
  • 申请内存

    • 成员变量赋默认值
    • 调用构造函数,成员变量顺序设置初始值
    • 执行构造方法语句
  • 对象的卸载

    • GC

classLoader

  • bootStrap

    • 最上层加载器使用C++实现,核心类加载器
    • 范围:jre/lib
  • extClassLoader

    • 扩展类加载器
    • 范围:jre/lib/ext(或由-Djava.ext.dirs指定)
  • AppClassLoader

    • 应用类加载器
    • 范围:加载clsspath指定内容
  • 自定义加载

双亲委派机制(JDK 1.2出现)

  • 加载类的时候,appClassLoader找不到就问父亲ExtClassLoader,也找不到就问他的父亲,BootStrarp。如果BootStrarp也没有找到就尝试加载类,没有找到就让ExtClassLoader加载,如果还没有就让appClassLoader自己加载,appClassLoader也找不到就会异常,notClassFindException

  • 好处

    • 不会重复加载类
    • 防止核心类被篡改

打破双亲委派

  • JDK1.2前,自定义ClassLoader都必须重新loadClass()
  • ThreadContextClassLoader可以实现基础类调用实现类,通过thread.setContextLoader指定,典型使用:JNDI服务(需要用基础类调用用户的代码)
  • OSGI是基于Java语言的动态模块化规范,类加载器之间是网状结构,更加灵活,但是也更复杂
  • tomcat实现打破双亲委派机制

自定义类加载器

  • 继承classLoader重写findClass,里面是通过流读取class文件转成二进制加载到内容中,然后调用defindClass方法转成对象

内存屏蔽

X86 CPU内存屏蔽

  • sfence:store| 再sfence指令前的写操作必须在sfence指令后写操作前完成
  • lfence:load | 再lfence指令前的读操作必须在lfence指令后读操作前完成
  • mfence: modify/mix | 再mfence指令前的读写操作必须在mfence指令后的读写操作前完成

JVM级别规范

  • LoadLoad屏蔽

    • 如Load1;LoadLoad;load2这样的语句,要保证Load2读取数据前,Load1的读取数据要被读取完毕
  • StoreStore屏蔽

    • 如Store1;StoreStore;Store2这样的语句,要保证Store2写数据操作前,Store1的写入操作对其他处理器可见
  • LoadStore屏蔽

    • 如Load1;LoadStore;Store2这样的语句,要保证Store2写数据操作前,Load1读取数据要被读取完毕
  • StroeLoad屏蔽

    • 如Store1;StoreLoad;Load2这样的语句,要保证Load2读取数据前,Store1写入操作对其他处理器可见

volatile实现细节

  • 字节码层面

    • 只是增加了一个访问符 ACC_VOLATIE
  • JVM层面

    • 写操作

      • 在写操作前加了个StoreSotre,写操作后增加StoreLoad
    • 读操作

      • 在读操作前面加了个LoadLoade,读操作后增加LoadSotre
  • OS及硬件层面

    • windows系统是用lock指令实现

synchronized

  • JVM层面:实际是使用C和C++的调用操作系统提供的同步机制,windows和linux实现不一样

  • 与volatie区别

    • synchronized是针对锁定线程,范围针对变量、类或方法,防止并发,可能会造成线程堵塞
    • volatile是防止指令重排
    • volatile是告诉jvm该变量必须每次从主存读取,保证操作可见性,不会造成线程堵塞
    • volatile可被编译器优化

内存模型

程序计算器

本地方法栈

方法区(JDK 1.8改名 MetaSpace) 都是方法区的实现, 最大的区别是 1.8使用的是本地内存,不在JVM里。解决了1.8以前永生代的OOM问题。

  • MetaspaceSize

    • 设置元空间的内存大小,可以控制发生GC阈值
  • MaxMetaspaceSize

    • 限制元空间大小上限,防止异常占用过多物理内存

  • 逻辑分代

    • 逻辑划分

      • eden区(内存占用比例)

        • 新生代区 8
        • S1 1
        • S2 1
      • old区

    • 好处

      • 将存活时间较长的对象,区分不同内存区域存储,减少GC发生的频率。利用不用区存储的特行,使用不用的垃圾回收算法
      • 年轻代-标记-拷贝
      • 老年代-标记清除/压缩
  • 触发GC。所有GC都会停止应用所有线程。

    • 触发-MinorGc

      • eden区里新生代内存不够。存活对象转移到S0区,清理新生代区垃圾
      • S0区内存满了,S0转移到S1,清理S0区垃圾
    • 触发-MajorGc

      • 老年代内存满了,清理老年代内存。同时触发FullGc
    • FullGc

      • 清理所有区域,包括新生代和老年代

GC

如何查找垃圾

  • 引用计数算法

    • 原理

      • 有一个引用对象就标记1
    • 缺点

      • 无法标记循环引用对象;如A->B->C->A; 整块都没有其他引用,实际为垃圾
  • 根可达算法

    • 原理

      • 程序main方法里的栈中对象引用为根对象,开始查找查找依赖引用的对象
    • 根对象的定义

      • 常量
      • 静态变量
      • main方法里栈对象
      • JNI指针对象

垃圾回收的算法

  • 标记-清除

    • 原理:

      • 从根对象查找,有被引用就标记。第二遍开始清除未被标记的对象。适用存活对象较多的情况
    • 缺点:

      • 执行效率较低,需要扫描两次才能清除。
      • 会产生内存空间碎片
  • 拷贝

    • 原理:

      • 将内存区域分成两部分,将标记的存活对象复制到另一内存区域,然后清理原区域。适用存活对象较少情况
    • 缺点:

      • 内存分割,使用内存减少
      • 会改变对象的引用地址
    • 优点:

      • 不会产生内存碎片
  • 标记-压缩

    • 原理:

      • 从根对象查找,有被引用就标记。第二遍清理未被标记的对象,同时压缩内存空间。
    • 缺点:

      • 执行效率最低,需要扫描两次才能清除。
      • 也会改变对象的引用地址
    • 优点:

      • 不会产生内存碎片

垃圾回收器

  • 默认方式:PS+P0(组合方式的垃圾回去器)

    • 原理:

      • 多线程处理垃圾
  • cms

    • 1.7以前常用,存在问题,目前没有在使用的

      • 核心多线程处理

      • 4个阶段

        • 初始化标记initial mark

          • 查找根对象
        • 并发标记concurrent mark

          • 并发从根对象找到引用对象并标记
        • 重标记remark

          • 并发过程中垃圾又被引用,所以重新标记,楼标的重新标记。也是STW过程
        • 并发回收concurrent sweep

          • 把不用的垃圾回收
      • 难点是多线程标记对象的时候,对象的引用状态再变更

  • G1

    • 1.9以后默认算法

      • 与CMS相同的三色标记法

        • 原理:

          • 逻辑上采用颜色区分

            • 黑色:自身和成员变量都被标记过
            • 灰色:自身被标记,成员变量未被标记
            • 白色:未被标记的对象
        • 漏标

          • 条件

            • 有至少一个黑色对象在自己被标记之后指向了这个白色对象
            • 所有的灰色对象在自己引用扫描完成之前删除了对白色对象的引用
          • 解决方案

            • 写屏障+增量更新

              • 可以简单理解为,当一个黑色对象增加了对白色对象的引用,那么这个黑色对象就被变灰
            • 写屏障+关注引用的删除

              • 可以简单理解为,当一个灰色对象取消了对白色对象的引用,那么这个白色对象被变灰
      • 逻辑模型与之前有区别

        • 特点

          • 并发收集
          • 压缩空间不会延长GC的暂停时间
          • 更容易预测GC暂停时间
          • 比较适合服务端的,多内核,大内存,同时还能保持高吞吐量的场景
      • 颜色指针

        • 一个指针在内存中没有被压缩的时候是64位,拿出3位做一下标记,,来标记变化过的指针。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值