虚拟机内存结构
虚拟机内存结构.jpg程序计数器
线程私有的,学过ARM或者汇编的同学应该很熟悉,表示的是当前线程执行到了哪一行字节码。
java虚拟机栈
“java虚拟机栈”这个名字很让人误会,看起来一点都不像是线程私有的,看起来像是“虚拟机”级别的东西,更像是线程共享的,但实际上它确实是线程私有的,所以我觉得它更应该叫做“线程栈”诸如此类更加“人如其名”的称呼。“java虚拟机栈”的生命周期是和线程相同的,线程执行某个方法的时候会为方法创建一个叫做“栈帧”的数据结构进行入栈,这个“栈帧”用来记录方法的局部变量等信息。“java虚拟机栈”深度是有限的,如果一个线程在调用方法数超过栈所能容纳的深度时候会出现StackOverflowError,告诉你栈爆了,怎么模拟这种情况呢,哈哈,执行没有递归终止条件的递归方法,如下:
public class App {
public static int f(int num) {
// 把递归终止条件去掉
// if(num>10){
// System.out.println("----->"+num);
// return num;
// }
num++;
return f(num);
}
public static void main(String[] args) {
f(0);
}
}
执行上述代码会出现StackOverflowError,如下:
本地方法栈
又是一个栈,还是本地的(native),容易想到这是一个与本地方法有关的栈,实际上确实如此,本地方法栈与java虚拟机栈发挥的作用是非常相似的,它们的区别在于虚拟机栈是为java方法服务,本地方法栈为本地方法服务,所以本地方法栈也是线程私有的。
java堆
java堆是线程共享的用来存储类实例和数组对象的(但不是唯一不能够类实例和数组对象)存储区域。java堆上的对象生命周期不一,有长有短,为了提高回收效率,采用了分代收集的策略。这里的“代”可以分为“新生代”、“老年代”
java堆结构.png
上图结构可以看到,新生代可以分为Eden、Survivor,Survivor还分为From、to两部分,Survivor是存放年龄不大的对象,而Eden存放的是新生对象(PS:Eden是伊甸园的意思,亚当夏娃他们家,搞不懂为啥叫这名,这里姑且记住行了,不纠结!),而oldGen则相对好理解了,存放的是生命周期比较长的对象。看到这里你可能会有个疑问就是——“我创建一个对象怎么变成老生代的对象?”这里打个不太恰当的比方,如果一个人不要太短命,活过60岁被送敬老院,这时候我们说这个人是老年人了,而对象这个“身份”的转变过程也是有点相似的,结合下文例子就更加容易理解了:
image.png
新生代存放了大多数新生对象(注意是大多数,大对象可能直接往老年代里放,毕竟大对象回收再创建比较昂贵,伤不起!),From Survivor存放的是不至于老到变成老年代的对象,To Survivor则是还没使用的空间。假设此时来一次Minor Gc(指的是针对新生代的回收),演示如下:
s Minor Gc.png
上图中打叉的矩形表示被回收对象,(a)表示Minor Gc的过程,(b)表示Minor Gc的结果,Eden中存活的对象复制到未使用Survivor,在被占用的Survivor里不够老的也复制到未使用的Survivor,而那些经过Minnor Gc 存活且足够老的则加入老年代,之后Survivor两部分(from、to)交换角色,而Eden则完全为空。
方法区
线程共享的用来保存系统的类信息(包括类的字段、方法、常量池、静态变量)的存储区域,方法区也叫永久代,这个是因为它不像java堆一样被Gc回收得那么频繁,但也不是说不能被回收,如果一个类不在被“引用”,这个类就应该从虚拟机卸载,被卸载的类是可以被回收的,如果系统的类太多的话也会导致方法去溢出。
网友评论