JVM相关知识

JVM相关知识整理

一、运行时数据区域

按照线程是否私有将JVM的运行时数据区域分为两部分

  • 线程共享

    • ①虚拟机所管理的内存最大的一块区域,几乎所有的对象实例都会在这里分配内存,也是垃圾收集器管理的主要区域。也称为GC堆

      ②如果按照分代收集算法的话,堆还可以分为新生代和老年代,其中新生代又可以分为Eden/FromSurvivor/ToSurvivor

      ③堆的体现可以为不连续的内存空间,只要保证逻辑上的连续即可。可以通过JVM指令指定初始化时的堆大小,-Xmx 初始化最大堆内存,-Xms 初始化的最小堆内存

    • 方法区

      ①方法区用来存储被虚拟机加载的类信息、常量、静态变量、JIT编译器编译后的代码等内容

      ②方法区是JVM虚拟机规范中的一种,而JDK1.7中的永久代和JDK1.8中的元数据区域分别为两个版本的不同实现。

      ③运行时常量池为方法区里的一部分,存储Java中支持的基本类型和引用类型的数据,局部变量表的字面量引用为方法区中常量池的地址。

  • 线程私有部分

    • 程序计数器:当前线程执行字节码的行号指示器,字节码解释器工作时,通过这个计数器的值来选择需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成

    • 本地方法栈:由C/C++编写的底层指令,为虚拟机执行Native服务

    • 虚拟机栈:方法执行时,创建出对应的栈帧,描述的是Java方法执行的内存模型

      • 局部变量表
      • 操作数栈
      • 动态链接
      • 返回值地址
二、对象的内存布局
  • 对象头(markword)
    • 对象哈希码,分代年龄
    • 指向锁记录的指针
    • 指向重量级锁的指针
    • GC标记,表现为空
    • 偏向线程ID,偏向时间戳,对象分代年龄
  • 实例数据
  • 对其数据
    • 非必须存在,目的是为了保证该对象的大小为8字节的整数倍,如不足,自动填充
三、垃圾回收算法
  • 判断对象能否被回收的常见算法

    1. 引用计数法:给对象添加一个计数器,如果该对象被引用,数值+1,弃用-1,缺点是无法解决两个对象相互应用,造成的无法被回收现象

    2. 可达性分析算法

      • 通过GCRoot为起点,向下进行引用链搜索,标记与GCRoot无关联的对象,根据不同的回收策略进行清除

      • 可以作为GCRoot的点

        ①虚拟机栈中引用的对象

        ②方法区中静态属性引用的对象

        ③方法区中常量引用的对象

        ④本地方法栈中Native方法引用的对象

      • 引用的四个层级

        ①强引用:代码中存在的如Object obj = new Object();

        ②软引用:有用,但非必须的对象。如果新系统将要发生内存溢出时,会将这些对象列入二次回收范围进行回收,如果这次回收还没有足够的内存,将抛出OOM

        ③弱引用:非必须对象,强度比软引用更弱,被引用关联的对象只能生存到下一次垃圾回收前,无论下一次垃圾回收时,内存是否够用,这些对象都会被回收掉

        ④虚引用:称为幽灵或者幻影引用,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响

  • 判断一个类不被引用的条件

    • 该类中所有的实例已被回收
    • 加载该类的ClassLoader已被卸载
    • 该类对应的java.lang.Class对象没有被任何地方引用,无法在任何位置通过反射访问该对象
  • 常见的垃圾回收算法

    • 标记-清除法(mark - sweep)
      • 过程为标记和清除两个阶段
      • 缺点之一是效率不高,标记和清除两个阶段的效率均比较低
      • 缺点之二是标记清除后会在内存中产生大量的碎片,造成内存中大量不连续的空间存在,可能导致后续内存占用较大的对象无法创建
    • 标记-整理法:标记过程与MarkSweep过程相同,会将所有存活的对象存放在一端,直接清理掉端边界以外的内存
    • 复制:为了解决效率问题,将内存分为大小相等的两块,每次使用其中的一块,每次清除使用的内存,清除过后将存活的对象复制到另一块上面
    • 分代:上述三种算法的整合,HotSpot虚拟机根据对象存活时间的不同,将内存划分为几个区域,例如新生代使用复制算法,老年代使用标记清除算法(可以根据JVM参数指定每进行几次回收后,将内存空间整理一次)
四、常见垃圾回收器
  • Serial收集器

    • 串行垃圾收集器,单线程,适用于Client端的一种垃圾收集器
    • 用户线程达到安全点之后,只能用于新生–代采用复制算法,并且暂停掉所有用户线程。
    • 配套为Serial Old收集器,对应回收老年代的垃圾
  • ParNew收集器:Serial 收集器的多线程版本

  • Parallen收集器:适用于吞吐量优先的收集器,对应老年代收集器为Parallen Old收集器

  • CMS收集器

    • Concurrent Mark Sweep
    • 并发收集器,尽可能减少STW的时间
      • 初始标记
      • 并发标记
      • 重新标记
      • 并发清除
    • 缺点
      • 占用CPU资源,导致程序变慢。使用线程数为:(CPU + 3) / 4,如果CPU数量在4个以下,会对程序造成很大影响
      • 无法回收浮动垃圾,由于是并发清理,则在无法回收在清理时生成的垃圾
      • Mark-Sweep,通过设置-XX:CMSFullGCsBeforeCompaction,来指定进行了多少次不压缩的FullGC之后,对内存空间进行一次整理
  • G1收集器

    • 并行与并发,与CMS的场景一致
    • 分代收集,没有其他收集器的配合,可以独立管理GC堆
    • 空间整理,基于标记整理法实现的G1收集器,无论何时都不会产生空间碎片
    • 可预测的停顿(STW)
五、内存分配回收策略
  • 对象优先在Eden区进行分配,如果即将分配的对象在Eden区容纳不下,将进行MinorGC(复制算法)
  • 移入老年代的规则
    • 大对象通常直接进入老年代
    • 对象的回收年龄默认值为15,如果某对象在新生代回收次数=15次,将会被移入老年代
    • 如果在Sruvivor中同一年龄的对象总和大于整个Survivor区的50%,直接移入老年代
  • 如果即将分配内存的对象在Eden区容纳不下,则老年代会先行担保,即直接将其放入老年代,如果老年代仍然无法容纳,则整个堆将进行FullGC,如果在FullGC之后仍然无法容纳,则会抛出OOM

参考文献:《深入理解Java虚拟机》 第二版

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值