1、程序计数器(线程私有)
- 线程执行字节码的行号指示器(注意是字节码,本地方法计数器为空的)
2、Java虚拟机栈(线程私有):
- 描述的是Java方法执行的内存模型
- 每一个方法对应一个栈帧
- 栈帧包含:局部变量表、操作数栈、动态链接、方法出口
- 局部变量表slot会复用,导致一些对象不能被及时的回收,所以建议对象不用之后要赋值为空;
- 局部变量表在编译器就已经完成分配
- 方法的返回地址即方法出口有二:正常完成方法(如有返回值则是return的地方)方法异常时异常块(如果没有finally的情况下)
public void clear() { // 此处若有参数,参数也属于局部变量表
Node[] tab; // tab 是 对象引用
modCount++; // ++ 是在操作数栈中 modCount属于类实例的属性值,在堆中
if ((tab= table) != null && size> 0) {
size= 0;
for (int i= 0; i< tab.length; ++i)
tab[i] = null;
}
// 这里是方法出口
}
本地方法栈(线程私有)
- 抛异常如Java虚拟机栈,不过是服务于本地方法的
Java堆(线程共享)
- 几乎所有的实例都在这里分配内存(因为JIT和逃逸技术发展导致了不是绝对的所有)
方法区(线程共享)
- 存储 已被虚拟机加载的类信息(即每个类的class类)、常量、静态变量、即时编译后的代码(第一次编译后的代码)
运行时常量池(属于方法区的一部分)
存放编译期的字面量和符号引用
字面量:String str=”abc” 中的”abc” int i = 1;中的 1
符号引用:就是类引用(类的全路径)
String类中intern()方法就是运行期间将新的常量放入池中,class文件的动态性及并不是只有在编译期产生常量
直接内存
- NIO中使用native方法直接分配堆外内存
- 使用Java堆中的DirectByteBuffer对象作为这块引用进行操作
OOM
- 可以出现OOM的一般场景
- 创建对象超出了最大堆
- 创建了太多的线程
- 如果想创建很多线程又不能OOM,就需要减小最大堆和减少栈容量
- 虚拟机进行扩展栈失败时
- 框架中可能出现字节码技术导致方法区溢出
- 在如Netty等使用NIO的通信框架中可能导致直接内存溢出