Java虚拟机运行时内存区域

来源:互联网 发布:sql数据库删除 编辑:程序博客网 时间:2024/05/19 21:16

Java虚拟机运行时内存区域

JavaVM实现了程序内存的管理,减少了Java程序员内存泄露问题的发生。但是在现实开发中由于对Java虚拟机内存了解的不够容易导致程序内存泄露,所以作为一个Java程序员还是十分有必要了解一些Java虚拟机方面的知识。了解了Java虚拟机的内存管理方面的基本知识,对程序发生内存泄露问题才会有深刻的认识,进而避免初级的错误的发生。

    多线程编程环境下应用程序基本不会只有一个线程在运行,所以JVM针对程序的内存划分会出现线程私有的内存区域和线程共享的内存区域。而在多线程编程环境下,线程共享数据所发生的同步问题一定是发生在线程共享区域的。


线程私有数据区域

    线程私有数据区域是进程中每个线程所拥有的区域,每个线程拥有自己的数据区域而不会跟其他线程共享这块内存区域。由于并发导致的同步问题一定是由于共享资源引起的,所以线程在访问私有数据区域就不会构成同步问题的充分条件也就不会发生同步问题。同时线程私有数据区域随着线程的创建而创建,随着线程销毁而销毁。

    下面将逐个介绍JVM线程私有的数据区域:

程序计数器

    程序计数器(PC)与CPU中的程序计数器有些类似。CPU中的程序计数器本质是一个寄存器,用来指示当前进程中执行到的指令地址。而JVM中的程序计数器是一块内存区域并且进程中的每一个线程都有一个自己的程序计数器,JVM的程序计数器用来指示对应线程当前执行的字节码指令地址。总之,CPU的程序计数器不是线程私有的,用来指示当前进程执行的机器指令的地址而JVM的程序计数器是每个线程私有的内存区域,用来指示每个线程当前执行的字节码指令的地址。

                    程序计数器是虚拟机中唯一一个没有规定会OOM的内存区域

Java虚拟机栈

    Java虚拟机栈用来描述Java方法执行的内存模型,线程调用Java方法时会在虚拟机栈中创建一个虚拟机栈桢。每个栈桢内包含了局部变量表、操作数栈、动态链接和方法出口信息。其中局部变量表大小在编译期间已经确定,存放编译期可知的各种数据类型boolean、byte、char、 short、int、float、long、 double和reference类型。在该内存数据区域可能抛出StackOverflowException和OutOfMemoryError, 具体抛出何种异常则由所使用的JVM来决定。一般情况下在递归调用时,如果退出条件不满足时将会抛出StackOverflowException。

本地方法栈

    本地方法栈与Java方法栈十分相似,本地方法栈为程序的native代码服务而Java方法栈为Java方法服务。本地方法栈也会抛出StackOverflowException和OutOfMemoryError。


线程共享的数据区域

    线程共享的数据区域的生命周期比线程私有数据区域的生命周期要长,线程共享的数据区域随进程的创建而创建,随进程销毁而销毁。线程共享数据区域包括堆、方法区和运行时常量池。

堆(Heap)

    堆是JVM所管理的内存区域中最大的一块,Java堆被进程中所有的线程共享。应用程序内几乎所有的Java对象都存放在Java堆内,也就是说Java堆空间是用来给Java对象分配空间的。Java堆是虚拟机内存回收的主要区域,也是垃圾收集效率最高的区域。由于现在大部分垃圾收集器都是采用分代收集算法,所以Java堆严格上来说还可以继续划分为新生代和老年代等。

    Java堆在进程的逻辑地址空间上是连续的但是在实际物理地址空间中可以是不连续的,可以由内存中不同的地址区块组成。在理论上Java堆可以实现成固定大小,但是主流Java虚拟机一般都实现成可动态改变大小。例如在HotSpot虚拟机中可以通过参数-Xmx和-Xms配置虚拟机Java堆大小的最大值和最小值。在Java堆无法继续为应用程序对象分配内存空间时,虚拟机将会抛出OutOfMemoryException。

方法区

    虽然在Java虚拟机规范中把方法区列为Java堆的一部分,但是我们一般会将方法区和Java堆区分开来。方法区主要存放虚拟机加载的类信息、常量、静态变量和编译器编译后产生的代码。在习惯上也经常把方法区称为“永久代“,但是这并不意味着数据进入方法区就永久存在于方法区。Java虚拟机也会针对方法区进行内存回收,不过这个区域内存回收的目标主要是常量池和类信息。总得来说,Java虚拟机针对方法区的内存回收效果不会很理想。根据Java虚拟机规范规定,在方法区无法满足内存分配需求时也会抛出OutOfMemoryException。

运行时常量池

    运行时常量池是方法区的一部分,但是我们经常把它单独拿出来讨论。每个字节码文件包含一项信息是常量池,用于存放编译期生成的各种字面量和符号引用。在类信息加载进虚拟机的时候字节码文件中的常量池将会被加载到运行时常量池。运行时常量池和字节码文件中常量池的一个区别是运行时常量池具备动态性,也就是运行期间可以将新的常量放入池中。

直接内存

    直接内存分配的大小不受Java堆大小的限制,但是会受到进程可用空间大小的限制。一般应用程序中的native代码通过动态分配方式申请的空间都分配在直接内存,Java类库NIO中也有部分类会在直接内存中申请空间。在直接内存无法为应用程序分配空间时也会抛出OutOfMemoryException。

0 0
原创粉丝点击