JAVA虚拟机结构之运行时数据区

  • jvm的运行时数据区根据用途一共可以分为这几类:pc寄存机,java虚拟机栈,java堆,方法区,运行时常量池,本地方法栈。其中java堆,方法区,运行时常量是公有的数据区,随着虚拟机的启动而创建,随着虚拟的退出而销毁。而pc寄存器,java虚拟机栈,本地方法栈则是线程私有的,随着线程的开始和结束而创建和销毁,运行时数据区如图下。


    这里写图片描述

PC(Program Counter)寄存器

  • 每个线程启动的时候,都会创建一个PC(Program Counter,程序计数器)寄存器。PC寄存器里保存有当前正在执行的JVM指令的地址。 每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。保存下一条将要执行的指令地址的寄存器是 :PC寄存器。PC寄存器的内容总是指向下一条将被执行指令的地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。
  • JVM的多线程是通过线程轮流切换分配执行时间来实现的,在任何时刻,每个处理器都只会执行一个线程中的指令,当线程进行切换的时,为了线程能恢复当正确的位置,所以每个线程必须有个独立的线程计数器,这样才能保证线程之间不互相影响;
  • 如果该线程当前执行的方法是native的,对应的寄存器的值是undefined的,否则就是将要执行的指令地址;
  • pc寄存器也是为一个不会出现内存溢出问题的数据区;
java虚拟机栈

  • 这个也是一个线程私有的,生命周期与线程是同步的,每个方法在执行的同时,都会创建一个栈帧(栈帧详解),用于存储局部变量表,操作数栈,动态链接,方法出入口等信息。每个方法的调用到执行完成的过程就是一个栈帧入栈到出栈的过程;
  • java虚拟机栈的除了栈帧的出栈入栈之外,不会再受到其他参数的影响,所以java虚拟机栈所使用的内存不需要保证是连续的;
  • 我个人的理解,在某种程度上,jvm虚拟机是以栈帧(方法)为基本元素,java虚拟机栈为载体,来解释并执行我们的程序的;所以虽然我们是面向对象式的编程,但实际解释执行时仍就只是面向过程式的执行?
  • java虚拟机栈可能会出现的异常:
    • 线程请求栈的深度大于虚拟机栈所允许的深度,这时候将会抛出StackOverflowError异常
    • 当Java虚拟机允许动态扩展虚拟机栈的时候,当扩展的时候没办法分配到内存的时候就会报OutOfMemoryError异常
本地方法(Native)栈

  • 本地(native)方法:可以理解为不是java语言写的方法代码,而是C,C++等其他语言写的代码。java语言提供了机制去调用其他语言实现的方法的接口,这需求也是我们在实际业务中会碰到的,所需要的。而非java语言写的方法对应与java统称为本地(native)方法。
  • java虚拟机需要单独用到本地方法栈来执行native方法,一般就是传统的栈(通常称为C stack),有的虚拟机(Sun HotSpot)也会将本地方法栈与java虚拟机栈合并实现,而不需要单独实现本地方法栈。另java虚拟机规范中好像也没有规定一定要支持本地方法栈,支持native方法;
  • 可以看出本地方法栈和java虚拟机栈的功能几乎是一样的,只是对象不同;
方法区(method area)

  • 方法区是可供各个线程共享的运行时内存区域,方法区域传统语言的编译代码存储区的作用非常类似。它存储每一个类的结构信息,例如运行时常量池,字段,方法数据,构造函数等等;
  • 方法区我是这样理解,方法区里面存的是“类”的结构信息和一些静态的数据,在java堆中存储的是对象实例。而我们实际要用创建对象实例时,就依赖与方法区里面的数据;
  • 方法区的大部分数据(类信息)其实在编译阶段就已经确定下来了,所以我们可以选择固定大小或者扩展的大小,也可以选择不在这个内存区域内实现垃圾收集和压缩,所以一般我们也称方法区为永久代;
  • 方法区的大小由-XX:PermSize和-XX:MaxPermSize来调节;
  • JDK8之后,取消了永久代,提出了元空间,并且常量池、静态成员变量等迁移到了堆中;元空间不在虚拟机内存中,而是放在本地内存中;
运行时常量池

  • 这个区域属于方法区。但由于功能特殊,也单独介绍;
  • 该区域存放类和接口的常量,除此之外,它还存放成员变量和成员方法的所有引用。当一个成员变量或者成员方法被引用的时候,JVM就通过运行常量池中的这些引用来查找成员变量和成员方法在内存中的的实际地址;
  • 常量池主要用于存放编译生成的各种字面量和符合引用,由于常量池属于方法区的一部分,所以当常量池没有内存空间的时候就抛出OutOfMemoryError异常;
JAVA 堆

  • 堆区是Java虚拟机所管理的内存中最大的一块,Java堆是被所有线程共享的内存区域,主要存储对象的实例、数组等;
  • java 堆也是垃圾回收的主要区域,为了方便垃圾回收再java堆的设计上还会根据垃圾回收的算法进行相应的划分,如新生代,老年代等;
  • 当堆中没有内存完成实例分配,并且堆无法扩展的时候,将会抛出OutOfMemoryError异常;
  • java 堆所使用的内存不需要保证是连续;
直接内存(Direct Memory)

  • 直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域。在JSK1.4中新加入NIO类,引入了一种基于通道与缓存区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过DirectByteBuffer对象作为这块内存的引用进行操作。这样在一些场景中可以显著提高性能,因为避免了在Java堆和Native中来回复制数据。
  • 本机的直接内存不会受到java堆内存大小的限制,但是服务器管理员在配置虚拟机参数时,会根据实际内存设置-Xmx等参数信息,忽略了直接内存也会占用一部分内存空间,使得各个内存区域的总和大于物理内存限制(包括物理的和操作系统层级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

猜你喜欢

转载自blog.csdn.net/u014296316/article/details/82431109
今日推荐