Unsafe.defineClass挂起-反射速度慢实战案例

简介: Unsafe.defineClass挂起-反射速度慢实战案例

Unsafe.defineClass挂起-反射速度慢



在Java 8 / SpringBoot 1.5.13 / Tomcat 8.5 Web应用程序中观察到运行时间极长的请求(> 30s)。而且卡顿时间会随程序运行时间变久。但是大部分请求的响应还是正常的。卡顿发生时的cpu负载增长到100%,过几分钟后会自己降下来。

Threaddumps显示Unsafe.defineClass正在挂起:


"http-nio-8080-exec-9" #254 daemon prio=5 os_prio=0 tid=0x00007f8a98b4f000 nid=0x2258 runnable [0x00007f89fb0b0000]
   java.lang.Thread.State: RUNNABLE
    at sun.misc.Unsafe.defineClass(Native Method)
    at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:63)
    at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399)
    at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:394)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:393)
    at sun.reflect.MethodAccessorGenerator.generateMethod(MethodAccessorGenerator.java:75)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:53)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.BeanUtils.copyProperties(BeanUtils.java:618)
    at org.springframework.beans.BeanUtils.copyProperties(BeanUtils.java:538)
    at com.haigui.stateaction.action_impl.DisputeSubmit.insertDisputeInfo(DisputeSubmit.java:128)


卡顿发生时的JVM情况: 31.jpg 回复


[你假笨]:从你的这些输出来看,主要是挂载了一个agent来对类进行操作,建议正常环境去掉这个agent,理论上加载的类都加载的差不多了,这个问题就不会有了。


关于不同虚拟机版本下的-XX:+TraceClassUnloading


在JDK8中尝试重现类卸载的例子, 加入该参数并无法看到控制台有对应的unload日志输出(macos环境jdk1.8.0_161, linux环境jdk1.8.0_91均有尝试);由于TraceClassUnloading在JDK9后不建议使用了, 被替换为了-Xlog:class+unload=info。 同样的代码在JDK9(macos环境make编译), JDK11下, 在新参数下, 均能看到对应的unload日志。为何?


测试代码如下:


public class Loop {
    public static void main(String[] args) throws Exception{
        ClassLoaderB loaderB = new ClassLoaderB("CLB");
        loaderB.setPath("/Users/mozilla/core/universe/java-base/target/classes/");
        Class<?> clazz = loaderB.loadClass("com.github.universe.java.base.Pow");
        Object object = clazz.newInstance();
        System.out.println(object);
        System.out.println("-----------------");
        loaderB = null;
        clazz = null;
        object = null;
        System.gc();
        // System.gc();
        while (true){
            Thread.sleep(100000);
        }
    }
    private static class ClassLoaderA extends ClassLoader{}
    private static class ClassLoaderB extends ClassLoader {
        private String classLoaderName;
        //类的扩展名
        private final String fileExtension = ".class";
        private String path;
        public void setPath(String path) {
            this.path = path;
        }
        public ClassLoaderB(String classLoaderName) {
            super();
            this.classLoaderName = classLoaderName;
        }
        public ClassLoaderB(String classLoaderName, ClassLoader parent) {
            super(parent);
            this.classLoaderName = classLoaderName;
        }
        @Override
        protected Class<?> findClass(String className) throws ClassNotFoundException {
            System.out.println("findClass invoked: " + className);
            System.out.println("class loader name: " + this.classLoaderName);
            byte[] data = this.loadClassDate(className);
            return this.defineClass(className, data, 0, data.length);
        }
        private byte[] loadClassDate(String name) {
            InputStream is = null;
            byte[] data = null;
            ByteArrayOutputStream baos = null;
            try {
                name = name.replace(".", "/");
                is = new FileInputStream(new File(this.path + name + this.fileExtension));
                baos = new ByteArrayOutputStream();
                int ch = 0;
                while ((ch = is.read()) != -1) {
                    baos.write(ch);
                }
                data = baos.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    is.close();
                    baos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return data;
        }
    }
}

回复


[大空翼]:Loop.class这个类的路径也在/Users/mozilla/core/universe/java-base/target/classes/?如果也是在那个位置的话,可能是appclassloader加载的,那就卸载不了的,可以dump出来看看com.github.universe.java.base.Pow这个类的类加载器是哪个。


[Mozilla]:如果相同等于CL是活的, 确实不能unload。 但是如上所示, 代码了指明让ClassLoaderB去load。而且load成功后我也会打出log在console。而且同一份代码JDK8可以JDK9,11可以就很头疼了。


[大空翼]:ClassLoader的委托类加载机制,并不一定是你指定的这个类加载器加载的,可能委托给父类加载器加载。


相关文章
|
21天前
|
监控 安全 Java
Java多线程调试技巧:如何定位和解决线程安全问题
【4月更文挑战第6天】本文探讨了Java并发编程中的线程安全问题,包括数据不一致、死锁和性能下降。为解决这些问题,文章介绍了理解线程安全的重要性,如互斥、同步和避免死锁,并提供了识别问题的迹象和调试工具,如JConsole、VisualVM、堆栈跟踪和Thread Dump分析。此外,还建议使用原子类、线程安全数据结构和静态代码分析工具来加强同步和减少锁粒度。最后,强调了避免共享状态和合理设计的重要性,以确保多线程程序的正确性和效率。
|
7月前
|
Java
多线程学习(三)多线程开发带来的问题与解决方法
多线程学习(三)多线程开发带来的问题与解决方法
74 1
|
7月前
|
Java
多线程开发带来的问题与解决方法
多线程开发带来的问题与解决方法
53 0
如何处理JDK线程池内线程执行异常?讲得这么通俗,别还搞不懂
本篇 《如何处理 JDK 线程池内线程执行异常》 这篇文章适合哪些小伙伴阅读呢? 适合工作中使用线程池却不知异常的处理流程,以及不知如何正确处理抛出的异常
|
Java 调度
【Java原理探索】深入分析Mutex锁的运行原理
【Java原理探索】深入分析Mutex锁的运行原理
179 0
|
Java 编译器 调度
基本的线程机制—Java编程思想
并发编程使我们可以将程序分为多个分离的、独立运行的任务。通过使用多线程机制,这些独立人物(也被称为子任务)中的每一个都将由执行线程来驱动。 一个线程就是在进程中的一个单一的顺序控制流,因此,单个进程可以拥有多个并发执行的任务。 在使用线程时,CPU将轮流给每个任务分配其占用时间。 线程的一大好处是可以使你从这个层次抽身出来,即diamante不必知道它是运行在具有一个还是多个CPU的机器上。
java线程的三种创建方式详细分析(全)
目录前言1. 继承Thread类2. 实现Runnable 接口3. 通过Callable和Future创建线程4. 总结 前言 关于线程这部分内容讲的比较多,可看我之前的文章 【操作系统】线程与进程的深入剖析(全) 【操作系统】守护线程和守护进程的区别 对于线程Thread的分析 也可看我之前的文章 java之Thread类详细分析(全) java之Thread类实战模板(全) 多线程中run()和start()的异同详细分析(全) 对于创建方式的汇总以及方式 可看我这篇文章的总结对比 1. 继承
java线程的三种创建方式详细分析(全)
|
存储
多线程原理和实现方式
多线程原理和实现方式
155 1
|
Java
编写Java程序,实现多线程操作同一个实例变量的操作会引发多线程并发的安全问题。
编写Java程序,实现多线程操作同一个实例变量的操作会引发多线程并发的安全问题。
271 0
编写Java程序,实现多线程操作同一个实例变量的操作会引发多线程并发的安全问题。
线程的3种实现方式并深入源码简单分析实现原理(1)
线程的3种实现方式并深入源码简单分析实现原理(1)
132 0
线程的3种实现方式并深入源码简单分析实现原理(1)