Material Render Phone

Tomxin7

“工欲善其事,必先利其器”,作为Java核心之一的JVM,是一个Java开发者必须要去理解的部分,在校招的时候,走马观花式的过了一遍,很多东西没有深入理解,所以打算再看一遍《深入理解JAVA虚拟机》,把其中重要的部分整理出来,记录在博客之中。

一、Java虚拟机内存管理

image

线程共享区

  1. 存放对象实例(对象实例及数组)。JIT编译期发展 + 逃逸分析技术 ——> 栈上分配、标量替换(后面章节会讲到,只需知道所有对象在堆上分配非绝对)。
  2. 垃圾收集管理的主要区域
  3. 新生代,老年代,Eden空间
  4. 如果新建对象无法申请下来内存,会抛出OutOfMemory

方法区

  1. 存储类信息,常量,静态变量,编译后的代码等数据
  • 类的版本
  • 字段
  • 方法
  • 接口
  1. 方法区(HotSpo中为称为永久代)
  2. 垃圾回收在方法区中比较少进行
  3. OutOfMemory

运行时常量池

  1. 方法区的一部分
  2. 存放编译期生成的各种字面量和符号引用。这部分内容将在类加载后进入方法区的运行时常量池中存放
  3. 动态性。运行期也可放入新的常量。eg. String.intern()方法
    image

直接内存

NIO:可以使用Native函数库直接分配堆外内存。避免Java堆和Native堆来回复制数据。

线程私有区

程序计数器

  1. 线程私有。各条线程之间计数器互不影响,独立存储。
  2. 程序计数器是一块较小的内存空间,它可以看做当前线程执行的字节码行号指示器。
  3. 如果线程在执行java方法,计数器记录的是正在执行的虚拟机字节码指令地址,如果执行的是native方法,计数器的值为undefined。
  4. 此区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的区域。


      针对4,程序计数器,保存的是当前执行的字节码的偏移地址(也就是之前说的行号,其实那不是行号,是指令的偏移地址,只是为了好理解,才说是行号的,),当执行到下一条指令的时候,改变的只是程序计数器中保存的地址,并不需要申请新的内存来保存新的指令地址;因此,永远都不可能内存溢出的;因此,jvm虚拟机规范,也就没有规定,也是唯一一个没有规定 OutOfMemoryError 异常 的区域;

      针对3,当线程执行的是本地方法的时候,程序计数器中保存的值是空(undefined);原因很简单:本地方法是C++/C 写的,由系统调用,根本不会产生字节码文件,因此,程序计数器也就不会做任何记录

虚拟机栈

  1. 线程私有,虚拟机栈描述的是java方法执行的动态内存模型
  2. 栈帧:每个方法执行,都会创建一个栈帧,栈帧伴随着方法创建到完成。用于存放局部变量表,操作数栈,动态链接,方法出口等。
  3. 局部变量表:存放编译期可知的各种基本数据类型,引用类型和returnAddress类型。其中long, double占用2个局部变量空间(slot),其余占用1个slot。当进入一个方法的时候,这个方法的内存已经在编译期固定了,在运行期间是不会改变局部变量表的大小。
  4. 可能会抛出的异常:StackOverflowError(虚拟机栈内存溢出)OutOfMemory(系统内存溢出)


    每一个方法都会生成一个栈帧,递归的情况下,如果超过栈的长度,会抛出StackOverflowError
    image
    使用代码演示递归:
    image

本地方法栈

  1. hotspot中本地方法栈和虚拟机栈是在一块的
  2. 虚拟机栈是执行java方法服务的,本地方法栈是为执行native方法服务的
  3. 其他的两者相同

转载声明:写作不易,商业转载请联系作者获得授权,非商业转载请注明出处,并附上原文链接,感谢!