​深入理解java虚拟机 内存区域与内存溢出异常

JDK1.6 和 1.8 是有区别的

img

img

| 线程私有的: | 线程共享的: | | ---------------- | --------------------------------- | | 程序计数器 | 堆 | | 虚拟机栈 | 方法区 | | 本地方法栈 | 直接内存 (非运行时数据区的一部分) |

程序计数器

  • 程序计数器是一块较小的内存空间, 它可以看做是 当前线程所执行的字节码的行号指示器. 他是程序控制流的指示器, 分支 循环 跳转 异常处理 线程恢复等基础功能都需要依赖这个计数器来完成

  • 每条线程都需要有一个独立的程序计数器, 各条线程之间互不影响, 独立存储, 线程私有

程序计数器主要有两个作用

  1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

  2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

虚拟机栈 ( 栈 )

  • 虚拟机栈也是线程私有的, 它的生命周期与线程相同

  • 虚拟机栈描述的是Java方法执行的线程内存模型, 每个方法被执行的时候, Java虚拟机都会同步创建一个栈帧. 每一个方法被调用直 至执行完毕的过程, 就对应这一个栈帧在虚拟机栈中从入栈到出栈的过程.

  • 栈帧中包含 局部变量表 操作树栈 动态连接 方法出口

  • 线程请求的栈深度大于了虚拟机所允许的栈深度,将抛出 StackOverflowError

局部变量表主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别是 虚拟机栈为虚拟机执行Java方法,本地方法栈执行本地方(Native)

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java世界中“几乎”所有的对象都在堆中分配,但是,随着JIT编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。从jdk 1.7开始已经默认开启 逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存。

由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

在 JDK 7 版本及JDK 7 版本之前,堆内存被通常被分为下面三部分:

  1. 新生代内存(Young Generation)

  2. 老生代(Old Generation)

  3. 永生代(Permanent Generation)

img

JDK 8 版本之后方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是直接内存。

img

大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值.

方法区

* 方法区与堆一样, 是线程共享的内存区域 * 它用来存储已经被虚拟机加载的 类型信息 常量 静态变量 即时编译器编译后的代码缓存等数据

方法区和永久代的关系

《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。 方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。 也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。 面试必备

方法区是一种标准思想, java 8 之前的永久代是实现 .

在 JDK6 的时候,HotSpot 团队就有放弃永久代, 逐步改为本地内存来实现方法区的计划了

到 JDK7 的时候, 把原本在永久代中的 字符串常量池 和 静态变量 移出.

到 JDK8 的时候, 完全废弃了永久代, 把 JDK7 中还剩余的 类型信息 全部移到 元空间中.


相关内容

实战教学·项目驱动

132 2811 3191
预约免费试学
点击咨询
预约试学