Java虚拟机(JVM)面试题备份2

  1. Java内存区域,JVM包含了两个子系统和两个组件,两个子系统为class loader(类装载)、Execution engine(执行引擎);两个组件为Runtime data area(运行时数据区)、Native interface(本地接口)

Java虚拟机(JVM)面试题备份2

  •   class locader(类装载):根据给定的全限定类名称(如:java.lang.Object)来装载class文件到Runtime data area中的methodim area.
  • Execute engine(执行引擎):执行class中的指令
  • Native interface(本地接口):与native libraries交互,是其它编程语言交互的接口。
  • Runtime data area(运行时数据区域):这就是我们常说的JVM内存
  • 作用:首先通过编译器把java代码转换成字节码,类加载器(class Loader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作系统中去执行,因此需要特定的命令解析器执行引擎(Execution engine),将字节码翻译成底层系统指令,再交由CPU去执行,而这个执行过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

   2.Java程序的运行机制过程:

  • 首先利用IDE继承开发工具编写Java源代码代码,源文件的后缀为.java;
  • 再利用编译器(javac命令)将源代码编译成字节码文件,字节码文件的后缀名为.class;
  • 运行字节码的工作由解释器(java命令)来完成的。
  • Java虚拟机(JVM)面试题备份2

从上图可以看出,java文件通过编译器变成了.class文件,接下来类加载器又将这些.class文件加载到JVM中。类加载器指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法内,然后再堆区创建一个java.lang.class对象,用来封装类在方法区内的数据结构。

3.Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干不同的数据区域。

  • 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成。
  • Java虚拟机栈(Java virtual Machine Stacks):用来存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务Java方法的,只不过虚拟机栈是服务Java的,而本地方法栈是为虚拟机调用Native方法服务的。
  • Java堆(Java Heap):Java虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配的内存。
  • 方法区(Method Area):用于存储已被虚拟机编译后的代码等数据。

4.深拷贝和浅拷贝

  • 浅拷贝(shallow copy)只是增加了一个指针指向已存在的内存地址。
  • 深拷贝(deepcopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存。
  • 使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。

5.堆、栈的区分?

  • 物理地址:堆的物理地址分配对对象是不连续的,因此性能慢点,在GC的时候也要考虑到不连续的分配,所以有各种算法,比如:标记-消除,复制,标记-压缩。堆存放的都是对象的实例和数组。因此该区更关注的数据的存储。
  • 栈的地址是连续的,并且是在编译的时候运行的。栈存放的局部变量,操作数栈,返回结果。该区更关注是程序方法的执行。
  • 静态变量存在方法区。静态的对象还是放在堆。
  • 程序可见度:堆对于整个应用程序都是共享的、可见的;栈只对于线程是可见的,所以也是线程私有,它的生命周期和线程不同。

6.HotSpot虚拟机对象探秘

  • 虚拟机遇到一个new指令时,先检查常量池是否已经加载相应的类,如果没有,必须先执行相应的类加载。类加载通过后,接下来分配内存。若Java堆中内存是绝对规则的。
  • 为对象分配内存,类加载完成后接着会在Java堆中划分一块内存分配给对象,内存分配根据Java堆是否是规整,有两种方式:指针碰撞,如果Java堆的内存是规整,即所有的用过的内存放在一边,而空闲的放在一边,分配内存时候将位于中间的指针指示器向空闲的内存移动一段与对象大小相等距离,这样便完成了内存分配的工作;空闲列表,如果Java堆的内存不是规整的,则需要由虚拟机维护一个列表来记录哪些内存是可用的,这样在分配的时候可以冲列表中查询到足够大的内存分配给对象,并在分配后更新列表记录。