我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JVM类加载,垃圾回收

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JVM类加载,垃圾回收

类加载子系统

classLoader 只负责对字节码文件的加载,至于是否可以运行,还要看执行引擎。

在这里插入图片描述

  • 加载的类信息存放于方法区的内存空间,除了类信息之外,还会存放有运行时常量池的信息,还可能包含字符串字面量和数字常量。

loading加载:

通过一个类的全限定名获得定义此类的二进制字节流。也就是根据完整的路径找到对应类的二进制字节流。

将这个字节流所代表的静态的数据结构转化为方法区运行时的数据结构

  • 在内存中生成一个代表这个类的Java.lang.Class对象,作为方法区这个方法数据的入口。

链接:验证、准备、解析

验证:目的在于确保二进制字节流包含的信息符合虚拟机的要求,保证被加载类的正确性,不会危害虚拟机的安全。

验证文件格式、字节码、元数据、符号引用的验证

准备:为类变量分配内存并设置默认的初始值,即零值。

不包含被final修饰的类变量

解析:将常量池的符号转化为直接引用的过程。

初始化:JVM将程序的执行权交给程序。

双亲委派模型

双亲委派模型的原理:如果一个类加载器收到了类加载的请求的话,它首先不会自己去尝试加载这个类,而是把这个请求委派给自己的父类加载器去完成,每一层的加载器都是如此,因此所有的加载请求都会传送到最顶层的启动类加载器中,只有当父类加载器反馈自己无法加载这个请求的时候,即父类搜索不到这个类的时候,子类才会自己尝试去加载。


 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

在这里插入图片描述

使用双亲委派模型来组织类加载之间的关系,一个显而易见的好处就是Java中类随着它的类加载器一起具备了一种带有优先级的层级关系。因此Object类在程序中的各种类加载的环境中都能保证是同一个类。反之,如果没有双亲委派模型的话,都由各个的类加载器去加载的话,如果用户自定义了一个java.lang.Object的类,并放在ClassPath 中的话,就会出现多个Object类,Java中最基础的体系也就会无法保证。

破坏双亲委派模型:

1.双亲委派模型被破坏的第一次就是刚引入双亲委派模型的时候,是为了兼容JDK1.2之前的代码

2.双亲委派模型的第二次的破坏就是自生的缺陷导致的。但发生父类调用子类的时候。

3.第三次就是用户对于程序动态性的追求而导致的:代码热的替换、模块热的部署。

垃圾回收

什么是垃圾?

GC 中的垃圾就是特指在内存中的、不会在被使用的对象。

判断对象已死

引用计数器法:

每个对象添加一个引用计数器,每被引用一次,计数器加1,失去引用,计数器减1,当计数器在一段时间内保持为0时,该对象就认为是可以被回收得了。(在JDK1.2之前,使用的是该算法)

缺点:当两个对象A、B相互引用的时候,当其他所有的引用都消失之后,A和B还有一个相互引用,此时计数器各为1,而实际上这两个对象都已经没有额外的引用了,已经是垃圾了。但是却不会被回收,引用计数器法不能解决循环引用的问题。

JVM垃圾回收采用的是可达性分析算法:

在这里插入图片描述

从GC set中的GC roots 作为起点,从这些节点向下搜索,搜索的路径被称为引用链,如果一个对象不存在引用链的话,那么说明这个对象已死。就会被GC回收器回收。

GCroots 是:

1.来自于JVM栈中引用的对象。比如各个线程中被调用的方法堆栈中使用到的参数、局部变量、临时变量等。

2.方法区中静态属性引用的对象。比如Java中的引用类型静态变量。

3.方法区中常量引用的对象。比如字符串常量池(String Table)中的引用。

4.本地方法栈中引用的对象。

5.Java虚拟机内部的引用。比如基本数据类型对应的Class对象,一些常驻的异常类等,还有系统的类加载器。

6.所有被同步锁持有的对象。 …

并不是所有的引用对象一定就会被GC 的时候回收掉。

JDK1.2之后的四种引用类型:

1.强引用:

就是程序中一般使用的引用类型,即使发生OOM也不会回收的对象。

就是为刚被new出来的对象所加的引用,它的特点就是,永远不会被GC,除非显示的设置null,才会GC。

比如:


package Reference;

public class Test {
    public static void main(String[] args) {
        StringBuilder stringBuilder = new StringBuilder("I am FinalReference");
        System.gc();
        System.out.println(stringBuilder);
        //触发GC
        byte[] bytes = new byte[1024 * 940 * 7];
        System.gc();
        System.out.println(stringBuilder);
        try {
            byte[] bytes2 = new byte[1024 * 1024 * 7];
        } catch (Exception e) {
        } finally {
            System.out.println("发生了OOM:");
            System.out.println(stringBuilder);
        }
    }
}

在这里插入图片描述

2.软引用:

就是用来描述一些还有用,但是非必须的对象,只被软引用关联着的对象,在系统将要发生OOM前会回收的对象。如果这次回收还没有足够的内存,才会抛出OOM的异常。


package Reference;

public class SoftReference {
    public static class User {
        private int id;
        private String name;
        User(int id, String name) {
            this.id = id;
            this.name = name;
        }
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    public static void main(String[] args) {
        //user 是强引用
        User user = new User(1001, "小明");
        //softReference 是软引用
        java.lang.ref.SoftReference<User> softReference = new java.lang.ref.SoftReference<>(user);
        //  显示的将强引用置为null
        user = null;
        System.out.println(softReference.get());
        System.gc();
        System.out.println("After GC: ");
        System.out.println(softReference.get());
        //触发GC
        byte[] bytes = new byte[1024 * 940 * 7];
        System.gc();
        System.out.println(softReference.get());
    }
}

在这里插入图片描述

3.弱引用:

弱引用也是用来描述那些非必要的对象,它的强度比软引用还低一些。当垃圾回收器开始工作的时候,无论内存是否够用,都会回收掉只被弱引用关联着的对象。


    public static void main(String[] args) {
        User user = new User(1002,"小明");
        java.lang.ref.WeakReference<User> weakReference = new java.lang.ref.WeakReference<>(user);
        user = null;
        System.out.println(weakReference.get());
        System.gc();
        System.out.println("After GC");
        System.out.println(weakReference.get());
    }

在这里插入图片描述

4.虚引用:

它是最弱的一种引用关系。刚被创建就会被GC回收器回收。它的价值就是在GC 的时候触发一次方法的回调。

虚拟机中的并行与并发:

并行:

并行描述的是多条垃圾收集器线程之间的关系,说明同一时间有多条这样的线程在同时的进行工作。

并发:

并发描述的是垃圾收集器线程与用户线程之间的关系,说明同一时间垃圾收集器的线程和用户线程之家同时在运行。

常见的垃圾回收算法:

1.标记–清除算法:(Mark–Sweep)

将死亡的对象标记,然后进行GC。

在这里插入图片描述

执行的效率不稳定。如果堆中有大量的对象,其中有大部分都是要被回收的话 ,那么必需要进行大量的标记–清除的步骤,导致执行效率的降低。

会造成内存碎片的问题,使空间的利用率降低。标记–清除之后会产生大量的不连续的内存碎片。

2.标记–复制算法:

将空间分为两部分,一部分始终是未使用的状态。当进行垃圾回收的时候

将存活的对象复制到未使用的空间上,然后将另一半的区域进行全GC。

在这里插入图片描述

但是标记–复制算法在对象存活率比较高的时候就要进行多次的复制操作,效率会降低。而且每次只有50%的内存空间被使用。

3.标记–整理算法:

将存活的对象进行移动,然后进行GC。

对象的移动需要STW

在这里插入图片描述

解决了垃圾碎片的问题

常见的垃圾回收器:

在这里插入图片描述

  • Serial/Serial Old收集器 是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。
  • ParNew收集器 是Serial收集器的多线程版本,使用多个线程进行垃圾收集。
  • Parallel Scavenge收集器 是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。
  • Parallel Old收集器 是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact算法。
  • CMS(Concurrent Mark Sweep)收集器 是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。
  • G1收集器 是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

新时代、老年代

Java 中的堆也是 GC 收集垃圾的主要区域。GC 分为两种:Minor GC、FullGC ( 或称为 Major GC )。 Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法。新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。

当一个对象被判定为 “死亡” 的时候,GC就有责任来回收掉这部分对象的内存空间。新生代是GC收集垃圾的频繁区域。当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 也就是经历15次GC之后还存活的对象),这些对象就会被移动到老年代。

但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法。

另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

为什么大对象会直接存在老年代?

大对象的创建和销毁随需要消耗的时间比较多,所以性能也比较满,如果存到新生代的话,那么有可能频繁的创建和销毁大对象,导致JVM对的运行的效率变低,所以直接存放在老年代。

在这里插入图片描述

新生代的各个区域的占比分别是:8:1:1 新生代与老年代的占比是:1:2

在这里插入图片描述

总结

本篇文章就到里了,希望能帮到你,也希望您能多多关注编程网的更多内容!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JVM类加载,垃圾回收

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

JVM垃圾回收器详解

这篇文章主要介绍“JVM垃圾回收器详解”,在日常操作中,相信很多人在JVM垃圾回收器详解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JVM垃圾回收器详解”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!1
2023-06-02

JVM基本垃圾回收算法

这篇文章主要介绍“JVM基本垃圾回收算法”,在日常操作中,相信很多人在JVM基本垃圾回收算法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JVM基本垃圾回收算法”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-06-17

JVM垃圾回收器有哪些

这篇文章主要介绍“JVM垃圾回收器有哪些”,在日常操作中,相信很多人在JVM垃圾回收器有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JVM垃圾回收器有哪些”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-06-05

JVM垃圾回收器是什么

这篇文章主要讲解了“JVM垃圾回收器是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JVM垃圾回收器是什么”吧!并发与并行并行(Parallel):并行描述的是多条垃圾收集器线程之间的关
2023-07-02

有哪些jvm垃圾回收算法

这篇文章将为大家详细讲解有关有哪些jvm垃圾回收算法,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。jvm垃圾回收算法:1、“标记–清除”算法;首先标记出所有需要被回收的对象,然后在标记完成后
2023-06-14

浅谈一下JVM垃圾回收算法

这篇文章主要介绍了一下JVM垃圾回收算法,Java有着自己一套的内存管理机制,不需要开发者去手动释放内存,开发者只需要写好代码即可,运行过程中产生的垃圾都由JVM回收,需要的朋友可以参考下
2023-05-18

JVM垃圾回收机制有什么用

这篇文章主要介绍JVM垃圾回收机制有什么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.JVM的gc概述gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有
2023-06-17

jvm回收垃圾的机制是什么

JVM(Java虚拟机)使用自动垃圾回收(Garbage Collection)机制来管理和回收不再使用的对象的内存。以下是JVM垃圾回收的机制:1. 引用计数:这是一种最简单的垃圾回收机制,它通过对每个对象维护一个引用计数器来记录当前有多
2023-08-30

JVM回收跨代垃圾的方式详解

Java虚拟机(JVM)采用分代垃圾回收机制,包括新生代和老年代。跨代垃圾回收涉及将新生代中的对象晋升到老年代的过程。新生代是垃圾回收最频繁的区域,分为伊甸花园区和幸存者区。当伊甸花园区已满时,触发MinorGC,将可达对象复制到幸存者区。经历一定次数MinorGC后,仍存活的对象晋升到老年代。老年代的垃圾回收不那么频繁,但需要更多时间。跨代垃圾回收受到对象生存模式、分配速率、老年代大小和垃圾回收器配置的影响。优化跨代回收可以通过减少对象分配、调整幸存区和老年代大小以及选择合适的垃圾回收器来实现。
JVM回收跨代垃圾的方式详解
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录