java备忘录之内存区域

java运行时数据区域

java运行时数据区域图解示例

程序计数器

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取吓一跳需要执行的字节码指令。
如果正在执行的方法是Native方法,这个计数器的值为空。

该内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

java虚拟机栈

java虚拟机栈也是线程私有的,虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
可以通过 -Xss 这个虚拟机参数来指定一个程序的 Java 虚拟机栈内存大小:

1
java -Xss=512M

java虚拟机规范中,该该区域有两种异常状况:
如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverFlowError异常
如果虚拟机动态扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常

本地方法栈

本地方法栈是为虚拟机加所使用到的Native方法服务。Native方法使用其他语言编写的方法,一般都是c/c++

与虚拟机栈一样,本地方法区域也会抛出StackOverFlowError和OutOfMemoryError异常

java堆

java堆是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。该内存区域的唯一目的是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
java堆是垃圾收集的主要区域(”GC堆”),现代的垃圾收集器基本都是采用分代收集算法,该算法的思想是针对不同的对象采取不同的垃圾回收算法,因此虚拟机把 Java 堆分成以下三块:

  • 新生代(Young Generation)
  • 老年代(Old Generation)
  • 永久代(Permanent Generation)
    当一个对象被创建时,它首先进入新生代,之后有可能被转移到老年代中。新生代存放着大量的生命很短的对象,因此新生代在三个区域中垃圾回收的频率最高。为了更高效地进行垃圾回收,把新生代继续划分成以下三个空间:
  • Eden
  • From Survivor
  • To Survivor
    可以通过 -Xms 和 -Xmx 两个虚拟机参数来指定一个程序的 Java 堆内存大小,第一个参数设置初始值,第二个参数设置最大值。
    1
    java -Xms=1M -Xmx=2M

    如果在堆中没有内存完成实例分配,并且对也无法完成再扩展时,将会抛出OutOfMemoryError异常

方法区

方法区和java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,又叫永久代。
对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。
JDK 1.7 之前,HotSpot 虚拟机把它当成永久代来进行垃圾回收,JDK 1.8 之后,取消了永久代,用 metaspace(元数据)区替代。

当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常

运行时常量池

运行时常量池是方法区的一部分。用于存放编译期生成的各种字面常量和符号引用。
Class 文件中的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入这个区域。
除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。这部分常量也会被放入运行时常量池。

当运行时常量池无法再申请到内存时会抛出OutOfMemoryError异常

直接内存

在 JDK 1.4 中新加入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。

打赏

请我喝杯咖啡吧~

支付宝
微信