共计 3305 个字符,预计需要花费 9 分钟才能阅读完成。
介绍 Java 内存区域
1. 程序计数器:
- 记录下 1 条须要执行的字节码指令: 分支、循环、跳转、异样解决、线程复原等性能都须要依赖程序计数器;
线程公有;
次要有两个作用:- 字节码解释器通过改变程序计数器来顺次读取指令,从而实现代码的流程管制,如:程序、抉择、循环、异样解决。
- 在多线程下,记录以后线程执行地位,从而当线程被切换回来的时候可能晓得该线程上次运行到哪了。
- 惟一不会呈现 OutOfMemoryError (内存透露) 的内存区域!
2. Java 虚拟机栈:
- 线程公有
- 形容的是 Java 办法执行的内存模型,每次⽅法调用都通过栈传递数据
- Java 内存能够毛糙的辨别为堆内存(Heap)和栈内存 (Stack), 其中栈就是当初说的虚拟机栈,或者说是虚拟机栈中局部变量表局部。(实际上,Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都领有:局部变量表、操作数栈、动静链接、办法进口信息。)
局部变量表次要寄存了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象援用(reference 类型,它不同于对象自身,可能是一个指向对象起始地址的援用指针,也可能是指向一个代表对象的句柄或其余与此对象相干的地位)。
Java 虚拟机栈会呈现两种谬误:StackOverFlowError 和 OutOfMemoryError。
- StackOverFlowError:
若 Java 虚拟机栈的大小不容许动静扩大,当线程申请栈的深度超过以后 Java 虚拟机栈的最大深度,就抛出 StackOverFlowError。- OutOfMemoryError:
若 Java 虚拟机堆中没有闲暇内存,并且垃圾回收器也无奈提供更多内存的话。就会抛出 OutOfMemoryError。
Java 办法有两种返回形式, 不论哪种返回形式都会导致栈帧被弹出:
- return 语句。
- 抛出异样。
3. 本地办法栈
虚拟机栈为虚拟机执行 Java 办法(也就是字节码)服务,而本地办法栈则为虚拟机应用到的 Native 办法服务。在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一
本地办法被执行的时候,在本地办法栈也会创立一个栈帧,用于寄存该本地办法的局部变量表、操作数栈、动静链接、进口信息。
办法执行结束后相应的栈帧也会出栈并开释内存空间,也会呈现 StackOverFlowError 和 OutOfMemoryError 两种谬误
4. 堆
JVM 治理的内存中最大的一块,Java 堆是 所有线程共享 的一块内存区域,在虚拟机启动时创立.
- 寄存对象实例,简直 所有的对象实例以及数组都在这里分配内存。
- 从 jdk1.7 开始曾经默认开启逃逸剖析,如果某些办法中的对象援用没有被返回或者未被里面应用(也就是未逃逸进来),那么对象能够间接在栈上分配内存。
- Java 堆是 GC 治理的次要区域,因而也被称作 GC 堆(Garbage Collected Heap). 从垃圾回收的角度,因为当初收集器根本都采纳 分代 垃圾收集算法,所以 Java 堆还能够细分为:新生代 和老年代:再粗疏一点有:Eden 空间、From Survivor、To Survivor 空间等。
新生代:
Eden 区、两个 Survivor 区都属于新生代 -> 依照程序被命名为 s1 和 s2
老年代:
大部分状况,对象都会首先在 Eden 区域调配,在一次新生代 GC 后,如果对象还存活,则会进入 s1 或者 s2,并且年龄加 1(Eden 区 ->Survivor 区后对象的初始年龄变为 1),当它的年龄减少到肯定水平(默认为 15 岁),就会被降职到老年代中。对象降职到老年代的年龄阈值,能够通过参数 -XX:MaxTenuringThreshold 来设置。
几个谬误:
OutOfMemoryError: GC Overhead Limit Exceeded:
当 JVM 花太多工夫执行 GC 且只能回收很少的堆空间,就会产生此谬误!
java.lang.OutOfMemoryError: Java heap space:
在创立新对象时, 堆内存空间有余, 会引发 java.lang.OutOfMemoryError: Java heap space 谬误。(和本机物理内存无关,和你配置的内存大小无关!)
5. 办法区 / 元数据区
⽅法区与 Java 堆一样,是 各个线程共享 的内存区域,它用于存储已被虚拟机 加载的类信息 、 常量 、 动态变量 、 即时编译器编译后的代码 等数据。
尽管 Java 虚拟机标准把办法区形容为堆的一个逻辑局部,然而它却有⼀个别名叫做 Non-Heap(非堆),目标应该是与 Java 堆辨别开来。
扩大 1. 办法区和永恒代的关系
- 办法区是标准, 永恒区是 HotSpot 的实现
《Java 虚拟机标准》规定了办法区的概念和它的作用,并没有规定如何去实现。
办法区和永恒代 的关系很像 Java 中 接口和类 的关系,类实现了接口,而永恒代就是 HotSpot 虚拟机对虚拟机标准中办法区的一种实现形式。也就是说,永恒代 是 HotSpot 的概念,办法区 是Java 虚拟机标准 中的定义,是一种 标准 ,而永恒代是一种 实现,一个是规范一个是实现,其余的虚拟机实现并没有永恒代这一说法。 Metaspace(元空间)和 PermGen(永恒代)相似,都是对 JVM 标准中办法区的一种落地实现。
不过元空间与永恒代之间最大的区别在于:元空间并不在虚拟机中,而是应用本地内存。
扩大 2. 办法区 / 永恒代 /Metaspace
- 办法区:(逻辑上)
逻辑上的货色,是 JVM 的标准,所有虚拟机必须恪守的。
是 JVM 所有线程共享的、用于存储类的信息、常量池、办法数据、办法代码等。 - 永恒代:(办法区的实现、JDK7 及之前、次要是和元空间比照)
PermGen,就是 PermGen space,全称是 Permanent Generation space,是指内存的永恒保留区域。PermGen space 则是 HotSpot 虚拟机基于 JVM 标准对办法区的一个落地实现,并且只有 HotSpot 才有 PermGen space。而如 JRockit(Oracle)、J9(IBM)虚拟机有办法区,然而就没有 PermGen space。PermGen space 是 JDK7 及之前,HotSpot 虚拟机对办法区的一个落地实现。在 JDK8 被移除。‘ - Metaspace(元空间、JDK8 及之后):
元空间与永恒代之间最大的区别在于:元空间并不在虚拟机 中,而是应用本地内存。
应用本地内存有什么益处呢?最间接的体现就是 OOM 问题将不复存在,因为默认的类的元数据调配只受本地内存大小的限度,也就是说本地内存残余多少,实践上 Metaspace 就能够有多大,这解决了空间有余的问题。不过,让 Metaspace 变得无限大显然是不事实的,因而咱们也要限度 Metaspace 的大小:应用 -XX:MaxMetaspaceSize 参数来指定 Metaspace 区域的大小。JVM 默认在运行时依据须要动静地设置 MaxMetaspaceSize 的大小。
移除 PermGen(永恒代)从从 JDK7 就开始。例如,字符串外部池,曾经在 JDK7 中从永恒代中移除。直到 JDK8 的公布将宣告 PermGen(永恒代)的终结。
其实,移除 PermGen 的工作从 JDK7 就开始,永恒代的局部数据就曾经转移到了 Java Heap 或者是 Native Heap。
但永恒代仍存在于 JDK7 中,并没齐全移除,比方:
字面量(interned strings)转移到 Java heap;
类的动态变量(class statics)转移到 Java heap;
符号援用(Symbols)转移到 Native heap;
JDK 版本 | 办法区的实现 | 运行时常量池所在的地位 |
---|---|---|
JDK6 | PermGen space(永恒代) | PermGen space(永恒代) |
JDK7 | PermGen space(永恒代) | Heap(堆) |
JDK8 | Metaspace(元空间) | Heap(堆) |
字符串外部池,曾经在 JDK7 中从永恒代中移除(在堆中)-jdk7 中曾经是了.
JDK6、JDK7 时,办法区 就是 PermGen(永恒代)。
JDK8 时,办法区就是 Metaspace(元空间)。
扩大 3. 字符串常量池在哪
- jdk6->PermGen 永恒代
- jdk7/jdk8->Heap 堆内存