首页 > 编程语言 > 详细

Java虚拟机详解(二)------运行时内存结构

时间:2020-09-12 12:56:01      阅读:66      评论:0      收藏:0      [点我收藏+]

  首先通过一张图了解Java程序的执行流程技术分享图片

 

  关于这幅图涉及到的:

  ①、class文件

  ②、类加载器

  ③、运行时数据区

  ④、执行引擎

  ⑤、垃圾回收器

  这都是接下来将要介绍的重点。

1、运行时数据区结构图

技术分享图片

 

   HotSpot JDK1.8定义的运行时数据区

 技术分享图片

 

 

  注意:HotSpot实现的运行时数据区和Java虚拟机规范定义的还是有所不同的,

  ①、将Java虚拟机栈和本地方法栈合二为一;

  ②、元数据区取代了方法区,并且元数据区不在Java虚拟机中,而是在本地内存中。

  ③、运行时常量池由方法区中移到了堆中

 

2、程序计数器

  程序计数器(Program Conputer Register)这是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器,在虚拟机的概念模型里,字节码翻译器的工作就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

  1)线程私有:Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器都会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

  2)记录虚拟机字节码指令的地址:如果线程执行的是java方法,则记录,如果正在执行的是Native方法,则计数器则为空(Undefined)。

  3)不会抛OutOfMemoryError异常。

3、虚拟机栈

  Java虚拟机栈(Java Virtual Machine stack),这块区域也是线程私有的,与线程同时创建,用于存储栈帧。

虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)由于存储局部变量表,操作数栈、动态链接、方法出口等信息。

  每一个方法从调用直至执行完成的过程,就对应这一个栈帧在虚拟机栈中入栈到出栈的过程。

技术分享图片

 

   1)线程私有

  随线程创建而创建,声明周期和线程保持一致。

  2)由栈帧组成

  线程每个方法被执行的时候都会创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息,每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

  3)抛出 StackOverflowError 和 OutOfMemoryError 异常

  如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError  异常。

4、本地方法栈

  本地方法栈(Native Method Stacks)作用和虚拟机栈类型,虚拟机栈执行的是Java方法,本地方法栈执行的是 Native 方法(如线程启动方法start()是一个native方法,底层调用的是操作系统,所以Thread类启动多线程,不是马上执行,何时启动是由操作系统决定的),本地方法栈也会抛出抛出 StackOverflowError 和 OutOfMemoryError 异常。

5、Java堆

  Java堆是Java虚拟机所管理内存最大、被所有线程共享的一块区域,目的是用来存放对象,基本上所有的额对象实例和数组都在堆上分配(不是绝对)。Java堆也是垃圾回收器管理的主要区域。

  1)线程共享

  堆存放的对象,某个线程修改了对象属性,另外一个线程从堆中获取的该对象是修改后的对象,为什么堆要设计成线程共享呢?

  我们可以假设堆是线程私有的,很显然一个系统创建的对象会有很多,而且有些对象会比较大,如果设计成线程私有的,那么如果有很多线程同时工作,那么都必须给他们分配相应的私有内存,我相信内存很快就撑爆了,很显然将堆设计为线程共享是最好不过了,不过凡事都具有两面性,线程共享的设计这也带来了多线程并发资源冲突问题,关于这个问题由于不是本系列博客的主旨,这里就不做详细介绍了。

  2)存放对象

  3)垃圾收集

  Java堆也被称为“GC堆”,是垃圾回收器的主要操作内存区域。当前垃圾回收器都是使用的分代收集算法,所以Java堆还可以分为:新生代和老年代,而新生代又可以分为 Eden 空间、From Survivor 空间、To Survivor空间。这是为了更好的回收内存,关于垃圾回收算法在后续博客会详细介绍

技术分享图片

 

   4)抛StackOverflowError异常和OutOfMemoryError异常。

  在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

6、方法区

  方法区不是存放方法的区域,是存放类模板的。

  

方法区(Method Area)用来存储已被Java虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

  方法区也称为“永久代”,这是因为垃圾回收器对方法区的垃圾回收比较少,主要是针对常量池的回收以及对类型的卸载,回收条件比较苛刻。经常会导致对此内存未完全回收而导致内存泄露,最后当方法区无法满足内存分配时,将抛出 OutOfMemoryError 异常。

   在JDK1.8 的 HotSpot 虚拟机中,已经去掉了方法区的概念,用 Metaspace 代替,并且将其移到了本地内存来规划了。

7、运行时常量池

  在Java虚拟机规范中,运行时常量池(Runtime Constant Pool)用于存放编译期生成的各种字面量和符号引用,是方法区的一部分。但是Java虚拟机规范对其没有做任何细节的要求,所以不同虚拟机实现商可以按照自己的需求来实现该区域,比如在 HotSpot 虚拟机实现中,就将运行时常量池移到了堆中  

  ①、存放字面量、符号引用、直接引用

  通常来说,该区域除了保存Class文件中描述的引用外,还会把翻译出来的直接引用也存储在运行时常量池,并且Java语言并不要求常量一定只能在编译器产生,运行期间也可能将常量放入池中,比如String类的intern()方法,当调用intern方法时,如果池中已经包含一个与该String确定的字符串相同equals(Object)的字符串,则返回该字符串。否则,将此String对象添加到池中,并返回此对象的引用。

  ②、抛出 OutOfMemoryError 异常

  运行时常量池是方法区的一部分,会受到方法区内存的限制,当常量池无法申请到内存时,会抛出该异常。

8、直接内存

  直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,它也不是Java虚拟机规范定义的内存区域。我们可以看到在 HotSpot 中,就将方法区移除了,用元数据区来代替,并且将元数据区从虚拟机运行时数据区移除了,转到了本地内存中,也就是说这块区域是受本机物理内存的限制,当申请的内存超过了本机物理内存,才会抛出 OutOfMemoryError 异常

 

Java虚拟机详解(二)------运行时内存结构

原文:https://www.cnblogs.com/zsy-code/p/13656480.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!