JVM内存优化——小试牛刀

    最近在拜读周志明的《深入理解Java虚拟机》,看了Java的内存管理机制以及Jvm优化相关内容。于是,下载了sun提供的Jvm检测工具VisualVm。

VisualVm下载地址:https://visualvm.java.net/
安装就很简单了,在此略过。
作者博客地址:http://icyfenix.iteye.com/
可以下载PDF版的《Java虚拟机规范 (Java SE 7 中文版)》

一、启动程序


1. 查看系统中运行的Java进程

左侧显示当前系统中运行的虚拟机进程,双击连接后可以监测虚拟机的运行状况。
这里写图片描述

2. 安装VisualGC插件

工具栏中“工具”→“插件”
这里写图片描述

由于我已经安装过了,如果没有安装,目录中会有VisualGC。该工具可以查看不同内存区域的使用情况和GC情况,效果如图

这里写图片描述


二、问题描述

使用jps命令,可以查看系统中运行的jvm进程

$ jps
2064 TestMain
1389 Main
2267 Jps
1009
287

忽然发现个奇特的东西,就是TestMain进程,这个名字好奇怪,不是我创建的,在“活动监视器”中搜了一下PID,发现是公司的Hosts管理工具。奇怪的是,一款小小的Hosts管理工具竟然使用了将近1.5G的内存,吓死宝宝了。

于是反应给工具维护人员,很快进行了优化,释放了无用内存。但是总的内存占用量仍然有700+M,在切换几次环境之后,会稳定到这个值。因为我在公共配置里添加了Google的Hosts内容,所有触发了这个问题。

这个内存占用量,是特别的高,显然不是开发人员设定的值,而是OS X Java Plugin的默认设置。这种工具,内存占用应该只是在100M以下,最后能做20-30M最好。


三、使用VisualVM监视工具内存使用情况

这里写图片描述

十分钟的内存使用情况:
这里写图片描述

在1处,切换了环境,可见同事优化的地方正在此,在切换环境的时候释放了内存,触发了Eden区的GC。

但在这之前,将近十分钟,内存使用量在稳步上升,竟然一次都没有触发GC,这就很奇怪了。应该是内存空间足够有关,程序刚开始运行,竟然就申请了1200+M的内存,真不知道系统咋想的。这和OS X JRE运行环境有关,应该是使用的默认的对内存大小限制。但是就目前的程序而言,显然这么多的内存有些浪费。


四、堆内存优化过程


1. 优化堆内存大小

从曲线中可以看出来,堆内存的使用量最大也没超过200M,所以限定一下最大堆内存为200M,使用如下参数:

-Xmx200m

但是Mac系统的虚拟机参数在哪里设置呢?
这里写图片描述

打开应用程序的目录,可以看见Info.plist文件,程序的启动参数就在这里面设置的,找到如下节点,添加堆内存最大限制-Xmx参数:

    <key>JVMOptions</key>
    <array>
          <string>-Dapple.laf.useScreenMenuBar=true</string>
          <string>-Xdock:name=HostsManager</string>
          <string>-Xmx200m</string>
    </array>

所有在OS X下的Java Application都可以在该文件中设置虚拟机参数。

2. 初次优化堆内存

观察堆内存情况,如下图所示:

这里写图片描述

自动触发了GC操作之前,内存使用量快并未达到60M。所有200M还是比较多余的,大概4分钟触发一次GC,回收了内存。
可以试试将堆内存最大设置为80M,看看情况,会不会发生堆内存溢出。修改Info.plist

    <key>JVMOptions</key>
    <array>
          <string>-Dapple.laf.useScreenMenuBar=true</string>
          <string>-Xdock:name=HostsManager</string>
          <string>-Xmx80m</string>
    </array>


2. 再次优化堆内存

从堆内存使用数据来看,在内存到25M作用时,就主动触发了GC,回收了内存,可见,应用程序真正使用的内存还不到30M,堆内存最大使用率还不足25%,实在浪费。如图所示:
这里写图片描述

最大堆内存,这次设置为40M,在观察一下效果:

3. 第三次优化堆内存使用

这里写图片描述

这下堆内存的使用率能够达到一半以上了,最大40M,基本达到了预期效果。进一步压缩堆内存,还能进一步优化,会更加频繁的触发GC操作。

这里写图片描述

这里写图片描述

注意两幅图,截取的时间长度不同,所有不是GC越频繁,而是达到一定的内存的值,触发的GC操作。

最终设置堆内存大小为30M,总的内存使用量为110M作用,达到初期优化目标。



五、总体内存优化

这时候,该使用我们按的差距VisualGC了,看一下总体的内存使用情况:

这里写图片描述

前面我们已经优化了堆内存,所有堆内存区域的数据还是比较理想的。使用VisualGC插件,内存使用情况更加清晰,优化目标也更明确。

数据说明:
Space:内存使用情况,实线格子是已经申请的内存,有颜色的部分是已使用内存,总体是内存区域的最大值。
Perm:永久代
Old:老年代
S0、S1、Eden: 新生代
老年代和新生代都属于Java堆内存(Heap),即-Xmx限制的区域。

最大内存:82+20+3+3+9 = 117M,接近于系统“活动监视器”中显示的内存值114.5M

Graphs:
Compile Time: 352 compiles 2.960s
    运行时编译时间,对热点代码(HotSpot JDK默认虚拟机的名称由来),将字节码编译成本地二进制,编译了352次,共使用时间2.960s

Class Loader Time:
    类加载时间,加载了2946个类,共花费719.913ms,有1个类未加载。

Eden Space (9.000M, 4.000M):3.444M 959 collections, 849.736ms。
    9.000M 该区域最大内存,4.000M当前已经申请的内存,3.444M已经使用的内存,变化很快。也是GC最频繁的区域,幸存的对象,移入S0或者S1区域。该区域的GC为Moni GC,最频繁,也是速度最快的。老年代内存满了,会触发Full GC,会暂停所有用户进程值安全点,进行GC,速度比较慢。具体不在赘述,放JVM内存管理中详细叙述。
其他内存区域参数同理。

    可以看到,新生代和老年代的使用率已经没有太大的空间可以优化了,但是永久代的内存82M,才使用了17M,使用率不足35%,可以大刀阔斧的,将其最大值设置为25M,六点余地,以免不够用。虽然才申请了21M,但是剩下的61M,也是化为了私有内存的,其他进程也是无法使用的。

最终优化参数如下:

    <key>JVMOptions</key>
    <array>
          <string>-Dapple.laf.useScreenMenuBar=true</string> <!-- 菜单配置 -->
          <string>-Xdock:name=HostsManager</string> <!-- dock显示名称 -->
          <string>-Xmx30m</string> <!-- 最大堆内存 -->
          <string>-Xms30m</string> <!-- 最小堆内存 -->
          <string>-Xmn10m</string> <!-- 堆内存块单元大小 -->
          <string>-XX:PermSize=25m</string> <!-- 初始永久代大小 -->
          <string>-XX:MaxPermSize=25m</string><!-- 永久代最大值 -->
    </array>


最终,总的内存消耗,如图:
这里写图片描述

jvm内存详情:

这里写图片描述

    可以看出来,内存总体使用情况还是可以进一步优化,但是空间并不大,也没这个必要了。优化完成后,总消耗内存控制在70M左右。基本达成目标。

PS:Java应用记得设置内存参数哦,OS X系统默认是个坑!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值