查看原文
其他

面试题:你简历中说你擅长JVM调优,给我一个关于Code Cache调优的案例吧。

以下文章来源于占小狼的博客 ,作者占小狼

前语:由于行情不太好,本号决定对文章进行了重新排版,在每篇文章后面都会给各位读者推荐一个近期热招的职位「来源于我身边的朋友,绝对靠谱」在此,建议大家为本公众号加“星标”。如文章写得好,望大家阅读后在右下边“在看”处点个赞,以示鼓励!

本文转自公众号:占小狼的博客


问题描述有业务反馈,线上一个应用运行了一段时间之后,在高峰期之后,突然发现处理能力下降,接口的响应时间变长,但是看Cat上的GC数据,一切都很正常。


通过跳板机上机器查看日志,发现一段平时很少见到的日志。


  1. Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.

  2. Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=.

  3. ...

  4. “CompilerThread0” java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?

其中CodeCache is full,说明Code Cache已经满了,导致Compiler失效,这是为什么?

首先,我们得了解什么是Code Cache。


# 1、什么是Code Cache


Java代码在执行次数达到一个阈值会触发JIT编译,一旦代码块被编译成本地机器码,下次执行的时候会直接运行编译后的本地机器码。所以这本地机器码必须被缓存起来,而缓存这个本地机器码的内存区域就是Code Cache,它并不属于Java堆的一部分,除了JIT编译的代码之外,Java所使用的本地方法代码(JNI)也会存在codeCache中。


# 2、Code Cache 调优


由于Code Cache是一块内存区域,那么肯定有大小的限制,但是不同版本的JVM、不同的启动方式,Code Cache的默认大小也不同,可通过 jinfo -flag ReservedCodeCacheSize 进行查看。


服务启动之后,随着时间的推移,肯定会有越来越多的方法被JIT编译成本地机器码,并存放到Code Cache,由于Code Cache大小是固定的,那么就存在被用完的风险。

一旦Code Cache被填满,就会出现下面情况。


  • JVM的JIT功能会被停止,将不会编译任何额外的代码。

  • 被编译过的代码仍然以编译方式执行,但是尚未被编译的代码只能以解释方式执行了。


这种情况下,如果应用中还有很多代码以解释方式执行,其性能会大大降低。为了避免这种情况,就需要对Code Cache比较深入的理解。


JVM启动的时候,Code Cache所需内存会被单独初始化,这时候Java堆还会被初始化,所以Code Cache和Java堆是两块独立内存区域。


在 codeCache.cpp的 CodeCache::initialize()方法中,实现了Code Cache的初始化。



Code Cache包含了3种数据:

  • NonNMethodCode

  • ProfiledCode

  • NonProfiledCode


通过 SegmentedCodeCache参数可以选择按照整体初始化,还是分段初始化。


通过 -XX:ReservedCodeCacheSize参数可以指定Code Cache的初始化大小,这个默认值在不同的JDK版本也不同,目前我这边调试的是OpenJDK11,默认大小是240M,这个已经够用了。


可以看下其它版本的默认大小。



对于那些只有32M、48M的就可能存在Code Cache不足的隐患,增加 ReservedCodeCacheSize可以是一个解决方案,但这通常只是一个临时的解决方案。


幸运的是,JVM提供了一种比较激进的codeCache回收方式:Speculative flushing。


在JDK1.7.0_4之后这种回收方式默认开启,而之前的版本需要通过一个参数来开启: -XX:+UseCodeCacheFlushing

在Speculative flushing开启的情况下,当Code Cache不足时。


  • 最早被编译的一半方法将会被放到一个old列表中等待回收;

  • 在一定时间间隔内,如果old列表中方法没有被调用,这个方法就会被从Code Cache清除;


很不幸的是,在JDK1.7中,Speculative flushing释放了一部分空间,但是从编译日志来看,JIT并没有恢复正常,并且系统整体性能下降很多,出现了大量超时。


在Oracle官网上,有这样一个Bug:http://bugs.java.com/bugdatabase/viewbug.do?bugid=8006952 

由于算法问题,当Code Cache不足之后会导致编译线程无法继续,并且消耗大量CPU,导致系统运行变慢。



这个bug在7u101及8以后的版本已经得到修复。


(完)



今天,文末发一个招聘岗位,如果你满足要求,欢迎扔简历过来,如果你身边有合适的朋友,欢迎转发该岗位给他,谢谢~~~

最后,也欢迎各位读者入群来交流切磋,戳这里:咱们来一起抱团取暖,好吗?


---END---



热文推荐

面试官:站点遇到攻击且无明显攻击特征,造成站点访问慢,nginx不断返回502等错误,你能给我分析一下吗?

面试题:你是如何选择顺序存储数据结构的?

为什么80%的人都觉得编程难,它难在哪里呢?

面试题:竟然有90%的程序员不能把这个算法完全写正确。。。

面试题:jdk那些类的底层实现使用过位运算,并且给你印象最深?

给个在看

人气满满

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存