《深入理解Java虚拟机》系列 | 第二篇:Java运行时数据区

Java运行时内存分为两大部分,分别为线程私有(程序计数器、虚拟机栈和本地方法栈)和线程共享(堆和方法区)。
    线程共享区:所有线程共享同一区域。
    线程独占区:在每个线程中都会分配自己的内存区域。
    程序计数器:(Program Counter Register)

  • 记录当前线程所执行到的字节码的行号。
  • 状态:线程私有,占用的内存空间小,可以看作当前线程所致行字节码的行号计数器;
  • 生命周期:和线程生命周期相同;
  • 异常:唯一一个不存在内存溢出的内存区域(OutOfMemoryError);
  • 当执行Java方法时,记录的是虚拟机字节码指令地址;当记录本地方法时该值为空(Undefined);
  • 在一个特定时刻,一个处理器(多核处理器来说是一个内核)只有一个线程在执行;
  • goto关键字:java的唯一一个保留字。在字节码中存在goto指令;
    • for\while循环通常是通过一个goto指令来实现字节码的循环,
    • do-while不需要goto指令,因为条件分支位于字节码的末尾。

    虚拟机栈:(Java Virtual Machine Stacks)

  • 存放方法运行时所需的数据,栈帧。
  • 状态:线程私有,描述Java方法执行的内存模型;
  • 生命周期:和线程声明周期一致;
  • 大小:可以动态扩展申请内存(可以申请足够内存,不然会有异常产生);
  • 64位的long和double会占用2个局部变量表空间,其余占用1个局部变量表空间;
  • 异常状况:线程请求的栈深度大于虚拟机允许的最大深度--StackOverFlowError
  • 虚拟机栈动态扩展无法申请足够内存--OutOfMemoryError
  • 栈帧:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表(平时所说的栈)、操作数栈、动态连接、方法出口等,每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的出栈和入栈操作。
  • 局部变量表:存放编译器可知的各种基本操作类型,引用类型、returnAddress(指向字节码指令的地址)。
  • 所需要的内存空间在编译器就已经确定,在方法运行期间是不会改变局部变量表的大小。

    本地方法栈:(Native Method Stack)

  • 为JVM所调用的Native方法服务。
  • 线程私有;
  • hotspot虚拟机把本地方法栈和虚拟机栈合二为一;
  • 会抛出OutOfMemoryError和OutOfMemoryError异常;
  • 虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用Native方法服务。

    堆:(Java Heap)

  • 存放对象的实例。(JIT和逃逸分析技术发展,对象不在绝对的分配在堆中)。
  • 状态:线程共享,Java虚拟机啊所管最大的内存区域,垃圾收集器管理的主要区域;
  • 为垃圾收集:
  • 新生代(Eden区、From Survivor区、To Survivor区)、老年代;
  • 线程共享的Java堆可以划分多个线程私有的分配缓冲(Thread Local Allocation Buffer,TLAB);
  • 可以物理上不连续只要逻辑上连续即可。大小可以是固定,也可扩展
  • 异常状态:无法扩展时,抛出OutOfMemoryError;
  • 设置大小:
    • -Xmx:设置最大值    -Xms20M
    •  -Xms:设置最小值   -Xmx20M

    方法区:(Method Area)

  • 方法区存储运行时常量池、已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 状态:线程共享;永久代(hotspot使用GC分代手机扩展到方法区,使用永久代来实现方法区);
  • 类信息:类的版本、方法、字段、接口等;
  • 异常状态:无法满足内存分配需求--OutOfMethodError;
  • JDK7把原来放在永久代的字符床常量池移出;
  • String类中inter()方法:会将字符串对象放入到常量池。
  • 大小:
    • -XX:PermSize=64M JVM初始分配的非堆内存
    • -XX:MaxPermSize=128M JVM最大允许分配的非堆内存,按需分配

    运行时常量池:(Runtime Constant Poll)

  • 用于存放编译器生成的字面量和符号引用,将在类加载后进入方法取的运行时常量池存放。
  • 方法区中的一块内存;
  • 运行时常量池具备动态性,运行期间将新的变量放入常量池,String中的intern();
  • 字节码常量:程序定义的常量。
  • 运行时常量:在程序运行时产生常量。intern()
  • Java虚拟机对Class文件的每一部分都有严格的规定,对于运行时常量池没有要求。
  • 一般保存Class文件描述的符号引用,存储翻译出来的直接引用。
  • 异常状态:无法满足内存分配需求--OutOfMethodError;

    直接内存:(Direct Memory)

  • 异常状态:各个区域总合大于物理内存限制,无法满足内存分配需求--OutOfMethodError;
  • jdk1.4 NIO直接分配堆外内存。

 

posted @ 2018-12-04 16:05  i孤独行者  阅读(131)  评论(0)    收藏  举报