Java程序员面试题(高级篇)(一)

1、ThreadLocal 特性

  • ThreadLocal存放的值是线程封闭,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递
  • 在Thread类中有一个Map,用于存储每一个线程的变量的副本。
  • 线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收
  • 对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式(ThreadLocal的缺点时占用了较多的空间。)

2、jvm内存区域划分

  • 运行时数据区包括:虚拟机栈区,堆区,方法区,本地方法栈,程序计数器
  • 虚拟机栈区 :也就是我们常说的栈区,线程私有,存放基本类型,对象的引用和 returnAddress ,在编译期间完成分配。
  • 堆区 , JAVA 堆,也称 GC 堆,所有线程共享,存放对象的实例和数组, JAVA 堆是垃圾收集器管理的主要区域。
  • 方法区 :所有线程共享,存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。这个区域的内存回收目标主要是针对常量池的对象的回收和对类型的卸载。
  • 程序计数器 :线程私有,每个线程都有自己独立的程序计数器,用来指示下一条指令的地址

3、类加载过程,classload机制

加载:将class文件字节流按照虚拟机所需的格式存储在元数据区
  • 通过类的全限定名获取定义此类的二进制字节流
  • 将这个字节流所有代表静态存储数据结构转化为元数据区(方法区)的运行时结构
  • 在内存中生成一个代表这个类的java.lang.Class对象,作为元数据区这个类的访问入口
验证:确保class文件字节流中信息符合虚拟机的要求,且不会危害虚拟机自身安全
  • 文件格式校验:验证字节流是否符合Class文件格式规范,并且能被当前版本的虚拟机处理
  • 元数据校验:对字节码进行语义分析,以保证描述的信息符合java语言规范
  • 字节码校验:对过数据流和控制流分析确定程序的语言是合法的符合逻辑的
  • 符号引用校验:校验在虚拟机中的符号引用转化为直接引用是否正常执行
准备:为类变量(仅包括被statis修饰的变量,不包括实例变量)分配内存并设置变量初始值。
解析:将常量池中符号引用替换为直接引用的过程。
初始化:执行类构造器<cinit>()方法,初始化实例对象。

使用:可以使用对象
卸载:对象内存内存回收

4、类加载器

  • 启动类加载器,c++实现,虚拟机自身的一部分,负载加载JAVA_HOME\lib目录中jar
  • 其他类加载器,java实现,虚拟机外部,全部继承抽线类java.lang.ClassLoader
  • 扩展类加载器,负载加载JAVA_HOME\lib\ext目录中jar
  • 应用程序类加载器,负载加载用户类路径classpath上所有指导的类库 * 自定义类加载器

5、垃圾回收回收算法

  • 两个最基本的java回收算法:复制算法和标记清理算法
  • 复制算法:两个区域A和B,初始对象在A,继续存活的对象被转移到B。此为新生代最常用的算法
  • 标记清理:一块区域,标记可达对象(可达性分析),然后回收不可达对象,会出现碎片,那么引出
  • 标记-整理算法:多了碎片整理,整理出更大的内存放更大的

6、垃圾回收集器

  • Serial New收集器是针对新生代的收集器,采用的是复制算法
  • Parallel New(并行)收集器,新生代采用复制算法,老年代采用标记整理
  • Parallel Scavenge(并行)收集器,针对新生代,采用复制收集算法
  • Serial Old(串行)收集器,新生代采用复制,老年代采用标记整理
  • Parallel Old(并行)收集器,针对老年代,标记整理
  • CMS收集器,基于标记清理
  • G1收集器:整体上是基于标记整理 ,局部采用复制

7、 Java中垃圾回收有什么目的?什么时候进行垃圾回收?

  • 垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。
  • 触发主GC(Garbage Collector,垃圾回收)的条件:

    • 当应用程序空闲时,即没有应用线程在运行时,GC会被调用。
    • Java堆内存不足时,GC会被调用。

8、进行垃圾回收几种类型?

  • 在新生代的Eden区满了,会触发新生代GC(Mimor GC);
  • 经过多次触发新生代GC存活下来的对象就会升级到老年代;
  • 升级到老年代的对象所需的内存大于老年代剩余的内存,则会触发老年代GC(Full GC);
  • 当程序调用System.gc()时也会触发Full GC;

9、线程的几种状态。

  • 在线程的生命周期中,它要经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。
  • 尤其是当线程启动以后,它不可能一直”霸占”着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。

参考资料:https://juejin.im/post/5b10e53b6fb9a01e5b10e9be

10、JVM优化策略

  • 给JVM设置-XX:+HeapDumpOnOutOfMemoryError参数,让JVM碰到OOM场景时输出dump信息。 说明:OOM的发生是有概率的,甚至有规律地相隔数月才出现一例,出现时的现场信息对查错非常有价值。
  • 在线上生产环境,JVM的Xms和Xmx设置一样大小的内存容量,避免在GC 后调整堆大小带来的压力。