JVM内存分配以及回收机制

175 阅读4分钟

java是一门编译与解释并存的语言,.java文件通过java编译器编译后会变成字节码.class文件,字节码只面向JVM。

JVM是java虚拟机,有针对不同系统的特定实现,能够将相同的字节码在不同的系统中展示出相同的效果。达到了一次编译,多系统使用的好处。这个过程会消耗资源,这也是导致java语言与面向过程语言比性能低的其中一部分原因。

那JVM在执行java程序的过程中,内存是如何分配?

java虚拟机在执行java程序的过程中,会将它管理的内存化分为多个数据区域,分别为堆,方法区,程序计数器,虚拟机栈,本地方法栈。

一、堆:堆是JVM管理内存最大的一块区域,也是线程共享的内存区域。堆区域存放对象实例,几乎所有对象实例和数组都在这里分配内存,同时堆也是内存回收的主要空间区域。

二、方法区: 方法区跟堆一样,属于线程共享,存放已加载的类、常量、静态变量,即时编译器编译后的代码。

三、程序计数器:程序计数器表示当前线程执行字节码的行号,该区域为线程私有,每个线程都有对应的程序计数器,随线程产生而生产,随线程死亡而释放。这也是唯一一个不会内存溢出报错的内存区域。解释器通过改变程序计数器来获取下一条执行的指令。多线程下通过程序计数器记录当前线程执行字节码对应的行号,以便于其他线程切换回来的时候能够知道上一次执行指令的位置。

四、虚拟机栈:线程私有,描述java方法执行的内存模型,每个线程都有对应的虚拟机栈,随线程产生而生产,随线程死亡而释放。可以这样理解:Object obj=new Object();等号右边存放在堆中,等号左边存放在虚拟机栈中。

五、本地方法栈:跟虚拟机栈差不错,但是虚拟机栈为虚拟机执行的java方法服务,本地方法栈为Native方法服务,也是线程私有,每个线程都有对应的本地方法栈,随线程产生而生产,随线程死亡而释放。

相比c++,java有内存回收机制,那内存回收的机制是如何实现的呢?

java内存回收机制发生在JVM中,堆中一般分为三部分,分别为新生代(Young Generation)、老年代(Old Generation),永久代(Permanent Generation)。

Young Generation划分为三块区域,由一块Eden和两块Survivor按照811组成(可更改比例),当创建对象的时候,最先把对象存放在Eden块,在一次MinorGC后,会将存活的对象会移到第一块Survivor中或第二块Survivor中,并且对象年龄+1,当年龄达到一定值之后,将会进入Old Generation内存区域。还有一种情况就是当一个对象内存过大的时候,也会存在老年代区域。

Old Generation则没有进行划分,相比Young Generation,Old Generation的MinorGc频率也会更低,一般在新生代进入老年代时内存不足,或者存放过大内存对象的时候导致内存不足才会触发。

Permanent Generation在jdk1.7之后又“元空间”替代了,两者之前最主要的区别在于,永久代是存在JVM中,元空间存在本地内存中,只跟磁盘有关。

内存回收的算法:

一、标记-清理算法:将不需要收回的对象做标记,对未标记的对象进行回收处理。

二、复制算法:将内存分为两块,每次使用一块,使用的对象存放到另外一块内存中,对原内存进行回收处理。

三、标记-整理算法:标记不需要回收的对象,并向一端游动,对一端外的对象进行回收操作。

四、分代收集算法:根据堆中不同代进行不同的回收机制,Young Generation采用复制算法,Old Generation采用标记-整理算法。

注:Permanent Generation属于方法区范畴,方法区中包含常量池,常量池中包含字符常量池,并且方法区在改为“元空间”之前,将字符常量池移到堆中,jdk1.7后改为“元空间”,也就是说,到目前,字符常量池存在堆中,常量池则存在“元空间”中。

以上内容可能存在不足或错误,如有发现请指出来。