背景

在使用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. 巨蟒python全栈开发-第11阶段 devops-git&&openpyxl2

    大纲 1.git分支 2.git tag 3.git 忽略文件 4.正则表达式 5.openpyxl写数据 6.openpyxl读数据 1.git分支 2.git tag 3.git 忽略文件 4.正 ...

  2. POJ1128 Frame Stacking(拓扑排序)

    题目链接:http://poj.org/problem?id=1128 题意:给你一个平面,里面有些矩形(由字母围成),这些矩形互相有覆盖关系,请从求出最底层的矩形到最上层的矩形的序列,如果存在多种序 ...

  3. Linux文件操作相关命令

    1.创建文件夹: [root@izuf6ih01h8fzeziddwkfdz sm]# mkdir a 创建一个名为a的文件夹 2.创建文件: [root@izuf6ih01h8fzeziddwkfd ...

  4. python爬虫系列(1)——一个简单的爬虫实例

    本文主要实现一个简单的爬虫,目的是从一个百度贴吧页面下载图片. 1. 概述 本文主要实现一个简单的爬虫,目的是从一个百度贴吧页面下载图片.下载图片的步骤如下: 获取网页html文本内容:分析html中 ...

  5. Python用MySQLdb, pymssql 模块通过sshtunnel连接远程数据库

    转载自 https://www.cnblogs.com/luyingfeng/p/6386093.html 安全起见,数据库的访问多半是要做限制的,所以就有一个直接的问题是,往往多数时候,在别的机器上 ...

  6. mysql联合其他表做更新

    在sql server中,我们可是使用以下update语句对表进行更新: update a set a.xx= (select yy from b) where a.id = b.id ; 但是在my ...

  7. go-008-循环语句

    一.循环语句[只有for] 1.基础结构: Go语言的For循环有3中形式,只有其中的一种使用分号. 和 C 语言的 for 一样: for init; condition; post { } 和 C ...

  8. NGS中的一些软件功能介绍

    1.bowtie 短序列比对工具,blast也是短序列比对工具,速度快,结果易理解. 输入可以是fastq或者fasta文件. 生成比对结果文件sam格式的吧. 2.bwa 转自:https://ww ...

  9. 【IO】- 关于ByteBuffer的一点认识

    我们经常使用ByteBuffer. 通俗的Non-DerictedByteBuffer结构如下 HeapByteBuffer extends ByteBuffer { Byte[] array; in ...

  10. 20165324 Java实验三 敏捷开发与XP实验

    20165324 Java实验三 敏捷开发与XP实验 一.实验报告封面 课程:Java程序设计 班级:1653班 姓名:何春江 学号:20165324 指导教师:娄嘉鹏 实验日期:2018年4月16日 ...