背景

在使用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. Java中的常用方法

    Java中的常用方法 第一章 字符串 1.获取字符串的长度:length() 2.判断字符串的前缀或后缀与已知字符串是否相同    前缀 startsWith(String s).后缀 endsWit ...

  2. Oracle涂抹oracle学习笔记第10章Data Guard说,我就是备份

    DG 是备份恢复工具,但是更加严格的意义它是灾难恢复 Data Guard是一个集合,由一个Primary数据库及一个或者多个Standby数据库组成,分两类逻辑Standby和物理Standby 1 ...

  3. android sdk 中跨平台部分

    跨平台部分: platforms samples sources system-images 这几个可以直接拷贝即可

  4. AJAX Form Submit Framework 原生js post json

    https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest <!doctype ht ...

  5. 荣誉墙项目day26 django 项目路由配置

    项目路由配置文件包括:配置目录里的urls.py文件和各个app目录里的urls.py文件 1.include()——从项目主路由分配到各APP主路由 from django.conf.urls im ...

  6. requests+BeautifulSoup详解

    简介 Python标准库中提供了:urllib.urllib2.httplib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作,甚至包括各种 ...

  7. linux定时任务常用命令大全

    脚本中时间戳 TIMESTAMP=`date +%Y%m%d%H%M%S`

  8. centos7配置IP地址

    有关于centos7获取IP地址的方法主要有两种,1:动态获取ip:2:设置静态IP地址 在配置网络之前我们先要知道centos的网卡名称是什么,centos7不再使用ifconfig命令,可通过命令 ...

  9. 浅析Spring AOP

    在正常的业务流程中,往往存在着一些业务逻辑,例如安全审计.日志管理,它们存在于每一个业务中,然而却和实际的业务逻辑没有太强的关联关系. 图1 这些逻辑我们称为横切逻辑.如果把横切的逻辑代码写在业务代码 ...

  10. 二进制x&(x-1);

    求下面函数的返回值(微软) int func(x) {     int countx = 0;     while(x)     {           countx ++;           x ...