1. JVM 的主要组成部分及其作用?
组成部分:
- Class loader(类装载)子系统:根据给定的全限定名类名来装载class文件到Runtime data area(运行时数据区域)中的method area(方法区)。
- Execution engine(执行引擎)子系统:执行classes中的指令。
- Native Interface(本地接口)组件:与native libraries(本地方法库)交互,是其它编程语言交互的接口。
- Runtime data area(运行时数据区域)组件:JVM的内存。
作用:
- 编译器把 Java 代码转换成字节码(.class)。
- 类加载器将字节码加载到内存中,将其放在内存的方法区中。
- 命令解析器执行引擎,将字节码翻译成底层系统指令,交由 CPU 去执行。
- 整个过程中调用其他语言的本地库接口来实现整个程序的功能。
2. 堆和栈的区别?
- 物理地址:堆的物理地址分配对对象是不连续,性能慢。栈的物理地址分配是连续的,采用先进后出原则,性能快。
- 内存大小:堆的内存大小不固定,在运行期确定,一般远大于栈。栈的内存大小是固定的,在编译期确定。
- 存放内容:堆存放对象的实例和数组。栈存放局部变量,操作数栈,返回结果。
- 可见性:堆对于整个应用程序都是共享、可见的。栈是线程私有的,仅对线程可见,且生命周期与线程一致。
3. 关于java内存泄漏?
- 内存泄漏是指不再被使用的对象或者变量一直被占据在内存中。
- 原因:长生命周期的对象持有短生命周期对象的引用,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收。
4. JVM 有哪些垃圾回收算法?
- 标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
- 标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
- 复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
- 分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
5. 新生代垃圾回收器和老年代垃圾回收器都有哪些?有什么区别?
- 新生代回收器:Serial、ParNew、Parallel Scavenge
- 老年代回收器:Serial Old、Parallel Old、CMS
- 整堆回收器:G1
- 区别:新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
特别的:Minor GC 是指发生在新生代的 GC,Major GC/Full GC 是指发生在老年代的 GC
6.对象的内存分配规则是什么?
- 对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。
- 大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
- 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,直到达到阈值,对象进入老年区。
- 动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。
- 空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。
7.什么是类的加载?
- 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
8. 什么是类加载器,类加载器有哪些?
- 实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。
- 启动类加载器(Bootstrap ClassLoader):用来加载java核心类库,无法被java程序直接引用。
- 扩展类加载器(extensions class loader):用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
- 系统类加载器(system class loader):根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取。
- 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。
9. 类装载的执行过程?
- 加载:根据查找路径找到相应的 class 文件然后导入。
- 验证:检查加载的 class 文件的正确性。
- 准备:给类中的静态变量分配内存空间。
- 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址。
- 初始化:对静态变量和静态代码块执行初始化工作。
10. 什么是双亲委派模型?
- 如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。