简单介绍GC

前言

上篇说到Handler的内存泄露,其实是因为主线程作为GC Root间接引用到了Handler。今天就来简单介绍下JVM中的GC,垃圾回收。

GC

GC,全程Garbage Collection,垃圾回收。

垃圾,就是内存中没有用的对象。

在Java中,我们无需手动释放内存,在JVM中有垃圾回收器自动帮我们回收。

JVM中如何决定对象是否可以回收

JVM中通过可达性分析算法来决定对象是否可以回收。

具体做法就是把内存中所有对象之间的引用关系看做一条关系链,

比如A持有B的引用,B持有C的引用。而在JVM中有一组对象作为GC Root,也就是根节点,然后从这些节点开始往下搜索,查看引用链,最后判断对象的引用链是否可达来决定对象是否可以被回收。

为了方便大家理解,我画了一张图来说明:

很明显,ABCD四个引用都是GCRoot可达的,通俗点讲,就是跟GCRoot直接或间接有关系,有线连着的。而EF虽然直接连着线,但是他们和GCRoot是没关系的,也就是GCRoot不可达的对象组。

所以当GC发生的时候,EF就会被回收。

GC发生的内存区域

在说GC发生的内存区域之前,我们先聊聊JVM中的内存分配。

在JVM中,主要有内存分成了五个数据区域:

  • 程序计数器:线程私有,主要用作记录当前线程执行的位置。
  • 虚拟机栈:线程私有,描述Java方法执行的内存模型。
  • 本地方法栈:线程私有,描述本地(native)方法执行的内存模型。
  • :存放对象实例。
  • 方法区:存放类信息、常量、静态变量等

通过上面的介绍,我们了解到前三个都是线程私有,所以会随着线程的死亡而消失。

而后面两块内存区域,也就是堆和方法区是所有线程共有的,如果不处理可能内存就会一直增长,直到超出可用内存。所以需要借助GC机制对这些区域内的无用内存进行回收,特别是堆区的内存,因为堆区就是存储对象实例的。

GC发生的时机

那具体什么时候会被回收呢?主要有两种情况:

  • 在堆内存中分配时,如果因为可用剩余空间不足导致对象内存分配失败,这时系统会触发一次 GC。
  • 在应用层,开发者可以调用System.gc()来请求一次 GC。

GCRoot的类型

刚才说过了可达性分析算法,所以大家应该知道GCRoot的重要性了。

GCRoot,说白了就是JVM认证的可以作为老大的人选,只有这些对象是可以作为引用链的头头,掌管并保护着有用的引用。

在Java中,有以下几种对象可以被作为GCRoot,这些对象是不会被GC的:

  • Java 虚拟机栈(局部变量表)中的引用的对象。

这里又涉及到一个问题了,什么是局部变量表。

刚才说过虚拟机栈是用于支持方法调用或者执行的数据结构,具体是怎么操作的呢?

当某个方法被执行,就会在虚拟机栈中创建一个栈帧,也就是一个方法就对应着一个栈帧,栈帧会管理方法调用和执行所有的数据结构。

而栈帧中又分为几块存储空间,进行存储方法对应的不同的数据结构,比如局部变量表就是用于存储方法参数和方法内创建的局部变量。

所以这第一个GC Root 指得就是方法的参数或者方法中创建的参数。

public class GCTest {
    public static void test1(){
        //局部变量作为GCRoot
        GCRoot root=new GCRoot();
        System.gc();
    }
}

顺便说下栈帧中其他几个内存结构:

  • 局部变量表:存储方法参数和方法内创建的局部变量
  • 操作数栈:后入先出栈。当方法执行过程中,就会通过操作数栈来进行参数传递,又或者进行加数
  • 动态连接:支持方法调用过程中的动态连接。
  • 返回地址:在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态,而这个返回地址区域就是用于存储返回地址信息的。一般方法正常退出时,是可以将调用者的PC计数器值作为返回地址。
  • 方法区中静态引用指向的对象。

这个很好理解,指得就是静态变量。

public class GCTest {
    private static GCRoot root2;
    public static void main(String[] args) {
        //静态变量作为GCRoot
        root2=new GCRoot();
        System.gc();        
    }
}
  • 仍处于存活状态中的线程对象。

活着的线程,比如主线程,上一篇文章就说过Handler内存泄露的原因就是被主线程所引用,所以无法被回收。

 Thread root3=new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    public void test2(){
        //活着的线程作为GCRoot
        root3.start();
        System.gc();
    }
  • Native 方法中 JNI 引用的对象。

在JNI中有如下三种引用类型可供使用:

  • 局部引用
  • 全局引用
  • 弱全局引用

其中局部引用和全局引用都可以作为GC Root,不会被GC回收。

总结

周末愉快~

参考

https://kaiwu.lagou.com/course/courseInfo.htm?courseId=67#/detail/pc?id=1856

感谢大家的阅读,有一起学习的小伙伴可以关注下公众号—码上积木❤️ 每日一个知识点,建立完整体系架构。

本文分享自微信公众号 - 码上积木(Lzjimu),作者:积木zz

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-03-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Clickjacking简单介绍

    今天没有原创文章发了,从乌云知识库里选了一个文章给大家分享一下,不知道这种方式,大家是否能够接我从乌云知识库里选择一些文章给大家分享,请大家给我提出来,我来根据...

    信安之路
  • SSM简单介绍

    SSM:Struts、Spring、Mybatis SSM三层集成框架系统总体设计:模块划分、数据库表,存储过程

    一点儿也不潇洒
  • tmux 简单介绍

    marsggbo
  • TensorFlow简单介绍

    TensorFlow深度学习框架 Google不仅是大数据和云计算的领导者,在机器学习和深度学习上也有很好的实践和积累,在2015年年底开源了内部使用的深度学习...

    IT派
  • PyPy简单介绍

    总结了这段时间在PyPy上的折腾,早上给同事分享了一下,不过关于PyPy里面还有很多东西需要去理解。这里先把简单介绍版拿出来,其实是做成一个html5的ppt的...

    the5fire
  • MapReduce简单介绍

    mapReduce是一个计算框架,是指实现某项任务或某项工作从开始到结束的计算过程或流的结构

    用户4870038
  • JavaScript简单介绍

    基于事件和对象驱动,并具有安全性的语言。 该语言运行在浏览器端,也有在服务器端的名称为Node.js

    河湾欢儿
  • Linux简单介绍

    mwangblog
  • Istio简单介绍

    Istio是一个开放平台,提供统一的方式来集成微服务,管理跨微服务的流量,执行策略和汇总遥测数据。Istio的控制面板在底层集群管理平台(如Kubernetes...

    dogfei
  • ESP8266简单介绍

    是面向物联网应用的高性价比、高度集成的 Wi-Fi MCU。MCU是为控制单元的意思。

    挥刀北上
  • Fortinet简单介绍

    以前对飞塔的防火墙也有一些了解,但是那时候并没有现在的地位和技术优势。现在的Fortinet在Gartner魔力象限中已在leader中了。

    刘銮奕
  • vuejs简单介绍

    使用vue编写网页是一个让人愉悦的过程,它同时具备angular和react的优点,轻量级,api简单,文档齐全,简单强大,麻雀虽小五脏俱全.

    javascript.shop
  • Java-Parallel GC介绍

    http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/rev/24cae3e4cbaa

    入门小站
  • iOS CALayer 简单介绍

    总结:能看到的都是uiview,uiview能显示在屏幕上是因为它内部的一个层calyer层。

    用户1219438
  • 简单介绍下modJS

    近期把团队的项目构建迁移到fis3,同时把模块加载器也由之前的requirejs切换到了modJS。 有些同学可能不了解modJS,这里做个简单的介绍。 那么,...

    IMWeb前端团队
  • RethinkDB的简单介绍

    RethinkDB最早是作为一个对SSD进行专门优化的MySQL存储引擎出现的,其特点在于对SSD的充分利用。而目前RethinkDB已经脱离MySQL成为一个...

    Debian中国
  • C++/CLI 简单介绍

    托管的C++(MC++)饱受诟病的一个地方就是语法格式和普通的编程语言差别很大,很多人都评价为: ugly 和 twisted 语法。

    田春峰-JCJC错别字检测
  • 简单介绍 protocol buffer

    protocol buffer protocol buffer 是谷歌的一款序列化结构数据的工具. 它有几个核心的概念: .proto文件: 定义proto...

    ke1th
  • Git简单介绍(一)

    Git是目前世界上最先进的开源的分布式版本控制系统(没有之一),用于敏捷高效地处理任何或小或大的项目。

    程序猿小亮

扫码关注云+社区

领取腾讯云代金券