溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

發(fā)布時間:2020-04-19 00:12:40 來源:網(wǎng)絡(luò) 閱讀:796 作者:wx5d6cccb1cb158 欄目:編程語言

在使用log4j2打日志時,當發(fā)生大量異常時,造成大量線程block問題的問題。

大量線程block原因

發(fā)生異常,打印異常棧時,會調(diào)用org.apache.logging.log4j.core.impl.ThrowableProxy.toExtendedStackTrace方法。

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

ThrowableProxy.toExtendedStackTrace內(nèi)部會進行l(wèi)oadClass操作。

并且可以看到ClassLoader的loadClass在加載類時

1)首先會持有鎖。2)調(diào)用findLoadedClass看下是否類已經(jīng)被加載過了

3)如果類沒被加載過,根據(jù)雙親委派模型去加載類。

可以看到當某個類被加載過了,調(diào)用findLoadedClass會直接返回,鎖也會被很快釋放掉,無需經(jīng)過雙親委派等后面的一系列步驟。

但是,在進行反射調(diào)用時,JVM會進行優(yōu)化,會動態(tài)生成名為sun.reflect.GeneratedMethodAccessor的類,這個類無法通過ClassLoader.loadClass方法加載(為什么無法通過ClassLoader.loadClass加載?因為JVM內(nèi)部自定義一個加載器DelegatingClassLoader來加載這個類,這導(dǎo)致應(yīng)用類加載器 Launcher$AppClassLoader找不到它)。

導(dǎo)致每次解析異常棧進行類加載時,鎖占有的時間很長,最終導(dǎo)致阻塞。

關(guān)于JVM對反射調(diào)用的優(yōu)化

Java中對反射的優(yōu)化

使用反射調(diào)用某個類的方法,jvm內(nèi)部有兩種方式

  1. JNI:使用native方法進行反射操作。

  2. pure-Java:生成bytecode進行反射操作,即生成類sun.reflect.GeneratedMethodAccessor,它是一個被反射調(diào)用方法的包裝類,代理不同的方法,類后綴序號會遞增。這種方式第一次調(diào)用速度較慢,較之第一種會慢3-4倍,但是多次調(diào)用后速度會提升20倍

對于使用JNI的方式,因為每次都要調(diào)用native方法再返回,速度會比較慢。所以,當一個方法被反射調(diào)用的次數(shù)超過一定次數(shù)(默認15次)時,JVM內(nèi)部會進行優(yōu)化,使用第2種方法,來加快運行速度。

JVM有兩個參數(shù)來控制這種優(yōu)化

-Dsun.reflect.inflationThreshold=<value>value默認為15,即反射調(diào)用某個方法15次后,會由JNI的方式變?yōu)閜ure-java的方式

-Dsun.reflect.noInflation=true

默認為false。當設(shè)置為true時,表示在第一次反射調(diào)用時,就轉(zhuǎn)為pure-java的方式

下面是一個驗證反射優(yōu)化的樣例:

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

配置如下JVM參數(shù),使得在第一次反射調(diào)用時,就轉(zhuǎn)為pure-java的方式

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

打斷點跟蹤:

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

可以看到GeneratedMethodAccessor1的classLoader為DelegatingClassLoader,其parent為AppClassLoader。

JVM反調(diào)調(diào)用優(yōu)化,導(dǎo)致發(fā)生大量異常時log4j2線程阻塞

如何關(guān)閉JVM對反射調(diào)用的優(yōu)化?

想關(guān)閉JVM對反射優(yōu)化怎么辦?

JVM中只提供了兩個參數(shù),因此,沒有辦法完全關(guān)閉反射優(yōu)化。

一種能想到的接近于關(guān)閉反射優(yōu)化的方法就是將inflationThreshold設(shè)為的一個特別大的數(shù)。

inflationThreshold是java中的int型值,可以考慮把其設(shè)置為Integer.MAX_VALUE ((2^31)-1)。

$ java-Dsun.reflect.inflationThreshold=2147483647MyApp




向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI