JVM核心知识讲解

前言

前一阵子在公司内部做了一次技术分享,主要讲的就是JVM核心知识。由于JVM涉及的知识太多太广,所以我就以个人的经验把内容做了一下精简,只保留最核心的内容,并且把核心的内容都给抽出来,让大家记住最重要的部分。现在,我把分享的内容总结出来。(文中如有纰漏,还望您批评指正,谢谢)

什么是JVM?

JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。

Java文件被加载到JVM的过程

Java代码执行过程.png

Java类的加载

1.四大主动引用

  • 调用类的构造方法 new Test();

  • 调用类的静态变量、静态方法已经反射

  • 初始化子类(java规定:初始化子类的时候会先初始化其父类,所以父类会被加载)

  • 含有main方法类会被提前加载

    注意:调用常量不会引起类的加载

2.类的加载过程

未命名文件.png

类加载过程都做好了什么?

  • 准备:给静态变量开辟内存空间,同时设置初始化的值(注意:这里不是代码设置的初始化值,而是静态默认的缺省值,比如 int的默认值是0)

  • 解析:把常量的符号引用改成直接引用(直接引用即为内存地址)

  • 初始化:执行静态变量和静态代码块(当存在多个静态变量和静态代码块的时候,按照从上到下的顺序执行)

总结:当一个类被主动引用的时候,会进行类的加载,类的加载过程中会进行静态变量的初始化和静态代码块的执行,并且会把常量的符号引用替换成直接引用。整个类的加载过程网上一搜有一大堆,黑子加粗的是需要大家必须知道和记住。

为什么静态方法中不能引入非静态变量?

答:当类被加载的时候,非静态变量还没有得到初始化,而静态方法不需要拥有对象实例就可以得到执行,所以静态方法中不能引用非静态变量。

为什么可以使用 private static final Singleton instance = new Singleton();这种方式做单例模式?

答:因为类只会被初始化加载一次,当类被加载的时候静态变量会得到初始化,Java的枚举类就是利用这种方式,里面的枚举类型的数据就是静态常量

类加载器

  • 启动类加载器: 负责加载存放在<JAVA_HOME>\lib目录中的类;被-Xbootclasspath参数所指定路径中、并且是被虚拟机识别的类库

  • 扩展类加载器:负责加载<JAVA_HOME>\lib\ext目录中的类库;被java.ext.dirs系统变量所指定的路径中的所有类库

  • 应用类加载器:负责加载 用户类路径(ClassPath)上所指定的类库(如无特殊指定,我们的写的在项目里写的java代码都是被这个类加载器进行加载的)

    类加载器.png

4.双亲委派模型

当一个类被加载的时候,所有的类加载器都会把加载任务交给父加载器,结合上这张图来看,也就说双亲委派模型最后都会交给启动类加载器进行加载,只有当父类加载器加载不了的时候才会交给子类进行加载。

protected Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
        synchronized(this.getClassLoadingLock(var1)) {
            Class var4 = this.findLoadedClass(var1);
            if (var4 == null) {
                long var5 = System.nanoTime();

                try {
                    if (this.parent != null) {
                        var4 = this.parent.loadClass(var1, false);//先让父类进行加载
                    } else {
                        var4 = this.findBootstrapClassOrNull(var1);//当parent为null的时候使用启动类加载器加载
                    }
                } catch (ClassNotFoundException var10) {
                }

                if (var4 == null) {
                    long var7 = System.nanoTime();
                    var4 = this.findClass(var1);//当父类加载不了的时候才会交给子类进行加载
                    PerfCounter.getParentDelegationTime().addTime(var7 - var5);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(var7);
                    PerfCounter.getFindClasses().increment();
                }
            }

            if (var2) {
                this.resolveClass(var4);
            }

            return var4;
        }
    }

优点:保证了类的唯一性

注意:双亲委派模型是java推荐的一种技术模型,不是自定义ClassLoader加载器必须遵守的,另外这里的双亲指的就是父类,别问我我也不知道为啥非要叫双亲

JVM内存结构

运行时数据区

JVM内存结构.png

JVM的运行时数据区主要5个部分组成,分别是方法区、程序计数器、方法栈、本地方法栈以及堆内存。其中用红色标注的堆内存和方法区是线程共享,剩下的三个是线程私有的。

方法区

方法区,又叫永久代,jdk1.6以后也叫元空间。是JVM内存区域中比较稳定的一块,很少发生GC。在方法区中存放类信息、静态变量以及常量等信息。方法区是线程共享的,存在线程安全问题。

栈内存

栈内存结构.png

也叫线程栈,每一个线程都有线程栈,每一个方法都是一个栈帧,在方法从被调用到结束的过程就是对应栈帧入栈以及出栈的过程。在栈帧中又有4个区域

  • 局部变量表:存放方法中的局部变量
  • 操作数栈:执行方法中的各种操作,例如:赋值之类的操作
  • 动态链接:把方法中的符号引用转化为直接引用(直接引用即内存地址)
  • 方法出口:记录返回值以
    及程序当前执行的位置

本地方法栈

同方法栈类似,存放的是本地调用方法(即native修饰的,如public static native void yield();

程序计数器

线程私有,每个线程会有一个区域存放程序计数器

作用:记录所在线程执行到了哪一步

场景:当某个所有的线程的时间片被抢走时,过了一段时间再被恢复以后,该线程的代码不会从头执行,而是会按照程序计数器所记录的位置执行

堆内存

运行时数据区中栈内存最大,线程共线,是主要发生GC的位置,存放的是Java实例对象,也就是说所有通过new的对象都存在这里。在堆内存中,有划分了成了新生代和老年代,默认比例1:2,在新生代中又划分为了eden、sur0和sur1,默认的比例是8:1:1,所有刚new的出来的对象都会放到eden区域中。


堆内存.png

总结

JVM内存结构是非常重要的一个部分,有几个关键的点一定要记住:

  • 所有new的对象一定存在堆内存中

  • 非静态成员变量存在堆内存中,局部变量都在栈内存中

    通过代码再来加固一下

public class Test {

    public static User user = new User();//static修饰 user这个实例存在方法区 new User()生成的对象在堆区,此时user引用了堆区的对象

    private int a = 10;//a存在了堆区,引用了栈里面的整型变量10
    
    private Date date = new Date();//date存在堆区,new Date()生成的对象也在堆区,date引用了生成的对象

    private int compute() {
        int a = 1;
        int b = 2;
        User user = new User();//注意此时user是局部变量,所以存在栈区,而new User()生成的对象在堆区,此时栈区的user引用了堆区生成的对象
        int c = (a + b) * 10;
        int d = com();
        return c;
    }

    public static void main(String[] args) {
        Test test = new Test();
        System.out.println(test.compute());
    }
    
    }

垃圾回收

垃圾回收即GC,刚才讲堆内存的时候,提到了堆内存是存放的java对象,是主要发生GC的位置。

垃圾回收.png

这里需要注意的是,垃圾回收器在判断一个对象是否存活时用的是可达性分析法。可达性分析法是判断一个对象有没有被GC ROOT直接或者间接持有,只要被GC ROOT直接或者间接持有,那么该对象就是存活状态,或者即为死亡。

GC ROOT:

1.方法栈(局部变量表)中引用的对象
2.本地方法栈 中 JNI引用的对象
3.方法区 中常量、类静态属性引用的对象

总结

本文主要讲了类的加载过程、JVM的内存结构以及垃圾回收。由于我个人读过很多JVM的文章都写得太多太详尽,反正读多了容易掌握不到重点,而本文就是对核心知识一个总结,虽然不够详尽,但是无论是工作还是面试当中应该都够用了。如果文章有错误的内容或者漏讲了一些重要的知识点,欢迎大家在评论区给我留言,谢谢~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343