• 讲到栈帧的时候不了解JVM指令的话也不会知道栈帧是怎么用的
  • jvm指令集200多条,常用的就那几条,只说常用的和一些难的

一道面试题

  1. 正确答案是8
  2. 假如变成++i答案是多少???
  1. public static void main(String[] args) {
  2. int i = 8;
  3. i = i++;
  4. //i = ++i;
  5. System.out.println(i);
  6. }

image.png
image.png

参考资料:JVMS

  • 如果觉得某些书或者某些博客说的有问题的话就去找oracle上的英文文档去看(权威

详细区域

PC(Program Counter)

  • 指令寄存器,存放指令位置
  • 虚拟机的运行类似于这样的循环
    • 从PC取值
    • 找指令
    • 找完指令之后执行
    • 执行完之后去取下一条
    • 什么时候结束,什么时候为止
  • 每一个java虚拟机线程都有他自己的PC寄存器—->记录下一个指令在哪

Heap堆内存

  • GC垃圾回收时的重点
  • 在所有线程之间共享

JVM stacks

  • 每一个线程对应一个栈
  • 每一个方法对应一个栈帧
  • JVM虚拟机里所管理的栈
  • 栈里面装的是一个一个的栈帧Frame
  • 重点
  • 每一个JVM线程都有一个私人的JVM stacks,这个栈和线程一起被创建出来
  • 栈里面装的是栈帧

method area

  • 方法区
  • 里面装的各种各样的class与常量池(runtime constant pool)内容
  • 有很多细节
  • 重点
  • 方法区是所有线程所共享的
  • 里面装的是每一个class里面的结构
  • 容易和永久区、元空间混淆
    • 方法区是抽象的概念,是一个jvm规范
    • 永久区和元空间是具体的jvm实现,是具体的方法区的实现,是不同版本的method area的实现
    • 在jdk1.8之前他是永久区,1.8之后就变成了元空间

Run-Time Constant Pool

  • 字节码文件中有一个常量池,在字节码文件运行的时候这个常量池就扔在Run-Time Constant Pool中
  • A run-time constant pool is a per-class or per-interface run-time representation of the constant pool table in a class file
  • 运行时常量池是每类或每个接口在运行时类文件中常量池表的表示


Native method stacks

  • 本地方法C/C++
  • java调用了jni方法,即调用了java虚拟机内部的某些用C/C++写的方法
  • 等同于java自身的栈,一般我们牵扯不到,没办法调优,没办法管理他,一般不去管理他

Direct Memory

  • java1.4之后增加的内容:直接内存
  • NIO的内容
  • 一般情况下,所有的内存都归JVM直接管理,为了增加IO效率,在1.4之后增加了直接内存
  • 从JVM内部可以访问操作系统管理的内存
  • 用户空间可以直接访问内核空间的一些内存—->这些内存一般是在写网络程序的时候,写IO的时候
  • 不归JVM虚拟机直接管,归操作系统管
  • 原来的IO中,要访问网络传过来的数据的时候:现在内核空间开辟一块内存,将传过来的数据放到内核空间中去,当jvm要用的时候直接将内核空间的这块内存拷贝到jvm空间中去,中间有拷贝的过程,来一个数据拷贝一份,这样效率比较低
  • jdk1.4之后添加了NIO,可以直接使用内核空间的内存了,省了拷贝的过程了,直接就可以访问内核空间里的网络上传过来的这块缓存区的内容了
  • JSU Copy零拷贝(zero copy—->epoll和poll)的意思就是增加了Direct Memory,可以直接去访问内核空间的那块内存

线程共享区域

image.png

  • 线程不能共用一个PC,因为线程会进行切换,假如没有自己的PC,那么切回去的时候之前线程执行到的位置会丢失,就不能再继续往下执行了

几点

  1. 非静态方法中的局部变量表中有this,而静态没有
  2. 并且也是没有父类的super,因为调用父类方法,直接通过一个连接,直接就指向父类的常量池里面去了(所以没有super)
  3. 面试最好的方法是那台电脑给个任务让你干活,在规定时间内能干就来,不能干就走
  4. invokevirtual是自带多态的,调用invokevirtual的时候要从栈中弹出对象,弹出什么对象,就调用什么对象的方法
  5. invokestatic,java内部实现复杂,不同方法可以进行不同的优化;对于二进制码这个级别,调静态方法必须使用这条指令,这条指令的优化方式是虚拟机的事
  6. invokespecial是用于那些可以直接定位,不需要多态的方法,比如构造方法、private修饰的方法(final方法不是invokespecial的,而是invokevirtual的)
  7. invokeinterface表示多态情况下,用接口类型调用方法!!!
  8. 最难的指令)invokedynamic(1.7之前是没有的,1.7才刚加进来)—-> lambda表达式或者反射或者其他动态语言:scala、kotlin……或者CGLIB或者ASM或者动态产生的class会用到的指令 的时候会有这条指令,因为java支持动态语言了—->意味着可以动态产生很多class,在运行时产生的class,而不是之前写好的并且编译好的,运行时才产生了很多class
    1. 后面跟两个参数:bootstrapmethod(记录了一系列的方法:可能是动态产生的类的方法)和方法的签名
  9. 函数式接口,该接口中只有一个方法,所以任何一个具体的方法都可以认为是这个接口的一个对象
  10. 将函数的具体实现赋值给接口类型,其实n就是一个函数指针(函数指针的概念),这个函数指针指向了这个函数—->java的内部实现是内部产生了一个class并且产生了一个class的对象
  11. 用lambda表达式的时候会产生一个lambda的内部类,然后还有一个内部类—->内部类的内部类(每有一个lambda表达式就会有**内部类)—->lambda表达式的语法的问题(内部类的匿名内部类**)
  12. jvm没有纯粹的函数
  13. 接口不加static也可以,老师刚开始写的有问题(**因为是函数式接口**)
  14. 每次产生一个新的内部类
  15. 在循环中用lambda表达式或者反射的情况下,就会产生很多class,会放到methodArea(1.8之前的永久代有一个巨大的bug—->**FGC不回收???**)(1.8之后的metaSpace会触发FGC会自动清理,并且字符串常量池位于堆中了)
  16. 小于1.8时会产生OOM,大于1.8时会触发清理,清理不掉也会产生OOM!!!