JVM - 上

 

官网Java8虚拟机文档说明

 

java程序执行流程图

       编写java源代码程序,通过java编译器javac编译成java虚拟机识别的class文件(字节码文件),然后由jvm中的类加载器加载编译生成的字节码文件,加载完毕之后再由jvm执行引擎去执行。在加载完毕到执行过程中,jvm会将程序执行时用到的数据和信息存储在运行时数据区(Runtime data area),这块区域即为jvm内存结构,垃圾回收也是作用在该区域。

      虚拟机的种类有很多种,目前比较流行的是sun的HotSpot

java虚拟机规范定义的运行时数据区

 

程序计数器

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

  • 线程私有

        Java虚拟机支持多线程,是通过线程轮流切换并分配处理器执行时间的方式来实现的。在任一确认的时刻,一个处理器只能执行一个线程中的指令。因此线程启动时,jvm会为每个线程分配一个寄存器,即为程序计数器。

  • 记录当前字节码指令执行地址

        若当前执行的方法是一个java方法,则这个计数器记录的是正在执行的虚拟机字节码 指令的地址;若正在执行的是一个native方法,则这个计数器为空(Undefined)。

  • 唯一一个没有规定任何OutOfMemoryError情况的区域

 

虚拟机栈

        Java虚拟机栈(java virtual machine stack)是线程私有的,与线程同时创建,用于存储帧栈。Java的每个方法执行的时候都会同时创建一个帧栈(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息,每一个方法调用直至执行完成的过程,对应着帧栈在虚拟机栈中从入栈到出栈的过程。

 

  • 线程私有

  • 由帧栈组成

  • 抛出StackOverflowError和OutOfMemoryError异常

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

本地方法栈

        本地方法栈(Native Method Stacks)作用个虚拟机栈类似。虚拟机栈执行的是java方法,本地方法栈执行的是native方法,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

Java堆

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

  • 线程共享

        堆存放的对象,某个线程修改了对象属性,另外一个线程从堆中获取的对象是修改后的对象。

        堆被设计成线程共享是为了节省内存空间,但是这样却带来了线程并发资源冲突问题。

  • 存放对象

        基本上所有的对象实例和数组都要在堆上进行分配,但随着Jit编译器发展和逃逸技术的成熟,栈上分配、标量替换等优化技术导致对象不一定在堆上分配。

  • 垃圾收集

        Java堆也成为了“GC堆”,是垃圾收集器主要操作的内存区域。当前垃圾回收使用的是分代收集算法,所以java堆分为:新生代和来年代,而新生代又可以分为Eden空间、From Survivor空间、To Survivor空间。这是为了更好的回首内存。

 

  • 抛出OutOfMemoryError异常

        Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可,实现时既可以是固定的,也可以是可扩展的。如果在堆中没有实例分配,并且堆也无法扩展,将抛出此异常。

方法区

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

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

运行时常量池

        运行时常量池(Runtime Constant Pool)用于存放编译期生成的各种字面量和符号引用,是方法区的一部分。

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

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

  • 抛出OutOfMemoryError异常

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

 

直接内存

       直接内存(Direct Memory)并不是运行时数据区的一部分,也不是java虚拟机规范定义的内存区域。这块区域受本机物理内存的限制,当申请的内存超过了本机物理内存,会抛出OutOfMemoryError异常。在jdk1.4中新加入了NIO(new input/output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆里面的DirectByteBuffer对象作为这块内存的引用操作,这样避免了在java堆和native堆中来回复制数据,显著提高新能。

猜你喜欢

转载自blog.csdn.net/clm2017/article/details/99851530