运行时数据区域
Java虚拟机在运行时会将它所管理的内存划分为不同的数据区域,这些运行时数据区域如下图
程序计数器
每个线程都有独立的程序计数器,各线程相互独立,属于线程私有的内存。
在执行Java方法时,计数器记录正在执行的虚拟机字节码指令地址;
执行native方法时,计数器为Undefined。
不规定任何OOM情况
Java虚拟机栈
通常被有人称为栈内存区域
线程私有。生命周期与线程一致
每个方法在执行时会创建一个栈帧(StackFrame),调用到完成对应入栈到出栈。
栈帧(StackFrame)
用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
局部变量表
局部变量表所需内存空间在编译时完成分配,运行方法期间不改变局部变量表大小。
用于存放
- 基本数据类型(boelean,byte,char,short,int,float,long,double)
- 对象引用(reference)
- returnAddress类型
异常
此区域规定了两种异常StackOverflowError异常和OutofMemoryError异常。
StackOverflowError:线程请求栈深度大于虚拟机允许栈深度,如递归调用方法时未设置合理的结束递归的条件
OutofMemoryError:虚拟机栈动态拓展时无法申请足够内存,如大量线程创建
本地方法栈
与Java虚拟机栈类似,为native方法服务,规定了StackOverflowError和OutofMemoryError
Java堆(Heap)
所有线程共享的内存区域,在虚拟机启动时创建。
用于存放对象实例。
Java堆是垃圾收集器(GC?)管理的主要区域。
可以处于物理上的不连续内存空间中。通过-Xmx,-Xms控制扩展大小
异常
OutofMemoryError:堆中没有内存完成实例分配,并且堆无法再拓展。如Bitmap创建时太大会触发此类OOM
方法区(Method Area)
线程共享内存区域
用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器(JIT)编译后的代码等数据。
不需要连续内存,可以选择不实现垃圾收集GC。
异常
OutofMemoryError:当方法区无法满足内存分配需求时,抛出异常。
运行时常量池
属于方法区的一部分,存放编译期生产的字面量和符号引用
异常
OutofMemoryError:当常量池无法申请到内存时抛出异常。
直接内存
并不属于虚拟机运行时数据区
JDK1.4 NIO类基于通道与缓冲区的IO方式,使用native分配堆外内存,避免在Java堆和Native堆中来回分配数据
异常
OutofMemoryError:动态拓展时,超过物理内存限制。
HotSpot中的对象
new 关键字创建对象
- 收件进行类加载检查
- 类加载完成后对象所需内存大小为确定大小,在Java堆中划出
对象的内存布局分为三个区域:对象头,实例数据,对齐填充
对象头
对象头分为两部分:
第一部分用于存储对象自身的运行时数据,如hashcode,GC分代年龄,锁状态标志,线程持有锁,偏向线程PID,偏向时间戳,长度为32bit或64bit;
第二部分是类型指针,对象指向它的类元数据的指针
实例数据
存储顺序收到虚拟机分配策略参数和Java源码中的定义顺序影响。
默认策略为:longs/doubles,ints,shorts/chars,bytes/booleans,oops。同时父类的变量会在子类前
与Android Dalvik虚拟机比较
Dalvik并不是一个java虚拟机,它执行dex(Dalvik Executable) 文件而不是class文件,使用寄存器架构而不是栈架构。
JVM字节码中,局部变量会被放入局部变量表中,继而被压入堆栈供操作码进行运算,当然JVM也可以只使用堆栈而不显式地将局部变量存入变量表中。对应Java虚拟机栈(Stack)区域
Dalvik字节码中,局部变量会被赋给65536个可用的寄存器中的任何一个,Dalvik指令直接操作这些寄存器,而不是访问堆栈中的元素。(所以会遇到65536的BUG?)
dex文件合并了class文件的常量池,常量池也被修改为只使用32位的索引
另外,关于基于栈与基于寄存器的区别,下面有一个例子:
Java代码
public class Hello {
public int foo(int a, int b) {
return (a + b) * (a - b);
}
public static void main(String[] args) {
Hello t = new Hello();
System.out.print(t.foo(5, 3));
}
}
class字节码
0: iload_1
1: iload_2
2: iadd
3: iload_1
4: iload_2
5: isub
6: imul
7: ireturn
dex字节码
0000: add-int v0, v3, v4
0002: sub-int v1, v3, v4
0004: mul-int/2addr v0, v1
0005: return v0
执行过程
参考文章链接
[1]https://blog.csdn.net/x356982611/article/details/21983267
[2]https://blog.csdn.net/huacai2010/article/details/24437619
[3]https://www.cnblogs.com/lxjshuju/p/7191910.html
[4]https://source.android.google.cn/