Java虚拟机--Jvm内存结构

1.JVM是什么?

  JVM:Java Virtual Mechinal(Java虚拟机).它是一个虚构的计算机,是通过在实际的计算机上模拟各种功能来实现的。JVM的主要工作是解释自己的指令集(字节码,如java源码编译成class文件在虚拟机上运行)并映射到本地的CPU指令集或OS的系统调用。Java语言跨平台的本质就是不同的操作系统使用不同的JVM映射规则,使其与操作系统无关,从而实现跨平台。

2.JVM的内存结构是什么样子?

  Java虚拟机在运行Java程序的时候,会把它所管理的内存划分为若干个不同的数据区域,如图:

      

3.那么接下来每个数据区域都是做什么的呢?

  (1).程序计数器:是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器。尤其在多线程的情况下,尤为重要。Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,即在任何一个确定时刻,一个处理器只会执行一条线程,当线程切换后就需要恢复到正确位置,因此,程序计数器要实现线程隔离,每个线程都有自己的专属的计数器。值得注意的是:此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

  (2).堆此内存区域是Java虚拟机管理的最大一块内存,同时也是线程共有的,在虚拟机启动时创建。它用来存储Java中的对象实例(无论成员变量,局部变量还是类变量,它们只想的对象都存储在堆内存中),几乎所有的对象实例都在这分配内存。同时这里也是GC的主要区域。从内存回收的角度来看,由于收集器基本都采用分代收集法,所以在Java堆中还可以细分为:新生代和老年代(可以理解为不用代的对象内存位置是不同的);再细致可分为:Eden空间,From Survivor空间,To Survivor空间(8:1:1)。从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。划分的目的是为了更好地回收内存或更快的分配内存。

    注意:Java堆可以处在物理上不连续的内存空间中,只要逻辑上连续即可。

  (3).虚拟机栈它是线程私有的,生命周期与线程相同。三部分组成:局部变量区、操作数栈、帧数据区。(文章后面会详细介绍)

  (4).本地方法栈和虚拟机栈很类似,他们的区别不过是前者为虚拟机执行Native方法服务,后者为虚拟机执行Java方法服务。但不同虚拟机的实现不同,如Sun HotSpot虚拟机直接就把两栈合二为一。

  (5).方法区线程共享的内存区域,作用是存储Java类的结构信息。当我们创建对象实例后,对象的类型信息存储在方法区中;实例数据存放在堆中;实例数据指的是Java中创建的各种实例对象以及他们的值,类型信息指的是定义在Java代码中的常量、静态变量以及在类中声明的各种方法、方法字段等;同时可能包括即时编译器编译后产生的代码数据。

4.深入理解栈:

  (1).让我们在来总结一下堆和栈的区别:

    ①.功能不同:栈内存用来存储局部变量和方法调用;而堆用来存储对象实例。

    ②.共享不同:栈是线程私有;而堆是线程共有。

    ③.异常错误不同:栈空间不足时为StackOverFlowError;堆空间不足时为OutOfMemoryError。

    ④.空间大小不同:堆远远大于栈的内存大小。

  (2).我们都知道栈的三部分组成:局部变量区、操作数栈和帧数据区。其中局部变量区和操作数栈要视对应的方法大小而定,是按字长计算的。但调用一个方法时,它从类型信息中得到此方法局部变量区和操作数栈大小并分配栈内存,然后压入栈中。

    ◆局部变量区:

      是一个以字节为单位、从0开始计数的数组,类型为short、byte、char的值存入数组前要转为int值,long和double占连续的俩字节,但访问long和double时只需要取出连续的第一项索引即可。(可以理解为方法中局部变量根据类型的不同占了不同大小的内存,并有自己的索引下标,refrence占一个字节)                       

    ◆操作数栈:

      同局部变量区一样,即以字节为单位的数组。但不同的是它不是通过索引来访问,而是通过出栈和入栈来访问。可以把操作数栈理解为存储计算时的临时数据的地方。

    ◆帧数据区:

      除了上述两个部分,Java栈帧还需要一些数据来支持常量池解析、正常方法返回以及异常派发机制。这些数据都储存在帧数据区中。当JVM执行到需要常量池中的数据时,它会通过帧数据区中指向常量池的指针来访问它。除此之外,帧数据区中的数据还要处理Java方法的正常结束和异常终止:如果通过return正常结束,当前栈帧弹栈,若有返回值,则把此值压入发起调用方法的操作数栈;如果异常中止,帧中保存了一个对此方法异常引用表的引用,有异常时,JVM找catch代码块中的代码,若没有则方法立即中止,帧中的信息恢复发起调用的方法的帧,再发起调用方法的上下文重新抛出异常。                                                

5.注意点:

  (1).Java中的基本数据类型不一定存储在栈中。占内存用来存储局部变量和方法调用,如果该局部变量是基本数据类型,则存储在栈中;如果局部变量是一个对象,则存储在堆中。

  (2).方法区,有人叫“永久代”,本质不等价,只是因为把GC分代收集扩展至方法。在该区域进行内存回收的目的主要是对常量池的回收和类型(内存数据)的卸载,但回收效率很低。运行时常量池是方法区的一部分,用来存储java类文件常量池中的符号信息。

  (3).HotSpot方法区的变化:JDK1.2-6,使用永久带实现方法区,使用GC分代实现方法区;JDK7 Oracle HotSpot 移除永久代,JDK 7 中的符号表被移动到Native Heap中,字符串常量池和类引用被移动到Java Heap中;JDK 8 永久代已完全被元空间取代。

猜你喜欢

转载自www.cnblogs.com/lemon-pomelo/p/9218797.html