Code Cache区域是JVM用来存储经过C1/C2编译优化后的代码的,因为是存在内存中的,所以肯定得限制大小,Code Cache的最大大小可通过jinfo -flag ReservedCodeCacheSize来获取,通常在64 bit机器上默认是48m,当code cache用满了后,编译优化就被禁掉了,此时会回归到解释执行,RT可想而知不会好到哪去。
为什么这个应用会造成这样的现象呢?
和应用方了解了下,应用会采用groovy来加载一些脚本,在这些脚本修改后会动态更新,而且更新的是比较频繁的,这些groovy
script执行的也是比较多的,这样的话基本是可以解释通的,意味着C2需要在groovy
script更新后和执行到达阈值时重新编译,这自然会导致随着运行时间越长,Code Cache使用的就越多。
解决方法
Code
Cache用满一方面是因为空间可能不够用,另一方面是Code
Cache是不会回收的,所以会累积的越来越多(其实在不采用groovy这种动态更新/装载class的情况下的话,是不会太多的),所以解法一可以是增大code
cache的size,可通过在启动参数上增加-XX:ReservedCodeCacheSize=256m(Oracle JVM Team那边也是推荐把code
cache调大的),二是启用code cache的回收机制(关于Code Cache flushing的具体策略请参见此文),可通过在启动参数上增加:-XX:+UseCodeCacheFlushing来启用。
-XX:ReservedCodeCacheSize=256m
-XX:+UseCodeCacheFlushing
=================================================
1、通过groovyShell类直接执行脚本,例如:
2、通过groovyScriptEngine执行文件或者脚本,例如:
?3、通过GroovyClassLoader来执行,例如:
需要注意的是,通过看groovy的创建类的地方,就能发现,每次执行的时候,都会新生成一个class文件,这样就会导致JVM的perm区持续增长,进而导致FullGCc问题,解决办法很简单,就是脚本文件变化了之后才去创建文件,之前从缓存中获取即可,缓存的实现可以采用简单的Map或者使用之前文章提到的EhCache(同时可以设置缓存有效期,降低服务器压力)。
在使用时,最好每次重新new classloader,因为如果脚本重新加载了,这时候就会有新老两个class文件,如果通过一个classloader持有的话,这样在GC扫描的时候,会认为老的类还在存活,导致回收不掉,所以每次new一个就能解决这个问题了。
注意CodeCache的设置大小(来自:http://hellojava.info/)
对于大量使用Groovy的应用,尤其是Groovy脚本还会经常更新的应用,由于这些Groovy脚本在执行了很多次后都会被JVM编译为native进行优化,会占据一些CodeCache空间,而如果这样的脚本很多的话,可能会导致CodeCache被用满,而CodeCache一旦被用满,JVM的Compiler就会被禁用,那性能下降的就不是一点点了。
Code Cache用满一方面是因为空间可能不够用,另一方面是Code Cache是不会回收的,所以会累积的越来越多(其实在不采用groovy这种动态更新/装载class的情况下的话,是不会太多的),所以解法一可以是增大code cache的size,可通过在启动参数上增加-XX:ReservedCodeCacheSize=256m(Oracle JVM Team那边也是推荐把code cache调大的),二是启用code cache的回收机制(关于Code Cache flushing的具体策略请参见此文),可通过在启动参数上增加:-XX:+UseCodeCacheFlushing来启用。
========================================== =