jvm类加载及垃圾回收机制

一.类加载过程

  1. 加载:把.class文件找到,读取文件内容
  2. 验证:根据jvm虚拟机规范检查.class文件的格式是否符合要求
  3. 准备:给类对象分配内存空间(内存初始化全0)
  4. 解析:针对字符串常量进行初始化,把符号引用转化为直接引用
  5. 初始化:真正的针对类对象里面的内容进行初始化,加载父类,执行静态代码块的代码

  其中,字符串常量需要一块内存空间,用来存字符的实际内容,另外还有一个引用,保存这个内存空间起始地址;

  类加载之前,字符串常量是在.class文件中,而此时的引用并非这个字符串常量的真正的地址,而指的是这个常量在文件中的”偏移量”;类加载之后,才真正把这个字符串常量放到内存中,此时才有内存地址,此时的引用才能转换成直接引用;

 java程序运行后,只有用到了的类才会被加载(懒汉模式);1构造类的实例;2调用这个类的静态方法/使用静态属性;3加载子类,就会先加载其父类。一旦加载过后,后续再使用就不必再加载

二.双亲委派模型

双亲委派模型描述的就是类加载过程找到.class文件的基本过程;

jvm默认提供了三个类加载器

  1. BootStrapClassLoader 负责加载标准库中的类
  2. ExtensionClassLoader 负责加载jvm扩展库中的类
  3. ApplicationClassLoader 负责加载用户提供的第三方库/用户项目代码中的类

上述的三个类存在“父子关系”,1是2的父类,2是3的父类

类加载的工作配合过程:

    首先就先从ApplicationClassLoader开始,但是其并非真正加载,而是交给父亲,这时进行到ExtensionClassLoader,也不是真正加载,也是交给父亲,进行到BootStrapClassLoader,这个类加载器也想先给父亲,可父亲为null,就由自己加载,搜索标准库中的类去加载,若没找到,就交由子类去加载,最终又返回到ApplicationClassLoader,若还没找到,就会抛出类找不到的异常;

实现上述加载逻辑顺序的目的是:保证标准库中的类优先加载,然后是扩展库,最后是用户类,避免用户在自己的代码中写的类名和标准库的名字重合,这样一来jvm加载的还是标准库中的类;另一方面,类加载器用户也是可以自己实现的,用户自定义的类加载器,也可以加入到上述流程中,就可以和现有加载器配合使用;当然自已写的加载器,可以去遵守双亲委派模型,也可以不遵守;

“双亲”委派模型是机翻的,其英文为parent,实为父亲委派模型;

三。垃圾回收机制GC

      垃圾回收指的就是把不用的内存自动释放了;C/C++中内存回收需要程序员手动释放,若不释放,这块内存空间就会持续存在,直到进程结束。也可能会导致内存泄漏,服务器不停的运转的,可用内存越用越少,后续可能出现严重问题;

      java go python php js大部分语言用GC来实现垃圾回收;

     GC好处自然是省心,程序员的代码简单,不易出错;坏处是需要消耗额外的系统资源和性能;

     C++是追求性能的极致的,GC是违背初心的,还会引起STW问题(stop the world);

     STW: 若内存中的垃圾过多,触发了GC操作,开销很大,也有可能涉及到锁操作,导致业务代码无法正常执行,形成卡顿,极端的情况下,几十毫秒到上百毫秒;

     GC主要针对堆进行释放的,以“对象”为基本单位进行回收,整个对象不再使用时会被回收;

     GC实际的工作过程:1,找到垃圾/判定垃圾    2,释放对象

     1.找到垃圾

          1)引用计数(非java做法)

           给每个对象分配一个计数器,每创建一个引用指向给对象,计数器+1,销毁引用就-1;

           简单有效,但java不用,因为:

           a:内存空间浪费多,每个对象都要分配一个计数器,若按4字节算,对象一旦多了,占用的额外空间就很多,特别是一个对象本身就4字节,加入计数器,体积大一倍;

           b:存在循环引用的问题

          2)可达性分析(java做法)

java中的对象都是通过引用来指向并访问的,整个java中的对象时通过一种链式/树形结构穿起来的

可达性分析,就是从组织所有对象树的根结点出发,遍历树,所有能被访问到的对象标记成“可达”

 jvm自己拿着一个所有对象的名单,通过上述遍历,把可达的标记出来,不可达的就进行回收了

可达性分析相对于引用计数来说会慢一点,但并不需要一直执行,隔段时间分析一遍就好了

   2.清理垃圾

      1)标记清除

          简单粗暴,直接把不用的内存给释放掉

           缺点是内存碎片问题,被释放的空间是零散的,不是连续的;

      2)复制算法

           解决了内存碎片化问题,把整个内存分成两半,把不是垃圾的对象复制到另一半,然后把一空间释放掉;

        缺点是:1空间利用率低,2若是垃圾少,有效对象多,复制成本大

      3)标记整理

           保证了空间利用率,把后面有用的对象往前面搬运

          缺点效率不高,搬运的空间大,开销大

上述的三种为基本策略,做一个复合策略“分代回收”

 刚new出来的对象,年龄是0,放在伊甸区,熬过一轮GC的对象就要放到幸存区,幸存区的对象也要周期性接受GC考验,两个幸存区通过复制算法来回拷贝,幸存区中一段周期考验后就进入老年代了,老年代都是年纪大的对象,生命周期普遍更长,GC扫描的频率变低了,如果老年代的对象成垃圾了,就使用标记整理的方式进行释放;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值