你是怎么理解JVM跨平台的?

我们说的跨平台,一般指的是跨操作系统,比如windows、linux、macos等,而如果我们用Java语言来开发程序,那么我们只需要写一份代码即可,但是最终运行的是Java代码编译之后的Java字节码,而JVM就是用来执行Java字节码的,不过需要注意的是,不同操作系统对应的JVM是不一样的,也就是我们在下载JDK的时候需要按对应的操作系统进行下载,所以相当于,windows上的jvm和linux上的jvm是不同的,但是它们都能执行同一份Java字节码,这样就使得,我们只需要写一份Java代码,就能在多个操作系统上运行,就是因为JVM帮我们屏蔽了不同操作系统的区别。

介绍一下JVM的整体结构

JVM中存在三个子系统,类加载子系统、运行时数据区、执行引擎。
类加载子系统会负责将字节码加载到JVM中,具体就是加载到运行时数据区的方法区中。
运行时数据区中又分为了方法区、堆区、虚拟机栈、程序计数器、本地方法栈,主要用来存储程序运行过程中所产生的对象。
执行引擎又包括解释器、即时编译器、垃圾回收器,主要用来解释执行字节码的,并且进行垃圾回收。

介绍一下JVM运行时数据区域各个区域的作用

方法区,是JVM规范中的定义,而HotSpot作为虚拟机的具体实现,把方法区成为永久代或元空间,JDK8之后叫元空间,方法区主要用来存储,某个类的信息(比如某个类的名字、父类的名字),某个方法的信息以及常量池。方法区是多个线程共享的区域。
程序计数器,每个线程单独一个程序计数器,用来记录当前线程所执行的字节码的某条指令地址,用来保证线程切换后能恢复到正确的位置
虚拟机栈,也是每个线程独有的,用来记录当前线程正在执行的哪个方法,每执行一个方法就会生成一个栈帧入栈,方法执行完之后对应的栈帧就会出栈
本地方法栈,和虚拟机栈类似,虚拟机栈用来管理Java方法的执行,本地方法栈用来记录native方法的执行
堆区,多个线程共享,主要用来存放对象的,堆区又可以分为新生代和老年代,新生代又可以分为Eden区和Survivor区,一个新对象创建出来后会先存放在Eden区,Eden区满了后就会触发Minor GC,存活的对象就会转移至Survivor区,默认情况下经过15次GC后,该对象还存活,就会从Survivor区转移到老年代

有哪些垃圾回收算法?

垃圾回收算法,准确的说应该分为标记阶段、清除阶段,标记阶段表示找到垃圾对象,有引用计数法和可达性分析法,引用计数法就是给每个对象保存一个引用计数器,用来记录对象被引用的情况,为0则表示是垃圾对象,可达性分析法,就是找到哪些对象是可达的,从而其他对象就是不可达的,也就是垃圾对象,需要利用GC Root来找到可达对象,一般的GC Roots有,栈中的本地变量、方法区中的静态变量、本地方法栈中的变量、正在运行的线程等。

找到垃圾对象后,就开始清除,第一种是标记-清除算法,这种算法简单但是效率低,就是线性的找到待回收区域中的垃圾对象,然后清除它们,会产生碎片,第二种就是复制算法,直接把存活对象复制到另外一个区域,性能比较快,不会产生碎片,但是需要两倍的内存空间,第三种就是标记-整理算法,先利用GC Root找到存活对象,然后将存活对象压缩到内存的一端,之后清理掉边界另一端的所有对象,这样不会产生碎片,并且也不需要额外的空间,相当于是标记-清除之后进行一次碎片整理,缺点是性能不高,是三种算法中最慢的,当然,不同的垃圾回收器,不同的区域会采用不同的算法。

什么JIT即时编译器,有什么作用?

在Java中,我们会先把Java代码编译成为字节码,然后有JVM开始运行后,会利用解释器来逐行解释执行字节码指令,注意这个过程是JVM在运行过程中,所以解释器相当于一个运行时翻译器,将字节码翻译为机器执行进行执行,但是在这个过程中可能会重复执行一些代码,而每次执行都要翻译一次,这就比较消耗资源了,所以就有了JIT即时编译器,它相当于会把字节码再编译一次,JIT会在运行时针对调用频繁的热点代码,直接将其编译为对应的机器指令,而以后再执行该热点代码时候,就不需要再翻译了,而能够直接执行机器指令。

在Java中,相当于解释器和JIT编译器同时在使用,一开始用解释器,这样程序能更快的启动,不需要等全部编译成机器指令后再启动,随着程序运行过程中,发现热点代码后,再利用JIT即时编译,这样又能提高执行效率,是一种折中,即考虑到启动速度,也考虑到执行速度。

什么是类加载的双亲委派机制?

在一个类加载器需要加载一个类时,会先将交给父类加载器来加载,而父类加载器如果还有父类加载器,则会进一步的进行委托,一般情况下,应用程序会使用AppClassLoader来进行类加载器,而AppClassLoader的父类加载器为ExtClassLoader,ExtClassLoader的父类加载器会BootClassLoader,这应该就是双亲的由来吧,通过双亲委派机制,可以方式Java核心API被篡改,比如String类中的某个方法,因为当AppClassLoader去加载String类时,会利用双亲委派机制,使得最终由BootClassLoader来加载String类,从而会加载不到程序员自己所定义的String类。