天天看点

深入理解Java虚拟机(一)——虚拟机的内存结构

JVM-JVM内存结构

参考资料:

链接:Java虚拟机的内存组成以及堆内存介绍-HollisChuang's

Blog链接:Java堆和栈看这篇就够 - Johnny-Zhuang's Technology

Java 内存之方法区和运行时常量池 - 漠然的博客 | mritd Blog

链接:从0到1起步-跟我进入堆外内存的奇妙世界 - 简书

一、虚拟机栈

栈主要用来存储局部变量和方法的调用。栈中的内存是线程私有的并且生命周期跟线程相同。栈主要是由栈帧组成。当线程调用java的方法时候,会形成一个栈帧并压入到栈中。当栈空间不足时,会抛出StackOverFlowError异常。
栈帧主要是由局部变量表、操作数栈和帧数据区这三部分组成的。

        (1)局部变量表

                    局部变量表是由数组构成所以通过索引即可进行访问,它主要存放的是基本数据类型(short,byte,int,char,boolean,double,long)和reference类型(对象的引用),当存入一个long或者double类型的时候会在数组中形成连续的两项,要取值的时候只需取其第一个索引即可。

        (2)操作数栈

                    操作数栈主要是用来存储临时的操作数据,例如:当两个局部变量进行计算时,会先将变量入栈到操作数栈中,进行计算完成后会将结果先放在操作数栈中,所以操作数栈主要用来存放临时的数据。

        (3)帧数据区

                   帧数据区主要是通过数据用来支持常量池的解析和方法的正常返回以及异常的处理。当需要用到常量池时,JVM会使用帧数据区中指向常量池的指针。当方法正常返回时,帧里的数据会处理,使之从栈中弹出,恢复到发起调用的方法栈中,如果有返回值时,会将返回值保存到发起调用该方法的操作数栈当中。当方法抛出异常时,JVM执行catch块中的代码,如果没有,则直接终止,然后JVM用帧数据区中的数据信息恢复到发起调用该方法的栈帧中,然后向上抛出同样的异常。

二、本地方法栈

与虚拟机栈类似,主要为Native方法服务。

三、方法区

           是全局共享的一块内存区域,主要存储的是定义在类中的常量、静态变量、类中声明的方法和方法字段等。默认最小值为16MB,最大值为64MB,可用-XX:PermSize和-XX:MaxPermSize参数限制方法区的大小。

            运行常量池是方法区中的一部分。同样是全局共享的区域。运行常量池主要存放的是Class被编译器编译生成的各种符号引用。当运行时常量池无法申请到新的内存时,会抛出OutOfMemoryError异常。

四、堆

        堆是全局共享的区域,主要存放的是对象的实例以及数组(所有new的对象)。可通过-Xms(最小值),-Xmx(最大值)等参数来设置堆的大小,默认当空余堆内存大于70%时,JVM会减小堆内存到-Xms指定大小的值,默认当空余堆小于40%是,JVM会增加堆内存到-Xmx指定的大小值,为了避免运行时频繁的调整堆的大小,通常将-Xms和-Xmx设置成一样。现在收集器采用的是分代收集器,所以堆内存划分为新生代、老年代和持久代。新生代主要存储新创建的对象和尚未进入老年代的对象,老年代存储的是经过多次新生代GC依然存活的对象(例如:缓存对象)。新生代主要由Eden Space和两块一样大小的 From Space和To Space构成。

五、程序计数器

        它是当前线程所执行字节码的行号指示器。字节码解释器通过改变程序计数器来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能。