首页   >   代码编程   >   JAVA开发

JVM内存模型及分区的理解

Java自从面世以来,得到了广泛的应用和飞速的发展,除去一些抽象的概念性的创新,让人津津乐道的就是实用性和上手快。

在以前使用C编程的时候,程序员对于内存是有生杀大权的,每一个对象从开始创建到销毁,都需要通过代码手动来管理,这就让人觉着很麻烦,也很辛苦,而java正是解决了这一大痛点,所以一经面世,就一直火到现在。

java虚拟机提供了GC来自动管理内存,这样开发者就不用专门写代码来处理这个问题,可以将大量的时间投入到代码层面。

这一切看起来非常美好,但正是因为太过于美好,将细节全部都封装起来了,开发者丢失了对jvm内存的基本了解,在出现内存问题的时候,排查错误就变得异常困难。

今天就来跟大家一起探讨一下:JVM内存模型及分区,了解一下每个内存区域的概念。

Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间,有的随着jvm进程的启动而存在,有的则是依赖用户线程的启动和结束而建立和销毁。

既然JVM是一个内存中的虚拟机,那它的存储就是内存了,我们写的所有类、常量、变量、方法都在内存中

JVM内存模型及分区的理解

程序计数器:

一块较小的内存区域,可以看作是当前线程所执行的字节码的行号指示器。

1、字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成;

2、如果执行的是用户编写的java方法,这个计数器记录的是正在执行这个方法字节码的地址;如果执行的是native方法,这个计数器的值则为空(undefined);

3、在多线程环境下,每个线程是并非有序执行,而是需要竞争CPU的时间,所以为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的计数器,互不干扰,独立存储,这块儿区域可以看作是“线程私有内存”,此内存区域是唯一一个在jvm规范中没有规定任何OutOfMemoryError的区域

虚拟机栈:

描述Java方法执行的内存模型,与计数器一样,同属于线程私有。

1、每个方法被执行的时候,都会同时创建一个栈帧(Stack Frame),用于存储局部变量表、操作栈、动态链接、方法出口等信息;

2、每一个方法被调用直至方法执行完成的过程,都对应一个栈帧在虚拟机栈中的由入栈到出栈的过程,字节码指令都只针对栈顶的栈帧进行操作;

3、栈里面保存的主要内容就是栈帧,每一次方法的调用,都会有一个对应的栈帧入栈,方法执行结束之后,对应的栈帧被弹出(非正常结束,抛异常也会弹出栈帧);

4、在编译期间,每一个栈帧需要占用多大的内存空间就已经完全确定了,并写入到方法表,在程序运行期间不会受到影响;

5、如果请求的栈深度超过最大可用栈深度,将会抛出StackOverflowError异常;

6、如果虚拟机允许动态扩展stack,当扩展的部分无法申请到足够的内存时,也会抛出OutOfMemoryError异常。

JVM内存模型及分区的理解

本地方法栈:

与虚拟机栈的作用是类似的,区别在于,虚拟机栈为执行java方法(字节码)服务,而本地方法栈则是为执行本地Native方法服务。

此区域也会产生StackOverflowError和OutOfMemoryError异常。

堆:

JVM管理的内存中占比最大的一块区域,虚拟机启动时创建,唯一目的是存放对象实例,几乎所有对象实例都在这里分配内存。

1、公有内存区域,所有的线程都可以共享;

2、属于GC的重点监管区域,所以也称之为“GC堆”;

3、可以处于物理上不连续的内存空间中,只需要在逻辑上连续即可(类似于电脑上的磁盘空间);

4、当前主流的虚拟机都是可以自行扩展堆内存大小的,通过-Xmx和-Xms来实现;

5、当内存回收完毕之后,还是无法拥有足够的内存来分配对象,将会抛出OutOfMemoryError异常;

6、Java堆主体分为新生代、老年代,新生代又分为Eden区、From Survivor区、To Survivor区;

7、此区域GC主要采用的是分带回收机制。

JVM内存模型及分区的理解

注意:JVM规范中,所有的对象实例以及数组都需要在Java堆上分配内存,但是随着JIT编译器的发展和逃逸分析技术的逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在Java堆上面,则不是那么“绝对”了。

方法区:

存放被虚拟机加载的类信息、常量、静态变量、即时编译器编译之后的代码等数据。

1、与java堆一样,属于线程共享的区域;

2、别名“非堆”,主要目的是为了与Java堆区分开来;

3、也可以处于物理上不连续的内存空间;

4、此区域也会有GC,但不会频繁触发,主要目标是针对常量池的回收和对类型的卸载;

5、无法满足内存分配时,会抛出OutOfMemoryError异常。


注意:方法区也被称之为“永久代”,但是这个概念是基于老版本的Hotspot虚拟机而言,之所以这么称呼它,仅仅是因为Hotspot虚拟机的设计团队将GC覆盖到了该区域,换言之,他们用永久代来实现了方法区,而在其他虚拟机中是不存在永久代这个概念的;而且,在jdk8版本的时候,也将永久代废除了,取而代之的是“元空间(元空间脱离了jvm,存在本地内存中)”


至此,JVM中的核心分区已经讲述完毕,概念性的东西不太好记,后面的文章中会用一些具体的代码案例来逐一讲解!

QQ群Ⅰ: 686430774 (已满)

QQ群Ⅱ: 718410762 (已满)

QQ群Ⅲ: 638620451 (已满)

QQ群Ⅳ: 474195684

如果文章有帮到你,可以考虑请博主喝杯咖啡!

分享到:

欢迎分享本文,转载请注明出处!

作者:不忘初心

发布时间:2019-02-28

永久地址:https://www.jiweichengzhu.com/article/557c1fb95efc4a878c9f81bcafee46e2

评论