jvm调优一些思路及分析堆快照

什么是吞吐量?   

吞吐量:指在应用程序的生命周期内,应用程序所花费的时间 和 系统
总 运行时间的比值。系统总运行时间=应用程序耗时+GC耗时。如果系统运行了
100分钟,GC耗时1分钟,那么系统的吞吐量就是(100-1)/100 = 99%

 

什么是停顿时间?

停顿时间:指垃圾回收器正在运行时,应用程序的暂停时间。对于独占回收器而言,停顿时间可能比较长。使用并发的回收器时,由于垃圾回收和应用程序交替运行,程序的停顿时间会变短,但是,由于其效率很可能不如独占垃圾回收器,故系统的吞吐量可能会较低

 

怎么观察 jvm?

a 可以 用 jconsole,jvisualvm.exe 观察

b 使用jvm参数 ,输出相应的信息

-XX:+PrintGCDetails 输出详细的GC日志
-XX:+PrintGCTimeStamps选项。打开这个开关后,将额外输出GC的发生时间,以此,可以知道GC的
频率和间隔
-XX:+PrintTenuringDistribution 查看新生对象晋升老年代的实际阈值  
-XX:PrintHeapAtGC开关,打印详细的堆信息,一旦打开它,那么每次GC时,都将打印堆得使用情况。
-XX:+TraceClassUnloading用于跟踪类卸载信息
-XX:+TraceClassLoading用于跟踪类加载情况
-XX:+PrintGCApplicationStoppedTime 和 -XX:+PrintGCApplicationConcurrentTime 参数。它们显示,
一次垃圾回收,GC的停顿时间 和 应用程序的执行时间
为了能将以上的输出信息保存到文件,可以使用 -Xloggc 参数指定GC日志的输出位置。

 

对于jvm的调整,至今还没有通用的规则。但是在 业内 的 一些 比较公认的观点,我还是比较赞成的。

1 尽可能让短命对象 存放在 young 区, 尽可能减少Full GC的次数

        进入old区的对象,一般是生命周期比较长的对象。如果 常有 一些 朝生夕灭的对象进入old区,会增加Full GC的次数。

     

        那么进入old区的条件是什么呢?

        a 当对象的在young区的年龄达到一定的阈值,会晋升到old区

        b 当s区空间的空闲比例 低于 一个阈值,会将s区部分对象 晋升到 old区(未测试过)

        c 当分配一个较大的对象时,eden区的剩余空间放不下,s区的剩余空间也存放不下。那么就将对象直接放到old区,所以

我们开发的时候 要 避免 短命大对象

     

       我们可以通过 -Xmx -Xms  -Xmn 调整堆大小 , 年轻代老年代的比例。来减少GC的频次

       我们 可以通过 调整 eden区 和 s区 的比例,来减少短命对象进入 old区的几率

       下面是 调整 eden区 和 s区的 参数

        

-XX:SurvivorRatio = eden/s0 = eden/s1 设置新生代中,eden空间和S0空间的比例关系
-XX:NewRatio 可以用来设置 新生代 和 老年代的比例, ;老年代 / 新生代
这些JVM的XX参数在不同的JDK的版本中的实现可能会略有不同,在具体使用时,可以使用
-XX:+PrintGCDetails 参数打印出堆得实际大小

  

2 基于以上两点, 如果对响应时间有要求,可以 在可接受停顿范围内 限制 堆内存 的大小

       堆太大会增加单次GC时间,stop the world 的时间会相应增长。这对于 一些响应时间要求比较高的 server来说,是不

能接受的。所以有时候,堆大小不建议设置太大,可以用多进程的方案来代替

      堆内存大小 对于系统的性能有决定性的影响,分配太大 GC过程慢,分配太小,GC次数频次高

   
 

3  针对 吞吐量优先 与 响应时间优先, 选择合适的垃圾选择 器

 

 

怎么 分析 堆 快照 

使用以下jvm参数运行 下面的java代码 

 -Xmx20m -Xms20m -Xmn10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\m.hprof

其中

-XX:HeapDumpOnOutOfMemoryError 参数在程序发生OOM时,导出应用程序的当前快照
-XX:HeapDumpPath=C:\m.hprof 可以指定堆快照的保存位置

另外 也可以 在 程序运行 时  dump 堆快照文件来

 jmap -dump:format=b,file=c:\heap.hprof ${pid}

java 代码是

public class HelloWorld {

    public static void main(String[] args) throws InterruptedException {
            byte[] c1 = new byte[1024*1024*8];
            byte[] c2 = new byte[1024*1024*50];
        System.out.println("........................");
    }

}


 下面 我使用 MAT工具 大开 堆 快照文件


    

在首页 就已经 帮我们 推断出 内存 溢出 的原因 ,是由于 一个 byte数组 实例 占用了 百分之94 内存

点击 箭头的图标,可以  排序出 相应的类 占用 堆内存的大小 , 也发现  一个 byte数组 实例 占用了 百分之94 内存,

此外 右键 可以 查看 该实例的 引用链,还有其他 的一些 操作 这里 就 不多说了

   
    
    

 

性能调优的 一般步骤

下面我来简单的演示一下 性能调优的一般步骤,由于程序过于简单,不喜勿喷

 ,具体调优 还要参考 程序 实现逻辑

工具 

jmeter5.1

jvisualvm 或者观察 GC日志 ,也可以使用CAT,zabbix等的一些监控工具

下面 的 基于spring boot2.1的 java代码

@RestController
public class HelloWorldController {

    @RequestMapping("hello")
    public String hello(@RequestBody Map user){

        byte[] bytes = DigestUtils.md5Digest(user.toString().getBytes());
        java.util.List list = new ArrayList();
        list.add(bytes);
        System.out.println(user);
     //   System.out.println(age);
        System.out.println(".......................");

        return "hello";
    }


}

初始 jvm参数 -Xmx50m  -Xms50m

启动后,我们使用  jmeter 一秒 发送500个请求

可以 看到 young gc57次,full gc2次 ,平均响应 时间是 482ms

 

第一次 调整, 扩大堆内存,-Xmx700m  -Xms700m

 

可以 看到 平均响应 时间 变为 15ms

 

第二次调整 使用 专注响应时间 的 G1垃圾回收器 -Xmx:]700m -Xms700m -XX:+UseG1GC

可以看到 平均响应 时间  只有 4ms

 

 

参考资料 

深入理解java虚拟机

java程序性能优化指南

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值