最近在学习JVM,了解了一些关于JVM的内存分配和垃圾回收的知识,其中有有一个实战是优化Eclipse的启动,从类加载时间、JIT编译时间、垃圾收集时间三个方面做了优化,简单、综合性强,可以加深对JVM的理解,所以这里对其进行验证。
硬件和操作系统环境:
java版本(HotSpot虚拟机):
Eclipse相关信息:
原始的启动配置:
最大永久代空间是256M,初始堆40M,最大512M
启动后的GC信息,使用jvisualvm工具查看:
使用eclipse插件来对eclipse启动计时。
优化类加载速度:
使用jstat -class ID查看类加载,原始情况如下:
考虑到不需要再加载的时候进行字节码验证,因此通过参数-Xverify:none禁止掉字节码验证过程。优化后的类加载时间变为,大概降低为原来的一半时间:
优化编译时间:
编译时间是指虚拟机的JIT(just in time compiler)编译器编译热点代码的耗时,虚拟机解释执行字节码速度慢,通过虚拟机内置的运行时编译器,将热点代码即时编译为本地代码,以提高运行速度。
-Xint参数禁止编译器运作,强制虚拟机对字节码采用纯解释方式执行。
当虚拟机运行在-client模式的时候,使用的是一个代号为C1的轻量级编译器,
当虚拟机运行在-server模式的时候,使用的是一个代号为C2的重量级编译器,提供更多的优化措施。这不会降低编译时间,但是长久运行将会获益。
优化垃圾收集次数:
本机上的Eclipse启动过程共进行12次Minor GC, 2次Full GC,说明内存分配还是比较合理的。
如果Minor GC次数太多,说明分配的新生代空间太小,可以使用-Xmn调整新生代空间。
而对于老年代Old Gen来说,没达到最大容量却发生了多次Full GC主要是由于老年代扩容导致的。
永久代Perm Gen空间扩展也会带来一部分时间开销。
为了避免扩容带来的性能浪费,可以
把-Xms和-XX:PermSize参数分别设置为-Xmx和-XX:PermSizeMax参数值,使得初始化空间和最大空间相等。这样虚拟机在启动时就把老年代和永久代的容量固定下来了。
优化垃圾收集器:
考虑到CPU使用率很低,可以尝试采用并行收集器。-XX:+UseConcMarkSweepGC和-XX:+UseParNewGC。充分利用多核CPU的能力,可以降低GC停顿时间。注意CMS默认老年代使用了68%就进行收集,必要时可以使用-XX:CMSInitiatingOccupancyFraction=85将临界值修改为85%。
最后优化完毕的eclipse启动配置为:
-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.200.v20140116-2212
-product
org.eclipse.epp.package.jee.product
--launcher.defaultAction
openFile
-showsplash
org.eclipse.platform
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.6
-Xverify:none
-Xms512m
-Xmx512m
-Xmn171m
-XX:MaxPermSize=256m
-XX:PermSize=128m
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
可以看到Minor GC只进行了4次(500ms),而Full GC则没有发生,老年代的空间直接就是340M不用扩展。永久代初始化空间则初始化为最大256M的一半128M。
另外,如果老年代空间很充足但还是发生了Full GC,则可以尝试关闭System.gc()的显式调用,-XX:+DisableExplicitGC。
附 http://blog.csdn.net/yoland/article/details/6981440:
参数中-vmargs的意思是设置JVM参数,所以后面的其实都是JVM的参数了,我们首先了解一下JVM内存管理的机制,然后再解释每个参数代表的含义。堆(Heap)和非堆(Non-heap)内存按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。 堆内存分配 JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。 非堆内存分配 JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。 JVM内存限制(最大值) 首先JVM内存限制于实际的最大物理内存(废话!呵呵),假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了。