追加: 首先要声明一下,这个案例在<深刻理解JVM虚拟机>这本书中也提到过. 这本书是我曾经学习JVM的第一本书.里面关于Heap的优化思想,来源于此.建议你们想学JVM原理的,能够找来此书看看. 写这篇文章,是由于最近在给一个社交网站服务器作调优,忽然以为我机器上的eclipse跑的比较多,因此顺便优化下eclipse.至于基于WebSphere服务器的性能调优,这回涉及到更多的工具和方法,会在之后的文章中看到. java
最近自从eclipse安装了不少插件之后,启动变得很是的慢,每次启动,要消耗近半分钟.这是不正常的. 今天决定好好优化一下. android
我所使用的eclipse是Eclipse Java EE IDE for Web Developers 3.8版本. 跑在MAC OSX上, SSD+8G RAM, 这么高性能的机器居然不能秒开eclipse, 这太说不过去了. 哦,还有我使用的JVM是Oracle的HotSpot,来自于JDK1.6 64bit. 服务器
首先,在优化前,让咱们看看eclipse启动时,JVM的各项性能指标. 由于我并不能准确的断定eclipse的启动完成时间, 因此我只能说大约事件. eclipse
首先启动JDK自带的JVM性能监视工具,在java\bin的目录下,有一个jvisualvm,它是绑定在JDK中的visualvm.双击启动visualvm. 而后启动eclipse, 在eclipse启动完成之后,使用visualvm的查看eclipse的Visual GC状况, 如图: 工具
上图中说明在eclipse的启动过程当中,JIT对字节码进行了向机器码的编译,花去了22秒的时间.Class加载花去了10秒的时间,Minor GC发生了72次,花去0.64秒,Full GC发生了12次,仅仅花去了61毫秒. 性能
咱们再去MBean选项查看,发现新生代使用ParNew垃圾收集器,而老年代使用的是CMS垃圾收集器. 学习
总上状况看出,因为MAC的性能比较好,因此垃圾回收并无消耗太多的时间,而且CMS+ParNew自己就是并行垃圾回收,不会形成用户程序太多的停顿. 时间主要消耗在了JIT的即时编译和Class加载上了. 优化
首先要优化的就是class加栽.由于eclipse这个工具是一个成熟的工具,通过了这么多人的验证,因此我充分信任eclipse的代码,容许eclipse的代码在加载的时候,跳过字节码验证. 关闭字节码验证的方法是在vm的args中加入参数 -Xverify:none. 对于eclipse来讲,找到eclipse.ini, 加入-Xverify:none. 让咱们再重启一下eclipse,看看class加载时间是否减少. 再次启动,发现class加载事件缩小到7秒,比以前少了3秒. 网站
而后优化的是JIT的时间. 在使用eclipse编写程序时,主要是文本编辑,编译和运行,JIT虽然能够带给咱们高性能,可是JIT在编译机器码的时候,却要消耗不少的时间. eclipse对项目的编译和运行自己就很慢,切运行时是启动一个新的java进程,跟eclipse自己无关,因此,我能够接受抛弃JIT编译器,而只是用JVM解释器执行字节码所带来的效率下降. 这样能够去除JIT编译的时间. 作法以下,在eclipse.ini中加入vm的参数 -Xint, 意思是只使用解释器. 让咱们来看看结果: spa
JVM编译器时间变成了0, 一下减掉20秒. 可是,因为缺乏了运行时的即时编译优化方案,代码的运行时间变长了, eclipse的总体启动时间慢了更多,超过了30秒. 因而可知,JIT是多么有用的一项技术.因此禁止JIT的尝试失败了.咱们把以前的参数-Xint去掉.
哦,对了,我还装了不少的插件,尤为是android开发插件.启动的时候对插件的激活也会花去不少时间. 屏蔽插件激活的方法: Windows -> Preferences, 输入 “startup”, 点击 “Startup and Shutdown”, 把不须要的插件勾掉. 此外,还须要关掉没必要要的validation,方法为:Windows -> Preferences -> Validation. 只选你须要的.
作完以上工做,我发现eclipse启动稍微快了一些. 掐着秒表计算的花了大约15秒.
最后,再优化一下GC和堆栈吧.虽说,GC已经表现的很好了,都没有超过1秒,可是GC的频率如此高,说明JVM的内存的分配是不合理的.为此,咱们须要从新对JVM内存进行划分. 为了对JVM的内存进行合理分配,咱们须要了解eclipse启动过程当中,GC到底发生了什么事情. 打开gc log的方法以下:
想eclipse.ini的vm参数中添加
-XX:+PrintGCDetails
-Xloggc:/users/joey/Documents/gc.log
启动eclipse,生成gc.log, 打开log,进行分析.
第一次Minor GC发现,新生代的大小约为20M. 堆的大小约为40M. 再接下来的GC中,新生代始终没有扩容.这说明,新生代的大小合适.
0.720: [GC 0.720: [ParNew: 17024K->2112K(19136K), 0.0099529 secs] 17024K->2324K(38848K), 0.0100285 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
第一次发生Full GC时,发现老年代已经扩容到约93M,而永生代扩容到约128M
67.213: [Full GC (System) 67.213: [CMS: 57969K->57877K(93124K), 0.3563491 secs] 62179K->57877K(112260K), [CMS Perm : 80490K->80392K(128708K)], 0.3565176 secs] [Times: user=0.36 sys=0.00, real=0.36 secs]
而直到最后一次GC, 老年代占用也没超过125M,永生带占用也没有超过125M. 但他们的占用空间均超过了100M. 由此,咱们有理由规定一个初始堆大小. 最终,经过分析,我给eclipse.ini添加了以下几个参数:
-server
-Xverify:none
-XX:PermSize=128m
-XX:MaxPermSize=256m
-Xms256m
-Xmx512m
-Xmn40m
-Xss2m
-server是让JVM以server模式运行,加剧JIT的优化做用,因为eclipse是常常开着不关,在server模式下,JIT会随着运行的时间,把字节码更深入的变成成机器代码.加快运行速度.
-Xverify:none, 跳过对字节码的验证.
PermSize永生带设置为128M,堆的初始大小设置为256M,新生代站了40M. 每一个线程栈大小设为2M.
在这种设置下,Full GC已经彻底消失,但仍是剩下了20次左右的Minor GC,大约花掉0.3秒, 这是能够接受的. 若是为了彻底消除GC而把新生代的空间设大,那也是一种内存的浪费. 重启eclipse,启动时间已经落在了15秒以内.如图: