JVM基础

1.基础

JDK  将java文件编译成class文件

JRE   包含JVM

JVM可以进行内存管理

利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的文本java文件编译成JAVA字节码,在JRE上运行这些JAVA字节码,JVM解析这些字节码,映射到CPU指令集或OS的系统调用。

2.JVM运行时数据区

JVM基础

2.1程序计数器

 记录当前线程所执行的字节码行号,用于获取下一条执行的字节码。

当多线程运行时,每个线程切换后需要知道上一次所运行的状态、位置。由此也可以看出程序计数器是每个线程私有的。

 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一个在Java虚拟机规范中没有规定任何OurOfMemoryError情况的区域。

引申参考:什么是程序计数器

2.2虚拟机栈

  存储当前线程运行方法所需要的数据、指令、返回地址。

  虚拟机栈是有一个一个的栈帧组成,栈帧是在每一个方法调用时产生的。

  每一个栈帧由局部变量表操作数栈等组成。每创建一个栈帧压栈,当一个方法执行完毕之后则出栈。

  局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用和returnAddress类型(指向了一条字节码指令的地址)。其中64位长度的long和double类型的数据会占用2个局部变量空(slot),  其余的数据类型占1个。局部变量表所需的内存空间在编译期间分配完成,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。如果线程请求栈的深度大于虚拟机所允许的深度,将抛出*Error异常;无法申请到内存抛出OutOfMemoryError异常。

如果出现方法递归调用出现死循环的话就会造成栈帧过多,最终会抛出 * 异常。

  这块内存区域也是线程私有的。

2.3本地方法栈

为线程所私有,功能和虚拟机栈非常类似。线程在调用本地方法时,来存储本地方法的局部变量表,本地方法的操作数栈等等信息。

2.4堆

  Java 堆是整个虚拟机所管理的最大内存区域,所有的对象创建都是在这个区域进行内存分配。

  这块区域也是垃圾回收器重点管理的区域,由于大多数垃圾回收器都采用分代回收算法,所有堆内存也分为 新生代老年代,可以方便垃圾的准确回收。

  这块内存属于线程共享区域。

2.5方法区

  方法区主要用于存放已经被虚拟机加载的类信息,如常量,静态变量。 这块区域也被称为老年代。是各个线程共享的内存区域。

运行时常量池

  • 运行时常量池是方法区的一部分,所以也是全局共享的。
  • 其作用是存储 Java 类文件常量池中的符号信息。
  • class 文件中存在常量池(非运行时常量池),其在编译阶段就已经确定;JVM 规范对 class 文件结构有着严格的规范,必须符合此规范的 class 文件才会被 JVM 认可和装载。
  • 运行时常量池 中保存着一些 class 文件中描述的符号引用,同时还会将这些符号引用所翻译出来的直接引用存储在 运行时常量池 中。
  • 运行时常量池相对于 class 常量池一大特征就是其具有动态性,Java 规范并不要求常量只能在运行时才产生,也就是说运行时常量池中的内容并不全部来自 class 常量池,class 常量池并非运行时常量池的唯一数据输入口;在运行时可以通过代码生成常量并将其放入运行时常量池中。
  • 同方法区一样,当运行时常量池无法申请到新的内存时,将抛出 OutOfMemoryError 异常

2.6直接内存

  直接内存不是虚拟机运行时数据区的一部分。但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。在JDK1.4中新加入了NIO类,引入了一种基于通道与缓存区(buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

上一篇:【IOS笔记】Creating Custom Content View Controllers


下一篇:性能优化之reflow和repaint