详解Java内存模型

详解Java内存模型

下图为jdk1.8运行时数据区域,本文逐一讲解各个内存的的职责在这里插入图片描述
1.堆
这是Java虚拟机管理内存中最大的一块,也是所有线程共享的一块内存区域,在虚拟机启动时被创建。堆的唯一作用就是存放对象实例,也就是说你new的对象都在这里,几乎所有的对象实例和数组都在这里分配内存。

Java堆空间是垃圾回收的主要区域,因此也叫GC堆,因为现在的垃圾回收器大多采用分代回收,所以通常将堆空间分成新生代和老年代,再细分一点,就是以下四个区域。
在这里插入图片描述
其中eden,s(Survivor)0,s1都是新生代,tentired是老年代。
a.新生代
新生代分为三个区域,一个Eden区和两个Survivor区,它们之间的比例为(8:1:1),这个比例也是可以修改的。通常情况下,对象主要分配在新生代的Eden区上,少数情况下也可能会直接分配在老年代中。Java虚拟机每次使用新生代中的Eden和其中一块Survivor(From),在经过一次Minor GC(新生代的垃圾回收动作,新的对象通常分配到Eden区,当Eden区内存不足时,虚拟机就发动一次MinorGC,MniorGC的频率高,速度快)后,将Eden和Survivor中还存活的对象一次性地复制到另一块Survivor空间上(这里使用的复制算法进行GC),最后清理掉Eden和刚才用过的Survivor(From)空间。将此时在Survivor空间存活下来的对象的年龄设置为1,以后这些对象每在Survivor区熬过一次GC,它们的年龄就加1,当对象年龄达到某个年龄(默认值为15)时,就会把它们移到老年代中。

在新生代中进行GC时,有可能遇到另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。

b.老年代
老年代里面存放都是生命周期长的对象,对于一些较大的对象(即需要分配一块较大的连续内存空间),是直接存入老年代的,还有很多从新生代的Survivor区域中熬过来的对象。

老年代中使用的是Full GC,Full GC所采用的是标记-清除算法。老年代中的Full GC不像Minor GC操作那么频繁,并且进行一次Full GC所需要的时间要比Minor GC的时间长。

2.虚拟机栈
Java虚拟机栈是线程私有的,生命周期与线程相同,描述的是Java方法执行的内存模型,每次方法调用的数据都是通过栈传递。

Java虚拟机栈由一个个栈帧组成,每个栈帧中都拥有:局部变量表,操作数栈,动态链接,方法出口信息。局部变量表存放的就是编译器可知的各种数据类型和对象引用。

当调用一次方法/函数,实际上就是虚拟机栈在运作,每次调用一个函数都有一个栈帧(栈帧里有函数的信息,对象引用,局部变量)被压入栈,每次方法调用结束(return,抛出异常)栈帧就从虚拟机栈中弹出。虚拟机栈会抛出StackOverFlowError(栈深不足)和OutOfMemoryError(内存不足)异常。

3.本地方法栈
本地方法栈也是线程独有的,它与虚拟机栈其实差不多,只不过虚拟机栈用来执行Java方法,而本地方法栈执行Native(不是用Java实现,但是Java)可以调用的方法。在HotSpot虚拟机中与虚拟机栈合二为一。

4.程序计数器
程序计数器是每个线程私有的,是一块比较小的区域,可以看作当前线程所执行的字节码的信号指示。主要有两个作用:
1.字节码解释器通过改变程序计数器来依次读取指令,完成程序的顺序、选择、循环等控制。比如我想让A方法在B方法前面执行,通常我们怎么做,把A写在B前面顺序执行,这是我们肉眼看到的,Java判断顺序又没法看,这是可以把A的计数器设为1,B的设为2,就能AB顺序执行了。
2.多线程里程序计数器记录当前线程执行位置,从而可以在线程切换时继续执行。比如A线程执行一半,控制权被B线程抢了,B线程执行完了又把执行权还给A线程,此时A根据他的程序计数器位置就能接着被抢之前的地方执行程序,没必要从头再来。要注意的是程序计数器永远不会OOM,线程创建它创建,线程结束他结束。

5.元空间
这个名字比较陌生,其实在1.6时还叫作方法区也就是永久代(因为垃圾回收在这里不常执行,所以叫永久代,但还是会执行,并不是永久存在),此时这块内存还在堆中,在1.8时永久代被彻底移除,取而代之的是直接内存中的元空间。

也就是说元空间里也存放了与方法区中一样的东西:类的方法代码,常量,静态变量,方法名,访问权限,返回值等等,大家熟知的运行时常量池也是在方法区中的(1.7后从方法区中移出,在堆中单独开辟了一块内存存放常量池)。

那为什么要把方法区改成元空间呢?
一开始的方法区是在堆中的,通常我们通过设置参数设置其大小,如果不设置的话随着线程创建,系统内存会很快耗尽,抛出OOM,永久代受JVM本身设置大小限制,无法进行调整。改成元空间后,存放在直接内存中,受本机可用内存限制,永远不会抛出OOM。其实底层还有一些其他的原因。

6.直接内存
这不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,分配不受Java堆的限制,受的是本机总内存大小的限制。但是频繁访问也会抛出OOM。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值