关于内存模型网上资源简直不要太多,此文根据自己的理解总结了一下
博客小白,文章为原创,里面一些个人的观点可能不太准确或严谨,若有问题欢迎各路大神批评指正
首先是上图:
java的内存可大范围分为两类:
一、线程隔离(线程私有):
所谓线程隔离也就是说这个区域的内存各个线程间相互独立,互不影响
1. 程序计数器(program counter register):
是一块比较小的内存空间,作用可以看做是当前线程执行字节码的行号指示器
在虚拟机的概念模型里,字节码解释器就是通过改变计数器的值来选取下一条要执行的字节码
比如分支,循环,跳转,异常处理等
说的简单些就是用来控制程序的走向;
一个处理器在一个时间只会处理一条线程中的指令,因此每条线程都会有一个单独的程序计数器,各个线程间互不影响
如果线程正在执行一段java方法,那么计数器记录的就是虚拟机指令的的地址;
如果执行的是native方法,计数器的值为空(Undefined)
程序计数器是java内存模型中唯一不会出现outOfMemory的地方
2. 虚拟机栈(java virtual machine stacks)
虚拟机栈也就是我们一般常说的栈
每个方法被执行的时候都会同时创建一个栈帧,用于存储局部变量表,操作栈,动态链接,方法出口等
每一个方法被执行到结束的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
编译期间的各种基本数据类型、对象的引用都存放于虚拟机栈
如果线程请求的栈深度大于虚拟机允许的深度,则会抛出StackOverflow error,该情况常发生在死循环或死递归中;
当线程扩展时无法申请到足够的内存则会抛出outOfMemory error;
3. 本地方法栈(native method stacks)
java不是万能的,有一些方法需要借助其他语言来完成,比较常见的如hashCode方法
本地方法栈就是运行native方法的地方,一般情况下不需要关心
二、线程共享
该部分的内存由多线程共享
1. 方法区(Method Area)
方法区用于存放类的信息:类的字节码,类的结构,常量,静态变量等
字符串常量就放在方法区 如String a = "Hello" 这个hello就存放于方法区
java1.8之前该区域一般被称为永久代(Permanent Generation),从1.8开始就取消了这个说法
2. 堆
堆内存可以看做只存放对象实例的区域
堆内存分为新生代,老年代
其中新生代又可以分为eden和两个survivor区域(from survivor 和to survivor)
From Survivor和To Survivor在GC的过程中是可以相互转化的
新实例化的对象一般首先在eden区生成,80%的对象都是朝生夕死,也就是经过一次GC后就销毁了
经过一次gc还存活着的对象就会复制到survivor区
当对象经过了一定次数的GC(默认15次,可通过XX:MaxTenuringThreshold配置)后依然存活,就会放入老年代
当要新实例化一个需要很大连续空间的对象时(如数组)虚拟机会将其直接放入老年代
关于新生代和老年代将在GC学习里面详细讲解