jvm学习
1.程序计数器
java源代码经过编译成为二进制字节码,就是jvm的指令,指令经过解释器解释成机器码,机器码可以被cpu处理,程序计数器可以记录下一条jvm指令的执行地址。
程序计数器会将cpu的寄存器作为程序计数器使用。线程私有的。每个线程有自己的程序计数器,不会存在内存溢出问题
2.虚拟机栈
一个线程就是一个虚拟机栈,先进后出。栈里有栈帧,一个方法的调用就会创建一个栈帧,栈帧实际上就是一个方法运行需要的内存
每个线程运行需要的内存空间,称为虚拟机栈
每个栈由多个栈帧(Frame)组成,对应着每次调用方法时所占用的内存
每个线程只能有一个活动栈帧,对应着当前正在执行的方法
问题辨析:
1.垃圾回收是否涉及栈内存?
不会。栈内存是方法调用产生的,方法调用结束后会弹出栈。
2.栈内存分配越大越好吗?
不是。因为物理内存是一定的,栈内存越大,可以支持更多的递归调用,但是可执行的线程数就会越少。
3.方法呢的局部变量是否线程安全
如果方法内部的变量没有逃离方法的作用访问,它是线程安全的,比如将变量改为static修饰的话。
如果是局部变量引用了对象,并逃离了方法的访问,那就要考虑线程安全问题。
2.1栈内存溢出
栈帧过多,一直入栈没有出栈的话会导致栈内存溢出,例如方法的递归调用,method()方法本身调用自身方法
public class test {
public static int count;
public static void main(String[] args) {
try {
method();
}catch (Throwable e){
e.printStackTrace();
System.out.println(count);
}
}
private static void method() {
count++;
method();
}
}
2.2线程诊断
案例一:cpu 占用过多
解决方法:Linux 环境下运行某些程序的时候,可能导致 CPU 的占用过高,这时需要定位占用 CPU 过高的线程
top 命令,查看是哪个进程占用 CPU 过高
ps H -eo pid, tid(线程id), %cpu | grep 刚才通过 top 查到的进程号 通过 ps 命令进一步查看是哪个线程占用 CPU 过高
jstack 进程 id 通过查看进程中的线程的 nid ,刚才通过 ps 命令看到的 tid 来对比定位,注意 jstack 查找出的线程 id 是 16 进制的,需要转换。
线程死锁代码
3、本地方法栈
一些带有 native 关键字的方法就是需要 JAVA 去调用本地的C或者C++方法,因为 JAVA 有时候没法直接和操作系统底层交互,所以需要用到本地方法栈,服务于带 native 关键字的方法。
4、堆
1)定义
Heap 堆
通过new关键字创建的对象都会被放在堆内存
特点
它是线程共享,堆内存中的对象都需要考虑线程安全问题
有垃圾回收机制
/**
* 演示堆内存溢出 java.lang.OutOfMemoryError: Java heap space
* -Xmx8m
*/
public class T01_HeapOutOfMemoryError {
public static void main(String[] args) {
int i = 0;
try {
List<String> list = new ArrayList<>();
String a = "hello";
while (true) {
list.add(a); // hello, hellohello, hellohellohellohello ...
a = a + a; // hellohellohellohello
i++;
TimeUnit.MILLISECONDS.sleep(2000);
}
} catch (Throwable e) {
e.printStackTrace();
System.out.println(i);
}
}
}