深入理解Java虚拟机 调优案例分析与实战总结

案例分析
1.高性能硬件上的程序部署策略
在高性能硬件上部署程序 目前主要有两种方式:
通过64位JDK来使用大内存
使用若干个32位虚拟机建立逻辑集群来利用硬件资源

对于用户交互性强 对停顿时间敏感的系统 可以Java虚拟机分配超大堆的前提是有把握把应用程序的Full GC频率控制得足够低 至少要低到不会影响用户使用 譬如十几个小时乃至一天才出现一次Full GC 这样可以通过在深夜执行定时任务的方式触发Full GC甚至自动重启应用服务器来保持内存可用空间在一个稳定的水平

2.集群间同步导致的内存溢出
JBossCache是基于自家的JGroups进行集群间的数据通信 JGroups使用协议栈的方式来实现收发数据包的各种所需特性自由组合 数据包接收和发送时要经过每层协议栈的up()和down()方法 其中NAKACK栈用于保障各个包的有效顺序及重发
由于信息有传输失败需要重发的可能性 在确认所有注册在GMS(Group MembershipService)的节点都收到正确的信息前 发送的信息必须在内存中保留 而此MIS的服务端中有一个负责安全校验的全局Filter 每当接收到请求时 均会更新一次最后操作时间 并且将这个时间同步到所有的节点去 使得一个用户在一段时间内不能在多台机器上登录 在服务使用过程中 往往一个页面会产生数次乃至数十次的请求 因此这个过滤器导致集群各个节点之间网络交互非常频繁 当网络情况不能满足传输要求时 重发数据在内存中不断堆积 很快就产生了内存溢出

3.堆外内存导致的溢出错误
除了Java堆和永久代之外 我们注意到下面这些区域还会占用较多的内存 这里所有的内存总和受到操作系统进程最大内存的限制
Direct Memory:可通过-XX:MaxDirectMemorySize调整大小 内存不足时抛出OutOfMemoryError或者OutOfMemoryError:Direct buffer memory
线程堆栈:可通过-Xss调整大小 内存不足时抛出StackOverflowError(纵向无法分配 即无法分配新的栈帧)或者OutOfMemoryError:unable to create new native thread(横向无法分配 即无法建立新的线程)
Socket缓存区:每个Socket连接都Receive和Send两个缓存区 分别占大约37KB和25KB内存 连接多的话这块内存占用也比较可观 如果无法分配 则可能会抛出IOException:Too many open files异常
JNI代码:如果代码中使用JNI调用本地库 那本地库使用的内存也不在堆中
虚拟机和GC:虚拟机 GC的代码执行也要消耗一定的内存

4.外部命令导致系统缓慢
每个用户请求的处理都需要执行一个外部shell脚本来获得系统的一些信息 执行这个shell脚本是通过Java的Runtime.getRuntime().exec()方法来调用的 这种调用方式可以达到目的 但是它在Java虚拟机中是非常消耗资源的操作 即使外部命令本身能很快执行完毕 频繁调用时创建进程的开销也非常可观 Java虚拟机执行这个命令的过程是:首先克隆一个和当前虚拟机拥有一样环境变量的进程 再用这个新的进程去执行外部命令 最后再退出这个进程 如果频繁执行这个操作 系统的消耗会很大 不仅是CPU 内存负担也很重

5.服务器JVM进程崩溃
由于MIS系统的用户多 待办事项变化很快 为了不被OA系统速度拖累 使用了异步的方式调用Web服务 但由于两边服务速度的完全不对等 时间越长就累积了越多Web服务没有调用完成 导致在等待的线程和Socket连接越来越多 最终在超过虚拟机的承受能力后使得虚拟机进程崩溃 解决方法:通知OA门户方修复无法使用的集成接口 并将异步调用改为生产者/消费者模式的消息队列实现后 系统恢复正常

6.不恰当数据结构导致内存占用过大
在HashMap<Long,Long>结构中 只有Key和Value所存放的两个长整型数据是有效数据 共16B(2×8B) 这两个长整型数据包装成java.lang.Long对象之后 就分别具有8B的MarkWord、8B的Klass指针 在加8B存储数据的long值 在这两个Long对象组成Map.Entry之后 又多了16B的对象头 然后一个8B的next字段和4B的int型的hash字段 为了对齐 还必须添加4B的空白填充 最后还有HashMap中对这个Entry的8B的引用 这样增加两个长整型数字 实际耗费的内存(Long(24B)×2)+Entry(32B)+HashMap Ref(8B)=88B 空间效率为16B/88B=18%实在太低了

7.由Windows虚拟内存导致的长时间停顿
除GC日志之外 还观察到这个GUI程序内存变化的一个特点 当它最小化的时候 资源管理中显示的占用内存大幅度减小 但是虚拟内存则没有变化 因此怀疑程序在最小化时它的工作内存被自动交换到磁盘的页面文件之中了 这样发生GC时就有可能因为恢复页面文
件的操作而导致不正常的GC停顿
在MSDN上查证后确认了这种猜想 因此 在Java的GUI程序中要避免这种现象 可以加入参数“-Dsun.awt.keepWorkingSetOnMinimize=true”来解决 这个参数在许多AWT的程序上都有应用 例如JDK自带的Visual VM 用于保证程序在恢复最小化时能够立即响应 在这个案例中加入该参数后 问题得到解决

实战:Eclipse运行速度调优
Eclipse启动耗时统计插件

ShowTime.java代码:

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IStartup;
/**
*统计Eclipse启动耗时
*@author zzm
*/
public class ShowTime implements IStartup{
public void earlyStartup(){
Display.getDefault().syncExec(new Runnable(){
public void run(){
long eclipseStartTime=Long.parseLong(System.getProperty("eclipse.startTime"));
long costTime=System.currentTimeMillis()-eclipseStartTime;
Shell shell=Display.getDefault().getActiveShell();
String message="Eclipse启动耗时:"+costTime+"ms";
MessageDialog.openInformation(shell,"Information",message);
}
});
}
}
plugin.xml代码:
<?xml version="1.0"encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.ui.startup">
<startup class="eclipsestarttime.actions.ShowTime"/>
</extension>
</plugin>

编译时间和类加载时间的优化
调整内存设置控制垃圾收集频率
选择收集器降低延迟

修改配置后的Eclipse配置

-vm
D:/_DevSpace/jdk1.6.0_21/bin/javaw.exe
-startup
plugins/org.eclipse.equinox.launcher_1.0.201.R35x_v20090715.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_1.0.200.v20090519
-product
org.eclipse.epp.package.jee.product
-showsplash
org.eclipse.platform
-vmargs
-Dcom.sun.management.jmxremote
-Dosgi.requiredJavaVersion=1.5
-Xverify:none
-Xmx512m
-Xms512m
-Xmn128m
-XX:PermSize=96m
-XX:MaxPermSize=96m
-XX:+DisableExplicitGC
-Xnoclassgc
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=85
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值