划分

PC寄存器:记录线程执行的指令。
JVM栈:线程的独立内存,用于存储局部变量和堆对象的句柄。
本地方法栈:线程执行native方法时的独立内存
堆:对象存储的地方,GC主要的发生的地方
方法区:存储类的元数据,以及运行时常量。

线程安全

  1. 线程安全:PC寄存器、JVM栈、本地方法栈
  2. 线程共享:堆、方法区

运行时常量存在哪里

运行时常量存储在运行时常量池中,那么运行时常量池在哪里划分,取决于方法区怎么实现,在jvm实现规范中,对方法区只是一个逻辑划分而非物理划分,所以jvm厂商可以针对方法作不同的实现,不同版本之间也可能不同。比如hotspot在jdk8之前,将堆划分为年轻代、年老代、永久代,永久代作为方法区来使用,也就是说方法区在堆中分配,也就是说常量池在堆中分配。在jdk8之后,hotspot移除了永久代的概念,将方法区独立出来作为metaspace。所以常量池又在方法区中分配了。所以总的来说,常量池是在方法区中分配。

对象如何分配内存

对象生成方式有new、反射等,
对象生成过程:类型检查、内存分配、对象初始化、句柄赋值。
其中内存分配存在竞态条件:多个线程、共享堆内存。那么如何保证线程安全。
一种是加锁,同一时刻只能一个线程可以分配内存。但这种方式性能较差。
第二中是为每一个线程提前分配一个私域内存,这样各个线程之间的内存分配就不存在竞争。这就是TLAB分配方式。Thread Local Allocation Buffer。可能在分配内存私域的时候存在并发也就是生成线程的时候,但这种场景较少,所以可以加锁控制。

为什么基本类型分配在栈上而引用类型只存储符号引用?

因为基本类型能在编译期确定大小,而引用类型不能确定大小。如果为栈设置太大的内存,
因为栈的是和线程绑定的,如果线程太多会导致oom。