JVM内存模型
参考链接
运行时数据区:
线程共享:堆、方法区
线程私有:程序计数器、虚拟机栈、本地方法栈
程序计数器:
线程是CPU调度的基本单位。
时间片轮询抢夺CPU资源。
每个线程都有一个独立的程序计数器去记录正在执行的字节码指令地址。
唯一一块不会出现OutOfMemoryError的区域。
虚拟机栈:
一个线程有自己的一个栈,栈的大小决定了方法调用的可达程度。
一个虚拟机栈由多个栈帧构成。
每个方法在执行的时候都会创建一个栈帧。
栈帧用于存储局部变量、操作数栈、动态连接方法、方法出口等。
局部变量主要存储基本数据类型的变量(byte、short、int、long等八个)和句柄,可以是方法参数也可以是局部变量。
可达度(递归多少层、嵌套多少层。-Xss可以调节虚拟机栈的大小)。
如果线程请求的栈深度大于虚拟机栈允许的深度,则抛出StackOverFlowError异常。
虚拟机栈的大小是可固定也可动态扩展的,如果扩展时申请不到足够的内存空间,则抛出OutOfMemoryError异常。
本地方法栈:
为虚拟机执行native方法服务,其余的与虚拟机栈类似。
java堆:
用于存放对象实例,给实例分配内存。
垃圾收集器管理的主要区域(也称GC堆)
堆空间分为老年代、新生代(eden、s0、s1)。eden刚创建的,s0、s1至少经过一次GC并幸存。
java堆大小可固定可扩展,-Xmx最大堆容积、-Xms最小堆容积。
如果实例没有内存可以分配且堆无法扩展时,抛出OutOfMemery异常。
TLAB(线程私有分配缓冲区):
为了提升对象内存分配的效率和分配时的线程安全问题。
每个线程在java堆中预先分配一小块TLAB内存,哪个线程需要分配内存,就在自己的TLAB上分配,如果TLAB分配完并要重新分配时加同步锁定。
方法区:
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池是方法区的一部分,用于存放编译器生成的各种字面量(常量)和引用符号(类和接口的全限定名、字段和方法的名称和描述符)。
java堆和方法区都是线程共享且不需要连续的内存。
对象的创建过程:
类加载 --> 内存分配 --> 内存空间初始化为零值 --> init对象初始化。