欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

深入理解Java虚拟机——Java内存区域,深入理解虚拟机

来源: javaer 分享于  点击 12428 次 点评:113

深入理解Java虚拟机——Java内存区域,深入理解虚拟机


Java内存区域

  • Java内存区域
    • 程序计数器
      • 特征
    • Java虚拟机栈
      • 特征
      • 虚拟机栈溢出
      • 栈帧
    • 本地方法栈
    • Java堆
      • 特征
      • Java堆溢出
    • Java方法区
      • 特征
      • 运行时常量池
    • 直接内存

Java 虚拟机在执行程序时会将管理内存分为若干数据区域。这些区域各有用途,创建和销毁时间。有的区域随着虚拟机进程启动而存在,有些依赖用户线程启动和结束而建立和销毁。以下是 Java 虚拟机中主要数据区域:
程序计数器
Java堆
Java虚拟机栈
本地方法栈
方法区

程序计数器


特征

  • 线程私有。
  • 一块较小的内存空间,作用可以看做是当前线程所执行的字节码的「 行号指示器 」。
    字节码解释器工作时通过改变这个计数器选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能均依赖这个计数器。
  • 如果线程正在执行的是一个 Java 方法,计数器记录的是正在执行的虚拟机字节码指令地址,如果执行的是 Native 方法,这个计数器值为空。
  • 内存区域小,是唯一一个在Java虚拟机中没有规定任何 OutOfMemoryError 情况的区域。

Java虚拟机栈


特征

  • 线程私有,生命周期与线程相同。
  • 后进先出。
  • 存储栈帧,支撑 Java 方法的调用、执行和退出。
  • 可能出现 OutOfMemoryError 和 StackOverflowError 。

虚拟机栈溢出

/**
 * 无边界值的递归会引起StackOverflowError
 */
public class Test {

    public static int num = 0;
    public static void recursion(){
        ++num;
        recursion();
    }

    public static void main(String[] args) throws Throwable{
        try {
            recursion();
        }catch (Throwable e){
            System.out.println("stack length" + num);
            throw e;
        }

    }
}
/**
 * 不断创建新线程,引起OutOfMemoryError
 */
public class Test {

    public static int num = 0;
    public static void dontStop(){
        while (true){

        }
    }
    public static void stackLeakByThread(){
        while (true){
            new Thread(()->dontStop()).start();
        }
    }

    public static void main(String[] args) throws Throwable{
        stackLeakByThread();
    }
}

栈帧

Java 虚拟机栈中存储的内容。它被用于存储数据和部分过程结果的数据结构,也同时被用来处理动态链接、方法返回值和异常分派 。
完整栈帧包含:局部变量表操作数栈动态链接方法出口方法正常完成异常完成 等信息。
* 局部变量表:
局部变量表的容量是以局部变量槽 Slot 为最小单位,由编译期决定 。
单个 Slot 可以存储一个类型为 boolean、byte、char、short、float、reference 的数据,两个 Slot 可以存储一个类型为 long 或 double 的数据 。
局部变量表用于方法间的参数传递,及方法执行过程中存储基础数据类型的值和对象引用。
* 操作数栈:
后进先出栈,由若干 Entry 组成,长度由编译期决定。单个 Entry 即可存储一个Java虚拟机定义的任意数据类型的值,包括 long、double 类型,但存储 long 和 double 类型的 Entry 深度为 2 ,其他深度为1 。
方法执行过程中,栈帧用于存储计算参数和计算结果,方法调用时,操作数栈用来准备调用方法参数以及接收方法返回结果。

本地方法栈


作用是支撑 Native 方法的调用、执行和退出,其余与 Java 虚拟机栈作用相似,HotSpot 将虚拟机栈和本地方法栈二合为一 。

Java堆


特征

  • 全局共享 。
  • 通常是 Java 虚拟机中最大的一块内存,被所有线程共享 。
  • 作用是作为 Java 对象及数组的主要存储区域 。
  • 具有自动内存管理,即「 GC 」,Java 堆是 GC 机制主要管理部分,所以 Java 堆也被称为 GC 堆 。
  • 由于选在收集器主要采取分代收集算法,Java 堆可分为新生代老年代
  • 可能出现 OutOfMemoryError 。

Java堆溢出

/**
 * 不断创建对象,并保持引用将导致OOM
 */
public class Test {

    static class OOMObject{

    }
    public static void main(String[] args){
        List<OOMObject> list = new ArrayList<>();
        while (true){
            list.add(new OOMObject());
        }
    }
}

Java方法区


特征

  • 全局共享。
  • 存储 Java 类的「 结构信息 」,可以理解成 Class 文件在内存中的存放位置。
  • 可能出现 OutOfMemoryError 。
  • JDK1.7中,HotSpot 已将原本放在永久代中的字符串常量池移出!

运行时常量池

运行时常量池是方法区一部分 。Class 文件中存在一部分用于存放编译期生成的各种字面量及符号引用。
这部分在类加载后进入方法区的运行时常量池存放。
运行时常量池与 Class 文件中的常量池不同的是:具有动态性,及允许运行时加入数据。如 String 类的 intern() 方法。

直接内存


直接内存并非虚拟机运行时数据区的一部分,但可能导致 OutOfMemoryError 。
随「 NIO 」引入,目的是避免在 Java 堆和 Native 堆中来回复制数据带来性能损耗 。

相关文章

    暂无相关文章
相关栏目:

用户点评