JVM学习04-垃圾回收概念与算法
JVM学习04-垃圾回收概念与算法
一、常用的垃圾回收算法
引用计数法
为对象配备一个整型计数器,只要有任何一个对象引用了这个对象,这个对象的计数器就加1,引用失效时引用计数器就减1。只要对象的引用计数器的值为0,则对象就不可能再被使用。
问题:
无法处理循环引用的情况。
每次因引用产生和消除,都需要伴随一个加法和减法操作,对系统性能会有一定影响。
Java虚拟机并未选择此算法作为垃圾回收算法。
标记清除法(Mark-Sweep)
标记清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。
- 标记阶段,通过根节点标记所有的从根节点开始的可达对象。未被标记的对象就是未被引用的垃圾对象
- 清除阶段,清除所有未被标记的对象。
问题:回收后的空间不连续,会产生空间碎片。在对象的堆空间分配过程中,尤其是大对象的内在分配,不连续内在空间的工作效率要低于连续空间。
复制算法
将原有的内在空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中存活的对象复制到未使用的内在块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。
优点:效率高、没碎片
效率高体现在:如果垃圾多,要复制的存活对象相对会少。
没碎片体现在:对象在垃圾回收过程中被统一复制到新内存空间中,所以可确保没碎片。
缺点:系统内存折半
在Java新生代串行垃圾回收器中,使用了复制算法思想。新生代分为eden、from、to三个部分。from和to可视为用于复制的两块大小相同、地位相等且可进行角色互换的空间块。
from和to空间也被称为survivor空间,即幸存者空间用于存放未被回收的对象,如下图。
- 新生代 :存放年轻对象的堆空间。年轻对象指刚刚创建的或经历垃圾回收次数不多的对象。
- 老年代:存放老年对象的堆空间。老年对象就是经历多次垃圾回收依然存活的对象。
垃圾加收时,eden空间中存活的对象,会被复制到未使用的survivor空间中(假设是to ),正在使用的survivor空间(假设是from) 中的年轻对象也会被复制到to空间中(大对象或者老年对象会直接进入老年代,如果to空间满了,对象也会直接进入老年代)。此时 eden和from空间中的剩余对象就是垃圾对象,可以直接清空,to空间则存放此次回收后的存活对象。
这种改进的复制算法既保证了空间的连续性,又避免了大量的内存空间的浪费。
PS:复制算法比较适合用于新生代。因为在新生代,垃圾对象通常会多于存活对象,复制算法的效果会比较好。
标记压缩法
老年代大部分对象是存活对象,标记压缩法是一种老年代的回收算法,先从根节点开始对所有可达对象做一次标记,之后将所有的存活对象压缩到内存的一端,之后清理边界外所有的空间。
优点:既避免了碎片的产生,又不需要两块相同的内在空间,性价比较高。
分代算法
即前面的汇总,不同的内存区间使用不同的回收算法。
新生代回收频率高,每次回收耗时短
老年代回收频率低,耗时长。
为了支持高频率的新生代回收,虚拟机可能使用一种叫卡表的数据结构。卡表为一个比特位集合,每个比特位表示老年代区域中所有对象是否持有新生代的对象引用。这样在新生代GC时,可以不用花大量时间扫描所有老年代对象来确定每个对象的引用关系,先扫描卡表,只有卡表的标记位为1时,才需要扫描特定老年代对象。卡表位为0的所在区域必定不含有新生代对象引用。
分区算法
分区算法将整个堆空间划分为连续不同的小区间,每个小区间都独立使用独立回收。
优点:可以控制一次回收多少个小区间,更好控制GC产生的停顿时间。每次合理地回收若干个小区间,而不是整个堆空间。(有没有使用场景?比如什么版本的的虚拟机这么用)
二、判断可触及性
可触及性包含三种状态
- 可触及:从根节点开始,可以到达这个对象
- 可复活:对象的所有引用都被释放,但对象有可能在finalize()函数中复活。
- 不可触及的:对象的finalize()函数被调用,并且没有复活,就会进入不可触及状态。
以上三种状态中,只有对象不可触及时,才可以被回收。
对象复活
- finalize方法至多由GC执行一次(用户当然可以手动调用对象的finalize方法,但并不影响GC对finalize的行为)
- finalize()函数可能引用外泄,无意中复活对象
- finalize()被系统调用,调用时间不明确,推荐在try-catch-finally中进行资源释放。
public class CanReliveObj {public static CanReliveObj obj;public static void main(String[] args) throws InterruptedException {obj = new CanReliveObj();obj = null;//这里调用 gc以后,会调用到finalize()方法,将obj对象复活System.gc();Thread.sleep(1000);if (obj == null) {System.out.println("obj 是 null");} else {System.out.println("obj 可用");}System.out.println("第二次gc");obj = null;// finalize()方法这次不会被调用,所以这回 obj对象真正被回收System.gc();Thread.sleep(1000);if (obj == null) {System.out.println("obj 是 null");} else {System.out.println("obj 可用");}}@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("CanReliveObj finalize called");obj = this;}@Overridepublic String toString() {return "I am CanReliveObj";} }
引用和可触及性的强度
强引用、软引用、弱引用、虚引用,不知道干啥用的,先略过。
强引用特点:
- 强引用可以直接访问目标对象
- 强引用指向的对象在任何时候都不会被系统回收,虚拟机宁可OOM,也不回收强引用指向的对象
- 强引用可能导致内存泄漏
public class StrongReference {public static void main(String[] args) {StringBuilder sb1 = new StringBuilder("abcde");StringBuilder sb2 = sb1;System.out.println(sb1 == sb2);System.out.println(sb1.equals(sb2));sb1 = new StringBuilder("fgihjk");System.out.println(sb1 == sb2);System.out.println(sb1.equals(sb2));} } 运行结果 true true false false
垃圾回收时的停顿现象:Stop-The-World
import java.util.HashMap;/*** -Xmx1g -Xms1g -Xmn512k -XX:+UseSerialGC -XX:+PrintGCDetails -Xloggc:StopWorldTestGcLog.log* @author dmn*/ public class StopWorldTest {public static void main(String args[]) {MyThread t = new MyThread();PrintThread p = new PrintThread();t.start();p.start();}/*** 不断申请内存操作*/public static class MyThread extends Thread {HashMap map = new HashMap();@Overridepublic void run() {try {while (true) {// System.out.println((map.size() * 512) / 1024 / 1024);if (map.size() * 512 / 1024 / 1024 >= 880) {map.clear();System.out.println("clean map");}byte[] b1;for (int i = 0; i < 100; i++) {b1 = new byte[512];map.put(System.nanoTime(), b1);}Thread.sleep(1);}} catch (Exception e) {}}}/*** 输出时间*/public static class PrintThread extends Thread {public static final long starttime = System.currentTimeMillis();@Overridepublic void run() {try {while (true) {long t = System.currentTimeMillis() - starttime;System.out.println(t / 1000 + "." + t % 1000);Thread.sleep(100);}} catch (Exception e) {}}} }
日志里可以看到 [Times: user=0.83 sys=0.01, real=0.85 secs] 的real就是Full GC实际花费的时间
29.998: [Full GC (Allocation Failure) 29.998: [Tenured: 1048063K->1048063K(1048064K), 0.7081273 secs] 1048511K->1048461K(1048512K), [Metaspace: 8695K->8695K(1056768K)], 0.7083946 secs] [Times: user=0.70 sys=0.01, real=0.71 secs] 30.707: [Full GC (Allocation Failure) 30.707: [Tenured: 1048063K->1048063K(1048064K), 0.7488220 secs] 1048511K->1048511K(1048512K), [Metaspace: 8593K->8593K(1056768K)], 0.7489386 secs] [Times: user=0.73 sys=0.01, real=0.75 secs] 31.456: [Full GC (Allocation Failure) 31.456: [Tenured: 1048063K->1031386K(1048064K), 0.8560296 secs] 1048511K->1031386K(1048512K), [Metaspace: 8563K->8563K(1056768K)], 0.8560950 secs] [Times: user=0.83 sys=0.01, real=0.85 secs]
用VisualGC看效果可太炫酷了。
JVM学习04-垃圾回收概念与算法相关推荐
- jvm学习——18.垃圾回收之STW
159 Stop The World Stop一the一World,简称STW,指的是GC事件发生过程中,会产生应用程序的停顿.停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这 ...
- JVM分代垃圾回收策略的基础概念
JVM分代垃圾回收策略的基础概念 由于不同对象的生命周期不一样,因此在JVM的垃圾回收策略中有分代这一策略.本文介绍了分代策略的目标,如何分代,以及垃圾回收的触发因素. 文章总结了JVM垃圾回收策略为 ...
- 【JVM】垃圾回收机制及算法
垃圾回收机制及算法 一.垃圾回收概述 二.对象是否存活 1. 判断对象是否存活 - 引用计数算法 2.判断对象是否存活-可达性分析算法 1.可达性分析算法 2.JVM之判断对象是否存活 3.关于引用 ...
- jvm 垃圾回收(常见算法介绍)
1. 什么是垃圾回收? 程序的运行必然申请内存资源,如果无效的对象不清理一直占用资源,那么肯定会导致内存溢出,所以内存资源的管理就很重要了 2. 垃圾回收的常见算法 2.1. 引用计数法 2.1.1 ...
- java知识点8——垃圾回收原理和算法、通用的分代垃圾回收机制、 JVM调优和Full GC、开发中容易造成内存泄露的操作
垃圾回收原理和算法 内存管理 Java的内存管理很大程度指的就是对象的管理,其中包括对象空间的分配和释放. 对象空间的分配:使用new关键字创建对象即可 对象空间的释放:将对象赋值null即可 垃圾回 ...
- 【JVM基础】垃圾回收算法详解(引用计数、标记、清除、压缩、复制)
前言 笔记参考 Java 全栈知识体系.星羽恒.星空茶 文章目录 前言 垃圾回收概述 引用计数法 案例 优点 缺点 标记.清除.压缩 标记 清除 压缩 标记清除算法 优点 缺点 标记压缩算法 优点 缺 ...
- JAVA之JVM分代垃圾回收策略(一)
一.为什么要分代 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大量的对 ...
- GC:垃圾回收机制及算法
GC:垃圾回收机制及算法 关键词 算法:标记(清除/复制/整理).分代收集 收集器(Serial[串行].ParNew[并行].Parallel Scavenge[并行].Serial Old[串行] ...
- java垃圾回收理解与算法
垃圾回收机制概述 Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理.由于有个垃圾回收机制,Ja ...
- a*算法的优缺点_垃圾回收的常见算法
垃圾回收的常见算法 2.1 引用计数法 2.1.1 原理 2.1.2 优缺点 2.2 标记清除法 2.2.1 原理 2.2.2 优缺点 2.3 标记压缩算法 2.3.1 原理 2.3.2 优缺点 2. ...
最新文章
- sql server 2008数据导入Oracle方法
- 如何生成符合高斯分布的数据集
- Mysql允许外网接入
- 【计算机组成原理】计算机软硬件组成
- iOS - OC NSTimeZone		时区
- Caused by: java.lang.IllegalArgumentException: @EnableAsync annotation metadata was not injected
- mysql存储脚本实例_Mysql存储过程中游标的用法实例
- eclipse 配置maven tomcat 环境
- python之sqlalchemy的使用
- VS2010 MVC的 安装
- python 操作psd_python psd
- java开源打印控件_这个WEB打印控件略牛逼,还免费
- 关于悟空CRM部署经历--钟艳明
- 基于python flask的网上商城源码 mysql数据库
- Docker中部署.NET CORE应用(控制台应用程序篇)
- cookie的工作原理、cookie的重要性
- 物联网发展跨越拐点!2020 AIoT产业年终盛典圆满落幕
- python forward函数___call__、__init__和 forward三个函数在python中的调用方法
- python 用cx_Freeze打包程序详细解读setup.py
- 记录ant design vue a-select Form编辑时回显数据库数据为value而不是label的问题
热门文章
- L1-010 比较大小 (10 分)—团体程序设计天梯赛
- SQL Server — 更改数据库名
- ubuntu执行configure配置代码出现unable to guess system type报错
- luogu1984 烧水问题 (找规律)
- 通通玩blend美工(3)——可爱的云
- Silverlight 2.0学习笔记——RIAs的缺点
- VBA中,可以利用下面的语句来调用Excel内置对话框
- Page.IsPostBack
- HTTP基础(图解HTTP笔记)幕布
- Kubernetes详解(二)——Kubernetes结构与资源对象