面试官:别的我不管,这个JVM虚拟机内存模型你必须知道

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

}

public int compute(){
int a = 1;
int b = 2;
int c = (a + b) * 10;
return c;
}
}

对生成的class文件进行反编译,生成对应的JVM指令码:

使用javap -c StackDemo.class命令,表示将这个class文件反编译并将反编译,并直接输出指令码到控制台。

public class com.jdc.demo.StackDemo {
public com.jdc.demo.StackDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.“”😦)V
4: return

public static void main(java.lang.String[]);
Code:
0: new #2 // class com/jdc/demo/StackDemo
3: dup
4: invokespecial #3 // Method “”😦)V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method compute:()I
12: istore_2
13: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #6 // class java/lang/StringBuilder
19: dup
20: invokespecial #7 // Method java/lang/StringBuilder.“”😦)V
23: ldc #8 // String 计算之后结果是:
25: invokevirtual #9 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: iload_2
29: invokevirtual #10 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
32: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: return

public int compute();
Code:
0: iconst_1 //将int类型的常量1压入到操作数栈中。
1: istore_1 //将int类型值 存入到局部变量1 这里指的是a
2: iconst_2 //将int类型的常量2压入到操作数栈中。
3: istore_2 //将int类型值 存入到局部变量2 这里指的是b
4: iload_1 //从局部变量1中装载int类型值入栈。
5: iload_2 //从局部变量2中装载int类型值入栈。
6: iadd //将栈顶的两个int类型数相加,结果重新入栈。
7: bipush 10 //往栈中压入10
9: imul //将栈顶的两个int类型数相乘,结果重新入栈。
10: istore_3 //将int类型值 存入到局部变量3 这里指的是c
11: iload_3 //从局部变量3©中装载int类型值入栈。(是为了return)
12: ireturn //返回
}

不难看出这一段中就是我们刚才写的简单的StackDemo类,可以通过查Oracle官方提供的指令码解析。网上也有很多。这里我已将compute()方法用到的指令意思标明到注解中。 看一下compute()方法的注释中的执行过程。

再来结合图看看执行过程:

栈的数据结构:FILO
上述图中main线程中有两个方法,main()方法和一个负责计算数字的compute()方法,具体执行过程如下:

  1. main线程启动之后,先执行main()方法,执行main()方法的时候给它在栈空间中开辟出一块新的空间(栈帧)。
  2. main()方法内部的局部变量的创建就是在这个栈帧中存放。但是要注意的是这里的局部变量是对象的话,它的值并不是存放在局部变量表中,而是在堆中存放具体,这里指向堆中对应的地址,之前第一节的图一中可以看出,栈中有很多指向堆的对象引用。
  3. 等main()方法执行到调用compute()方法的那一步,线程调起compute()方法这时候compute()方法进栈,同样为它分配一块栈帧存放它自己的局部变量。
  4. 等这个compute()方法执行完成自己的逻辑,就退出整个栈compute()方法出栈
  5. 这个时候返回并继续执行main()方法中接下来的操作。

局部变量表

如上节图中以及执行过程中可以看出,局部变量表和操作数栈配合完成对数据处理的操作。 比如int a=1, 在指令码中分为三步:

  • 把这个1就是先放入操作数栈,
  • 同时给a这个变量在局部变量表中申请了一小块地方来存放。
  • 然后将1从操作数出栈,并赋值给局部变量表中的a,完成赋值操作。

操作数栈

如栈那一节图中以及执行过程中可以看出,局部变量表和操作数栈配合完成对数据处理的操作。 比如int a=1, 在指令码中分为三步:

  • 把这个1就是先放入操作数栈,
  • 同时给a这个变量在局部变量表中申请了一小块地方来存放。
  • 然后将1从操作数出栈,并赋值给局部变量表中的a,完成赋值操作。

动态链接

动态链接:方法是存放在方法区中的,方法加载到方法区的对应的入口内存地址(其他方法调用的时候)通过动态链接就可以很方便的知道对应方法的代码在方法区内的地址。

方法出口

compute()方法执行完之后返回到main()方法中,这个时候继续从main()方法中调用compute()方法的下一句开始执行,而不是重新从main()方法的第一句开始执行,这个就是方法出口。

程序计数器

程序计数器,很简单但是又很重要的一个设计。

每个线程开启都会有一个程序计数器,如上和栈帧章节中生成的jvm指令码,最左边有数字0 1 2…,这个值就是给程序计数器的。

为什么要设计程序计数器?

程序计数器作用:因为Java是多线程执行,所以就存在线程优先级高低之分,如果在这个线程执行过程中,有一个优先级更高的线程过来抢占CPU资源,等优先级高的那个线程执行完成之后,再将CPU资源还给当前线程,当前线程就是通过程序计数器才能知道目前它执行到哪一步。

堆内存

堆内存是最重要也是最复杂的一部分,这里面不仅要负责创建新的对象,还要负责gc,判断一个系统性能的重要指标之一就是程序员对堆内存的管理。因为大部分的JVM调优都会提到堆内存。

先介绍一下堆内存的构成:

1.创建对象都会放在年轻代的Eden区,当Eden区对象放满之后,这时候虚拟机会进行gc,但是这里的gc并不是full gc,而是minor gc,就是只清理年轻代的对象,而不管老年区的对象,这时候就要提一下GCRoots根节点(线程栈中的本地变量,静态变量,本地方法栈的变量等),当需要进行gc之前,jvm会根据Eden中的每一个GCRoots根节点去找它底下的引用,一层一层往下找,直到找到最后一个对象没有其他引用,这时候虚拟机会将这整个过程中的所有对象看做是非垃圾对象。

2.在gc的时候会将这些非垃圾对象赋值到S1区,然后将Eden区中剩余的没有引用的垃圾对象清理,清理完成之后,Eden区空出来了,有用的对象现在都存放在S1中,然后将S1和S2替换(之前是Eden和S1配合,目下一次的gc就是Eden和S2配合)。第二次进行gc的时候,会对Eden区和刚才的S1区进行和第一次gc同样的操作…每次gc之后,活下来的对象年龄会+1,进行一定次数的gc之后,也就是说这个幸存者对象年龄达到足够大,这时候虚拟机会将它放入老年代。------->使用java自带工具查看jvisualvm

3.当老年区装满之后,jvm会进行一次非常耗时的full gc,这个时候程序整个是没办法继续进行的,当full gc完了之后,如果顺利,程序将继续执行,只是性能上有一些损耗,因为常说的jvm调优说白了就是减少gc次数和减少每次gc时间(可以设置初始堆大小。。。等等),如果目前老年代中的对象还都是非垃圾对象,那么就会出现OOM内存溢出。

Ending

Tip:由于文章篇幅有限制,下面还有20个关于MySQL的问题,我都复盘整理成一份pdf文档了,后面的内容我就把剩下的问题的目录展示给大家看一下

如果觉得有帮助不妨【转发+点赞+关注】支持我,后续会为大家带来更多的技术类文章以及学习类文章!(阿里对MySQL底层实现以及索引实现问的很多)

吃透后这份pdf,你同样可以跟面试官侃侃而谈MySQL。其实像阿里p7岗位的需求也没那么难(但也不简单),扎实的Java基础+无短板知识面+对某几个开源技术有深度学习+阅读过源码+算法刷题,这一套下来p7岗差不多没什么问题,还是希望大家都能拿到高薪offer吧。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
可以添加V获取:vip1024b (备注Java)**
[外链图片转存中…(img-KwzdxJaA-1713560167590)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值