类似“不可持续的内存分配率”和“你需要维持低的内存分配率”这样的短语看起都像是属于 Java 冠军(Java Champions)的专有词汇。复杂、吓人、充满神秘色彩。


这些词语经常出现,但是如果你深入了解这些概念,它的神秘色彩就烟消云散了。这篇文章将试着揭开上面这些术语的神秘面纱。


什么是内存分配率?我们为什么要关心它?


内存分配率是指单位时间内分配内存的总数量,通常用 MB/sec 表示。不过,如果你乐意,也可以用 PB/year 来表示。这就是全部的内容——没那么神秘,仅仅是指 Java 代码在一定时期内内存分配的大小。


不过只知道这一点没有太大的意义。如果你能忍受,我将带你在实践中应用这个概念。高分配率意味着你的程序存在性能问题。从实践角度来说,主要影响是使得 GC(Garbage Collection) 成为了瓶颈。从硬件角度来说,即使常用的硬件也能支持每核几 GB/sec 的分配率。而实际上,你的分配率不会超过 1 GB/sec/core。所以你可以放心,硬件基本不可能成为应用的瓶颈。


所以,当我们关注 GC 的时候,就可以和真实情况类比了——如果你创建很多的成员,之后就需要做很多清理工作。我们知道,JVM 建立垃圾回收机制需要知道内存分配率,由此来改变 GC 执行的频率和 GC 停顿的时间。


内存分配率的测量


我们开始测量内存分配率。我们设置 JVM 参数:-XX:+PrintGCDetails -XX:+PrintGCTimeStamps 来打开 GC 日志。现在,JVM 开始以下列方式记录 GC 停顿日志:


0.291: [GC (Allocation Failure) [PSYoungGen: 33280K->5088K(38400K)] 33280K->24360K(125952K), 0.0365286 secs] [Times: user=0.11 sys=0.02, real=0.04 secs] 

0.446: [GC (Allocation Failure) [PSYoungGen: 38368K->5120K(71680K)] 57640K->46240K(159232K), 0.0456796 secs] [Times: user=0.15 sys=0.02, real=0.04 secs] 

0.829: [GC (Allocation Failure) [PSYoungGen: 71680K->5120K(71680K)] 112800K->81912K(159232K), 0.0861795 secs] [Times: user=0.23 sys=0.03, real=0.09 secs]


根据上述 GC 日志,我们可以从年青代(Young Generation)上一次回收后的大小及下一次回收前的大小来计算出分配率。利用上面的例子,我们可以抽取出如下信息:


  • JVM 启动291毫秒后,加载的对象大小是33280K。第一次 minor GC 清理后,年青代剩余的对象大小是 5088K。

  • 启动446毫秒后,年青代占用的空间已经增长到38368K,并触发了下一次 GC,这次 GC 后,年青代占用的空间减少到5120K。

  • 启动829毫秒后,年青代的大小是71680K,GC 后再次减少到 5120K。


这些数据用如下的表格展示,计算出来的内存分配率添加年青代占用空间的后面:


图片


有了这些信息,我们就可以说对这个特定软件,在测量期间的内存分配率是161 MB/sec。


影响分析


现在,通过这些信息,我们能够明白改变内存分配率,可以增加或减少 GC 停顿的频率,从而影响应用的吞吐率。首先,也是最重要的,你应该注意只有 Minor GC 清理年青代的停顿才会受影响。老年代(Old Generation)的清理,无论是频率还是持续时间都不直接受分配率的影响,但是受增长率(promotion rate)的影响,增长率是下一篇文章讨论的术语。


了解这些后,我们就只需要关注 Minor GC 的停顿,我们应该更进一步的去理解在年青代内部内存池的不同之处。因为内存分配是在 Eden 区中进行的,我们可以直接看 Eden 区的大小对分配率的影响。所以我们可以假设,随着 Eden 区的增长,minor GC 停顿频率频率会降低,应用就能满足更快的分配率。


事实上,当我们采用不同的 Eden 区大小(-XX:NewSize -XX:MaxNewSize 和 -XX:SurvivorRatio参数)来执行相同的实例时,我们可以看到两种不同的内存分配率:


  • 设置 Eden 区为100M,运行上面的例子,内存分配率降低到100MB/sec。

  • 增加个 Eden 区到1GB,增加的内存分配率接近 200MB/sec。


如果你仍然疑惑这为什么是对的——假设减少应用线程 GC 的频率,你就可以做更多的有用的工作。这样就可以生成更多的对象,从而支持更高内存分配率。


现在,在你得出“ Eden 区越大越好”的结论之前,你应该注意内存分配率可能不会直接关联应用的真实吞吐率。并且这只是一种侧重吞吐率的技术测量。内存分配率只影响 Minor GC 暂停应用线程的频率,但从整体的影响来说,你还需要考虑 Major GC 的停顿以及应用提供的业务操作。


更多相关文章

  1. Java内存模型-JMM简介
  2. Java内存模型-堆和栈
  3. Java内存模型-本机内存
  4. 介绍 Java 中的内存泄漏
  5. 如何将JQuery变量值分配给Laravel Blade输入
  6. MySql-cluster中NDBD进程占用内存能否通过配置修改
  7. MySQL在C++中使用后务必释放 result,否则会造成内存泄露
  8. require():使用module.exports vs直接分配给“this”
  9. 我在Firefox中遇到内存泄漏或内存使用率高吗?

随机推荐

  1. Jquery如何获取下一个控件的ID?
  2. Ajax技术--服务器返回数据格式(HTML,XML,J
  3. HTML5实现一个可编辑的模板页面
  4. 把html 文件放在cgi-bin下遇到问题
  5. 李宇春居然传闻是梁山好李逵的后裔!!(神
  6. 组合两个下拉菜单以确定提交按钮链接
  7. 【可移动的】模拟弹窗div层
  8. 如何为django模板中的标记创建动态id
  9. Wordpress网站上的亚马逊链接无法正确打
  10. 如何在git特性分支工作流中处理xml/html