五大区
1. 程序计数器:
线程私有,字节码解释器通过改变计数器值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等。
2. Java虚拟机栈:
线程私有,生命周期与线程相同。每个方法的执行都会创建一个栈帧用于存储局部变量表、操作数、动态链接、方法出口等信息。
局部标量表:基本数据类型(boolean,byte...)、对象引用(指向对象的引用地址)、returnAddress类型。
如果线程请求的栈深度大于虚拟机允许的深度,抛出StackOverflowErro。
3. 本地方法栈:
类似于Java虚拟机栈,区别在于,Java虚拟机栈为Java虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用Native方法服务。
常用的 Sun HotSpot虚拟机,将两者合二为一了。
4. Java堆:
线程公有,内存中最大的一块,在虚拟机启动时创建,用来存放对象实例,几乎所有的对象实例都在这里分配。
可分为新生代和老年代:Eden空间、From Survivor空间、To Survivor空间。
为了更快更好的回收和分配内存,从Java堆可划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB)。
-Xmx
-Xms
堆中没有内存可完成实例分配,并无法扩展时,抛出OutOfMemoryError。
5. 方法区:
线程共享,用于存储已被虚拟机加载的类信息、常亮、静态变量、即时编译后的代码等数据。
是堆中一个逻辑部分,不经常垃圾回收,也别成为永久代。
(1)运行时常量池
方法区一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有用于存放编译期生成各种字面量和符号引用的常量池。
(2)直接内存
不是虚拟机运行时数据区的一部分。
NIO引入一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它用Native函数库直接分配对外内存。
-XX:MaxPermSize 永久代上限
-XX:PermSize
对象的创建:
内存分配方法:
1. 指针碰撞法
用于规整内存的分配。指针移动对象大小的空间来分配。
Serial
ParNew
2. 空闲列表法
记录那块内存可用。
CMS
采用哪种分配方法,由Java内存是否规整决定,是否规整又与垃圾收集器是否带有压缩整理功能决定。
分配空间的线程安全问题,如何避免内存不被重复分配,两种方案:
1. 对分配空间的动作进行同步处理,实际上虚拟机采用CAS配上失败重试方式保证更新操作的原子性。
2. 把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为TLAB。
-XX:/-UseTLAB 是否使用TLAB