JVM垃圾回收和类加载器

part 01:JVM垃圾回收

1、JVM运行时数据区(内存模型)

  • Java堆:存储new出来的对象,存放对象实例(对象被GC之前会调用对象中的finalize),分为

    • 新生代(新生代包含eden区和两个survivor区 )

    • 老年代包括:

      • 动态年龄判断(某个相同年龄对象占用内存大小超过了survivor区内存的50%时);

      • 长期存活,年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)

      • 大对象直接进入老年代,虚拟机提供了一个阈值参数,令大于这个设置值的对象直接在老年代中分配。防止新生代采用复制算法时,大内存对象大量在两个Survivor区中复制

      • 空间分配担保:如果大量对象在 Minor GC 后仍然存活,导致 Survivor 空间不够用,就会通过分配担保机制,将多出来的对象提前转到老年代,但老年代要进行担保的前提是自己本身还有容纳这些对象的剩余空间,由于无法提前知道会有多少对象存活下来,所以取之前每次晋升到老年代的对象的平均大小作为经验值,与老年代的剩余空间做比较。

对象年龄:对象在Survivor区每熬过一次Minor GC,年龄就会增加1

  • 虚拟机栈:每个线程分配一块虚拟机栈内存,线程中的每个方法分配一块栈帧,存储方法中的局部变量,(局部变量表,操作数栈、动态链接、方法出口) 如果局部变量是对象的话存储对象在Java堆中的地址

  • 本地方法栈:存储本地方法,native修饰的方法

  • 方法区:存储常量+静态变量(如果静态变量是对象的话存储对象在Java堆中的地址)+类信息

  • 程序计数器:每个线程私有的,存储当前执行class代码在方法区中的地址,记录当前线程执行到哪了

2、垃圾回收机制(垃圾判断算法)

  • 可达性分析算法:从GC Roots出发,向下搜索引用的对象,找到的对象都标记为非垃圾对象,其他未标记的为垃圾对象可以被回收

    • GC Roots:虚拟机栈中的局部变量引用的对象、方法区中的常量引用的对象和静态变量引用的对象、本地方法栈中Native修饰方法中引用的变量

  • 引用计数法:每个对象含有一个引用计数器,当有引用时,计数器+1;当引用离开作用域被置为null时,计数器-1;当发现某个对象引用计数为0时,就释放其占用的空间。

3、垃圾回收算法

  • 标记-清除算法:分为两个阶段,标记阶段和清除阶段;在标记阶段采用可达性分析算法标记存活对象,在清除阶段,清除未被标记的对象

    • 适用场景:存活对象较多的情况下比较高效;适用于老年代

  • 复制算法:从根集合节点进行扫描,标记出所有存活对象,并将这些存活的对象复制到一块新的内存,之后将原来那块内存全部回收(两个survivor区)

    • 适用场景:存活对象较少、垃圾对象多的情况下比较高效;扫描了整个空间一次(标记存活对象并复制移动);适用于新生代;

    • 缺点:1.浪费空间:两个堆之间来回倒腾;2.当程序进入稳定态时,可能只会产生很少的垃圾,甚至不产生垃圾,尽管如此,复制式回收器仍会将所有内存自一处复制到另一处

  • 标记-整理:与标记清除一样采用可达性分析算法标记存活对象,然后移动所有存活对象,且按照内存地址次序排序,然后将末端内存地址以后的内存全部回收。性价比较高

  • 分代收集算法:分代收集算法就是目前JVM使用的算法,填解决了标记整理不适用于老年代的问题,将内存分为各个年代。在不同年代使用不同的算法:新生代存活率低,使用复制算法;老年代存活率高,没有额外空间对它进行分配担保,所以只能使用标记清除或者标记整理算法

4、垃圾回收类型:

  • Minor GC:对新生代进行回收,当Eden区满时触发

  • Full GC:对整个堆进行回收,包括新生代和老年代。速度比Minor GC慢,应减少Full GC次数。触发条件:

    • 1.老年代空间不足;

    • 2.System.gc调用(不是必然执行);

    • 3.元数据区超过水位线(方法区)

      • 元数据区动态扩展,默认-XX:MetaspaceSize值为21MB的高水位线,一旦触及则Full GC将被触发卸载没有用的类(类对应的类加载器不再存活),然后高水位线将会重置,新的高水位线取决于GC过后释放的元空间,如果释放的空间少,这个高水位线则上升,如果释放空间过多,则高水位线下降。(一般运行过程中不会有很多数据放入元数据区,除非运用了大量反射)

    • 4.通过Minor GC后进入老年代的平均大小大于老年代的可用内存;

    • 5.由Eden区,survivor区(From)向另一个survivor区(To)复制时,对象大小大于To区可用内存,把该对象转存到老年代,老年代的可用内存小于该对象大小时存放

5、GC中的STW(Stop the world)

  • 在执行垃圾回收时,Java应用程序的所有除垃圾收集线程之外的线程都被挂起,在用户不可见的情况下把用户正常工作的线程全部停下来,等待GC线程执行完毕后才能再次运行;防止在执行GC时,其他线程改变对象状态

  • 吞吐量:垃圾收集时间和总时间占比:1/(1+n),吞吐量为1-[1/(1+n)]。-XX:GCTimeRatio=n

6、常用的垃圾收集器

  • CMS收集器:尽可能的缩短STW的时间;作用于老年代,使用标记-清除算法; -XX:+UseConcMarkSweepGC

    • 执行过程:

      • 初始标记:标记GC Roots能直接关联到的对象,速度很快,需要STW

      • 并发标记:在初始标记的基础上继续向下追溯标记,并发执行,不需要STW

      • 重新标记:修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分的标记记录,需要STW

      • 并发清除:清除未标记的垃圾对象,不需要STW

    • 缺点:

      • 吞吐量低

      • 无法处理浮动垃圾

      • 标记-清除算法带来的空间碎片问题

  • G1收集器:面向服务端应用的垃圾收集器,适用于多核处理器、大内存容量的服务端系统。G1将Java堆划分为多个大小相等的独立区域(Region),虽然保留新生代和老年代概念,但不再是物理隔阂了,它们都是(可以不连续)Region的集合。作用于新生代和老年代,使用复制+标记整理算法; -XX:+UseG1GC

    • Humongous区:专门存放短期巨型对象,避免Full GC的大量开销。

    • 执行过程:

      • 初始标记:标记GC Roots能直接关联到的对象,速度很快,需要STW

      • 并发标记:在初始标记的基础上继续向下追溯标记,并发执行,不需要STW

      • 最终标记:修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分的标记记录,需要STW

      • 筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划(可预测的停顿)

    • 优点:可预测的停顿,可以设置STW的时间。(-XX:MaxGCPauseMillis)

part 02:JVM类装载子系统

1、类加载过程:类加载器将class文件加载到虚拟机的内存

  • 加载:在硬盘上查找并通过IO读入字节码文件

  • 连接:建立链接,执行校验、准备、解析(可选)步骤

  • 校验:校验字节码文件的正确性

  • 准备:给类的静态变量分配内存,并赋予默认值

  • 解析:类装载器装入类所引用的其他所有类

  • 初始化:对类的静态变量初始化为指定的值,执行静态代码块

JVM对class文件是按需加载(用到了才加载),运行期间动态加载,非一次性加载

启动时加上参数-verbose:class会把类加载情况输出

2、类加载器种类

  • 启动类加载器:负责加载JRE的核心类库,如jre目录下的rt.jar、charsets.jar等

  • 扩展类加载器:负责加载JRE扩展目录ext中的jar类包

  • 系统类加载器:负责加载classPath路径下的类包

  • 自定义类加载器:负责加载用户自定义路径下的类包

3、类加载机制:

  • 全盘负责委托机制:当一个类加载器A加载一个类时,除非显示的使用另一个类加载器,否则该类所依赖和引用的类都由该类加载器A加载

  • 双亲委派机制:在类加载器加载一个类的时候,会先委托父类加载器寻找目标类完成加载,一直递归到顶层,在父加载器加载不了的时候,子类才会尝试去加载。优势:

    • 沙箱安全机制:自己写的String.class类不会被加载,这样便可以防止核心API库被随意篡改

    • 避免类的重复加载:当父类加载器已经加载了该类时,子加载器不会再次加载

part 03:JVM调优命令

1、JVM命令

  • Jps:虚拟机进程状况工具

  • jinfo:Java配置信息工具,可以实时查看和调整虚拟机各项参数

    • jinfo -flags 进程id:查看JVM的参数

    • jinfo -sysprops 进程id:查看Java系统参数

  • jstat:虚拟机统计信息监视工具,可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行参数

    • jstat -class :监视类装置、卸载数量、总空间及类装置所耗费的时间

      • Loaded:加载class的数量

      • Bytes:所占用空间大小

      • Unloaded:未加载数量

      • Bytes:未加载占用空间

      • Time:时间

    • jstat -gc:监视Java堆状况,包括Eden区、两个survivor区、老年代等的容量、已用空间、GC时间合集等信息

  • jmap:Java内存印象工具,用于生成堆转储快照。查看内存信息,实例个数及占用内存大小(排查内存溢出)

    • jmap -heap:生成Java堆转储快照dump文件,可以放入visual VM 进行可视化分析

  • jhat:虚拟机堆转储快照分析工具,与jmap搭配使用

  • jstack:Java堆栈跟踪工具,用于生成虚拟机当前时刻的线程快照。排查死锁

  • jvisualvm: 可视化运行监制和故障处理程序,可以生成或装入dump文件,可视化分析内存信息

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值