1,下载插件: 进入:visualvm.github.io/pluginscent…
然后再下载:Visual GC插件
2,打卡JDK自带的jvisualvm
[JDK自带的一个用于Java程序性能分析的工具]
cd /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/bin
执行: ./jvisualvm &
然后选择 工具>插件>已下载 进行插件安装:
3,测试
- 3.1 堆(heap)的测试
@RestController
public class HeapController {
List<Person> list=new ArrayList<Person>();
@GetMapping("/heap")
public String heap() throws Exception{
while(true){
list.add(new Person());
Thread.sleep(1);
}
}
}
测试的时候注意设置下初始和最大内存,这样观察对象的的回收更快速:
-Xms20M:设置堆内存初始值为20M
-Xmx20M:设置堆内存最大值为20M
这里的ms是memory start的简称,mx是memory max的简称,分别代表最小堆容量和最大堆容量。
但是别看这里是-X参数,其实这是-XX参数,等价于:-XX:InitialHeapSize,-XX:MaxHeapSize
运行之后,访问:http://localhost:9090/heap
下面把代码改成--------
@GetMapping("/heap")
public String heap() throws Exception{
List<Person> list=new ArrayList<Person>();
int i = 100000000;
while(i > 0){ //把while(true)改成while(i > 0)就是为了可以把方法执行完。
list.add(new Person());
Thread.sleep(1);
i--;
System.out.println(i);
}
return "好吧那";
}
经过测试发现,方法在运行的过程中,堆内存依然快速增加,当方法执行完毕之后,堆内存增加速率明显下降。说明 跟线程绑定的 虚拟机栈 其实也是指向堆内存(即局部变量创建的对象也是在堆内存中),只不过方法结束后会及时释放回收。
- 3.2 非堆内存测试
/**
* 负责造类的元信息
*/
public class MetaspaceUtil extends ClassLoader {
public static List<Class<?>> createClasses() {
List<Class<?>> classes = new ArrayList<Class<?>>();
for (int i = 0; i < 10000000; ++i) {
ClassWriter cw = new ClassWriter(0);
cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
"java/lang/Object", null);
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
"()V", null, null);
mw.visitVarInsn(Opcodes.ALOAD, 0);
mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
"<init>", "()V");
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
MetaspaceUtil test = new MetaspaceUtil();
byte[] code = cw.toByteArray();
Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
classes.add(exampleClass);
}
return classes;
}
}
@RestController
public class NonHeapController {
List<Class<?>> list=new ArrayList<Class<?>>();
@GetMapping("/nonheap")
public String heap() throws Exception{
while(true){
list.addAll(MetaspaceUtil.createClasses());
Thread.sleep(5);
}
}
}
--
-XX:MetaspaceSize=20M --XX:MaxMetaspaceSize=20M
设置VM:
启动后报错:
这个时候说明 方法区也会抛出 OutOfMemoryError异常。另外是因为jdk一些系统类的加载也会放在 方法区中,所以20M太小了,此时调整成50M再启动,成功。 访问:http://localhost:9090/nonheap
- 3.3 栈溢出
然后看到压栈了6845次后报错:
现在调整一下线程的内存大小为256k:
【-Xss 为jvm启动的每个线程分配的内存大小】
然后可以看到,栈的深度为1412就发生了StackOverflowError
下面是监控远端java进程,自己测试了一下没成功,是因为防火墙可能,不过后面可以继续尝试,方法如下:
(1)在visualvm中选中“远程”,右击“添加”
(2)主机名上写服务器的ip地址,比如31.100.39.63,然后点击“确定”
(3)右击该主机“31.100.39.63”,添加“JMX”[也就是通过JMX技术具体监控远端服务器哪个Java进程]
(4)要想让服务器上的tomcat被连接,需要改一下 bin/catalina.sh 这个文件
【注意】下面的8998不要和服务器上其他端口冲突
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -
Djava.rmi.server.hostname=31.100.39.63 -Dcom.sun.management.jmxremote.port=8998
-Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=true -
Dcom.sun.management.jmxremote.access.file=../conf/jmxremote.access -
Dcom.sun.management.jmxremote.password.file=../conf/jmxremote.password"
(5)在 ../conf 文件中添加两个文件jmxremote.access和jmxremote.password
jmxremote.access 文件
guest readonly
manager readwrite
jmxremote.password 文件
guest guest
manager manager
授予权限:chmod 600 *jmxremot*
(6)将连接服务器地址改为公网ip地址 (--这个地方自己测试的时候失败,以后有机会重新测试一下)
hostname -i 查看输出情况
172.26.225.240 172.17.0.1
vim /etc/hosts
172.26.255.240 31.100.39.63
(7)设置上述端口对应的阿里云安全策略和防火墙策略
(8)启动tomcat,来到bin目录
./startup.sh
(9)查看tomcat启动日志以及端口监听
tail -f ../logs/catalina.out
lsof -i tcp:8080
(10)查看8998监听情况,可以发现多开了几个端口
lsof -i:8998 得到PID
netstat -antup | grep PID
(11)在刚才的JMX中输入8998端口,并且输入用户名和密码则登录成功
端口:8998
用户名:manager
密码:manager