JVM基础知识整理----垃圾回收和内存分配策略

垃圾回收和内存分配策略

三问

  • 那些内存需要回收
  • 什么时候回收
  • 如何回收

当高并发习引起错误是需要堆监控和调节

那些该回收

  • 活着
  • 死去

两种方法

  • 引用计数器

    • 虽然需要一些额外的控件计数
    • 但是效率高
    • java没有使用
    • 缺点
      • 很难解决对象的循环引用问题
      • 比如对象内相互指向但是外部实例置为空了
  • 可达性分析算法

    • 使用GC root作为根节点,如果节点不可达及为死亡

    • 虚拟机栈 引用的对象

    • 方法去京台面两引用的对象

    • 方法去常量引用的对象

    • 本地方法栈中国JNI应用的对象

    • java虚拟机内部的应用

      • 基本数据对应的Class对象,常驻异常对象
    • 所有被同步持有的对象

    • 反映java虚拟机内部情况的JM XBean,JVM TI中注册的回调,本地代码。

  • 支持局部回收的特征,需要将关联的都添加到GC Roots 避免过多做过优化

四种引用

  • 强引用
    • 只要有就绝不回收
    • 当内存不够就报错
  • 软引用
    • 有用但是非必须
    • 在内存溢出之前会回收
    • 如果还不够就抛出异常
  • 弱引用
    • 垃圾回收就会被回收
  • 虚引用
    • 在被回收之后有一个系统通知
  • 完成一个实例,并且在java中找一下应用的实例。为什么这么设计?

对象的自我拯救

  •     public static void main(String[] args) throws Throwable {
            SAVE_HOOK = new Main();
            SAVE_HOOK = null;
            System.gc();
            Thread.sleep(500);
            if(SAVE_HOOK != null){
                SAVE_HOOK.isAlive();
            }else{
                System.out.println("no,i am dead");
            }
            SAVE_HOOK = null;
            System.gc();
            Thread.sleep(500);
            if(SAVE_HOOK != null){
                SAVE_HOOK.isAlive();
            }else{
                System.out.println("i not dead once , but now,i am dead");
            }
    
        }
        public static Main SAVE_HOOK = null;
        public void isAlive(){
            System.out.println("yes,i am stall alive");
        }
        @Override
        protected void finalize() throws Throwable{
            super.finalize();
            System.out.println("finalize if executed");
            Main.SAVE_HOOK = this;
        }
    
    
    • 如上 对象第一次拯救成功,因为重写了finalize方法,在第一次回收会执行他自己的finalize方法。第二次在回收就必定会被回收。所有对象的fianlize方法之后被执行一次。
  • 垃圾回收不是立即执行的,想队列添加一个,优先级很低

回收方法区

  • 常量回收

  • 类型

    • 所有实例都被回收
    • 类加载器都被回收了
    • java。lang Class对象没有在任何地方被引用,无法通过反射访问该类的方法
  • Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是

    和对象一样,没有引用了就必然会回收。关于是否要对类型进行回收,HotSpot虚拟机提供了-

    Xnoclassgc参数进行控制,还可以使用-verbose:class以及-XX:+TraceClass-Loading、-XX:

    +TraceClassUnLoading查看类加载和卸载信息,其中-verbose:class和-XX:+TraceClassLoading可以在

    Product版的虚拟机中使用,-XX:+TraceClassUnLoading参数需要FastDebug版[1]的虚拟机支持。

垃圾收集算法

两种收集算法【从对象消亡的角度出发】

  • 引用计数垃圾收集
  • 追踪式垃圾收集

分带收集理论

  • java堆划分出不同的区域

  • 弱分代

    • 朝生夕死
  • 强分代

    • 熬过很多次的垃圾回收
  • 新生代

  • 老年代

  • 跨代引用假说(新生代被老年代指向,反着来)

    • 记忆集
    • 将可能发生
    • 发生新生代收集时只需要将老年代的一小部分进行扫描

标记-清除算法【老年代多】

  • 首先标记所有需要回收的对象

  • 缺点

    • 执行效率不够稳定
    • 内存空间碎片化

标记-复制算法【新生代多】

  • 先标记需要清除的,把不清除的复制到另一块。然后把原来的一半清空
  • 缺点
    • 产生大量内存间赋值的开销
    • 可用内存变为一半
  • 优化,大部分新生代熬不过第一轮,所有不用1:1划分空间

标记-整理算法【老年代多】

  • 先标记
  • 后整理,存活对象往一边移动
  • 然后清除后面的
  • 缺点
    • 需要移动对象
  • 优点
    • 没有碎片化

优化,容忍空间碎片存在,知道影响对象创建执行一次标记整理算法

根节点枚举

  • 会STOP the world的

  • 在HotSpot

    的解决方案里,是使用一组称为OopMap的数据结构来达到这个目的。一旦类加载动作完成的时候,

    HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来,在即时编译(见第11章)过程中,也

    会在特定的位置记录下栈里和寄存器里哪些位置是引用。这样收集器在扫描时就可以直接得知这些信

    息了,并不需要真正一个不漏地从方法区等GC Roots开始查找。

安全点

  • 不是每个指令都生成

  • 只是在“特定的位置”记录

    了这些信息,这些位置被称为安全点(Safepoint)。

  • 安全点位置的选取基本上是以“是否具有让程序长时间执行的特征”为标准

    进行选定的

  • “长时间执行”的最明显特征就是指令序列的复用,

  • 抢先式中断

    (Preemptive Suspension)和主动式中断(Voluntary Suspension),

  • 主动一条汇编指令就完成了轮询操作

安全区域

  • 线程睡眠或者blocked

记忆集和卡表

  • 从非收集区域指向收集区域的指针集合的抽象数据
    • 字长精度
    • 对象精度
    • 卡精度
      • 使用卡表
      • 具体实现 记忆集
      • 包含多个,只要有一个就变脏,没有就时干净的,垃圾收集时就筛选脏的元素

写屏障

  • 卡表变脏

    • 但出现跨代引用
    • 时间,当应用类型字段赋值的那一刻
  • 写屏障

    • aop切面
  • 伪共享问题

    • 现在是在缓存行中进行单位存储的,如果两个线程互相独立的变成,恰好在一个缓存行 就是彼此影响

并发的可达性分析

因为标记阶段是共有特性,如果随这堆变大等待时间变大,开销会很大

三色标记

  • 白色 便是为访问过,
  • 黑色 扫描过,安全存货
  • 灰色 北方问过,对象至少有一个引用为扫描过

产生对象消失的问题

  • 插入一条或者多条从黑色到白泽的新引用

  • ·赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。

  • 增量更新要破坏的是第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新

    插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫

    描一次。这可以简化理解为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象

    了。

  • 原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删

    除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描

    一次。这也可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来

    进行搜索。

经典的垃圾回收器

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kHMiChGM-1619316157182)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210416172015463.png)]

Serial收集器

CMS收集器

  • 缺点
    • 占用资源 cpu
    • 浮动垃圾
    • 内存碎片

jvm内存分配策略

  • 根据使用那种垃圾分类工具

  • 新生对象放在新生代,少数可能放在老年代

对象优先在EDEN分配

  • Eden区空间不够发起一次Minor GC
  • 使用参数 +PrubtGCDetails打印参数
  • 代码实现代码清单

大对象进入老年代

  • 消耗性能 所以直接放在老年代
  • 尽量避免大量的朝生夕死的大对象

长期存活的对象进入老年代

  • 默认15次
  • 可以通过参数设定

动态对象年龄判断

  • 同龄对象之和大于空间的一半,大于或者等于就进入老年代

空间分配担保

  • 流程如何

画一张流程图表示对象创建之后的新生代和老年代变化

jvm基础工具

jps 检查虚拟机进程的工具

  • jps -l
  • -q
  • -m
  • -v

jstat 监控虚拟机统计信息

  • 本地和远程不同
    • 远程需要加入LVMID

jinfo: Java配置信息工具

jmap: Java内存映像工具

jhat: 虚拟机堆转储快照分析工具

jstack: Java堆栈跟踪工具

面试时说使用可视化工具

需要知道基础的和每个参数代表了什么

jvm调优

筛选可以使用到自己的代码之后的

记录取来


版权声明:本文为qq_43248585原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。