提纲
案例分析
1. 高性能硬件上的部署策略
在高性能硬件上部署程序,目前主要有两种方式:
通过使用64位JDK来使用大内存
使用若干个32位虚拟机建立逻辑集群来利用硬件资源。
使用64位JDK来使用大内存的缺点:
1. 内存回收导致的长时间停顿
2. 现阶段,64位JDK的性能测试效果普遍低于32位JDK
3. 需要保证程序足够稳定,因为这种应用要是产生堆溢出几乎无法产生堆转储快照
4. 相同程序在64位JDK消耗的内存一般比32位JDK大,这是由于指针膨胀,以及数据类型对齐补白等因素导致的。
使用若干个32位虚拟机建立逻辑集群的缺点:
1. 尽量避免结点竞争全局资源。
2. 很难最高效率地利用某些资源池。
3. 各个节点仍然不可避免地收到32位的内存限制。
4. 大量使用本地缓存的应用,在逻辑集群中会造成大量浪费。
2. 集群间同步导致的内存溢出
采用亲和式集群时,由于节点之间没有采用session同步,会导致资源竞争激烈而影响性能,后来使用JBossCache构建了全局缓存,由于信息有传输失败重发的可能行,在确认所有注册在GMS的结点都受到正确的信息前,发送的信息必须在内存中保留。当网络情况不能满足要求时,重发数据就会不断堆积,产生内存溢出。
3. 对外内存导致的溢出错误
除了java堆和永久代之外,下面这些区域还会占用较多的内存,这里所有的内存总和收到操作系统进程最大内存的限制:
DirectMemory:可通过-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. 外部命令导致系统缓慢
通过Runtime.getRuntime().exec()方法来调用shell脚本时,JVM会首先克隆一个和当前虚拟机拥有一样环境变量的进程,再用新的进程去执行外部命令,这样操作对系统消耗很大。
5. 服务器JVM进程崩溃
做管理信息系统(MIS)时,web服务和OA门户系统的处理速度不对等,时间越长积累了越多的web服务没有完成,导致等待线程和socket连接增多,使得JVM进程崩溃。
解决方法:将异步调用改为生产者消费者模式的消息队列实现后,恢复正常。
6. 不恰当数据结构导致内存占用过大
HashMap<long,long>存储数据文件的空间效率太低
存放的两个长整型数据共16B,包装成java.lang.Lang对象之后,就分别具有8B的Mark Word、8B的Kclass指针,再加8B的存储数据的long值。在这两个Long对象组成Map.Entry之后,有多了16B的对象头,然后一个8B的next字段和4B的int型的hash字段,为了对齐,还必须添加4B的空白填充,最后还有Hash Map中对这个Entry的8B的引用,这样增加两个长整型数字,实际耗费的内存为(Long(24B)*2)+Entry(32B)+HashMap Ref(8B)=88B,空间效率为18%,实在太低了。
7. 由Windows虚拟内存导致的长时间停顿
程序最小化时,资源管理中显示的占用内存大幅度减小,但是虚拟内存则没有变化。程序最小化时的工作内存会被自动转换到磁盘的页面文件之中。
解决方法:设置参数-Dsun.awt.keepWorkingSetOnMinimize=true,对许多AWT程序都有应用。
Eclipse运行调优
开始启动时间插件制作:
1. 新建插件项目
2.在Activitor.java中添加如下方法
3.创建启动类:
package start;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IStartup;
public class ShowTime implements IStartup{
@Override
public void earlyStartup() {
// TODO Auto-generated method stub
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
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, "Informatuion", message);
}
});
}
}
4.创建配置文件:
5.生成jar包
esclipse.ini文件配置:
-Xverify:none //取消字节码验证
-Xmx1024m //设置堆内存的最大值
-Xms1024m //设置堆内存的最大值
-Xmn512m //设置堆内新生代的容量
-XX:PermSize=512m //设置永久代的容量
-XX:MaxPermSize=512m //设置永久代的最大值
-XX:+DisableExplicitGC //忽略来自system.gc触发的垃圾收集
-Xnoclassgc //
-XX:+UseParNewGC //在新生代使用parNew收集器
-XX:+UseConcMarkSweepGC //在老年代使用CMS收集器
-XX:CMSInitiatingOccupancyFraction=85 //设置老年代使用比例为多少后触发垃圾收集