1、Java运行是数据区
2、说明:
2.1 程序计数器
- 是一块较小内存空间
- 可看做当前线程执行的字节码的行号指示器,多线程执行时线程轮流切换时恢复到正确执行位置
- 线程私有
- 线程执行Java方法,记录虚拟机字节码指令地址,执行Native方法,计数器为空
- 唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError区域
2.2 虚拟机栈
- 线程私有,生命周期与线程相同
- 虚拟机栈描述Java方法执行的内存模型,每个方法执行都会创建一个栈帧,方法的调用和执行完成与该方法栈帧的入栈和出栈对应
- 栈帧是方法运行时的基础数据结构,用于存储局部变量表、操作数栈、动态链接、方法出口等信息
- 此区域包含两种异常:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError;如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError
2.3 本地方法栈
- 与虚拟机栈所发挥作用相似,区别在于一个为虚拟机执行Java方法服务,一个为虚拟机使用到的Native方法服务
- 虚拟机规范中未规定具体实现,虚拟机可以自由实现(最流行的Sun HotSpot虚拟机将本地方法栈和虚拟机栈合二为一)
- 与虚拟机栈一样,抛出StackOverflowError和OutOfMemoryError异常
2.4 堆
- Java虚拟机管理的内存中最大一块
- 所有线程共享
- 虚拟机启动时创建。唯一目的就是存放对象的实例,几乎所有的对象实例都在这里分配内存
- Java堆时垃圾收集器的主要区域
- java堆可以处于物理不连续的内存空间中,逻辑上连续即可。实现时,既可以固定大小,也可以时可扩展的。不过当前主流虚拟机都是可扩展的(-Xmx -Xms)
- 如果在堆中没有完成实例分配,并且堆无法再扩展,将会抛出OutOfMemoryError
2.5 方法区
- 所有线程共享
- 用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- Java虚拟机规范将其描述为堆的一个逻辑部分,但是它又一个别名叫做Non-Heap
- 习惯HotSpot开发部署人员更愿意把方法区称为永久代(Permanent Generation),本质上并不等价,只是HotSpot团队选择把GC分代收集扩展至方法区,以使垃圾收集像管理堆一样管理这部分内存,省去编写方法区内存管理工作,其他虚拟机(J9)不存在永久代概念,实现方法区不受虚拟机规范约束
- 使用永久代因为通过-XX:MaxPermSize(jdk8已经取消)限制的上限,更容易内存溢出。极少数方法(如String.intern())会因这个导致在不同虚拟机下有不同表现。JDK1.7的HotSpot中,已经把原本放在永久代的字符串常量池移出
- 此区域内存回收主要针对常量池的回收和对类型的卸载。但是回收成绩总是很差,但是确实又是有必要的,否则会有内存泄漏隐患
- 方法区无法满足内存分布需求时,抛出OutOfMemoryError
2.5.1 运行时常量池
- 方法区的一部分
- Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后进入方法区的运行时常量池中存放
- Java虚拟机对于Class文件每一部分的格式都有严格规定,但是运行时常量池没有特殊要求。不过一般来说,除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存在其中
- 运行时常量池时方法区的一部分,受方法区内存的限制,无法申请到内存是抛出OutOfMemoryError
2.6 直接内存
- 不属于虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁的使用,也可能导致OutOfMemoryError异常
- JDK1.4中加入了NIO,通过Native函数分配堆外内存,然后通过一个存储在Java堆中的DirectbyteBuffer对象作为对这块内存的引用。
- 这块内存收到本机总内存的限制,设置虚拟机内存时,直接内存也不可以忽略
参考
深入理解Java虚拟机 周志鹏著