深入理解Java虚拟机 1.Java内存区域详解

深入理解Java虚拟机 1.Java内存区域详解

Java虚拟机在执行Java程序的时候,自动会将他所管理的内存划分成若干个不同的数据区域。划分出来的数据区域按照线程是否共享划分为两类。一类是线程共享区域,另一类是线程隔离区域。其中线程共享区域包括方法区、堆区;线程私有的包括程序计数器、虚拟机栈、本地方法栈。运行时数据区域划分如下图:

1.程序计数器

虚拟机栈是属于线程隔离的数据区域,他的生命周期随着线程的创建而开始,随着线程的销毁死亡而结束。

程序计数器是一块比较小的内存区域,是当前执行线程执行字节码的行号指示器。Java代码编译后产生的字节码,其执行方式是通过字节码解释器进行执行。解释器读取装载人内存中的字节码文件,然后每次按照顺序读取字节码指令。Java中的分支、循环、跳转、异常以及确保线程切换之后可以恢复到正确位置的实现,都是通过改变程序计数器来值的实现。

在单线程模式,程序计数器可以认为并非是必须的。因为字节码的读取本身是按照顺序读取的,单线程模式下不存在切换的情况。如果是多线程模式下,实际工作中也都是多线程之间同步合作进行的,所有会存在线程之间的切换。JVM多线程是通过CPU轮转算法来实现的。也就是说,某个线程在执行过程中可能会因为时间片耗尽而被挂起,而另一个线程获取到时间片开始执行。此时,JVM需要先保存被挂起线程的上下文环境:将线程执行位置保存到程序计数器中,将调用方法的信息保存在栈中;同时将待执行线程的程序计数器和栈中的信息写入到处理器中,完成线程的上下文切换。当被挂起的线程重新获取到时间片的时候,它要想从被挂起的地方继续执行,就必须知道它上次执行到哪个位置,在JVM中,通过程序计数器来记录这个线程的字节码执行位置。因此,程序计数器是具备线程隔离的特性,也就是说,每个线程工作时都有属于自己的独立计数器。

2.虚拟机栈

虚拟机栈是属于线程隔离的数据区域,他的生命周期随着线程的创建而开始,随着线程的销毁死亡而结束。

虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成的过程,都对应着一个栈帧在虚拟中的入栈到出栈的过程。

栈帧:栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构。是虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态链接、方法出口等信息。在编译程序代码的时候,栈帧需要多大的局部变量表,多深的操作数栈都已经完全确定了。因此栈帧分配的内存是不受程序与=运行时变量数据的影响,仅仅取决于具体的虚拟机实现。

2.1 局部变量表

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量,栈帧需要多大的局部变量表,在编译期间已经确定。如编译期可知的各种数据类型,如8大基本数据类型、对象的引用地址等。其中long、double类型虚拟机会以高位对其的方式为期分配2个连续的局部变量表空间,其余的占用1个局部变量表空间。

2.2 操作数栈

操作数栈是一个先进先出的栈,常称为操作栈。操作栈的深度也是在编译期间就已经确定的,操作栈的深度不超过max_stacks的大小。在编译期间,操作栈的大小虽然已经确定,但是操作栈的内容却是空的。只有在方法执行过程中,才会有各种字节码指令入栈、出栈。所以,操作栈中存储的是方法执行过程中各种字节码指令。

2.3 虚拟栈异常类型

虚拟机栈内存中因为使用不当,会出现两种异常类型。第一种是,如果线程请求的虚拟站的深度大于虚拟机所允许的深度,会抛出StackOverflowError异常;第二种是当虚拟站动态扩展内存时,如果无法申请到足够的内存空间,会抛出OutofMemoryError异常。

3.本地方法栈

虚拟机栈是属于线程隔离的数据区域,他的生命周期随着线程的创建而开始,随着线程的销毁死亡而结束。

本地方法栈与虚拟站发挥的作用非常相似。区别是虚拟机栈是为虚拟机执行Java方法服务的,而本地方法栈是为虚拟机中的本地方法服务的。与虚拟机栈一样,本地方法栈也会抛出StackOverflowError和OutofMemoryError异常。

4.堆区

堆区是属于线程共享的数据区域,所有线程都可以共享、访问该数据区域存储的信息。

堆区是虚拟机中最大的一块内存,主要是用于存储对象实例。几乎所有的对象实例以及数组都要在堆上分配。Java堆区是垃圾收集器管理的主要区域,故而被很多人称为GC堆。

从内存回收的角度看,由于现在收集器基本都采用分代收集算法。因此,Java堆还可以细分为新生代和老年代。

根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要是逻辑上连续即可。在实现时,即可以实现成固定大小的,也可以实现成可扩展的。当前主流的都是可扩展的,可通过虚拟机参数(-Xmx和-Xms)控制。

当在内存中没有足够的内存进行实例对象的分配,或者没有足够的内存进行扩展时,就是抛出OutofMemoryError异常。

5.方法区

方法区是属于线程共享的数据区域,所有线程都可以共享、访问该数据区域存储的信息。

方法区用于存储已经被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。方法区同堆区一样,在空间分配上,可以是物理上不连续的内存空间中,只要是逻辑上连续即可。在实现时,即可以实现成固定大小的,也可以实现成可扩展的。当前主流的都是可扩展的,可通过虚拟机参数(-Xmx和-Xms)控制。还可以选择不实现垃圾收集。相对而言,垃圾收集器在方法区的收集行为还是比较少出现的。但并不是进去方法区就如进入永久代那样永远不会回收一样。该区域的内存回收主要是针对常量池的回收以及对类型的卸载。而且一般来说,回收效果难以令人满意。7.

6.运行时常量池

运行时常量池是方法区的一部分,存量池中主要用于存放编译期间生成的各种字面量以及符号引用,这部分内容将在类加载之后进入方法区的运行时常量池中存放。

7. 直接内存

直接内存并不属于虚拟机运行时数据区的一部分们也不是Java虚拟机规范中定义好的内存区域。主要用于直接对本机内存需要使用的一些场景。

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值