浅谈java内存区域
运行时数据区域
java虚拟机在运行程序时将所管理的内存划分为若干的数据区域,这些数据区域有不同的用途及生命周期。按照虚拟机规范分为程序计数器、虚拟机栈、本地方法栈、java堆以及方法区。

程序计数器
程序计数器可看成当前程序运行的行号指示器,其为线程私有(生命周期有当前程序线程决定),即可简单理解为控制程序字节码执行顺序的指针(java程序完成编写时,java文件由java编译器编译为class文件准确的说是jdk中tools.javac.main方法完成,再有java解释器完成class文件到jvm能识别的机器码的转化并对字节码进行执行,执行过程则依赖于程序计数器)。
虚拟机栈
虚拟机栈有(下文简称栈)可理解为程序逻辑处理的主要容器(处理引用),很明显也为线程私有,生命周期与当前程序线程相同。栈由一个个栈帧(类比视频与帧的关系)组成,每个栈帧对应一个调用的方法,栈与栈帧理解为一个逻辑上的链表结构,每调用一个方法则往栈上增加一个栈帧(当前栈帧则为栈顶),每个方法的从调用到执行完成(每个栈帧的生命周期)即为入栈和出栈的过程,每个栈帧由局部变量表、操作数栈、动态连接、返回地址、附加信息(可以由一个不大恰当的比喻大致根据方法的运行过程记忆,进入方法是首先是访问方法参数和局部变量即局部变量表,再对变量进行逻辑处理即操作数栈,如果需要其他方法引用则需要该方法的所属类的实例即动态链接,该方法运行完成则调用返回即访问返回地址)。
局部变量表
局部变量表最大容量在java编译器完成时就已经决定(class文件中该方法的code属性),用于保存该方法的方法变量和局部变量(变量引用)。
操作数栈
操作数栈则负责当前方法中的算数运算及是否进行其他方法调用即再次增加栈帧(即后进先出的原则)。
动态连接
每个栈帧上都持有一个当前方法在常量池中的引用(常量池中为符号对应方法的引用,每次运行到该方法时转化为直接引用)。
返回地址
每个栈帧中方法运行完成时,分为正常执行完成和异常退出,正常执行完成则进行出栈操作(调整前一栈帧的局部变量表,压栈)并调整程序计数器指向。
附加信息
虚拟机规范并没有规定具体虚拟机实现包含什么附加信息,这部分的内容完全取决于具体实现。在实际开发中,一般会把动态连接,方法返回地址和附加信息全部归为一类,称为栈帧信息。
本地方法栈
本地方法栈作用与虚拟机栈作用类型,区别在于本地方法栈执行native方法(调用其他类库,即程序计数器指针为空“undefined”)。
java堆
java堆的作用即是存放对象实例,几乎所有对象对在java堆上保存实例,既然实例都有明确的指向引用,则明显java堆为共享内存分布区域。java堆即为实例存储部分,则资源释放则显得尤为重要,其又称为"GC堆"。java堆主要有四个内存区域组成,Eden、servivor0(form)及servivor1(to)组成了新生代,以及old,分类原因主要是优化GC算法,对堆内存进行更有效的管理。
新生代
新生代与老年代又分别对应GC类型中的MGC和FGC,新生代中的内存分布比例默认为eden:servivor0:servivor1=8:1:1,由于java运行过程中局部方法引用占用较大比例则分配较多,form部分和eden部分用于存储最新创建的对象实例,to部分则存储一次GC或多次GC后存在的实例(新生代中GC算法为负值算法,即从程序中所有活跃的GC ROOT出发,简单理解为线程的根引用出发,将所有引用指向的实例复制并检查经历的MGC次数决定是复制到to部分中或者老年代内存部分中,,然后清理到eden和form部分的内存,再将form部分和to部分对调,每次MGC完成时只有form部分存储实例)。
老年代
老年代主要存储多次MGC过后仍存活的实例或者存储大文件(防止占用eden部分过多资源引发MGC),老年代主要使用FGC进行资源释放(标记算法和标记整理压缩算法,标记算法即从程序中所有活跃的GC ROOT出发,对所有有效引用指向的实例进行标记,对未标记部分的内存进行清理释放,标记整理压缩算法则是对标记的实例进行整理移动,清楚内存碎片,释放资源)。
方法区
方法区用于存储于java堆类型,为各个线程共享的内存区域,主要存储类信息、静态变量、常量及即时编译器编译的代码等,简单理解即时不可变的各种静态信息,可以理解也有人称方法区为“永久代”,不过方法区中的变量常量和类信息若是没用有效引用时依然会被清理,释放空间。

浙公网安备 33010602011771号