JVM调优【三】

48 篇文章 0 订阅
30 篇文章 0 订阅

标记整理算法

  • 对于老年代,回收的垃圾较少时,如果采用复制算法,则效率较低。标记整理算法的标记操作和“标记-清除”算法一致,后续操作不只是直接清理对象,而是在清理无用对象完成后让所有存活的对象都向一端移动,并更新引用其对象的指针。
    在这里插入图片描述
  • 很显然,整理这一下需要时间,所以与标记清除算法相比,这一步花费了不少时间,但从长远来看,这一步还是很有必要的。
    分代收集算法
  • 针对不同的年代进行不同算法的垃圾回收,针对新生代选择复制算法,对老年代选择标记整理算法
    垃圾收集器
  • Java的应用很广,内存区域也很多,可以使用不同的垃圾收集器。
    Serial收集器
  • 单线程垃圾收集器、最基本、发展最悠久。它的单线程的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。偶尔用在桌面应用中。
    在这里插入图片描述
    ParNew收集器
    可多线程收集垃圾,收集新生代,使用收集算法
    在这里插入图片描述
    Parallel收集器
  • 多线程收集垃圾,收集新生代,使用收集算法。Parallel收集器更关注系统的吞吐量,可以通过参数来打开自适应调节策略。
  • 吞吐量:CPU用于运行用户代码的时间与CPU消耗的总时间的比值。
    吞吐量 = (执行用户代码时间)/(执行用户代码时间+垃圾回收占用时间)
  • -XX:MaxGCPauseMillis 垃圾收集器最大停顿的时间,但最大停顿时间过短必然会导致新生代的内存大小变小,垃圾回收频率变高,效率可能降低。
    -XX:CGTIMERatio 吞吐量大小(0-100),默认为99。
    CMS收集器
  • Concurrent Mark Sweep,采用标记-清除算法,用于老年代,常与ParNew协同工作。优点在于并发收集与低停顿。
    注:并行是指同一时刻同时做多件事情,而并发是指同一时间间隔内做多件事情

工作过程
在这里插入图片描述

  • 初始标记
    标记老年代中所有的GC Roots对象和年轻代中活着的对象引用到的老年代的对象,时间短;
  • 并发标记
    从“初始标记”阶段标记的对象开始找出所有存活的对象;
  • 重新标记
    用来处理前一个阶段因为引用关系改变导致没有标记到的存活对象,时间短;
  • 并发清理
    清除那些没有标记的对象并且回收空间。
  • 缺点:占用大量的cpu资源、无法处理浮点垃圾、出现Concurrent MarkFailure、空间碎片。

G1收集器

  • G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一,早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。

优势:并行(多核CPU)与并发;
   分代收集(新生代和老年代区分不明显);
   空间整合;
   限制收集范围,可预测的停顿。
步骤:初始标记、并发标记、最终标记和筛选回收。

在这里插入图片描述
内存分配
原则:

  1. 优先分配到Eden
    \2. 大对象直接分配到老年代
    \3. 长期存活的对象分配到老年代
    \4. 空间分配担保
    \5. 动态对象的年龄判断

Eden区域

  • -verbose:gc -XX:+PrintGCDetails 表示输出虚拟机中GC的详细情况。
  • 默认使用Parallel收集器(服务器),Serial收集器(客户端),服务器和客户端可以通过java
    -version查看。也可以通过-XX:+UseSerialGC设置收集器。
  • -Xms20M -Xmx20M -Xmn10M设置内存大小大小为20M,新生代大小为10M。
  • -XX:SurvivorRatio=8设置eden 与survuvor 的比值大小 8:1
public class Eden {
    public static void main(String[] args) {
        // -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
         /*给大家推荐一个免费的学习交流君样:826021115  */
        byte[] b1 = new byte[2 * 1024 * 1024];
        byte[] b2 = new byte[2 * 1024 * 1024];
        byte[] b3 = new byte[2 * 1024 * 1024];
        byte[] b4 = new byte[4 * 1024 * 1024]; //第一次Minor回收
         /*给大家推荐一个免费的学习交流君样:826021115  */
        System.gc();
    }
}

GC日志:
在这里插入图片描述

  • JVM优先把对象放入Eden区,当Eden区放不下了后(2 * 3 = 6M),通过分配担保机制放入老年代6M(Minor
    GC),再把最后一个4M放入新生代。
    大对象直接分配到老年代
    我们认为大对象不是朝生夕死的,如果放在新生代,则需要不断移动,性能较差。
    -XX:PretenureSizeThreshold=6M 设置大文件大小。
public class Old {
    public static void main(String[] args) {
        byte[] b1 = new byte[7 * 1024 * 1024];
    } /*给大家推荐一个免费的学习交流君样:826021115  */
}
  • 7M大于设置的大文件的大小(6M),直接放入老年代。
    在这里插入图片描述
    长期存活的对象分配到老年代
    -XX:MaxTenuringThreshold`最大年龄,默认为15;
    Age 1 + 1 + 1 使用年龄计数器。
    空间分配担保
  • -XX:+HandlePromotionFailure 开启
  • -XX:-HandlePromotionFailure 禁用
  • 取之前每一次回收晋升到老年代对象容量的平均值大小作为经验值,与老年代的剩余空间进行比较,决定是否FullGC来让老年代腾出更多空间。

逃逸分析与栈上分配

  • 逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。
public static StringBuffer craeteStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
     /*给大家推荐一个免费的学习交流君样:826021115  */
	sb.append(s1);
    sb.append(s2);
    return sb;
}
  • StringBuffer
    sb是一个方法内部变量,上述代码中直接将sb返回,这样这个StringBuffer有可能被其他方法所改变,这样它的作用域就不只是在方法内部,虽然它是一个局部变量,称其逃逸到了方法外部。如果想要StringBuffersb不逃出方法,可以写成:return sb.toString();
    虚拟机工具
  • Sun公司自带了许多虚拟机工具,在bin目录下,其exe文件所依赖的源码在tools.jar包下,利用jar包中的文件可自己开发。

最新2021整理收集的一些高频面试题(都整理成文档),有很多干货,包含mysql,netty,spring,线程,SpringBoot、Spring Cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,需要获取这些内容的朋友请加Q君羊:826021115

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值