0
点赞
收藏
分享

微信扫一扫

JVM学习笔记总结

前端王祖蓝 2023-07-19 阅读 80
javajvm

目录

JVM内存区域划分

 1、堆(线程共享)

2、方法区(线程共享)

3、栈(线程私有)

4、程序计数器(线程私有)

JVM类加载机制

加载

验证

准备

解析

初始化

双亲委派模型

JVM垃圾回收机制(GC)

1、寻找

引用计数法(Python、PHP采用)

可达性分析(Java采用)

2、释放

标记清除

复制算法

标记整理

分代回收


此篇学习笔记总结包括了3个方面的知识点:

1、JVM内存区域划分

2、JVM类加载机制

3、JVM垃圾回收机制

后面就将重点来介绍这3个方面的知识收获


JVM内存区域划分

JVM内存区域划分可以分为四部分:方法区(元数据区)程序计数器

 1、堆(线程共享)

堆的作用:程序中创建的所有对象都在保存在堆中,即new  出来的所有对象(全局变量、成员方法)

堆是JVM 中最大的内存区域

2、方法区(线程共享)

方法区的作用:用来存储被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等数据,即类加载之后的类对象(静态变量、静态方法)

3、栈(线程私有)

栈的作用:维护方法之间的调用关系(局部变量)

栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中

4、程序计数器(线程私有)

程序计数器的作用:用来记录当前线程执行的行号的。 程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。

举例:

public class test {
    public int a; // 全局变量(也可叫做test这个类的成员变量)
    public static int b; // 静态变量
    public void method() {
        int c = 0; // 局部变量
        System.out.println("这是test这个类的一个普通成员方法");
    }
    public static void run() {
        System.out.println("这是一个静态方法!"); // 静态方法不需要借助实例化对象
    }
    public static void main(String[] args) {
        test test1 = new test(); // test1 与 test2 是引用类型,也是局部变量
        test test2 = new test(); // new test是一个实例化对象
    }
}

堆中存放的有:全局变量a、成员方法method、new test()

栈中存放的有:局部变量c、test1、test2

方法区中存放的有:静态变量b、静态方法run

JVM类加载机制

类加载可以理解为把.class文件从文件 (硬盘) 被加载到内存 (元数据区) 的过程

加载

在寻找.class文件的过程中,需要使用到双亲委派模型来寻找,双亲委派模型在下面文章讲解

验证

准备

解析

具体解析过程如下:

 

初始化

双亲委派模型

双亲委派模型的作用就是在进行加载过程中,找.class文件这个过程。

在JVM加载的过程中需要使用到类加载器,JVM里面内置了三个类加载器:

它们三者关系如下:

双亲委派模型的原理:当我们需要去加载一个具体类时,不会去扫描自己的目录,而是优先去扫描父类加载器,直到扫描到最后一个父类加载器。最后一个父类加载器若扫描到具体类就进行后续加载步骤,若没有扫描到就交给自己孩子来处理。

举例:若我们想要去加载"java.lang.String"这个类时,扫描过程如下:

 

双亲委派模型也是可以打破~~

你自己实现的类加载器,可以继续遵守~~也可以不遵守~~(Tomcat里针对webapp的类加载器就没遵守)

JVM垃圾回收机制(GC)

在JVM 内存区域中,

1、栈随着线程一起销毁,当方法调用完毕,方法的局部变量自然随着出栈操作就销毁了

2、程序计数器就是一个单纯存地址的整数,也是随着线程一起销毁

3、方法区(元数据区)存放的类对象,很少会卸载

4、堆(new 出来的对象)存放的是全局变量,因此GC回收的目标主要是堆,GC是以 对象 为单位进行释放


JVM垃圾回收机制主要分为2个阶段:

1、寻找:谁是垃圾

2、释放:把垃圾对象的内存给释放掉


1、寻找

一个对象判断是否是垃圾,只需要判断其是否有引用指向对象即可,若无引用指向对象,则该对象可以被视认为垃圾。

在垃圾回收机制中,对于如何判断一个对象是否有引用指向主要有2种方法:

1.引用计数法  (Python 、 PHP编程语言采用的这种方法)

2.可达性分析  (Java 采用的此方法)

引用计数法(Python、PHP采用)

引用计数法就是在对象里面安排一个额外的空间,保存一个整数,来记录指向该对象的引用个数。每个对象都有一个单独的计数器,不是每一个类独有。

若引用个数为0,说明该对象没有引用指向就可以便认为是垃圾

随着引用的增加,计数器就随着增加;引用的销毁,计数器就随着减少

当计数器为0时,则认为该对象没有引用了,可以当做垃圾进行回收

 

 

可达性分析(Java采用)

拿二叉树来举例,通过根节点root可以访问到整棵树的任意节点。

        可达性分析,总的来说就是从所有的起点出发,看看该对象里又通过引用能访问到哪些对象,顺藤摸瓜把所有可访问到达的对象都遍历一遍(遍历的同时把对象标记成“可达”),剩下的自然就是“不可达的”。

        可达性分析克服了引用计数的两个缺点,但也有自己的问题:

1、消耗更多的时间,因此某个对象成了垃圾,也不一定能第一时间发现,因为在扫描过程中是需要消耗时间的

2、在进行可达性分析的时候,要顺藤摸瓜,一旦在这个过程中,当前代码中的对象的引用关系发生了变化,就十分麻烦(于是,为了更准确的完成这个顺藤摸瓜的过程,就需要让其他业务线程暂停工作,此时便引来STW问题)

2、释放

标记清除

复制算法

        

        复制算法的缺点:复制算法虽然解决了内存碎片问题,但也造成了如下问题:

                                1、内存利用率比较低

                                2、如果当前大部分对象都是需要保留的,只有少部分垃圾,此时复制的层本比较高

标记整理

         

分代回收

 

 

 

 

举报

相关推荐

0 条评论