性能优化专题(3)----动态字节码技术

JDK可视化工具

  • jconsole
  • jps命令:可以监控现在有多少个Java程序正在运行。
  • jconsole是Java的JDK自带的
  • jconsole的启动方法

(1)命令行的启动,使JDK在Path上,运行jconsole即可
(2)GUI shell启动,找到JDK安装路径,打开bin文件夹,双击jconsole
我们的安装路径在
在这里插入图片描述
双击jconsole运行
在这里插入图片描述
此时我们没有运行其他的java程序,如果运行了再本地进程处会有其他的进程,比如:
在这里插入图片描述
我们运行了Test002类,就会有一个该进程出现。
这里的JConsole进程是监控本机的Java程序
假设我们连接了Test002,该进程是一个死锁,点击进入,发现一个界面
在这里插入图片描述
该软件也是占用内存的。

  • 非堆内存指的是方法区的内存
  • 有时候导入很多的类,也会造成内存泄漏(内存泄漏是指的是new了很多对象,没有释放)
  • jconsole也支持远程连接,查看测试或者生产环境的类的内存使用情况

jconsole定位死锁&visualvm的使用

  • jconsole若想监控java进程必须要该java进程没有结束,若已经结束,无法监控,必须是运行状态
  • jconsole提示的重连是连接不上的,因为程序改变重新运行,其PID也发生了改变,我们可以重新监控其进程
  • 死锁产生的原因是:同步方法中请求同步,导致锁没有释放,两个不同的锁,同时请求彼此,等待彼此释放

比如 : 两个线程,T!和T2, T1先拿到OJ锁,再请求this锁,T2先拿到This锁,再请求OJ锁,此时T1请求的this锁被T2获取,T2请求的OJ锁被T1获取,两个线程互相请求彼此的锁,两个线程都不会运行结束,也不会释放锁,发生死锁现象

// T2线程
 synchronized  T2 (){
		synchronized (OJ){
		}
}
// T1线程
synchronized(OJ){
		synchronizedthis{
		 }
}
  • 监控线程发现下面的错误,表示死锁。
    在这里插入图片描述
  • Visualvm的功能较为强大,具体的使用这里不做详细的接受
  • Visualvm可以明确的显示GC的次数
  • Visualvm我们可以用jdk自带的,也可以用第三方实现的。
  • 运行时只要发生死锁,无论哪个检测工具都可以检测出来。但是无法预期改程序是否有死锁。

字节码技术介绍

  • asm就是属于字节码技术,asm是CGLIB实现动态代理的核心,修改字节码实现子类,重写其方法。

  • 可以使用字节码技术对类完成其基本的操作。比如说对其类做增删改的相关操作
    (1)新增属性,常量信息,方法等
    (2)删除和修改其属性,常量,方法

  • lomok插件非常占用内存

  • 字节码技术的应用场景,

(1)AOP
(2)lombok取出重复代码插件
(3)动态修改class文件等。

使用javassist动态修改字节码

  • 在javassist中,$0代表this参数,$1代表第一个参数,$2代笔第二个参数,依次类推
  • javassist是一个开源的分析,编辑和创建Java字节码的类库,性能和ASM比较差,跟cglib差不多,但是使用非常简单。很多开源框架都在使用它。
  • javassist的优势:
  • 比反射的开销小,性能高
  • javassist性能高于反射,低于ASM

运行的时候操作字节码可以让我们实现如下的功能:
(1)动态生成新的类
(2)动态改变某个类的结构(添加/删除/修改 新的属性/方法)

javassist的最外层的API和JAVA反射包中的API颇为类似。
他主要是有CtClass,CtMethod,以及CtField几个类组成的。用以执行和JDK反射API中Java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method.Field相同的操作。
方法操作有:
(1)修改已经有的方法的方法体
(2)新增方法,删除方法

  • javassist的局限性:

(1)不支持内部类和匿名类
(2)不支持数组的初始化,除非数组只有一个元素
(3)不支持continue和break
(4)继承的传递不支持 如 A,B,C三个类,B继承A,C继承B

  • 使用javassist实现创建和修改字节码
package com.xiyou.mayi.thread5.myjavassist;

import javassist.*;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 使用java的字节码技术(javassist)直接创建字节码
 */
public class Test001 {

    public static void main(String[] args) {
        try {
            createClass();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            updateClass();
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (CannotCompileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     ** 创建字节码类
     **/
    public static void createClass() throws CannotCompileException, NotFoundException, IOException {
        ClassPool pool = ClassPool.getDefault();
        // 创建User类.要写全路径 包名加类名(包括分号)
        CtClass userClass = pool.makeClass("com.xiyou.javassist.User;");
        // 创建name和age属性
        CtField nameField = CtField.make("private String name;", userClass);
        CtField ageField = CtField.make("private Integer age;", userClass);
        // 添加属性
        userClass.addField(nameField);
        userClass.addField(ageField);
        // 创建方法
        CtMethod nameMethod = CtMethod.make("public String getName() {return name;}", userClass);
        // 添加方法
        userClass.addMethod(nameMethod);
        // 添加构造函数 声明形参
        CtConstructor ctConstructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String"), pool.get("java.lang.Integer")}, userClass);
        // 添加构造函数的方法体
        ctConstructor.setBody("{ this.name = name; this.age = age; }");
        // 赋值构造函数
        userClass.addConstructor(ctConstructor);
        // 生成class文件到指定目录
        // 生成的class字节码是在指定目录下以及我们规定的包名下的 D:\test\com\xiyou\javassist
        userClass.writeFile("D:/test");
    }

    /**
     * 修改字节码文件
     */
    public static void updateClass() throws NotFoundException, CannotCompileException, IOException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        ClassPool pool = ClassPool.getDefault();
        // 读取指定包名下的类
        CtClass userClass = pool.get("com.xiyou.mayi.thread5.myjavassist.User");
        // 新增方法
        // 声明了该方法的返回值,方法名,参数类型
        CtMethod method = new CtMethod(CtClass.voidType, "sum", new CtClass[]{CtClass.intType, CtClass.intType}, userClass);
        // 添加方法体
        // $1:表示的是第一个参数 $2: 表示的是第二个参数
        method.setBody("{System.out.println(\"sun:\" + ($1 + $2));}");
        userClass.addMethod(method);
        // 输出写路径
        userClass.writeFile("D:/test");
        // 动态执行该方法,利用反射
        Class clazz = userClass.toClass();
        Object newInstance = clazz.newInstance();
        // 得到该方法
        Method sumMethod = clazz.getDeclaredMethod("sum", int.class, int.class);
        System.out.println("开始执行");
        sumMethod.invoke(newInstance, 1, 2);
        System.out.println("执行结束");
    }

}
  • 修改的时候用到的User类
package com.xiyou.mayi.thread5.myjavassist;

/**
 * 该类就是我们用javassist要生成修改的类的原型
 */
public class User {

	private String name;
	private Integer age;

}
  • 上面的执行结果第一个是生成了指定的class文件
    在这里插入图片描述
    第二个的打印结果是:
开始执行
sun:3
执行结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值