Java内存区域
运行时内存区域
Java运行时内存区域分为两部分
一部分线程不共享的包括 程序计数器,虚拟机栈,本地方法栈。
一部分为线程共享的包括 方法区和堆。
程序计数器(Program Counter Register)
保存当前线程执行的行号指示器,通过改变该计数器的值获取下一次需要运行的指令,若执行的是Java方
法则计数器保存的是正在执行的字节码指令位置,如果执行的是Native 本地方法则计数器为空。
线程私有的数据,每个线程有独立的程序计数器,指向当前线程正在执行的位置,当线程挂起后被唤醒
则执行该计数器指向的位置。
虚拟机栈(Java Virtual Machine Stack)
保存当前线程执行的Java方法的内存模型,每个Java方法被执行时创建一个栈帧,插入虚拟机栈,当方
法被执行完毕后,栈帧出栈。
栈帧保存数据包括 局部变量表、操作数栈、动态连接、方法出口等信息。
本地方法栈
保存当前线程执行的Native方法的内存模型,具体和虚拟机栈类似。
虚拟机栈和本地方法栈都会发生StackOverflowError和OutOfMemoryError异常。
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常
方法区
保存已被虚拟机加载的类信息,常量池,静态变量,即时编译器(JIT)编译后的代码缓存等数据。是线
程共享的区域。
堆
几乎所有对象实例都分配在堆上,是线程共享的区域
从垃圾收集器分代收集的方式来看,堆又可分为年轻代Eden和老年代Old Generation。
从分配内存角度来看,堆会给每个内存分配一个线程私有的分配缓冲区(Thread Local Application
Buffer)TLAB,提升分配效率。
直接内存
不是运行时内存的一部分,直接内存大小不被堆大小所限制,会受到本机总内存限制。
使用场合 申请次数少,读写频繁。
对象的创建和访问
当虚拟机遇到一条创建对象的指令时,先去方法区查询此类是否已经被加载过,如果没有需要先对此类
进行类加载过程,然后在将创建对象分配到堆空间中。
类的加载过程
类的加载分为以下7步,加载、验证、准备、初始化和卸载这五部都是按照前后顺序进行的,但是不一定
是前一部分完成后后一部分才开始, 例如加载开始后,但是还未完成时,验证已经开始了。解析过程可以在
初始化之后在开始。
加载
通过类的全限定名获得类的二进制字节流,并将静态存储结构转化为方法区的存储结构,然后构建一个
代表这个类的java.lang.Class对象,作为方法区这个类的入口。
验证
对字节流的验证,保证这些代码执行后,整个虚拟机是安全的。
准备
为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。
如一个 static int value = 123; 在准备阶段完成后 value值为0而不是123。
解析
将常量池内符号引用转化为直接引用。包括类或接口、字段、类方法、接口方法、方法类型、方法句柄
和调用点限定符这7类符号引用。
符号引用例如 : int a = 0 ; 只是符号,而直接引用则是a指向0所在内存中的位置。
初始化
初始化阶段就是执行类构造器<clinit>()方法的过程,<clinit>方法是虚拟机自动收集类变量赋值语句
和static代码块自动生成的一个方法。
深入理解Java虚拟机(周志明)阅读笔记,理解有限,如有错误还望指出。