深入理解JVM--调优分析与实战(四)

提纲

案例分析

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  //设置老年代使用比例为多少后触发垃圾收集



猜你喜欢

转载自blog.csdn.net/yinweicheng/article/details/80666570