JVM面试常问的知识点总结

我是方圆,愿我们生活快乐呀

1. 谈谈你对Java的理解?

Java是一门面向对象的编程语言,它摒弃了C++中,多继承和指针的概念,所以Java比较简单易用,同样也功能强大,它的可移植性也是一个很强大的特点。

2. 编译过程

在这里插入图片描述

  • Java源码首先被编译成字节码文件(.java文件javac后成.class文件),再由不同平台的JVM解析
  • Java在不同的平台上运行时,不需要进行重新编译
  • Java虚拟机在执行字节码的时候,把字节码转换成具体系统上的机器指令

3. 为什么要先编译成字节码再解析成机器码?

因为编译成字节码文件(.class),放在其他的平台上,不需要在进行重新编译和校验的过程,直接跑就行。

4. 写一个反射的例子

创建一个简单的类,有一个私有字段,有参构造,公共方法,私有方法

package com.wyl.test;

public class Student {

    private String name;

    public Student(String name) {
        this.name = name;
    }

    public void sayName(){
        System.out.println("My name is " + name);
    }

    private void sayName1(){
        System.out.println("我的名字是" + name);
    }
}

简单的写写反射

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionDemo {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        //通过反射获取Student的Class对象
        Class<?> aClass = Class.forName("com.wyl.test.Student");

        //创建一个对象
        Constructor<?> constructor = aClass.getConstructor(String.class);
        Student  xiaowang = (Student) constructor.newInstance("xiaowang");

        //执行私有方法
        //getDeclaredMethod 能获取到的方法除了继承和实现的方法都行
        Method sayName1 = aClass.getDeclaredMethod("sayName1");
        sayName1.setAccessible(true);
        sayName1.invoke(xiaowang);

        //执行公共方法
        //getMethod 能获取到public的方法和继承、实现的方法
        Method sayName = aClass.getMethod("sayName");
        sayName.invoke(xiaowang);

        //修改私有字段
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(xiaowang,"dawang");
        sayName1.invoke(xiaowang);
    }
}

5. 类的加载方式

在这里插入图片描述

  • Class.forName() 得到的class是已经完成初始化
  • ClassLoader.loadClass() 得到的class是没有链接的
    (在Spring IOC中用Classpath加载就是用的loadClass,延迟加载,加快初始化速度,等到用的时候再进行赋值)

6. 类的装载过程

在这里插入图片描述

7. Java内存模型(JDK8)

在这里插入图片描述

8. Stack Overflow和Out Of Memory(OOM)

  1. Stack Overflow:栈溢出问题

  2. Out Of Memory(OOM):翻译为中文就是内存用完了。官方的说明:当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error

8.1 出现OOM的集中情况


  1. Java 堆是Java虚拟机所管理的内存中最大的一块,被所有线程共享的内存区域 ,用于存放对象实例,几乎所有的对象实例都在这里分配内存。这样分析来看,如果在堆中没有内存完成实例分配,并且也无法再扩展时,将会抛出OutOfMemoryError异常。
  2. 方法区
    和Java堆一样,方法区是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。而运行时常量池属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用。当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

9. 为什么要剔除永久代?

  • 一方面是节省空间,避免了常见的永久内存错误:java.lang.OutOfMemoryError: PermGen问题(JDK1.8不会再产生这个问题)。
  • 另一方面是为了整合JRockit,因为JRockit没有永久代这样类似的空间。
    其实,从jdk7开始,就开始了永久代的转移工作,将譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;等。但是指导JDK8永久代才被元空间替代。

10. 元空间与永久代的区别

元空间使用的是本地内存,并且将常量池移动到了中;永久代使用的是JVM内存
Java JDK1.8中类常量池、运行时常量池、字符串常量池所处区域

11. 元空间相比于永久代的优势

  • 字符串常量池存在于永久代中,容易出现性能问题和内存溢出
  • 类和方法的信息难以确定,给永久代的大小指定带来困难
  • 永久代会为GC带来不必要的复杂性

12. JVM调优参数

  • -Xss:规定每个线程的虚拟机栈的大小,一般256k足矣
  • -Xms:堆的初始大小
  • -Xmx:堆的最大大小(在初始大小用完的情况,会自动扩容,但是一般会设置初始值和最大值相等,来避免扩容产生的程序“抖动”)

13. 堆和栈的区别

  • 管理方式:堆要GC;栈自己释放
  • 空间大小:堆比栈大
  • 碎片相关:堆产生的碎片远大于栈
  • 分配方式:栈支持静态和动态分配,而堆仅支持动态分配
  • 效率:栈的效率高于堆

14. intern()方法在JDK1.6和JDK1.6+的区别

String str = new String("2020");
str.intern();
  • JDK1.6:当调用intern()方法时,如果常量池先前已创建出该字符串对象,则返回池中的该字符串的引用;否则,将此字符串对象添加到字符串常量池中,并且返回该字符串的对象引用
  • JDK1.6+:在常量池中有该对象时,情况一致;否则如果该字符串对象已经存在与堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用,若堆中不存在,则在池中创建该字符串并返回其引用。

在这里插入图片描述

参考文献

Java OOM问题

原创文章 56 获赞 19 访问量 6012

猜你喜欢

转载自blog.csdn.net/qq_46225886/article/details/106104431
今日推荐