国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決

Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決

2021-11-25 14:14猿碼架構(gòu) Java教程

這篇文章主要介紹了Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下

0 問題描述

一個(gè)應(yīng)用在運(yùn)行一段時(shí)間后,隨著訪問量不斷增加,突然處理能力下降。但是從流量,jstack,gc上看基本正常。感覺好像突然從 “健康狀態(tài)” 進(jìn)入了 “虛弱狀態(tài)”。

1 排查問題

  1. 在JVM日志里,可以發(fā)現(xiàn)如下log:

    ?
    1
    2
    3
    4
    Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
    Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=.
    ...
    “CompilerThread0” java.lang.OutOfMemoryError: requested 2854248 bytes for Chunk::new. Out of swap space?

    說明CodeCache已經(jīng)滿了。而且導(dǎo)致這個(gè)時(shí)候JIT就會停止,JIT一旦停止,就不會再起來了,可以想象一下,如果很多代碼沒有辦法去JIT的話,性能就會比較差。

  2. 使用如下命令檢查一下Code Cache的值:

    ?
    1
    jinfo -flag ReservedCodeCacheSize

2 解決問題

  1. 一個(gè)可行的方法,就是擴(kuò)大Code Cache空間:

    使用 -XX:ReservedCodeCacheSize= 指定一個(gè)更大的空間,來支持更多的JIT編譯;

  2. 此外,另一個(gè)可行的方法,啟用Code Cache的回收機(jī)制:

    通過在啟動(dòng)參數(shù)上增加:-XX:+UseCodeCacheFlushing 來啟用;

    打開這個(gè)選項(xiàng),在JIT被關(guān)閉之前,也就是CodeCache裝滿之前,會在JIT關(guān)閉前做一次清理,刪除一些CodeCache的代碼;

    如果清理后還是沒有空間,那么JIT依然會關(guān)閉。這個(gè)選項(xiàng)默認(rèn)是關(guān)閉的;

3 背景知識

3.1 JIT即時(shí)編譯

在Java中提到“編譯”,自然很容易想到 javac 編譯器將.java文件編譯成為.class文件的過程,這里的 javac 編譯器稱為前端編譯器,其他的前端編譯器還有諸如Eclipse,JDT中的增量式編譯器ECJ等。相對應(yīng)的還有 后端編譯器,它在程序運(yùn)行期間將字節(jié)碼轉(zhuǎn)變成機(jī)器碼(現(xiàn)在的Java程序在運(yùn)行時(shí)基本都是 解釋執(zhí)行加編譯執(zhí)行),如HotSpot虛擬機(jī)自帶的JIT(Just In Time Compiler)編譯器(分Client端和Server端)。

Java程序最初是僅僅通過解釋器解釋執(zhí)行的,即對字節(jié)碼逐條解釋執(zhí)行,這種方式的執(zhí)行速度相對會比較慢,尤其當(dāng)某個(gè)方法或代碼塊運(yùn)行的特別頻繁時(shí),這種方式的執(zhí)行效率就顯得很低。于是后來 在虛擬機(jī)中引入了JIT編譯器(即時(shí)編譯器),當(dāng)虛擬機(jī)發(fā)現(xiàn)某個(gè)方法或代碼塊運(yùn)行特別頻繁時(shí),達(dá)到某個(gè)閾值,就會把這些代碼認(rèn)定為“Hot Spot Code”(熱點(diǎn)代碼),為了提高熱點(diǎn)代碼的執(zhí)行效率,在運(yùn)行時(shí),虛擬機(jī)將會把這些代碼編譯成與本地平臺相關(guān)的機(jī)器碼,并進(jìn)行各層次的優(yōu)化,完成這項(xiàng)任務(wù)的正是JIT編譯器。

現(xiàn)在主流的商用虛擬機(jī)(如:Sun HotSpot、IBM J9)中幾乎 都同時(shí)包含解釋器和編譯器,三大商用虛擬機(jī)之一的JRockit是個(gè)例外,它內(nèi)部沒有解釋器,因此會有啟動(dòng)相應(yīng)時(shí)間長之類的缺點(diǎn),但它主要是面向服務(wù)端的應(yīng)用,這類應(yīng)用一般不會重點(diǎn)關(guān)注啟動(dòng)時(shí)間。

解釋器與編輯器二者各有優(yōu)勢:

  1. 當(dāng)程序需要迅速啟動(dòng)和執(zhí)行時(shí),解釋器可以首先發(fā)揮作用,省去編譯的時(shí)間,立即執(zhí)行;
  2. 當(dāng)程序運(yùn)行后,隨著時(shí)間的推移,編譯器逐漸會發(fā)揮作用,把越來越多的代碼編譯成本地代碼后,可以獲取更高的執(zhí)行效率;
  3. 解釋執(zhí)行可以節(jié)約內(nèi)存,而編譯執(zhí)行可以提升效率;

運(yùn)行過程中會被即時(shí)編譯器編譯的“熱點(diǎn)代碼”有兩類:

  1. 被多次調(diào)用的方法;
  2. 被多次調(diào)用的循環(huán)體;

3.2 Code Cache

Java代碼在執(zhí)行時(shí)一旦被編譯器編譯為機(jī)器碼,下一次執(zhí)行的時(shí)候就會直接執(zhí)行編譯后的代碼,也就是說,編譯后的代碼被緩存了起來。緩存編譯后的機(jī)器碼的內(nèi)存區(qū)域就是codeCache。這是一塊獨(dú)立于Java堆之外的內(nèi)存區(qū)域。除了JIT編譯的代碼之外,Java所使用的本地方法代碼(JNI)也會存在codeCache中。

 

 

Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決

 

Code Cache是JVM用于存儲經(jīng)過JIT C1/C2編譯優(yōu)化后的代碼。因?yàn)槭谴嬖趦?nèi)存中的,所以肯定得限制大小,Code Cache的最大大小可通過 jinfo -flag ReservedCodeCacheSize 來獲取,通常在64 bit機(jī)器上默認(rèn)是48m。

不同版本的JVM、不同的啟動(dòng)方式codeCache的默認(rèn)大小也不同:

JVM 版本和啟動(dòng)方式 默認(rèn) codeCache大小
32-bit client, Java 8 32 MB
32-bit server, Java 8 48M
32-bit server with Tiered Compilation, Java 8 240 MB
64-bit server, Java 8 48M
64-bit server with Tiered Compilation, Java 8 240 MB
32-bit client, Java 7 32 MB
32-bit server, Java 7 48 MB
32-bit server with Tiered Compilation, Java 7 96 MB
64-bit server, Java 7 48 MB
64-bit server with Tiered Compilation, Java 7 96 MB

3.3 分層編譯

JVM提供了一個(gè)參數(shù)-Xcomp,可以使JVM運(yùn)行在純編譯模式下,所有方法在第一次被調(diào)用的時(shí)候就會被編譯成機(jī)器代碼。加上這個(gè)參數(shù)之后,隨之而來的問題是啟動(dòng)時(shí)間變得很長,差不多是原來的2倍還多。

除了純編譯方式和默認(rèn)的mixed之外,從JDK6u25開始引入了一種分層編譯的方式。

Hotspot JVM內(nèi)置了2種編譯器,分別是 client方式啟動(dòng)時(shí)用的C1編譯器 和 server方式啟動(dòng)時(shí)用的C2編譯器 。

  1. C2編譯器在將代碼編譯成機(jī)器碼之前,需要收集大量的統(tǒng)計(jì)信息以便在編譯的時(shí)候做優(yōu)化,因此編譯后的代碼執(zhí)行效率也高,代價(jià)是程序啟動(dòng)速度慢,并且需要比較長的執(zhí)行時(shí)間才能達(dá)到最高性能;

  2. C1編譯器的目標(biāo)在于使程序盡快進(jìn)入編譯執(zhí)行階段,因此編譯前需要收集的統(tǒng)計(jì)信息比C2少很多,編譯速度也快不少。代價(jià)是編譯出的目標(biāo)代碼比C2編譯的執(zhí)行效率要低。

盡管如此,C1編譯的執(zhí)行效率也比解釋執(zhí)行有巨大的優(yōu)勢。分層編譯方式是一種折衷方式,在系統(tǒng)啟動(dòng)之初執(zhí)行頻率比較高的代碼將先被C1編譯器編譯,以便盡快進(jìn)入編譯執(zhí)行。隨著時(shí)間推進(jìn),一些執(zhí)行頻率高的代碼會被C2編譯器再次編譯,從而達(dá)到更高的性能。

通過以下JVM參數(shù)開啟分層編譯模式:

?
1
-XX:+TieredCompilation

在JDK8中,當(dāng)以server模式啟動(dòng)時(shí),分層編譯默認(rèn)開啟。需要注意的是,分層編譯方式只能用于server模式中,如果需要關(guān)閉分層編譯,需要加上啟動(dòng)參數(shù) -XX:-TieredCompilation;如果以client模式啟動(dòng),-XX:+TieredCompilation 參數(shù)將會被忽略。

3.4 Code Cache 滿了怎么辦

隨著時(shí)間推移,會有越來越多的方法被編譯,codeCache使用量會逐漸增加,直至耗盡。當(dāng)Code Cache用滿了后,會打印下面的日志:

在JDK1.7.0_4之前,你會在jvm的日志里看到這樣的輸出:

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

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

...

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

  1. JIT編譯器被停止了,并且不會被重新啟動(dòng),此時(shí)會回歸到解釋執(zhí)行;

  2. 被編譯過的代碼仍然以編譯方式執(zhí)行,但是尚未被編譯的代碼就 只能以解釋方式執(zhí)行了。

針對這種情況,JVM提供了一種比較激進(jìn)的codeCache回收方式:Speculative flushing。

在JDK1.7.0_4之后這種回收方式默認(rèn)開啟,而之前的版本需要通過一個(gè)啟動(dòng)參數(shù)來開啟:-XX:+UseCodeCacheFlushing。

在Speculative flushing開啟的情況下,當(dāng)codeCache將要耗盡時(shí):

  1. 最早被編譯的一半方法將會被放到一個(gè)old列表中等待回收;

  2. 在一定時(shí)間間隔內(nèi),如果old列表中方法沒有被調(diào)用,這個(gè)方法就會被從codeCache充清除;

很不幸的是,在JDK1.7中,當(dāng)codeCache耗盡時(shí),Speculative flushing釋放了一部分空間,但是從編譯日志來看,JIT編譯并沒有恢復(fù)正常,并且系統(tǒng)整體性能下降很多,出現(xiàn)大量超時(shí)。

在Oracle官網(wǎng)上看到這樣一個(gè)Bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8006952 由于codeCache回收算法的問題,當(dāng)codeCache滿了之后會導(dǎo)致編譯線程無法繼續(xù),并且消耗大量CPU導(dǎo)致系統(tǒng)運(yùn)行變慢。Bug里影響版本是JDK8,但是從網(wǎng)上其他地方的信息看,JDK7應(yīng)該也存在相同的問題,并且沒有被修復(fù)。

所以,目前來看,開啟UseCodeCacheFlushing會導(dǎo)致問題,如下:

  1. Code Cache滿了時(shí)緊急進(jìn)行清掃工作,它會丟棄一半老的編譯代碼;
  2. Code Cache空間降了一半,方法編譯工作仍然可能不會重啟;
  3. flushing可能導(dǎo)致高的cpu使用,從而影響性能下降;

3.5 Code Cache 調(diào)優(yōu)

以client模式或者是分層編譯模式運(yùn)行的應(yīng)用,由于需要編譯的類更多(C1編譯器編譯閾值低,更容易達(dá)到編譯標(biāo)準(zhǔn)),所以更容易耗盡codeCache。當(dāng)發(fā)現(xiàn)codeCache有不夠用的跡象(通過上一節(jié)提到的監(jiān)控方式)時(shí),可以通過啟動(dòng)參數(shù)來調(diào)整codeCache的大小。

?
1
-XX:ReservedCodeCacheSize=256M

那具體應(yīng)該設(shè)置為多大合適,根據(jù)監(jiān)控?cái)?shù)據(jù)估算,例如單位時(shí)間增長量、系統(tǒng)最長連續(xù)運(yùn)行時(shí)間等。如果沒有相關(guān)統(tǒng)計(jì)數(shù)據(jù),一種推薦的設(shè)置思路是設(shè)置為當(dāng)前值(或者默認(rèn)值)的2倍。

需要注意的是,這個(gè)codeCache的值不是越大越好。對于32位JVM,能夠使用的最大內(nèi)存空間為4g。這個(gè)4g的內(nèi)存空間不僅包括了java堆內(nèi)存,還包括JVM本身占用的內(nèi)存、程序中使用的native內(nèi)存(比如directBuffer)以及codeCache。如果將codeCache設(shè)置的過大,即使沒有用到那么多,JVM也會為其保留這些內(nèi)存空間,導(dǎo)致應(yīng)用本身可以使用的內(nèi)存減少。對于64位JVM,由于內(nèi)存空間足夠大,codeCache設(shè)置的過大不會對應(yīng)用產(chǎn)生明顯影響。

在JDK 8中,提供了一個(gè)啟動(dòng)參數(shù) -XX:+PrintCodeCache 在JVM停止的時(shí)候打印出codeCache的使用情況。其中max_used就是在整個(gè)運(yùn)行過程中codeCache的最大使用量。可以通過這個(gè)值來設(shè)置一個(gè)合理的codeCache大小,在保證應(yīng)用正常運(yùn)行的情況下減少內(nèi)存使用。

3.6 問題解決

問題的前因后果都弄清楚了,也就好解決了。上面提到過純編譯方式和分層編譯方式都可以解決或緩解啟動(dòng)后負(fù)載過高的問題,那么我們就有2種選擇:

  1. 采用分層編譯方式,并修改codeCache的大小為256M;

  2. 采用純編譯方式,并修改codeCache的大小為256M;

經(jīng)過一段時(shí)間運(yùn)行發(fā)現(xiàn),在啟動(dòng)后負(fù)載控制方面,純編譯方式要好一些,啟動(dòng)之后負(fù)載幾乎不上升,而 分層編譯方式啟動(dòng)后負(fù)載會有所上升,但是不會很高,也會在較短時(shí)間內(nèi)降下來。但是啟動(dòng)時(shí)間方面,分層編譯比原來的默認(rèn)啟動(dòng)方式縮短了大概10秒(原來啟動(dòng)需要110-130秒),而純編譯方式啟動(dòng)時(shí)間比原來多了一倍,達(dá)到了250秒甚至更高。所以看起來分層編譯方式是更好的選擇。

然而JDK 7在codeCache的回收方面做的很不好。即使我們將codeCache設(shè)置為256M,線上還是輕易達(dá)到了設(shè)置的報(bào)警閾值200M。而且一旦codeCache滿了之后又會導(dǎo)致系統(tǒng)運(yùn)行變慢的問題。所以我們的目標(biāo)指向了JDK 8。

測試表明,JDK 8對codeCache的回收有了很明顯的改善。不僅codeCache的增長比較平緩,而且當(dāng)使用量達(dá)到75%時(shí),回收力度明顯加大,codeCache使用量在這個(gè)值上下浮動(dòng),并緩慢增長。最重要的是,JIT編譯還在正常執(zhí)行,系統(tǒng)運(yùn)行速度也沒有收到影響。

3.7 運(yùn)行時(shí)查看Code Cache

如果想在運(yùn)行時(shí)查看code cache的大小,需要寫段代碼,目前只能通過JMX來獲取到Code Cache區(qū)域的使用狀況,代碼類似如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.io.File;
 
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
 
import com.sun.tools.attach.VirtualMachine;
 
public class CodeCacheUsage {
 
    private static final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress";
 
    public static void main(String[] args) throws Exception {
        if(args.length != 1) {
            System.err.println("Must enter one arg: pid");
            System.exit(0);
        }
        VirtualMachine vm = VirtualMachine.attach(args[0]);
        JMXConnector connector = null;
        try {
            String connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
 
            if (connectorAddress == null) {
                String agent = vm.getSystemProperties().getProperty("java.home")
                                        + File.separator
                                        + "lib"
                                        + File.separator + "management-agent.jar";
                vm.loadAgent(agent);
 
                connectorAddress = vm.getAgentProperties().getProperty(CONNECTOR_ADDRESS);
            }
 
            JMXServiceURL url = new JMXServiceURL(connectorAddress);
            connector = JMXConnectorFactory.connect(url);
            MBeanServerConnection mbeanConn = connector.getMBeanServerConnection();
            ObjectName name = new ObjectName("java.lang:type=MemoryPool,name=Code Cache");
            System.out.println(mbeanConn.getAttribute(name, "Usage"));
        } finally {
            if(connector != null)
                connector.close();
            vm.detach();
        }
    }
}

傳入pid,執(zhí)行上面的代碼后,會輸出類似下面的信息:

javax.management.openmbean.CompositeDataSupport(compositeType=javax.management.openmbean.CompositeType(name=java.lang.management.MemoryUsage,items=
(
(itemName=committed,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),
(itemName=init,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),
(itemName=max,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long)),
(itemName=used,itemType=javax.management.openmbean.SimpleType(name=java.lang.Long))
)),
contents={committed=50331648, init=2555904, max=50331648, used=48281152})

上面的信息顯示Code Cache區(qū)域初始化的時(shí)候?yàn)?555904,最大為50331648,已占用了50331648,使用了48281152。

到此這篇關(guān)于Java Code Cache滿導(dǎo)致應(yīng)用性能降低問題解決的文章就介紹到這了,更多相關(guān)Java Code Cache滿導(dǎo)致應(yīng)用性能降低內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://juejin.cn/post/6844903601786060808#heading-8

延伸 · 閱讀

精彩推薦
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經(jīng)有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯(cuò)誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java8中Stream使用的一個(gè)注意事項(xiàng)

    Java8中Stream使用的一個(gè)注意事項(xiàng)

    最近在工作中發(fā)現(xiàn)了對于集合操作轉(zhuǎn)換的神器,java8新特性 stream,但在使用中遇到了一個(gè)非常重要的注意點(diǎn),所以這篇文章主要給大家介紹了關(guān)于Java8中S...

    阿杜7482021-02-04
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關(guān)于小米推送Java代碼,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進(jìn)去或缺失數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml與Java對象的轉(zhuǎn)換詳解

    xml與Java對象的轉(zhuǎn)換詳解

    這篇文章主要介紹了xml與Java對象的轉(zhuǎn)換詳解的相關(guān)資料,需要的朋友可以參考下...

    Java教程網(wǎng)2942020-09-17
  • Java教程20個(gè)非常實(shí)用的Java程序代碼片段

    20個(gè)非常實(shí)用的Java程序代碼片段

    這篇文章主要為大家分享了20個(gè)非常實(shí)用的Java程序片段,對java開發(fā)項(xiàng)目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java實(shí)現(xiàn)搶紅包功能

    Java實(shí)現(xiàn)搶紅包功能

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)搶紅包功能,采用多線程模擬多人同時(shí)搶紅包,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙...

    littleschemer13532021-05-16
主站蜘蛛池模板: 欧美国产精品一区 | 国产精品视频久久 | 欧美一区二区网站 | 免费观看黄视频 | 亚洲精品偷拍自拍 | 成人在线视频免费 | 日本在线一区二区三区 | 亚洲天堂av在线 | 午夜影院在线观看 | 在线播放中文字幕 | 中国大陆高清aⅴ毛片 | 2023国产精品久久久精品双 | 亚洲综合中文 | 日韩av成人在线观看 | 成人在线免费电影 | 狠狠综合久久av一区二区老牛 | 亚洲国产精品久久久久 | 亚洲精品久久久久久久久久久 | 黄小视频| 天天看夜夜爽 | 国产精品视频一区二区三区 | 日韩亚洲一区二区 | 成人福利| 日韩精品免费一区二区夜夜嗨 | 男人久久久 | 国产99久久 | 国产精品亚洲第一区 | 国产欧美在线 | 久久久久亚洲美女啪啪 | 99国产在线| 日韩另类 | 国产美女一区二区三区 | 一级片网址 | 91视频网页版 | 久久久久久久成人 | 亚洲免费影院 | 欧美日韩精品 | 国产一区二区久久 | 久操免费视频 | 伊人激情综合网 | 欧美日韩精品一区二区在线观看 |