JVM虚拟机种的内存结构分了五个部分:
程序计数器
虚拟机栈
本地方法栈
堆
方法区
程序计数器
记住下条二进制字节码的指令(.class文件中的指令)地址
注意:取出下一条jvm指令 需要解释器解释成机器码,才能被cpu执行
线程私有(每个线程都有自己的程序计数器)
不会存在内存溢出
二、虚拟机栈
线程运行所需要的内存空间
每个线程一个栈(线程私有)
栈中由多个栈帧组成
一个方法一个栈帧
对应着每个方法的调用(每个方法需要的内存)存放着方法执行调用的数据
栈帧包括:局部变量表,操作数栈,返回地址,动态链接
注意:每个线程有一个活动栈帧(当前栈帧)
执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。
之前写过一篇深入了解栈帧的博客
问题:
1.垃圾回收是否涉及栈内存
不会
2.栈内存分配越大越好吗
不是,线程数会变少
3.方法内的局部变量是否线程安全
如果方法内局部变量没有逃离方法的作用范围,就是线程安全的(方法内定义的局部变量线程私有的,每个栈帧中都自己分配一块内存来存储局部变量)
如果局部变量引用了对象,并且逃离了方法的作用范围(return 了局部变量),线程不安全
static修饰的时候,就会有线程安全问题(多个线程共享这个数据)
栈内存溢出
java.lang.StackOverflowError
1.栈帧过多导致栈内存溢出
方法的递归调用,没有正确的退出条件的时候
2.栈帧过大导致栈内存溢出
比如:一致创建方法mothod
public class Test01 {
private static int c;
public static void main(String[] args) {
try {
mothod();
} catch (Throwable e) {
e.printStackTrace();
System.out.println(c);
}
}
private static void mothod(){
c++;
mothod();
}
}
为了可以更好的演示效果可以设置栈的大小
设置栈内存大小
线程运行诊断
案例1:cpu占用过多
top命令:定位哪个进程对cpu的占用过高
ps H -eo pid,tid,%cpu | grep 进程id(用ps命令定位哪个线程占用过高)
jstack 进程id :
根据线程id找到有问题的线程,进一步定位源码行数
案例2: 程序运行很长时间没出结果
比如死锁
三.本地方法栈
java虚拟机调用本地方法的时候,给本地方法提供的内存空间
本地方方法:不是java代码编写的方法(因为java方法可能无法直接调用底层)
JVM只是简单地动态链接并直接调用native方法;
例子:
Object类有很多方法
1.Public native Object clone();
2.public native int hashCode();
四、堆
线程共享的
定义:
通过new关键字,创建的对象都在堆内存
特点:
堆中的对象 ,他是线程共享的,堆中对象都需要考虑线程安全的问题
有垃圾回收机制(没人再使用的对象)
堆内存溢出
OutOfMemoryError:java heap space
如果我们不断的产生对象,并且不断地使用他们,就可能产生堆内存溢出
改堆内存空间(可以减小空间大小,尽早暴露堆内存空间溢出的问题)
-Xmx
堆内存诊断工具
1.jps工具
查看当前系统中有哪些java进程
2.jmap工具
根据得到的进程id -heap id
查看某一时刻堆内存占用的情况
3.jconsole工具
图形界面的,多功能检测工具,可以连续监测
案例
垃圾回收后,内存占用依然很高
执行代码后
jps 找到id
jmap -heap -id
发现新生代和老年代的总占用内存
jconsole工具中点击gc(垃圾回收)
jmap -heap -id
发现新生代和老年代中内存下降很少。
五、方法区
比较多,单独写一篇