java内存管理初见
java虚拟机所管理的内存包括以下几个运行时数据区域:
1.程序计数器(线程私有)
指向当前执行的字节码。字节码解释器通过改变程序计数器来选取下一条字节码指令。分支、循环、跳转、异常处理、线程恢复等功能需要依赖程序计数器。
唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2.java虚拟机栈(线程私有)
描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表:存放了编译期可知的各种基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址),其中long、double类型的数据会占用2个局部变量空间,其余类型只占用1个。局部变量表所需的内存空间在编译期间完成分配,因此此方法栈帧中的局部变量表的空间是完全确定的。方法运行期间不会改变局部变量表的大小。
两种异常情况:
1.线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
2.虚拟机栈可动态扩展时,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常
3.本地方法栈(线程私有)
通java虚拟机栈相似,其为虚拟机所使用到的Native方法服务。有的虚拟机将其与虚拟机栈合二为一。同样会抛出StackOverflowError和OutOfMemoryError异常
4.Java堆(线程公有)
java虚拟机所管理内存中最大的一块,虚拟机启动时创建,此区域唯一目的为存放对象实例。java虚拟机规范中的描述为:所有的对象实例以及数组都要在堆上分配。实际情况中是存在变化的。
垃圾收集器主要管理的区域,又名GC堆。
详细内容见后文。
5.方法区(线程公有,别名:Non-Heap)
用于存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。在java虚拟机规范中被描述为堆的一个逻辑部分。
可选择不实现垃圾收集。此区域回收的目标主要是对常量池的回收和对类型的卸载,很有必要但是成效甚微。
当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常
6.运行时常量池(方法区的一部分)
1.class文件中的常量池信息将在类加载后进入方法区的运行时常量池中存放。
2.存放编译期出现的字面量和符号引用
3.具备动态性。运行期间可将新的常量放入池中。
4.在常量池无法再申请到内存时抛出OutOfMemoryError异常
7.直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是java虚拟机规范中定义的内存区域。但却被频繁使用。
NIO类的应用,会出现OutOfMemoryError异常。