http://liuwangshu.cn/java/jvm/1-runtime-data-area.html
JVM
组成:方法区(包含运行时常量池),堆、程序计数器、JVM栈和本地方法栈
JVM 的内存区域可以分为两类:线程私有和区域和线程共有的区域。
线程私有的区域:程序计数器、JVM 虚拟机栈、本地方法栈
线程共有的区域:堆、方法区、运行时常量池
1. 方法区
别名:持久代
作用:主要用来存储虚拟机加载的类结构信息(版本、字段、方法、接口)、常量、静态变量、还有及时编译后的代码。
特点:
1.各线程共享的内存区域
2.内存区域可以不连续,可以动态扩展
3.该区域并不是真正的“永久代”,偶尔也会进行内存回收,包括对常量池的回收和内存数据的卸载,频率比堆内存回收低很多
方法区无法满足内存需求时,会报OutOfMenmeryError异常
2. 运行时常量池
作用:主要用于存储Java类文件常量池中的符号信息
特点:
1.是方法区的一部分,也是线程间共享
2.内容主要来源于JVM对Class的加载
运行期间,常量池无法申请到新的空间,也会报OutOfMenmoryError异常
3. 堆
别名:Java堆、GC堆
作用:用来存储对象实例和数组(简单来说所有new出来的对象都存在堆内存中)
特点:
1.各线程共享堆内存
2.是JVM管理的内存中最大一块内存区域
堆被划分为新生代、老年代
堆空间不足,也会报OutOfMenmoryError异常
3.1
新生代:为新创建的对象分配内存,目的是尽量快速回收生命周期短的对象。从上图可知,新生代分为一个EdenSpace(伊甸园)和两个Survivor(幸存区),分别是FromSpace和ToSpace。
大部分对象在Eden区,但Eden区满了,此区存活的对象会copy到from幸存区,当from幸存区满,Eden和from幸存区存活的对象会被copy到to幸存区,然后from和to两个指针交换位置,保证在下一次GC之前,to幸存区是空的,当一个对象经过多次(有的垃圾回收器回收策略是15次)copy,就会晋升到老年代。针对新生代的垃圾回收是MinorGC。
老年代:存放生命周期很长的对象(经过多次新生代GC仍然存活的对象),针对老年代的垃圾回收是FullGC。
持久代:就是上面介绍的方法区,当它的空间不足,也会触发FullGC。
4. JVM栈
作用:用来描述方法执行的内存区域。
特点:
1.线程私有内存区域
占空间不足,会报StackOverFlowError异常
栈的组成:
栈是用来存储栈帧的,每当线程调用一个方法,就会产生一个栈帧,压入栈内。
而栈帧是由局部变量表、操作数栈和帧数据区组成:
局部变量表:用来存放方法中的局部变量(包括基本数据类型和对象引用),通过索引取值。
操作数栈:可以理解为临时存储计算数据的区域。通过入栈出栈方式取值。
帧数据区:用来记录方法调用信息,处理方法的正常返回和异常终止。如果方法正常返回,则把当前栈帧从栈中弹出,如果方法有返回值,则把返回值压入到调用方法的操作数栈。也可以支持常量池解析。
5. 程序计数器
作用:保证在多线程环境中程序可以连续执行。存放当前线程执行字节码的行号,字节码解释器工作时,是通过改变计数器的值来选取下一条字节码指令。
特点:
1.各线程私有内存区域
2.JVM管理的内存中最小的一块内存区域
6. 本地方法栈
跟JVM栈类似,只不过JVM栈用来执行Java方法,而本地方法栈用来执行Native方法。
Java垃圾回收机制(GC)
(JVM执行GC时线程(包括主线程)都会处于等待状态,任何GC算法都是如此)。
确定对象是否可以回收
两种经典算法确定对象是否可以被回收:引用计数算法和可达性分析算法。
1.引用计数算法
简介:根据对象被引用的数量来判断对象是否可以被回收。
详细描述:这是垃圾收集器早期的一种策略,堆中每个对象实例都有一个引用计数器。对象A,当它的引用赋值给一个变量(如a = A,b = a),则引用计数器+1;当A的引用变量生命周期结束或者设置一个新值(如a = B),那么引用计数器-1。特殊情况:当一个对象实例被垃圾收集器回收,该对象引用的任何实例的引用计数器都-1。
优点:引用计数收集器执行速度很快,不会长时间打断程序的执行。
缺点:很难解决对象之间相互循环引用问题。
2.可达性分析算法
简介:根据对象引用链是否可达来判断对象是否可以被回收。
详细描述:程序把所有的引用关系看做一张拓扑图,通过一系列的"GC Roots"作为起点,这些节点向下索引,搜索所走过的路径称为引用链,当一个对象没有任何引用链到达"GC Roots",那么这个对象不可达,可以被回收。可以作为"GC Roots"的对象包括:
1.虚拟机栈和本地方法栈(栈帧的局部变量表)引用的对象
2.方法区中类静态变量引用的对象
3.方法区中常量引用的对象
垃圾回收时机
垃圾回收有两种类型:MinorGC和FullGC。
MinorGC
对新生代进行回收,不影响老年代,因为新生代对象大多死亡频繁,所以MinorGC也会频发触发。
FullGC
也叫MajorGC,对整个堆进行回收,包括新生代和老年代,由于回收范围大,所以速度慢,因此要尽量少触发FullGC。
对于不同的垃圾收集器,MinorGC和FullGC的触发时机也不一样
老年代空间不够、永久代空间不够或者手动调用System.gc()都会触发FullGC。
新生代的Eden区用完会触发MinorGC。
垃圾收集算法
经典的垃圾回收算法有**标记清除算法、复制算法、标记整理算法、分代收集算法。
标记清除算法
简介:从根集合进开始扫描,标记存活的对象。再扫描整个空间中没有被标记的对象,进行回收。
详细描述:标记和清除两个过程的效率都不高,该算法不需要对对象进行移动,仅清除未标记的对象,清除之后
容易产生大量不连续的内存碎片。程序运行过程中,需要分配较大的对象时,无法找到足够的连续的内存,可能不得不触发另一次垃圾回收操作。
复制算法
简介:把可用的内存空间划分为大小相同的两块,每次只使用其中一块,当这一块用完之后,就把存活的对象复制到另外一块空间,把当前内存空间一次性清理掉。
详细描述:适用于对象存活率低的场景,如新生代。由于每次都对整个半区进行回收,不用考虑内存碎片,只要移动堆顶指针,按顺序分配内存即可,实现简单高效。事实上,商用虚拟机都采用这种方式回收新生代,据统计,新生代每次回收大概只有10%的存活对象。
标记整理算法
简介:这是标记清除算法的改进版本,标记过程相同,不过后续让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
详细介绍:类似于磁盘整理过程,适用于存活率高的老年代。对于老年代,复制算法效率会变低,因为对象存活率高,每次都要对很多对象进行复制操作。
分代收集算法
简介:不同生命周期的对象位于堆中不同区域,生命周期短位于新生代,生命周期长位于老年代,不同区域采取不同的回收策略,提高JVM执行效率。
详细描述:当代商用虚拟机都采用了分代收集算法,新生代采用复制算法,老年代采用标记整理算法(或标记清除算法)。
四种引用
Java中有四种引用方式:强引用、软引用、弱引用、虚引用。