JVM学习

本文详细介绍了JVM的结构,包括类加载器、运行时数据区、垃圾回收算法及类加载过程。重点讲解了类加载的加载、链接、初始化阶段,以及不同类型的类加载器。此外,文章还探讨了垃圾回收的根可达算法、各种回收算法(如标记清除、复制、标记整理)以及分代收集策略。最后,提到了不同类型的垃圾收集器及其优缺点。
摘要由CSDN通过智能技术生成
  1. JVM结构
    1. 类加载器:执行类加载,加载>>链接>>初始化
    2. 运行时数据区
      1. 方法区:存放类信息,静态变量,常量,运行时常量池(编期间生成的各种子字面量和符号引用),线程共享
      2. 堆:存放对象的实例,线程共享
      3. 本地方法栈:为本地方法(native)服务,线程私有
      4. 虚拟机栈:由一个一个栈帧组成,执行一个方法就放入一个栈帧,执行完毕就取出,线程私有,栈帧中包括:
        1. 局部变量表
        2. 操作数帧
        3. 动态链接
        4. 方法出口
      5. 程序计数器(PC寄存器):当前线程,程序执行的指示器,存放下一条指令的地址,由执行引擎读取。线程私有
    3. 执行引擎:解析字节码文件,加载机器码,线程共享
    4. 本地方法接口:调用本地方法库(不是用Java语言写的程序),线程共享
  2. JVM类加载过程
    1. 加载>>链接>>初始化
    2. 何时需要进行类加载:
      1. new 一个对象的时候
      2. 调用对象静态变量或者静态方法
      3. 反射调用类
      4. 初始话子类的时候,若父类没有被初始化,则也会执行父类的初始化
      5.  程序启动时,main方法所在类
    3. 加载过程:
      1. 通过类的全限定名(所在包名 + 类名)来获得此类的二进制字节码流
      2. 将这些静态数据转换为方法区中的运行时数据
      3. 在方法区中生这个类对应的java.lang.Class对象,作为调用此类各种信息的接口
    4. 链接:
      1. 验证>>准备>>解析
      2. 验证:验证加载的类信息是否符合JVM规范,确保没有安全问题
      3. 准备:为类的静态变量分配内存,并赋予默认值(0值),在方法区中执行
      4. 解析:将常量池的符号引用替换成直接引用
    5. 初始化:
      1. 对类的静态变量赋予初始值,执行静态代码块
  3. 类加载器的种类
    1. 引导类加载器
    2. 扩展类加载器
    3. 应用程序类加载器
  4. 双亲委派机制(为了安全):当我们需要加载一个类时,JVM会向上传递需要加载的信息,然后从上往下加载,他会首先使用引导类加载器进行加载,如果加载失败,就会使用扩展类加载器进行加载,如果失败,就会使用应用程序类加载器进行加载。例:当自定义java.lang.String包和类,加载的时候,会首先通引导类加载器进行加载,他会加载他所对应的原始的java.lang.String的类。
  5. 沙箱安全机制
    1. 在Java1.0时,本地代码默认受信任,能够访问本地资源;而远程代码默认不受信任,不能够访问本地资源
    2. 在java1.1时,安全机制太严格不适合扩展,所以对沙箱进行了改进,允许用户指定远程代码对本地资源的访问权限
    3. Java1.2时,再次升级,增加了代码签名,不管本地,还是远程代码,都由用户指定安全策略,由类加载器加载到虚拟机中不同的权限空间、
    4. java1.6以后,引入了域,代码会被加载到权限不同的域中。
  6. native关键字:说明修饰的方法是原生态方法,不是用Java写的,而是其他语言写的,例如C,C++。执行该方法的时候,会进入本地方法栈,调用本地方法接口(JNI)
  7. 垃圾回收算法
    1. 两种找到垃圾的算法
      1. 引用计数法,增加对对的一个引用,计数加1,消除引用,计数减1,计数为0就是可消除对象。这中方法计数麻烦,耗内存,解决不了循环引用问题
      2. 根可达算法,选择一些对象为根对象GC Roots,通过对象之间的相互引用(引用链),判断从这些根对象能引用到那些对象,引用不到的对象就是可消除对象,其中GC Roots:
        1. 虚拟机栈中引用的对象
        2. 方法区中静态变量或常量引用的对象
        3. 本地方法栈(JNI)引用的对象
        4. 虚拟机内部所引用的对象
        5. 同步锁( synchronized 关键字)持有的对象
        6. 回调,代码缓存等
    2. 常用的垃圾回收算法
      1. 标记清除算法,遍历一遍,将可清除的对象进行标记,然后删除标记对象,缺点:会产生零碎的内存空间。
      2. 复制算法:将内存一分为二,一个活动区间,一个空间区间,所有新建的对象都存放在活动区间,当活动区间满的时候,执行复制算法,将活动区间存在引用的对象复制到空闲区间,清除活动区间的所有对象,并给复制对象的引用赋予新的值,此时活动区间变为空闲区间,空闲区间变为活动区间。
      3. 标记整理算法:在标记清除的基础上,把碎片化的内容进行整理压缩。
      4. 分代收集算法:根据对象存活的周期不同,将内存分为新生代和老年代,新生代中对象存活周期短,采用复制算法,老年代对象存活率高,采用标记清除或标记整理算法
    3. 垃圾回收算法是回收堆中的垃圾,栈中不存在垃圾,堆的划分:
      1. 新生代:
        1. 伊甸园区
        2. 幸存0区
        3. 幸存1区
        4. 伊甸园区:幸存0区:幸存1区=8:1:1
        5. 幸存0区不存放对象,当伊甸园区内存满了的时候,执行垃圾回收算法(轻GC)将伊甸园区和幸存1区中存活的对象复制到幸存0区,并阿静伊甸园区和幸存1区的内容清空,此时幸存1区变为幸存0区,幸存0区变为幸存1区
      2. 老年代:当一个对象多此轻GC以后仍然存活,这个对象就会被放到老年区,当老年代满了执行重GC。默认15词以后,对象就会被放入老年代
      3. 永久代(方法区):Java1.8以后变为元空间,最大的区别(元空间并不在JVM中,而是使用本地内存)。为什么将永久代变为元空间:永久代有默认大小,容易产生内存溢出的问题,而且有极少一些方法(Strign::intern(),返回字符串对象的规范化表示),会因为虚拟机不同而有不同的表现。
    4. 轻GC在新生区中执行,不会清除老年代的对象,重GC是对老年代的执行,全GC是对整个堆的执行。执行GC算法的时候程序会暂停,因为不能产生新的对象。
  8. 垃圾收集器:
    1. 串行垃圾收集器:单线程,执行时必须暂停引用线程
    2. 并行垃圾收集器:多条垃圾回收线程同时执行,适合多CPU,应用程序仍然处于停止状态。
    3. GMS收集器(之前用的最多):缩短暂停时间,基于标记清除算法,分成四个步骤:
      1. 初始标记,任需暂停,标记GC Roots能直接关联的对象
      2. 并发标记,从初始标记的对象遍历整个对象图,并发执行。
      3. 重新标记,任需暂停,修正并发标记期间应程序运行,而发生标记变动的标记(不不可达变成可达的对象)
      4. 并发清除:清理掉标记阶段已经判断为死亡的对象
      5. 缺点:
        1. 并发阶段会占用一定的线程,程序速度下降,CPU核心增多能减少这种情况
        2. 无法处理“浮动垃圾”(可达变成不可达,并发标记,清除阶段产生的垃圾)
        3. 需要一定预留一部分空间供并发标记时的程序使用,预留空间太小可能发生“并发失败”
        4. 基于标记清除算法,大量空间碎片
    4. G1收集器:把连续的Java堆划分为多个大小相等的独立区域(Region),每个Region都可以扮演新生代的Eden空间、Survivor空间,或者老年代空间。根据回收所获得的空间大小以及回收所需时间的经验值,去判断该回收那个区域。也分为4个阶段:
      1. 初始标记:标记一下GC Roots能直接关联到的对象
      2. 并发标记:从初始标记的对象遍历整个对象图,并发执行。
      3. 最终标记:处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。
      4. 筛选回收:更新Region的统计数据,根据回收价值和成本,用户所期望的停顿时间,来制定回收计划。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值