背景

在使用log4j2打日志时,当发生大量异常时,造成大量线程block问题的问题。

一个关于log4j2的高并发问题:https://blog.fliaping.com/a-high-concurrency-problem-of-log4j2/

大量线程block原因

发生异常,打印异常栈时,会调用org.apache.logging.log4j.core.impl.ThrowableProxy.toExtendedStackTrace方法。

ThrowableProxy.toExtendedStackTrace内部会进行loadClass操作。

并且可以看到ClassLoader的loadClass在加载类时

1)首先会持有锁。

2)调用findLoadedClass看下是否类已经被加载过了

3)如果类没被加载过,根据双亲委派模型去加载类。

可以看到当某个类被加载过了,调用findLoadedClass会直接返回,锁也会被很快释放掉,无需经过双亲委派等后面的一系列步骤。

但是,在进行反射调用时,JVM会进行优化,会动态生成名为sun.reflect.GeneratedMethodAccessor<N>的类,这个类无法通过ClassLoader.loadClass方法加载(为什么无法通过ClassLoader.loadClass加载?因为JVM内部自定义一个加载器DelegatingClassLoader来加载这个类,这导致应用类加载器 Launcher$AppClassLoader找不到它)。

导致每次解析异常栈进行类加载时,锁占有的时间很长,最终导致阻塞。

关于JVM对反射调用的优化

Java中对反射的优化

使用反射调用某个类的方法,jvm内部有两种方式

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

  2. pure-Java:生成bytecode进行反射操作,即生成类sun.reflect.GeneratedMethodAccessor<N>,它是一个被反射调用方法的包装类,代理不同的方法,类后缀序号会递增。这种方式第一次调用速度较慢,较之第一种会慢3-4倍,但是多次调用后速度会提升20倍

对于使用JNI的方式,因为每次都要调用native方法再返回,速度会比较慢。所以,当一个方法被反射调用的次数超过一定次数(默认15次)时,JVM内部会进行优化,使用第2种方法,来加快运行速度。

JVM有两个参数来控制这种优化

-Dsun.reflect.inflationThreshold=<value>
value默认为15,即反射调用某个方法15次后,会由JNI的方式变为pure-java的方式

-Dsun.reflect.noInflation=true

默认为false。当设置为true时,表示在第一次反射调用时,就转为pure-java的方式

关于如何验证上面所说的反射优化以及两个参数的具体作用,可以参考R大的这篇博客https://rednaxelafx.iteye.com/blog/548536

下面是一个验证反射优化的样例:

public class TestMethodInvoke {
public static void main(String[] args) throws Exception {
Class<?> clz = Class.forName("A");
Object o = clz.newInstance();
Method m = clz.getMethod("foo", String.class);
for (int i = 0; i < 100; i++) {
m.invoke(o, Integer.toString(i));
}
}
}
public class A {
public void foo(String name) {
System.out.println("Hello, " + name);
}
}

配置如下JVM参数,使得在第一次反射调用时,就转为pure-java的方式

打断点跟踪:

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

如何关闭JVM对反射调用的优化?

想关闭JVM对反射优化怎么办?

JVM中只提供了两个参数,因此,没有办法完全关闭反射优化。

一种能想到的接近于关闭反射优化的方法就是将inflationThreshold设为的一个特别大的数。

inflationThreshold是java中的int型值,可以考虑把其设置为Integer.MAX_VALUE ((2^31)-1)。

$ java -Dsun.reflect.inflationThreshold=2147483647 MyApp
 

参考资料

https://rednaxelafx.iteye.com/blog/548536 R大的博客

https://blogs.oracle.com/buck/inflation-system-properties

JVM反调调用优化,导致发生大量异常时log4j2线程阻塞的更多相关文章

  1. 深入浅出JVM的锁优化案例

    锁优化 适应性自旋(Adaptive Spinning) 线程阻塞的时候,让等待的线程不放弃cpu执行时间,而是执行一个自旋(一般是空循环),这叫做自旋锁. 自旋等待本身虽然避免了线程切换的开销,但它 ...

  2. 系统禁用执行FIPS政策导致程序发生“调用的目标发生了异常”

    工具是使用AES-256-CBC加密算法 问题 最近有客户反映, 在使用我们工具时候,会出现“调用的目标发生了异常”错误, 接到反馈之后, 我们进行了很多测试,甚至得到客户系统信息和framework ...

  3. jvm出现OutOfMemoryError时处理方法/jvm原理和优化参考

    The heap stores all of the objects created by your java program.The heap's contents is monitored by ...

  4. 大厂面试经:说一下你们线上JVM是如何优化的?

    JVM(Java虚拟机)简单来说就是运行Java代码的解释器,作为螺丝钉程序员JVM其实了解下就差不多啦,不懂JVM内部细节照样能写出优质的代码!但是一到造火箭.飞机的场景(面试)不懂JVM的你,会被 ...

  5. JVM OOM异常会导致JVM退出吗?

    出处:  https://mp.weixin.qq.com/s/8j8YTcr2qhVActLGzOqe7Q  https://blog.csdn.net/h2604396739/article/de ...

  6. 小师妹学JVM之:JDK14中JVM的性能优化

    目录 简介 String压缩 分层编译(Tiered Compilation) Code Cache分层 新的JIT编译器Graal 前置编译 压缩对象指针 Zero-Based 压缩指针 Escap ...

  7. JVM 输出 GC 日志导致 JVM 卡住,我 TM 人傻了

    本系列是 我TM人傻了 系列第七期[捂脸],往期精彩回顾: 升级到Spring 5.3.x之后,GC次数急剧增加,我TM人傻了:https://zhuanlan.zhihu.com/p/3970425 ...

  8. 【顽固BUG】Visual Studio 2013 + TestDriven.NET-3.8.2860_Personal_Beta 调用的目标发生了异常。

    前言 突然怎么弄也无法断点调试了 输出如下: ------ Test started: Assembly: Server5.V2.dll ------ 调用的目标发生了异常. 而且网站运行提示: -- ...

  9. JavaScript中的尾调用优化

    文章来源自:http://www.zhufengpeixun.com/qianduanjishuziliao/javaScriptzhuanti/2017-08-08/768.html JavaScr ...

随机推荐

  1. closure--- 闭包与并行运算

      闭包有效的减少了函数所需定义的参数数目.这对于并行运算来说有重要的意义.在并行运算的环境下,我们可以让每台电脑负责一个函数,然后将一台电脑的输出和下一台电脑的输入串联起来.最终,我们像流水线一样工 ...

  2. python基础-第八篇-8.1初识Socket

    socket基础 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. so ...

  3. JavaScript json和字符串互转

    JavaScript内置json和字符串互转的函数JSON,不需要引入外部组件 JSON.stringify(obj)将JSON转为字符串. JSON.parse(string)将字符串转为JSON格 ...

  4. python 递归深度优先搜索与广度优先搜索算法模拟实现

    一.递归原理小案例分析 (1)# 概述 递归:即一个函数调用了自身,即实现了递归 凡是循环能做到的事,递归一般都能做到! (2)# 写递归的过程 1.写出临界条件2.找出这一次和上一次关系3.假设当前 ...

  5. oracle入门(8)——实战:支持可变长参数、多种条件、多个参数排序、分页的存储过程查询组件

    [本文介绍] 学了好几天,由于项目需要,忙活了两天,写出了个小组件,不过现在还只能支持单表操作.也没考虑算法上的优化,查询速度要比hibernate只快了一点点,可能是不涉及多表查询的缘故吧,多表的情 ...

  6. (0.2.1)mysql数据库环境-操作系统配置

    目录 1.基于Linux平台的Mysql项目场景介绍 2.mysql数据库运行环境准备-最优配置 2.1.如何查看官方文档了解环境要求 2.2.安装虚拟机环境与操作系统 2.3.操作系统最优配置9大步 ...

  7. RE合同记账会计凭证

    *&---------------------------------------------------------------------* *& Title : 不动产转租合同自 ...

  8. Spark 2.2 DataFrame的一些算子操作

    Spark Session中的DataFrame类似于一张关系型数据表.在关系型数据库中对单表或进行的查询操作,在DataFrame中都可以通过调用其API接口来实现. 可以参考,Scala提供的Da ...

  9. RAW nand clear NAND eMMC

    raw NAND: raw NAND即是一般的NAND Flash内存芯片,所有的ECC除错机制(Error Correcting Code).区块管理(Block Management).磨损均衡技 ...

  10. Java应用程序中System.out.println输出中文乱码

    其实,解决办法比较简单,即:编译时指定编码为UTF-8,如: javac -encoding utf- HelloJava.java 这样,再运行时就不会出现乱码. 比较详细的内容可以参考:http: ...