Java与C++的围墙:内存动态分配,垃圾收集技术
程序计数器
当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选择下一条执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等依赖计数器。
线程私有,唯一不会OutOfMemory的区域。
执行Java方法时指向正在执行的虚拟机字节码,执行Native方法时为空。
Java虚拟机栈
描述方法执行的内存模型,每个方法执行的同时会创建一个栈帧。用于存储局部变量表,操作数栈,动态链接,方法出口等信息。
线程私有,生命周期与线程相同。
线程请求的栈深度大于虚拟机允许的栈深度,*Error;
虚拟机栈可动态扩展,扩展时无法申请到足够的内存,OutOfMemoryError。
局部变量:编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用(reference类型,对象起始地址的指针,对象的句柄),returnAddress类型(字节码指令的地址)。
局部变量表的大小及所需的内存空间在编译器完成分配,运行期不会改变。其中64位的long和double占用2个slot,其余类型占用一个。
本地方法栈
虚拟机栈为虚拟机执行的Java方法(也就是字节码)服务,本地方法栈指的是虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈使用的语言,方式和数据结构未做强制规定,虚拟机可以*实现。
也会有*Error和OutOfMemoryError异常。
Java堆
内存中最大的一块,所有线程共享。存放对象实例,几乎所有的实例都在这里分配内存。分为新生代和老年代,或者Eden空间,From Survivor空间,To Survivor空间。
Java虚拟机规范:所有的对象实例以及数组都要在堆上分配。
JIT的发展,逃逸分析技术的成熟,栈上分配,标量替换等优化技术使所有的对象实例以及数组都要在堆上分配这一规范不那么绝对。
方法区
(永久代-HotSpot)线程共享区域。
存储已被虚拟机加载的类信息,常量,静态变量,及时编译器编译后的代码等数据。GC的目标时是常量池和对类型的卸载。也会有OutOfMemoryError异常。
运行时常量池:方法区的一部分,Class文件中除了有类的版本,字段,方法,接口等信息,还有常量池,用于存放编译器生成的各种字面量和符号引用。
Java虚拟机对Class文件的每一部分都有严格规定,但对运行时的常量池没有任何要求。
运行时的常量池具备动态性,不要求常量一定只在编译器才能产生,运行期也可以。(String类的intern方法),也会有OutOfMemoryError异常。
intern 这个方法返回的是 返回字符串对象的规范化表示形式,当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并且返回此 String 对象的引用。
直接内存
不是虚拟机运行时内存区域。JDK1.4之后加入NIO,引入基于通道(channel)与缓存区(buffer)的I/O方式,使用Native函数直接分配堆外内存,任何通过存在在Java堆中的对象引用进行操作。