关于java:深入理解Java虚拟机第三版-第2章-Java内存区域与内存溢出异常

4次阅读

共计 3843 个字符,预计需要花费 10 分钟才能阅读完成。

Run Time Data Area(运行时数据区)

Program Counter Register(程序计数器)

 程序计数器是一块较小的内存区域,它能够看作是以后线程所执行的字节码的行号指示器。

作用

  1. 字节码解释器通过改变程序计数器来顺次读取指令,从而实现代码的流程管制,如:程序执行、抉择、循环、异样解决。
  2. 在多线程的状况下,程序计数器用于记录以后线程执行的地位,从而当线程被切换回来的时候可能晓得该线程上次运行到哪儿了。

留神

  • 程序计数器是惟一一个不会呈现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创立而创立,随着线程的完结而死亡。
  • 当执行 Java 办法时,程序计数器纪录的是正在执行的虚拟机字节码指令的行号。
  • 当执行 Native 办法时,程序计数器的值为空(Undefined)

Java Virtual Machine Stacks(虚拟机栈)

作用

  1. 为线程提供了一块公有的内存空间,用于存储办法的局部变量、操作数栈、动静链接、办法进口等信息。
  2. 每个线程都有本人的 Java 虚拟机栈,随着线程的创立而创立,随着线程的销毁而销毁,生命周期与线程雷同。
栈帧

Java 虚拟机栈中的每个栈帧(Stack Frame)对应着每个办法的调用,每个栈帧由以下四局部组成:

Local Variables (局部变量表)

次要寄存了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象援用(reference 类型,它不同于对象自身,可能是一个指向对象起始地址的援用指针,也可能是指向一个代表对象的句柄或其余与此对象相干的地位)。

Opreand Stacks (操作数栈)

操作数栈用于存储计算过程中的两头后果,例如将两个变量相加的后果,它是一个后进先出(LIFO)的栈构造。操作数栈中的每个元素能够是 Java 根本数据类型或者一个对象的援用。

Dynamic Linking (动静链接)

次要服务一个办法须要调用其余办法的场景。在 Java 源文件被编译成字节码文件时,所有的变量和办法援用都作为符号援用(Symbilic Reference)保留在 Class 文件的常量池里。当一个办法要调用其余办法,须要将常量池中指向办法的符号援用转化为其在内存地址中的间接援用。动静链接的作用就是为了将符号援用转换为调用办法的间接援用。

Return Address (返回地址)

返回地址实际上是一个指向代码行号的指针,用于标识办法返回后须要执行的下一条指令。当办法调用完结时,Java 虚构机会依据返回地址返回到调用该办法的地位,持续执行程序。

留神

  1. 虚拟机栈的 StackOverFlowError:若栈的内存大小不容许动静扩大,那么当线程申请栈的深度超过以后 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 谬误。
  2. 虚拟机栈的 OutOfMemoryError:如果栈的内存大小能够动静扩大,如果虚拟机在动静扩大栈时无奈申请到足够的内存空间,则抛出 OutOfMemoryError 异样。
  3. Java 办法有两种返回形式,一种是 return 语句失常返回,一种是抛出异样。不论哪种返回形式,都会导致栈帧被弹出。也就是说,栈帧随着办法调用而创立,随着办法完结而销毁。无论办法失常实现还是异样实现都算作办法完结。

Native Method Stack(本地办法栈)

  1. 本地办法栈和虚拟机栈所施展的作用十分类似,区别是:虚拟机栈为虚拟机执行 Java 办法(也就是字节码)服务,而本地办法栈则为虚拟机应用到的 Native 办法服务。在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
  2. 本地办法被执行的时候,在本地办法栈也会创立一个栈帧,用于寄存该本地办法的局部变量表、操作数栈、动静链接、进口信息。
  3. 办法执行结束后相应的栈帧也会出栈并开释内存空间,也会呈现 StackOverFlowError 和 OutOfMemoryError 两种谬误。

Heap(堆)

  • Java 虚拟机所治理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创立。此内存区域的惟一目标就是寄存对象实例,简直所有的对象实例以及数组都在这里分配内存。
  • Java 世界中“简直”所有的对象都在堆中调配,然而,随着 JIT 编译器的倒退与逃逸剖析技术逐步成熟,栈上调配、标量替换优化技术将会导致一些奥妙的变动,所有的对象都调配到堆上也慢慢变得不那么“相对”了。从 JDK 1.7 开始曾经默认开启逃逸剖析,如果某些办法中的对象援用没有被返回或者未被里面应用(也就是未逃逸进来),那么对象能够间接在栈上分配内存。
  • Java 堆是垃圾收集器治理的次要区域,因而也被称作 GC 堆(Garbage Collected Heap)。从垃圾回收的角度,因为当初收集器根本都采纳分代垃圾收集算法,所以 Java 堆还能够细分为:新生代和老年代;再粗疏一点有:Eden、Survivor、Old 等空间。进一步划分的目标是更好地回收内存,或者更快地分配内存。

留神

  1. 在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为新生代、老生代和永恒代,但 JDK 8 版本之后 PermGen(永恒) 已被 Metaspace(元空间) 取代,元空间应用的是间接内存。
  2. 大部分状况,对象都会首先在 Eden 区域调配,在一次新生代垃圾回收后,如果对象还存活,则会进入 S0 或者 S1,并且对象的年龄还会加 1(Eden 区 ->Survivor 区后对象的初始年龄变为 1),当它的年龄减少到肯定水平(默认为 15 岁),就会被降职到老年代中。对象降职到老年代的年龄阈值,能够通过参数 -XX:MaxTenuringThreshold 来设置。
  3. 堆最容易呈现的就是 OutOfMemoryError 谬误:
  • java.lang.OutOfMemoryError: GC Overhead Limit Exceeded:当 JVM 花太多工夫执行垃圾回收并且只能回收很少的堆空间时,就会产生此谬误。
  • java.lang.OutOfMemoryError: Java heap space : 如果在创立新的对象时, 堆内存中的空间不足以寄存新创建的对象, 就会引发此谬误。(和配置的最大堆内存无关,且受制于物理内存大小。最大堆内存可通过 -Xmx 参数配置,若没有特地配置,将会应用默认值。

Method Area(办法区)

 当虚拟机要应用一个类时,它须要读取并解析 Class 文件获取相干信息,再将信息存入到办法区。办法区会存储已被虚拟机加载的 类信息、字段信息、办法信息、常量、动态变量、即时编译器编译后的代码缓存等数据。

留神:

  1. 办法区在 JDK1.8 前后的区别?

    办法区和永恒代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就能够看作是永恒代和元空间,接口能够看作是办法区,也就是说永恒代以及元空间是 HotSpot 虚拟机对虚拟机标准中办法区的两种实现形式。并且,永恒代是 JDK 1.8 之前的办法区实现,JDK 1.8 及当前办法区的实现变成了元空间。

  2. 办法区罕用参数有哪些?
# 1.8 之前
-XX:PermSize=N // 办法区 (永恒代) 初始大小
-XX:MaxPermSize=N // 办法区 (永恒代) 最大大小, 超过这个值将会抛出 OutOfMemoryError 异样:java.lang.OutOfMemoryError: PermGen
## 相对而言,垃圾收集行为在这个区域是比拟少呈现的,但并非数据进入办法区后就“永恒存在”了。# 1.8 之后
-XX:MetaspaceSize=N // 设置 Metaspace 的初始(和最小大小)-XX:MaxMetaspaceSize=N // 设置 Metaspace 的最大大小
# 与永恒代很大的不同就是,如果不指定大小的话,随着更多类的创立,虚构机会耗尽所有可用的零碎内存。

运行时常量池

  1. 办法区保留有有类的元数据,类的元数据来源于 Class 文件,Class 文件中除了有类的版本、字段、办法、接口等形容信息外,还有用于寄存编译期生成的各种字面量(Literal)和符号援用(Symbolic Reference)的常量池表 (Constant Pool Table),运行时常量池就是类加载后寄存到办法区的常量池的内存表现形式。
  2. 既然运行时常量池是办法区的一部分,天然受到办法区内存的限度,当常量池无奈再申请到内存时会抛出 OutOfMemoryError 谬误。
  3. 字符串常量池(串池)

  • 字符串常量池是 JVM 为了晋升性能和缩小内存耗费针对字符串(String 类)专门开拓的一块区域,次要目标是为了防止字符串的反复创立。
  • JDK 1.7 为什么要将字符串常量池挪动到堆中?次要是因为永恒代(办法区实现)的 GC 回收效率太低,只有在整堆收集 (Full GC) 的时候才会被执行 GC。Java 程序中通常会有大量的被创立的字符串期待回收,将字符串常量池放到堆中,可能更高效及时地回收字符串内存。

Direct Memory(间接内存)

  1. 间接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机标准中定义的内存区域,然而这部分内存也被频繁地应用。而且也可能导致 OutOfMemoryError 谬误呈现。
  2. JDK1.4 引入 NIO 类,能够应用 Native 函数间接读写堆外内存,防止了在 Java 堆和 Native 堆之间来回复制数据,进步了读写效率。
  • 传统 IO:磁盘 -》零碎内存缓冲区 -》JVM 内存缓冲区 -》JVM 内存
  • NIO:磁盘 -》间接内存缓冲区 -》JVM 内存
  1. 间接内存的申请与开释底层是由 Unsafe 对象治理的。
正文完
 0