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){ synchronized(this){ } }
- 监控线程发现下面的错误,表示死锁。
- 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
执行结束